Update to UE5.5, sadly didn't fix hand movement after attachment
Took 5h to find out Engine was read only
This commit is contained in:
		
							parent
							
								
									55d567e436
								
							
						
					
					
						commit
						f086787883
					
				
					 40 changed files with 1044 additions and 564 deletions
				
			
		| 
						 | 
				
			
			@ -2,9 +2,11 @@
 | 
			
		|||
  "version": "1.0",
 | 
			
		||||
  "components": [
 | 
			
		||||
    "Microsoft.Net.Component.4.6.2.TargetingPack",
 | 
			
		||||
    "Microsoft.VisualStudio.Component.Unreal.Workspace",
 | 
			
		||||
    "Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL",
 | 
			
		||||
    "Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64",
 | 
			
		||||
    "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
 | 
			
		||||
    "Microsoft.VisualStudio.Component.Windows10SDK.22621",
 | 
			
		||||
    "Microsoft.VisualStudio.Component.Windows11SDK.22621",
 | 
			
		||||
    "Microsoft.VisualStudio.Workload.CoreEditor",
 | 
			
		||||
    "Microsoft.VisualStudio.Workload.ManagedDesktop",
 | 
			
		||||
    "Microsoft.VisualStudio.Workload.NativeDesktop",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap
									 (Stored with Git LFS)
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap
									 (Stored with Git LFS)
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
Subproject commit 006e2b4922eb4f7af0d6df1468a042a98eecf68f
 | 
			
		||||
Subproject commit 968fe10c9f08859c64ea88768d5216140fc340d9
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
	"FileVersion": 3,
 | 
			
		||||
	"Version": 5.4,
 | 
			
		||||
	"VersionName": "5.4",
 | 
			
		||||
	"Version": 5.5,
 | 
			
		||||
	"VersionName": "5.5",
 | 
			
		||||
	"FriendlyName": "OpenXRExpansionPlugin",
 | 
			
		||||
	"Description": "An set of utility functions for OpenXR",
 | 
			
		||||
	"Category": "Virtual Reality",
 | 
			
		||||
| 
						 | 
				
			
			@ -39,10 +39,10 @@
 | 
			
		|||
			"Name": "OpenXR",
 | 
			
		||||
			"Enabled": true,
 | 
			
		||||
			"PlatformAllowList": [
 | 
			
		||||
			"Win64",
 | 
			
		||||
			"Linux",
 | 
			
		||||
			"Android"
 | 
			
		||||
     ]
 | 
			
		||||
				"Win64",
 | 
			
		||||
				"Linux",
 | 
			
		||||
				"Android"
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"Name": "XRBase",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
#include "CoreMinimal.h"
 | 
			
		||||
#include "UObject/ObjectMacros.h"
 | 
			
		||||
#include "Kismet/BlueprintFunctionLibrary.h"
 | 
			
		||||
#include "Animation/BoneReference.h"
 | 
			
		||||
#include "UObject/Object.h"
 | 
			
		||||
#include "Engine/EngineTypes.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -189,16 +189,16 @@ public:
 | 
			
		|||
	// Need this as I can't think of another way for an actor component to make sure it isn't on the server
 | 
			
		||||
	inline bool IsLocallyControlled() const
 | 
			
		||||
	{
 | 
			
		||||
#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22)
 | 
			
		||||
//#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22)
 | 
			
		||||
		const AActor* MyOwner = GetOwner();
 | 
			
		||||
		return MyOwner->HasLocalNetOwner();
 | 
			
		||||
#else
 | 
			
		||||
//#else
 | 
			
		||||
		// I like epics new authority check more than mine
 | 
			
		||||
		const AActor* MyOwner = GetOwner();
 | 
			
		||||
	/*	const AActor* MyOwner = GetOwner();
 | 
			
		||||
		const APawn* MyPawn = Cast<APawn>(MyOwner);
 | 
			
		||||
 | 
			
		||||
		return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);
 | 
			
		||||
#endif
 | 
			
		||||
		return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);*/
 | 
			
		||||
//#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Using tick and not timers because skeletal components tick anyway, kind of a waste to make another tick by adding a timer over that
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7214,10 +7214,10 @@ void UGripMotionControllerComponent::ApplyTrackingParameters(FVector& OriginalPo
 | 
			
		|||
				if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, curRot, curLoc))
 | 
			
		||||
				{
 | 
			
		||||
 | 
			
		||||
					if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera)
 | 
			
		||||
					/*if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera)
 | 
			
		||||
					{
 | 
			
		||||
						AttachChar->VRReplicatedCamera->ApplyTrackingParameters(curLoc, true);
 | 
			
		||||
					}
 | 
			
		||||
					}*/
 | 
			
		||||
 | 
			
		||||
					//curLoc.Z = 0;
 | 
			
		||||
					LastLocationForLateUpdate = curLoc;
 | 
			
		||||
| 
						 | 
				
			
			@ -7234,7 +7234,7 @@ void UGripMotionControllerComponent::ApplyTrackingParameters(FVector& OriginalPo
 | 
			
		|||
				if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera)
 | 
			
		||||
				{
 | 
			
		||||
					// Sample camera location instead
 | 
			
		||||
					LastLocationForLateUpdate = AttachChar->VRReplicatedCamera->GetRelativeLocation();
 | 
			
		||||
					LastLocationForLateUpdate = AttachChar->VRReplicatedCamera->ReplicatedCameraTransform.Position; //GetRelativeLocation();
 | 
			
		||||
 | 
			
		||||
					if (!AttachChar->bRetainRoomscale && IsLocallyControlled())
 | 
			
		||||
					{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ AGrippableActor::AGrippableActor(const FObjectInitializer& ObjectInitializer)
 | 
			
		|||
 | 
			
		||||
	// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration
 | 
			
		||||
	// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
 | 
			
		||||
	MinNetUpdateFrequency = 30.0f;
 | 
			
		||||
	SetMinNetUpdateFrequency(30.0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AGrippableActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
 | 
			
		||||
| 
						 | 
				
			
			@ -152,13 +152,23 @@ void AGrippableActor::GatherCurrentMovement()
 | 
			
		|||
			bool bFoundInCache = false;
 | 
			
		||||
 | 
			
		||||
			UWorld* World = GetWorld();
 | 
			
		||||
 | 
			
		||||
			const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
 | 
			
		||||
			int ServerFrame = 0;
 | 
			
		||||
			if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
 | 
			
		||||
 | 
			
		||||
			if (bShouldUsePhysicsReplicationCache)
 | 
			
		||||
			{
 | 
			
		||||
				if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
 | 
			
		||||
				if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
 | 
			
		||||
				{
 | 
			
		||||
					RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
 | 
			
		||||
					bFoundInCache = true;
 | 
			
		||||
					if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
 | 
			
		||||
					{
 | 
			
		||||
						if (RepMovement.ServerFrame != ServerFrame)
 | 
			
		||||
						{
 | 
			
		||||
							RepMovement.FillFrom(*FoundState, this, ServerFrame);
 | 
			
		||||
							bWasRepMovementModified = true;
 | 
			
		||||
						}
 | 
			
		||||
						bFoundInCache = true;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,9 +52,16 @@ namespace PhysicsReplicationCVars
 | 
			
		|||
 | 
			
		||||
	int32 EnableDefaultReplication = 0;
 | 
			
		||||
 | 
			
		||||
	namespace DefaultReplicationCVars
 | 
			
		||||
	{
 | 
			
		||||
		bool bHardsnapLegacyInPT = false;
 | 
			
		||||
		bool bCorrectConnectedBodies = false;
 | 
			
		||||
		bool bCorrectConnectedBodiesFriction = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace ResimulationCVars
 | 
			
		||||
	{
 | 
			
		||||
		bool bRuntimeCorrectionEnabled = true;
 | 
			
		||||
		bool bRuntimeCorrectionEnabled = false;
 | 
			
		||||
		bool bRuntimeVelocityCorrection = false;
 | 
			
		||||
		bool bRuntimeCorrectConnectedBodies = true;
 | 
			
		||||
		float PosStabilityMultiplier = 0.5f;
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +69,18 @@ namespace PhysicsReplicationCVars
 | 
			
		|||
		float VelStabilityMultiplier = 0.5f;
 | 
			
		||||
		float AngVelStabilityMultiplier = 0.5f;
 | 
			
		||||
		bool bDrawDebug = false;
 | 
			
		||||
 | 
			
		||||
		// Inside of NetworkPhysicsComponent - UPDATE AS CHANGE
 | 
			
		||||
		int32 RedundantInputs = 2;
 | 
			
		||||
		int32 RedundantStates = 0;
 | 
			
		||||
		bool bAllowRewindToClosestState = true;
 | 
			
		||||
		bool bCompareStateToTriggerRewind = false;
 | 
			
		||||
		bool bCompareInputToTriggerRewind = false;
 | 
			
		||||
		bool bEnableUnreliableFlow = true;
 | 
			
		||||
		bool bEnableReliableFlow = false;
 | 
			
		||||
		bool bApplyDataInsteadOfMergeData = false;
 | 
			
		||||
		bool bAllowInputExtrapolation = true;
 | 
			
		||||
		bool bValidateDataOnGameThread = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace PredictiveInterpolationCVars
 | 
			
		||||
| 
						 | 
				
			
			@ -92,15 +111,24 @@ namespace PhysicsReplicationCVars
 | 
			
		|||
		bool bSkipVelocityRepOnPosEarlyOut = true;
 | 
			
		||||
		bool bPostResimWaitForUpdate = false;
 | 
			
		||||
		bool bVelocityBased = true;
 | 
			
		||||
		bool bPosCorrectionAsVelocity = false;
 | 
			
		||||
 | 
			
		||||
		// New or re-named 5.5
 | 
			
		||||
		bool bCorrectionAsVelocity = false;
 | 
			
		||||
		bool bCorrectConnectedBodies = false;
 | 
			
		||||
		bool bCorrectConnectedBodiesFriction = true;
 | 
			
		||||
		bool bSleepConnectedBodies = true;
 | 
			
		||||
		bool bKinematicPrediction = true;
 | 
			
		||||
		bool bKinematicHardSnap = false;
 | 
			
		||||
 | 
			
		||||
		bool bDisableSoftSnap = false;
 | 
			
		||||
		bool bAlwaysHardSnap = false;
 | 
			
		||||
		bool bSkipReplication = false;
 | 
			
		||||
		bool bDontClearTarget = false;
 | 
			
		||||
		bool bDrawDebugTargets = false;
 | 
			
		||||
		bool bDrawDebugVectors = false;
 | 
			
		||||
		float DrawDebugZOffset = 50.0f;
 | 
			
		||||
		float SleepSecondsClearTarget = 15.0f;
 | 
			
		||||
		int32 TargetTickAlignmentClampMultiplier = 1;
 | 
			
		||||
		int32 TargetTickAlignmentClampMultiplier = 2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -482,7 +510,25 @@ bool FPhysicsReplicationVR::ApplyRigidBodyState(float DeltaSeconds, FBodyInstanc
 | 
			
		|||
	const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrection.AngularVelocityCoefficient;
 | 
			
		||||
	
 | 
			
		||||
	static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance"));
 | 
			
		||||
	const float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrection.MaxLinearHardSnapDistance;
 | 
			
		||||
	float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrection.MaxLinearHardSnapDistance;
 | 
			
		||||
 | 
			
		||||
	static const auto CVarHardsnapLegacyInPT = IConsoleManager::Get().FindConsoleVariable(TEXT("p.DefaultReplication.Legacy.HardsnapInPT"));
 | 
			
		||||
	bool bHardsnapLegacyInPT = CVarHardsnapLegacyInPT->GetBool();
 | 
			
		||||
 | 
			
		||||
	static const auto CVarCorrectConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("p.DefaultReplication.CorrectConnectedBodies"));
 | 
			
		||||
	bool bCorrectConnectedBodies = CVarCorrectConnectedBodies->GetBool();
 | 
			
		||||
 | 
			
		||||
	static const auto CVarCorrectConnectedBodiesFriction = IConsoleManager::Get().FindConsoleVariable(TEXT("p.DefaultReplication.CorrectConnectedBodiesFriction"));
 | 
			
		||||
	bool bCorrectConnectedBodiesFriction = CVarCorrectConnectedBodiesFriction->GetBool();
 | 
			
		||||
 | 
			
		||||
	// Assign per-actor settings from NetworkPhysicSettingsComponent if this actor has one
 | 
			
		||||
	if (SettingsCurrent.Get())
 | 
			
		||||
	{
 | 
			
		||||
		MaxLinearHardSnapDistance = SettingsCurrent.Get()->DefaultReplicationSettings.GetMaxLinearHardSnapDistance(MaxLinearHardSnapDistance);
 | 
			
		||||
		bHardsnapLegacyInPT = SettingsCurrent.Get()->DefaultReplicationSettings.GetHardsnapDefaultLegacyInPT();
 | 
			
		||||
		bCorrectConnectedBodies = SettingsCurrent.Get()->DefaultReplicationSettings.GetCorrectConnectedBodies();
 | 
			
		||||
		bCorrectConnectedBodiesFriction = SettingsCurrent.Get()->DefaultReplicationSettings.GetCorrectConnectedBodiesFriction();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get Current state
 | 
			
		||||
	FRigidBodyState CurrentState;
 | 
			
		||||
| 
						 | 
				
			
			@ -606,7 +652,42 @@ bool FPhysicsReplicationVR::ApplyRigidBodyState(float DeltaSeconds, FBodyInstanc
 | 
			
		|||
			// Too much error so just snap state here and be done with it
 | 
			
		||||
			PhysicsTarget.AccumulatedErrorSeconds = 0.0f;
 | 
			
		||||
			bRestoredState = true;
 | 
			
		||||
			BI->SetBodyTransform(IdealWorldTM, ETeleportType::ResetPhysics, bAutoWake);
 | 
			
		||||
			// Hardsnap in physics thread
 | 
			
		||||
			bool bPTHardSnapSuccess = false;
 | 
			
		||||
 | 
			
		||||
			if (PhysicsReplicationAsyncVR != nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				if (bHardsnapLegacyInPT)
 | 
			
		||||
				{
 | 
			
		||||
					if (Chaos::FSingleParticlePhysicsProxy* Proxy = static_cast<Chaos::FSingleParticlePhysicsProxy*>(BI->GetPhysicsActorHandle()))
 | 
			
		||||
					{
 | 
			
		||||
						if (Chaos::FPBDRigidsSolver* Solver = Proxy->GetSolver<Chaos::FPBDRigidsSolver>())
 | 
			
		||||
						{
 | 
			
		||||
							Solver->EnqueueCommandImmediate([Solver, Proxy, IdealWorldTM, NewState, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction]()
 | 
			
		||||
								{
 | 
			
		||||
									Chaos::FRigidBodyHandle_Internal* Handle = Proxy->GetPhysicsThreadAPI();
 | 
			
		||||
 | 
			
		||||
									// Set XRVW to hard snap dynamic object and force recalculation of friction
 | 
			
		||||
									Solver->GetEvolution()->ApplyParticleTransformCorrection(Proxy->GetHandle_LowLevel(), IdealWorldTM.GetLocation(), IdealWorldTM.GetRotation(), bCorrectConnectedBodies, bCorrectConnectedBodiesFriction);
 | 
			
		||||
 | 
			
		||||
									Handle->SetV(NewState.LinVel);
 | 
			
		||||
									Handle->SetW(FMath::DegreesToRadians(NewState.AngVel));
 | 
			
		||||
								});
 | 
			
		||||
 | 
			
		||||
							bPTHardSnapSuccess = true;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!bPTHardSnapSuccess)
 | 
			
		||||
			{
 | 
			
		||||
				BI->SetBodyTransform(IdealWorldTM, ETeleportType::ResetPhysics, bAutoWake);
 | 
			
		||||
 | 
			
		||||
				// Set the new velocities
 | 
			
		||||
				BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake);
 | 
			
		||||
				BI->SetAngularVelocityInRadians(FMath::DegreesToRadians(NewState.AngVel), false, bAutoWake);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Set the new velocities
 | 
			
		||||
			BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake);
 | 
			
		||||
| 
						 | 
				
			
			@ -714,7 +795,8 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
	int32 LocalFrameOffset = 0; // LocalFrame = ServerFrame + LocalFrameOffset;
 | 
			
		||||
	if (FPhysicsSolverBase::IsNetworkPhysicsPredictionEnabled())
 | 
			
		||||
	bool LocalFrameOffsetAssigned = false;
 | 
			
		||||
	if (UPhysicsSettings::Get()->PhysicsPrediction.bEnablePhysicsPrediction)
 | 
			
		||||
	{
 | 
			
		||||
		if (UWorld* World = GetOwningWorld())
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -722,6 +804,7 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
 | 
			
		|||
			{
 | 
			
		||||
				if (APlayerController* PlayerController = World->GetFirstPlayerController())
 | 
			
		||||
				{
 | 
			
		||||
					LocalFrameOffsetAssigned = PlayerController->GetNetworkPhysicsTickOffsetAssigned();
 | 
			
		||||
					LocalFrameOffset = PlayerController->GetNetworkPhysicsTickOffset();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -753,6 +836,9 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
 | 
			
		|||
					{
 | 
			
		||||
						// Removed as this is server sided
 | 
			
		||||
						/*
 | 
			
		||||
						// Update actor replication settings overrides
 | 
			
		||||
						SettingsCurrent = UNetworkPhysicsSettingsComponent::GetSettingsForActor(OwningActor);
 | 
			
		||||
 | 
			
		||||
						const ENetRole OwnerRole = OwningActor->GetLocalRole();
 | 
			
		||||
						const bool bIsSimulated = OwnerRole == ROLE_SimulatedProxy;
 | 
			
		||||
						const bool bIsReplicatedAutonomous = OwnerRole == ROLE_AutonomousProxy && PrimComp->bReplicatePhysicsToAutonomousProxy;
 | 
			
		||||
| 
						 | 
				
			
			@ -821,9 +907,14 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
 | 
			
		|||
 | 
			
		||||
		AsyncInputData.RepMode = PhysicsTarget.ReplicationMode;
 | 
			
		||||
		AsyncInputData.ServerFrame = PhysicsTarget.ServerFrame;
 | 
			
		||||
		AsyncInputData.FrameOffset = LocalFrameOffset;
 | 
			
		||||
 | 
			
		||||
		AsyncInputData.LatencyOneWay = PingSecondsOneWay;
 | 
			
		||||
 | 
			
		||||
		if (LocalFrameOffsetAssigned)
 | 
			
		||||
		{
 | 
			
		||||
			AsyncInputData.FrameOffset = LocalFrameOffset;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		AsyncInputVR->InputData.Add(AsyncInputData);
 | 
			
		||||
	}
 | 
			
		||||
	ReplicatedTargetsQueueVR.Reset();
 | 
			
		||||
| 
						 | 
				
			
			@ -919,7 +1010,9 @@ bool FRepMovementVR::GatherActorsMovement(AActor* OwningActor)
 | 
			
		|||
 | 
			
		||||
void FPhysicsReplicationAsyncVR::OnPhysicsObjectUnregistered_Internal(Chaos::FConstPhysicsObjectHandle PhysicsObject)
 | 
			
		||||
{
 | 
			
		||||
	ObjectToTarget.Remove(PhysicsObject);
 | 
			
		||||
	RemoveObjectFromReplication(PhysicsObject);
 | 
			
		||||
 | 
			
		||||
	// Only clear Settings when PhysicsObject unregister (not when it stops replicating, hence why it's not baked into RemoveObjectFromReplication())
 | 
			
		||||
	ObjectToSettings.Remove(PhysicsObject);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -990,18 +1083,18 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal()
 | 
			
		|||
		{
 | 
			
		||||
			if (Input.TargetState.Flags == ERigidBodyFlags::None)
 | 
			
		||||
			{
 | 
			
		||||
				// Remove replication target 
 | 
			
		||||
				ObjectToTarget.Remove(Input.PhysicsObject);
 | 
			
		||||
				// Remove replication target
 | 
			
		||||
				RemoveObjectFromReplication(Input.PhysicsObject);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!bRewindDataExist && Input.RepMode == EPhysicsReplicationMode::Resimulation)
 | 
			
		||||
			{
 | 
			
		||||
				// We don't have rewind data but an actor is set to replicate using resimulation; we need to enable rewind capture.
 | 
			
		||||
				if (ensure(Chaos::FPBDRigidsSolver::IsNetworkPhysicsPredictionEnabled()))
 | 
			
		||||
				if (ensure(Chaos::FPBDRigidsSolver::IsNetworkPhysicsPredictionEnabled() && RigidsSolver->IsUsingFixedDt()))
 | 
			
		||||
				{
 | 
			
		||||
					const int32 NumFrames = FMath::Max<int32>(1, Chaos::FPBDRigidsSolver::GetPhysicsHistoryCount());
 | 
			
		||||
					RigidsSolver->EnableRewindCapture(NumFrames, true);
 | 
			
		||||
 | 
			
		||||
					RigidsSolver->EnableRewindCapture();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1018,6 +1111,41 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal()
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FReplicatedPhysicsTargetAsync* FPhysicsReplicationAsyncVR::AddObjectToReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject)
 | 
			
		||||
{
 | 
			
		||||
	if (ensure(PhysicsObject))
 | 
			
		||||
	{
 | 
			
		||||
		// Cache ParticleID in array of replicated objects
 | 
			
		||||
		Chaos::FReadPhysicsObjectInterface_Internal Interface = Chaos::FPhysicsObjectInternalInterface::GetRead();
 | 
			
		||||
		if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(PhysicsObject))
 | 
			
		||||
		{
 | 
			
		||||
			ReplicatedParticleIDs.Add(Handle->ParticleID());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Add to Object-Target map
 | 
			
		||||
		return &ObjectToTarget.Add(PhysicsObject, FReplicatedPhysicsTargetAsync());
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FPhysicsReplicationAsyncVR::RemoveObjectFromReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject)
 | 
			
		||||
{
 | 
			
		||||
	if (PhysicsObject == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove from Object-Target map
 | 
			
		||||
	ObjectToTarget.Remove(PhysicsObject);
 | 
			
		||||
 | 
			
		||||
	// Remove cached replicated ParticleID
 | 
			
		||||
	Chaos::FReadPhysicsObjectInterface_Internal Interface = Chaos::FPhysicsObjectInternalInterface::GetRead();
 | 
			
		||||
	if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(PhysicsObject))
 | 
			
		||||
	{
 | 
			
		||||
		ReplicatedParticleIDs.Remove(Handle->ParticleID());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input)
 | 
			
		||||
{
 | 
			
		||||
	if (Input.PhysicsObject == nullptr)
 | 
			
		||||
| 
						 | 
				
			
			@ -1025,6 +1153,12 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If there is no FrameOffset set then we have not synced up physics ticks with the server yet so don't cache this data
 | 
			
		||||
	if (Input.FrameOffset.IsSet() == false)
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Chaos::FPBDRigidsSolver* RigidsSolver = static_cast<Chaos::FPBDRigidsSolver*>(GetSolver());
 | 
			
		||||
	if (RigidsSolver == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -1041,10 +1175,10 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
 | 
			
		|||
	if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(Input.PhysicsObject))
 | 
			
		||||
	{
 | 
			
		||||
		// Cache all target states inside RewindData
 | 
			
		||||
		const int32 LocalFrame = Input.ServerFrame - Input.FrameOffset;
 | 
			
		||||
		const int32 LocalFrame = Input.ServerFrame - *Input.FrameOffset;
 | 
			
		||||
		RewindData->SetTargetStateAtFrame(*Handle, LocalFrame, Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData,
 | 
			
		||||
			Input.TargetState.Position, Input.TargetState.Quaternion,
 | 
			
		||||
			Input.TargetState.LinVel, Input.TargetState.AngVel, (Input.TargetState.Flags & ERigidBodyFlags::Sleeping));
 | 
			
		||||
			Input.TargetState.LinVel, FMath::DegreesToRadians(Input.TargetState.AngVel), (Input.TargetState.Flags & ERigidBodyFlags::Sleeping));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1060,7 +1194,7 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
 | 
			
		|||
	if (bFirstTarget)
 | 
			
		||||
	{
 | 
			
		||||
		// First time we add a target, set previous state to current input
 | 
			
		||||
		Target = &ObjectToTarget.Add(Input.PhysicsObject, FReplicatedPhysicsTargetAsync());
 | 
			
		||||
		Target = AddObjectToReplication(Input.PhysicsObject);
 | 
			
		||||
		Target->PrevPos = Input.TargetState.Position;
 | 
			
		||||
		Target->PrevPosTarget = Input.TargetState.Position;
 | 
			
		||||
		Target->PrevRotTarget = Input.TargetState.Quaternion;
 | 
			
		||||
| 
						 | 
				
			
			@ -1107,6 +1241,9 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
 | 
			
		|||
		// Cache if this target was previously allowed to be altered, before this update
 | 
			
		||||
		const bool bPrevAllowTargetAltering = Target->bAllowTargetAltering;
 | 
			
		||||
 | 
			
		||||
		// Cache if the physics frame offset has changed since last target
 | 
			
		||||
		const bool bFrameOffsetCorrected = Target->FrameOffset != Input.FrameOffset;
 | 
			
		||||
 | 
			
		||||
		// Set if the target is allowed to be altered after this update
 | 
			
		||||
		Target->bAllowTargetAltering = !(Target->TargetState.Flags & ERigidBodyFlags::Sleeping) && !(Input.TargetState.Flags & ERigidBodyFlags::Sleeping);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1130,7 +1267,7 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
 | 
			
		|||
		Target->ReceiveFrame = CurrentFrame;
 | 
			
		||||
		Target->TargetState = Input.TargetState;
 | 
			
		||||
		Target->RepMode = Input.RepMode;
 | 
			
		||||
		Target->FrameOffset = Input.FrameOffset;
 | 
			
		||||
		Target->FrameOffset = Input.FrameOffset.IsSet() ? *Input.FrameOffset : 0;
 | 
			
		||||
		Target->TickCount = 0;
 | 
			
		||||
		Target->AccumulatedSleepSeconds = 0.0f;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1145,6 +1282,8 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
 | 
			
		|||
			if (CVarDrawDebugTargets->GetBool())
 | 
			
		||||
			{
 | 
			
		||||
				const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
 | 
			
		||||
				// Port this?
 | 
			
		||||
				//const FVector Offset = FVector(0.0f, 0.0f, PhysicsReplicationCVars::PredictiveInterpolationCVars::DrawDebugZOffset);
 | 
			
		||||
 | 
			
		||||
				static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
 | 
			
		||||
				Chaos::FDebugDrawQueue::GetInstance().DrawDebugBox(Input.TargetState.Position + Offset, FVector(15.0f, 15.0f, 15.0f), Input.TargetState.Quaternion, FColor::MakeRandomSeededColor(Input.ServerFrame), false, CVarNetCorrectionLifetime->GetFloat(), 0, 1.0f);
 | 
			
		||||
| 
						 | 
				
			
			@ -1176,7 +1315,7 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
 | 
			
		|||
				*/
 | 
			
		||||
 | 
			
		||||
				// Run target alignment if we have been allowed to alter the target during the last two target updates
 | 
			
		||||
				if (!bFirstTarget && bPrevAllowTargetAltering && Target->bAllowTargetAltering)
 | 
			
		||||
				if (!bFirstTarget && bPrevAllowTargetAltering && Target->bAllowTargetAltering && !bFrameOffsetCorrected)
 | 
			
		||||
				{
 | 
			
		||||
					static const auto CVarTargetTickAlignmentClampMultiplier = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.TargetTickAlignmentClampMultiplier"));
 | 
			
		||||
					const int32 AdjustedAverageReceiveInterval = FMath::CeilToInt(Target->AverageReceiveInterval) * CVarTargetTickAlignmentClampMultiplier->GetInt();
 | 
			
		||||
| 
						 | 
				
			
			@ -1257,6 +1396,7 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
 | 
			
		|||
	for (auto Itr = ObjectToTarget.CreateIterator(); Itr; ++Itr)
 | 
			
		||||
	{
 | 
			
		||||
		bool bRemoveItr = true; // Remove current cached replication target unless replication logic tells us to store it for next tick
 | 
			
		||||
		FParticleID ParticleID;
 | 
			
		||||
 | 
			
		||||
		Chaos::FConstPhysicsObjectHandle& POHandle = Itr.Key();
 | 
			
		||||
		if (FGeometryParticleHandle* Handle = Interface.GetParticle(POHandle))
 | 
			
		||||
| 
						 | 
				
			
			@ -1266,6 +1406,8 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
 | 
			
		|||
 | 
			
		||||
			if (FPBDRigidParticleHandle* RigidHandle = Handle->CastToRigidParticle())
 | 
			
		||||
			{
 | 
			
		||||
				ParticleID = RigidHandle->ParticleID();
 | 
			
		||||
 | 
			
		||||
				// Cache custom settings for this object if there are any
 | 
			
		||||
				FetchObjectSettings(POHandle);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1290,6 +1432,7 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
 | 
			
		|||
 | 
			
		||||
		if (bRemoveItr)
 | 
			
		||||
		{
 | 
			
		||||
			ReplicatedParticleIDs.Remove(ParticleID);
 | 
			
		||||
			Itr.RemoveCurrent();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1463,7 +1606,8 @@ bool FPhysicsReplicationAsyncVR::DefaultReplication(Chaos::FPBDRigidParticleHand
 | 
			
		|||
	const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrectionDefault.AngularVelocityCoefficient;
 | 
			
		||||
 | 
			
		||||
	static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance"));
 | 
			
		||||
	const float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrectionDefault.MaxLinearHardSnapDistance;
 | 
			
		||||
	float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrectionDefault.MaxLinearHardSnapDistance;
 | 
			
		||||
	MaxLinearHardSnapDistance = SettingsCurrent.DefaultReplicationSettings.GetMaxLinearHardSnapDistance(MaxLinearHardSnapDistance);
 | 
			
		||||
 | 
			
		||||
	// Get Current state
 | 
			
		||||
	FRigidBodyState CurrentState;
 | 
			
		||||
| 
						 | 
				
			
			@ -1582,8 +1726,11 @@ bool FPhysicsReplicationAsyncVR::DefaultReplication(Chaos::FPBDRigidParticleHand
 | 
			
		|||
			// Too much error so just snap state here and be done with it
 | 
			
		||||
			Target.AccumulatedErrorSeconds = 0.0f;
 | 
			
		||||
			bRestoredState = true;
 | 
			
		||||
			Handle->SetX(TargetPos);
 | 
			
		||||
			Handle->SetR(TargetQuat);
 | 
			
		||||
 | 
			
		||||
			// Set XRVW to hard snap dynamic object and force recalculation of friction
 | 
			
		||||
			const bool bCorrectConnectedBodies = SettingsCurrent.DefaultReplicationSettings.GetCorrectConnectedBodies();
 | 
			
		||||
			const bool bCorrectConnectedBodiesFriction = SettingsCurrent.DefaultReplicationSettings.GetCorrectConnectedBodiesFriction();
 | 
			
		||||
			RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, TargetPos, TargetQuat, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
 | 
			
		||||
			Handle->SetV(NewState.LinVel);
 | 
			
		||||
			Handle->SetW(FMath::DegreesToRadians(NewState.AngVel));
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -1651,6 +1798,8 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
	static const auto CVarDrawDebugTargets = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugTargets"));
 | 
			
		||||
	if (CVarDrawDebugTargets->GetBool())
 | 
			
		||||
	{
 | 
			
		||||
		// Needs updated post 5.5 for DrawDebugZ CVAR
 | 
			
		||||
		 
 | 
			
		||||
		const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
 | 
			
		||||
		const FVector StartPos = Target.TargetState.Position + Offset;
 | 
			
		||||
		const int32 SizeMultiplier = FMath::Clamp(Target.TickCount, -4, 30);
 | 
			
		||||
| 
						 | 
				
			
			@ -1675,15 +1824,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
		if (bOkToClear && bShouldSleep && bCanSimulate)
 | 
			
		||||
		{
 | 
			
		||||
			RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Sleeping);
 | 
			
		||||
 | 
			
		||||
			static const auto CVarSleepConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepConnectedBodies"));
 | 
			
		||||
			if (CVarSleepConnectedBodies->GetBool())
 | 
			
		||||
			{
 | 
			
		||||
				RigidsSolver->GetEvolution()->ApplySleepOnConnectedParticles(Handle);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static const auto CVarSleepSecondsClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepSecondsClearTarget"));
 | 
			
		||||
		static const auto CVarDontClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DontClearTarget"));
 | 
			
		||||
		// --- Should replication stop? ---
 | 
			
		||||
		const bool bClearTarget =
 | 
			
		||||
			(!bCanSimulate
 | 
			
		||||
				|| (bOkToClear && bShouldSleep && Target.AccumulatedSleepSeconds >= CVarSleepSecondsClearTarget->GetFloat()) // Don't clear the target due to sleeping until the object both should sleep and is sleeping for n seconds
 | 
			
		||||
				|| (bOkToClear && !bReplicatingPhysics))
 | 
			
		||||
			((bOkToClear && bShouldSleep && Target.AccumulatedSleepSeconds >= CVarSleepSecondsClearTarget->GetFloat()) // Allow clearing the target due to sleeping after the object has been sleeping for n seconds
 | 
			
		||||
				|| (bOkToClear && !bReplicatingPhysics) // If replication say it's okay to clear the target and the object shouldn't replicate physics anymore, clear the target
 | 
			
		||||
				|| (bOkToClear && !bCanSimulate)) // If replication say it's okay to clear the target and the object can't simulate, clear the target
 | 
			
		||||
			&& !CVarDontClearTarget->GetBool();
 | 
			
		||||
 | 
			
		||||
		// --- Target Prediction ---
 | 
			
		||||
| 
						 | 
				
			
			@ -1694,10 +1849,16 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
			const int32 ExtrapolationTickLimit = FMath::Max(
 | 
			
		||||
				FMath::CeilToInt(Target.AverageReceiveInterval * CVarExtrapolationTimeMultiplier->GetFloat()), // Extrapolate time based on receive interval * multiplier
 | 
			
		||||
				FMath::CeilToInt(CVarExtrapolationMinTime->GetFloat() / DeltaSeconds)); // At least extrapolate for N seconds
 | 
			
		||||
			
 | 
			
		||||
			if (Target.TickCount <= ExtrapolationTickLimit)
 | 
			
		||||
			{
 | 
			
		||||
				FPhysicsReplicationAsyncVR::ExtrapolateTarget(Target, 1, DeltaSeconds);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				// If we reach the extrapolation limit, disable target from being altered
 | 
			
		||||
				Target.bAllowTargetAltering = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return bClearTarget;
 | 
			
		||||
| 
						 | 
				
			
			@ -1716,26 +1877,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
		// Get the rotational offset between the blended rotation target and the current rotation
 | 
			
		||||
		const FQuat TargetRotDelta = Target.TargetState.Quaternion * Handle->GetR().Inverse();
 | 
			
		||||
 | 
			
		||||
		// Convert to angle axis
 | 
			
		||||
		// Convert to angle and axis
 | 
			
		||||
		float Angle;
 | 
			
		||||
		FVector Axis;
 | 
			
		||||
		TargetRotDelta.ToAxisAndAngle(Axis, Angle);
 | 
			
		||||
		Angle = FMath::Abs(FMath::UnwindRadians(Angle));
 | 
			
		||||
		Angle = FMath::RadiansToDegrees(FMath::UnwindRadians(Angle));
 | 
			
		||||
		Angle = FMath::Abs(Angle);
 | 
			
		||||
 | 
			
		||||
		static const auto CVarEarlyOutAngle = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.EarlyOutAngle"));
 | 
			
		||||
		if (Angle < FMath::DegreesToRadians(CVarEarlyOutAngle->GetFloat()))
 | 
			
		||||
		if (Angle < CVarEarlyOutAngle->GetFloat())
 | 
			
		||||
		{
 | 
			
		||||
			// Early Out
 | 
			
		||||
			return EndReplicationHelper(Target, true);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Wake up if sleeping
 | 
			
		||||
	if (bIsSleeping)
 | 
			
		||||
	{
 | 
			
		||||
		RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Dynamic);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static const auto CVarAverageReceiveIntervalSmoothing = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AverageReceiveIntervalSmoothing"));
 | 
			
		||||
	// Update the AverageReceiveInterval if Target.ReceiveInterval has a valid value to update from
 | 
			
		||||
	Target.AverageReceiveInterval = Target.ReceiveInterval == 0 ? Target.AverageReceiveInterval : FMath::Lerp(Target.AverageReceiveInterval, Target.ReceiveInterval, FMath::Clamp((1.0f / (Target.ReceiveInterval * CVarAverageReceiveIntervalSmoothing->GetFloat())), 0.0f, 1.0f));
 | 
			
		||||
| 
						 | 
				
			
			@ -1745,13 +1901,13 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
	CurrentState.Position = Handle->GetX();
 | 
			
		||||
	CurrentState.Quaternion = Handle->GetR();
 | 
			
		||||
	CurrentState.LinVel = Handle->GetV();
 | 
			
		||||
	CurrentState.AngVel = Handle->GetW(); // Note: Current angular velocity is in Radians
 | 
			
		||||
	CurrentState.AngVel = Handle->GetW(); // Radians
 | 
			
		||||
 | 
			
		||||
	// NewState
 | 
			
		||||
	const FVector TargetPos = FVector(Target.TargetState.Position);
 | 
			
		||||
	const FQuat TargetRot = Target.TargetState.Quaternion;
 | 
			
		||||
	const FVector TargetLinVel = FVector(Target.TargetState.LinVel);
 | 
			
		||||
	const FVector TargetAngVel = FVector(Target.TargetState.AngVel); // Note: Target angular velocity is in Degrees
 | 
			
		||||
	const FVector TargetAngVel = FVector(FMath::DegreesToRadians(Target.TargetState.AngVel)); // Radians
 | 
			
		||||
 | 
			
		||||
	/** --- Reconciliation ---
 | 
			
		||||
	* If target velocities are low enough, check the traveled direction and distance from previous frame and compare with replicated linear velocity.
 | 
			
		||||
| 
						 | 
				
			
			@ -1799,9 +1955,11 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
 | 
			
		||||
	static const auto CVarErrorAccumulationSeconds = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.ErrorAccumulationSeconds"));
 | 
			
		||||
	static const auto CVarAlwaysHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AlwaysHardSnap"));
 | 
			
		||||
	const bool bHardSnap = !bCanSimulate ||
 | 
			
		||||
		Target.AccumulatedErrorSeconds > CVarErrorAccumulationSeconds->GetFloat() ||
 | 
			
		||||
		CVarAlwaysHardSnap->GetBool();
 | 
			
		||||
	static const auto CVarKinematicHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.KinematicHardSnap"));
 | 
			
		||||
 | 
			
		||||
	const bool bHardSnap = (!bCanSimulate && CVarKinematicHardSnap->GetBool())
 | 
			
		||||
		|| Target.AccumulatedErrorSeconds > CVarErrorAccumulationSeconds->GetFloat()
 | 
			
		||||
		|| CVarAlwaysHardSnap->GetBool();
 | 
			
		||||
 | 
			
		||||
	if (bHardSnap)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -1815,13 +1973,13 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			// Set XPRQVW to hard snap dynamic object
 | 
			
		||||
			Handle->SetX(Target.PrevPosTarget);
 | 
			
		||||
			Handle->SetP(Target.PrevPosTarget);
 | 
			
		||||
			Handle->SetR(Target.PrevRotTarget);
 | 
			
		||||
			Handle->SetQ(Target.PrevRotTarget);
 | 
			
		||||
			Handle->SetV(Target.TargetState.LinVel);
 | 
			
		||||
			Handle->SetW(FMath::DegreesToRadians(Target.TargetState.AngVel));
 | 
			
		||||
			// Set XRVW to hard snap dynamic object and force recalculation of friction
 | 
			
		||||
			const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
 | 
			
		||||
			RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, Target.PrevPosTarget, Target.PrevRotTarget, bCorrectConnectedBodies, /*bInRecalculateFrictionOnConnectedBodies*/ true, ReplicatedParticleIDs);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			Handle->SetV(TargetLinVel);
 | 
			
		||||
			Handle->SetW(TargetAngVel);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Cache data for next replication
 | 
			
		||||
| 
						 | 
				
			
			@ -1830,8 +1988,72 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
		// End replication and go to sleep if that's requested
 | 
			
		||||
		return EndReplicationHelper(Target, true);
 | 
			
		||||
	}
 | 
			
		||||
	else if (Handle->IsKinematic()) // Smooth Kinematic Replication
 | 
			
		||||
	{
 | 
			
		||||
		static const auto CVarKinematicPrediction = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.KinematicPrediction"));
 | 
			
		||||
		const bool bKinematicPrediction = CVarKinematicPrediction->GetBool();
 | 
			
		||||
		const float InterpolationTicks = FMath::CeilToInt(Target.AverageReceiveInterval) - (RigidsSolver->GetCurrentFrame() - Target.ReceiveFrame);
 | 
			
		||||
 | 
			
		||||
		if ((bKinematicPrediction && Target.bAllowTargetAltering) || InterpolationTicks > 0)
 | 
			
		||||
		{
 | 
			
		||||
			/* Calculate the Lerp value for a smooth interpolation
 | 
			
		||||
			* ------------------------------------------------------------------------------
 | 
			
		||||
			* bKinematicPrediction is True :: Interpolate towards the target that gets forward predicted each tick
 | 
			
		||||
			*	1 / 4 = 0.25 = 25% interpolation each time (if AverageReceiveInterval is 4)
 | 
			
		||||
			* ------------------------------------------------------------------------------
 | 
			
		||||
			* bKinematicPrediction is False :: Interpolate from current position to the static source for the current target, we need to cover the same amount of distance but from a decaying distance
 | 
			
		||||
			*	| ---> | ------------------ |
 | 
			
		||||
			*	0%    25%				   100%		(1 / 4 = 0.25)
 | 
			
		||||
			*		   | ---> | ----------- |
 | 
			
		||||
			*		   0%	 33%		   100%		(1 / 3 = 0.33)
 | 
			
		||||
			*				  | ---> | ---- |
 | 
			
		||||
			*				  0%    50%    100%		(1 / 2 = 0.5)
 | 
			
		||||
			*						 | ---> |
 | 
			
		||||
			*						 0%    100%		(1 / 1 = 1.0)
 | 
			
		||||
			* ------------------------------------------------------------------------------
 | 
			
		||||
			*/
 | 
			
		||||
			const float Lerp = 1.f / (bKinematicPrediction ? Target.AverageReceiveInterval : InterpolationTicks);
 | 
			
		||||
 | 
			
		||||
			// Interpolate position and rotation from current position towards target position based on either predicted target or source target
 | 
			
		||||
			const FVector KinTargetPos = FMath::Lerp(CurrentState.Position,
 | 
			
		||||
				(bKinematicPrediction ? Target.TargetState.Position : Target.PrevPosTarget),
 | 
			
		||||
				Lerp);
 | 
			
		||||
			const FQuat KinTargetRot = FQuat::Slerp(CurrentState.Quaternion,
 | 
			
		||||
				(bKinematicPrediction ? Target.TargetState.Quaternion : Target.PrevRotTarget),
 | 
			
		||||
				Lerp);
 | 
			
		||||
 | 
			
		||||
			// Apply kinematic target
 | 
			
		||||
			const Chaos::FKinematicTarget KinTarget = Chaos::FKinematicTarget::MakePositionTarget(KinTargetPos, KinTargetRot); // Uses EKinematicTargetMode::Position
 | 
			
		||||
			RigidsSolver->GetEvolution()->SetParticleKinematicTarget(Handle, KinTarget);
 | 
			
		||||
 | 
			
		||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
 | 
			
		||||
			//static const auto CVarDrawDebugTargets = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugTargets"));
 | 
			
		||||
			if (CVarDrawDebugTargets->GetBool())
 | 
			
		||||
			{
 | 
			
		||||
				
 | 
			
		||||
				static const auto CVarDrawDebugZOffset = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugZOffset"));
 | 
			
		||||
				const FVector Offset = FVector(0.0f, 0.0f, CVarDrawDebugZOffset->GetFloat());
 | 
			
		||||
				const FVector Pos = KinTargetPos + Offset;
 | 
			
		||||
				const int32 SizeMultiplier = FMath::Clamp(Target.TickCount, -4, 30);
 | 
			
		||||
				static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
 | 
			
		||||
				Chaos::FDebugDrawQueue::GetInstance().DrawDebugSphere(Pos, 3.0f + SizeMultiplier * 0.75f, 8, FColor::MakeRandomSeededColor(Target.ServerFrame), false, CVarNetCorrectionLifetime->GetFloat(), 0, 1.0f);
 | 
			
		||||
			}
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			// End replication and allow to clear target
 | 
			
		||||
			return EndReplicationHelper(Target, true);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else // Velocity-based Replication
 | 
			
		||||
	{
 | 
			
		||||
		// Wake up if sleeping
 | 
			
		||||
		if (bIsSleeping)
 | 
			
		||||
		{
 | 
			
		||||
			RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Dynamic);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Calculate interpolation time based on current average receive rate
 | 
			
		||||
		const float AverageReceiveIntervalSeconds = Target.AverageReceiveInterval * DeltaSeconds;
 | 
			
		||||
		const float InterpolationTime = AverageReceiveIntervalSeconds * SettingsCurrent.PredictiveInterpolationSettings.GetPosInterpolationTimeMultiplier();
 | 
			
		||||
| 
						 | 
				
			
			@ -1843,6 +2065,7 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
		const float RotCorrectionTime = FMath::Max(SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeBase() + AverageReceiveIntervalSeconds + RTT * SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMultiplier(),
 | 
			
		||||
			DeltaSeconds + SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMin());
 | 
			
		||||
 | 
			
		||||
		FVector CorrectionX = CurrentState.Position;
 | 
			
		||||
		if ((bXCanEarlyOut && SettingsCurrent.PredictiveInterpolationSettings.GetSkipVelocityRepOnPosEarlyOut()) == false)
 | 
			
		||||
		{	// --- Velocity Replication ---
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1853,10 +2076,10 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
			const FVector LinVelDiff = -CurrentState.LinVel + TargetLinVel;
 | 
			
		||||
 | 
			
		||||
			// Calculate velocity blend amount for this tick as an alpha value
 | 
			
		||||
			const float Alpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
 | 
			
		||||
			const float VelocityAlpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
			FVector RepLinVel;
 | 
			
		||||
			static const auto CVarPosCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.PosCorrectionAsVelocity"));
 | 
			
		||||
			static const auto CVarPosCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.CorrectionAsVelocity"));
 | 
			
		||||
			if (CVarPosCorrectionAsVelocity->GetBool())
 | 
			
		||||
			{
 | 
			
		||||
				// Convert PosDiff to a velocity
 | 
			
		||||
| 
						 | 
				
			
			@ -1866,20 +2089,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
				const FVector BlendedTargetVelocity = LinVelDiff + PosDiffVelocity;
 | 
			
		||||
 | 
			
		||||
				// Add BlendedTargetVelocity onto current velocity
 | 
			
		||||
 | 
			
		||||
				RepLinVel = CurrentState.LinVel + (BlendedTargetVelocity * Alpha);
 | 
			
		||||
				RepLinVel = CurrentState.LinVel + (BlendedTargetVelocity * VelocityAlpha); // Same as (BlendedTargetVelocity / InterpolationTime) * DeltaSeconds
 | 
			
		||||
			}
 | 
			
		||||
			else // Positional correction as position shift
 | 
			
		||||
			else // Positional correction as transform shift
 | 
			
		||||
			{
 | 
			
		||||
				// Calculate the PosDiff amount to correct this tick
 | 
			
		||||
				const FVector PosDiffVelocityDelta = PosDiff * (DeltaSeconds / PosCorrectionTime); // Same as (PosDiff / PosCorrectionTime) * DeltaSeconds
 | 
			
		||||
 | 
			
		||||
				// Add velocity diff onto current velocity
 | 
			
		||||
				RepLinVel = CurrentState.LinVel + (LinVelDiff * Alpha);
 | 
			
		||||
				RepLinVel = CurrentState.LinVel + (LinVelDiff * VelocityAlpha); // Same as (LinVelDiff / InterpolationTime) * DeltaSeconds
 | 
			
		||||
 | 
			
		||||
				// Calculate correction blend amount for this tick as an alpha value
 | 
			
		||||
				const float CorrectionAlpha = FMath::Clamp(DeltaSeconds / PosCorrectionTime, 0.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
				// Apply positional correction
 | 
			
		||||
				Handle->SetX(Handle->GetX() + PosDiffVelocityDelta);
 | 
			
		||||
				// Calculate the PosDiff amount to correct this tick
 | 
			
		||||
				const FVector PosDiffVelocityDelta = PosDiff * CorrectionAlpha; // Same as (PosDiff / PosCorrectionTime) * DeltaSeconds
 | 
			
		||||
 | 
			
		||||
				// The new position after correction
 | 
			
		||||
				CorrectionX = Handle->GetX() + PosDiffVelocityDelta;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Apply velocity replication
 | 
			
		||||
| 
						 | 
				
			
			@ -1890,7 +2114,8 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
			static const auto CVarDrawDebugVectors = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugVectors"));
 | 
			
		||||
			if (CVarDrawDebugVectors->GetBool())
 | 
			
		||||
			{
 | 
			
		||||
				const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
 | 
			
		||||
				static const auto CVarDrawDebugZOffset = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugZOffset"));
 | 
			
		||||
				const FVector Offset = FVector(0.0f, 0.0f, CVarDrawDebugZOffset->GetFloat());
 | 
			
		||||
				const FVector OffsetAdd = FVector(0.0f, 0.0f, 10.0f);
 | 
			
		||||
				const FVector StartPos = TargetPos + Offset;
 | 
			
		||||
				FVector Direction = TargetLinVel;
 | 
			
		||||
| 
						 | 
				
			
			@ -1908,37 +2133,63 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
			Target.PrevLinVel = FVector(RepLinVel);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		FQuat CorrectionR = CurrentState.Quaternion;
 | 
			
		||||
		{	// --- Angular Velocity Replication ---
 | 
			
		||||
			/* Todo, Implement InterpolationTime */
 | 
			
		||||
 | 
			
		||||
			// Extrapolate current rotation along current angular velocity to see where we would end up
 | 
			
		||||
			float CurAngVelSize;
 | 
			
		||||
			FVector CurAngVelAxis;
 | 
			
		||||
			CurrentState.AngVel.FVector::ToDirectionAndLength(CurAngVelAxis, CurAngVelSize);
 | 
			
		||||
			const FQuat CurRotExtrapDelta = FQuat(CurAngVelAxis, CurAngVelSize * DeltaSeconds);
 | 
			
		||||
			const FQuat CurRotExtrap = CurRotExtrapDelta * CurrentState.Quaternion;
 | 
			
		||||
			// Get AngVelDiff by adding inverted CurrentState.AngVel to TargetAngVel
 | 
			
		||||
			const FVector AngVelDiff = -CurrentState.AngVel + TargetAngVel;
 | 
			
		||||
 | 
			
		||||
			// Slerp from the extrapolated current rotation towards the target rotation
 | 
			
		||||
			// This takes current angular velocity into account
 | 
			
		||||
			const float RotCorrectionAmount = FMath::Clamp(DeltaSeconds / RotCorrectionTime, 0.0f, 1.0f);
 | 
			
		||||
			const FQuat TargetRotBlended = FQuat::Slerp(CurRotExtrap, TargetRot, RotCorrectionAmount);
 | 
			
		||||
			// Calculate velocity blend amount for this tick as an alpha value
 | 
			
		||||
			const float VelocityAlpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
			// Get the rotational offset between the blended rotation target and the current rotation
 | 
			
		||||
			const FQuat TargetRotDelta = TargetRotBlended * CurrentState.Quaternion.Inverse();
 | 
			
		||||
			FVector RepAngVel;
 | 
			
		||||
			static const auto CVarCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.CorrectionAsVelocity"));
 | 
			
		||||
			if (CVarCorrectionAsVelocity->GetBool())
 | 
			
		||||
			{
 | 
			
		||||
				// Get RotDiff
 | 
			
		||||
				const FQuat RotDiff = TargetRot * CurrentState.Quaternion.Inverse();
 | 
			
		||||
 | 
			
		||||
			// Convert the rotational delta to angular velocity
 | 
			
		||||
			float WAngle;
 | 
			
		||||
			FVector WAxis;
 | 
			
		||||
			TargetRotDelta.ToAxisAndAngle(WAxis, WAngle);
 | 
			
		||||
			const FVector TargetRotDeltaBlend = FVector(WAxis * (WAngle / (DeltaSeconds * SettingsCurrent.PredictiveInterpolationSettings.GetRotInterpolationTimeMultiplier())));
 | 
			
		||||
			const FVector RepAngVel = FMath::DegreesToRadians(TargetAngVel) + TargetRotDeltaBlend;
 | 
			
		||||
				// Convert RotDiff to a velocity
 | 
			
		||||
				float WAngle;
 | 
			
		||||
				FVector WAxis;
 | 
			
		||||
				RotDiff.ToAxisAndAngle(WAxis, WAngle);
 | 
			
		||||
				WAngle = FMath::UnwindRadians(WAngle);
 | 
			
		||||
				const FVector RotDiffVelocity = FVector(WAxis * (WAngle / RotCorrectionTime));
 | 
			
		||||
 | 
			
		||||
				// Add RotDiffVelocity to AngVelDiff to get BlendedTargetVelocity
 | 
			
		||||
				const FVector BlendedTargetVelocity = AngVelDiff + RotDiffVelocity;
 | 
			
		||||
 | 
			
		||||
				// Add BlendedTargetVelocity to CurrentState.AngVel
 | 
			
		||||
				RepAngVel = CurrentState.AngVel + (BlendedTargetVelocity * VelocityAlpha); // Same as (BlendedTargetVelocity / InterpolationTime) * DeltaSeconds
 | 
			
		||||
			}
 | 
			
		||||
			else // Positional correction as transform shift
 | 
			
		||||
			{
 | 
			
		||||
				// Add velocity diff onto current velocity
 | 
			
		||||
				RepAngVel = CurrentState.AngVel + (AngVelDiff * VelocityAlpha); // Same as (AngVelDiff / InterpolationTime) * DeltaSeconds
 | 
			
		||||
 | 
			
		||||
				// Calculate correction blend amount for this tick as an alpha value
 | 
			
		||||
				const float CorrectionAlpha = FMath::Clamp(DeltaSeconds / RotCorrectionTime, 0.0f, 1.0f);
 | 
			
		||||
 | 
			
		||||
				// The new position after correction
 | 
			
		||||
				CorrectionR = FQuat::Slerp(Handle->GetR(), TargetRot, CorrectionAlpha);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Apply velocity replication
 | 
			
		||||
			Handle->SetW(RepAngVel);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Cache data for next replication
 | 
			
		||||
		Target.PrevPos = FVector(CurrentState.Position);
 | 
			
		||||
 | 
			
		||||
		// Apply correction as a transform shift
 | 
			
		||||
		static const auto CVarCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.CorrectionAsVelocity"));
 | 
			
		||||
		if (!CVarCorrectionAsVelocity->GetBool())
 | 
			
		||||
		{
 | 
			
		||||
			const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
 | 
			
		||||
			const bool bCorrectConnectedBodiesFriction = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodiesFriction();
 | 
			
		||||
			RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectionX, CorrectionR, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (bSoftSnap)
 | 
			
		||||
		{
 | 
			
		||||
			const FVector SoftSnapPos = FMath::Lerp(FVector(CurrentState.Position),
 | 
			
		||||
| 
						 | 
				
			
			@ -1949,10 +2200,10 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
 | 
			
		|||
				SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapToSource() ? Target.PrevRotTarget : Target.TargetState.Quaternion,
 | 
			
		||||
				FMath::Clamp(SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapRotStrength(), 0.0f, 1.0f));
 | 
			
		||||
 | 
			
		||||
			Handle->SetX(SoftSnapPos);
 | 
			
		||||
			Handle->SetP(SoftSnapPos);
 | 
			
		||||
			Handle->SetR(SoftSnapRot);
 | 
			
		||||
			Handle->SetQ(SoftSnapRot);
 | 
			
		||||
			// Apply correction as a transform shift
 | 
			
		||||
			const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
 | 
			
		||||
			const bool bCorrectConnectedBodiesFriction = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodiesFriction();
 | 
			
		||||
			RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, SoftSnapPos, SoftSnapRot, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2003,17 +2254,48 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
 | 
			
		|||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const bool bShouldSleep = (Target.TargetState.Flags & ERigidBodyFlags::Sleeping) != 0;
 | 
			
		||||
	bool bClearTarget = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	static constexpr Chaos::FFrameAndPhase::EParticleHistoryPhase RewindPhase = Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData;
 | 
			
		||||
 | 
			
		||||
	const float ResimErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorThreshold(Chaos::FPhysicsSolverBase::ResimulationErrorThreshold());
 | 
			
		||||
	// Get state from locally cached history for frame corresponding to received data
 | 
			
		||||
	const Chaos::FGeometryParticleState PastState = RewindData->GetPastStateAtFrame(*Handle, LocalFrame, RewindPhase);
 | 
			
		||||
 | 
			
		||||
	const FVector ErrorOffset = (Target.TargetState.Position - PastState.GetX());
 | 
			
		||||
	const float ErrorDistance = ErrorOffset.Size();
 | 
			
		||||
	const bool ShouldTriggerResim = ErrorDistance >= ResimErrorThreshold;
 | 
			
		||||
	// Check which comparisons to perform to trigger resimulation from
 | 
			
		||||
	const bool bCompareX = Chaos::FPhysicsSolverBase::GetResimulationErrorPositionThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorPositionThreshold;
 | 
			
		||||
	const bool bCompareR = Chaos::FPhysicsSolverBase::GetResimulationErrorRotationThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorRotationThreshold;
 | 
			
		||||
	const bool bCompareV = Chaos::FPhysicsSolverBase::GetResimulationErrorLinearVelocityThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorLinearVelocityThreshold;
 | 
			
		||||
	const bool bCompareW = Chaos::FPhysicsSolverBase::GetResimulationErrorAngularVelocityThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorAngularVelocityThreshold;
 | 
			
		||||
	bool bShouldTriggerResim = false;
 | 
			
		||||
 | 
			
		||||
	// Check for positional discrepancy in Distance between client and server
 | 
			
		||||
	if (bCompareX)
 | 
			
		||||
	{
 | 
			
		||||
		const float ResimPositionErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorPositionThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorPositionThreshold());
 | 
			
		||||
		bShouldTriggerResim = Chaos::FRewindData::CheckVectorThreshold(Target.TargetState.Position, PastState.GetX(), ResimPositionErrorThreshold);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for linear velocity discrepancy in Distance / s between client and server
 | 
			
		||||
	if (!bShouldTriggerResim && bCompareV)
 | 
			
		||||
	{
 | 
			
		||||
		const float ResimLinVelocityErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorLinearVelocityThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorLinearVelocityThreshold());
 | 
			
		||||
		bShouldTriggerResim = Chaos::FRewindData::CheckVectorThreshold(Target.TargetState.LinVel, PastState.GetV(), ResimLinVelocityErrorThreshold);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for angular velocity discrepancy in Degrees / s between client and server
 | 
			
		||||
	if (!bShouldTriggerResim && bCompareW)
 | 
			
		||||
	{
 | 
			
		||||
		const float ResimAngVelocityErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorAngularVelocityThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorAngularVelocityThreshold());
 | 
			
		||||
		bShouldTriggerResim = Chaos::FRewindData::CheckVectorThreshold(FMath::DegreesToRadians(Target.TargetState.AngVel), PastState.GetW(), ResimAngVelocityErrorThreshold);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for rotational discrepancy in Degrees between client and server
 | 
			
		||||
	if (!bShouldTriggerResim && bCompareR)
 | 
			
		||||
	{
 | 
			
		||||
		const float ResimRotationErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorRotationThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorRotationThreshold());
 | 
			
		||||
		bShouldTriggerResim = Chaos::FRewindData::CheckQuaternionThreshold(Target.TargetState.Quaternion, PastState.GetR(), ResimRotationErrorThreshold);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
 | 
			
		||||
| 
						 | 
				
			
			@ -2021,7 +2303,7 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
 | 
			
		|||
	if (Chaos::FPhysicsSolverBase::CanDebugNetworkPhysicsPrediction())
 | 
			
		||||
	{
 | 
			
		||||
		UE_LOG(LogTemp, Log, TEXT("Apply Rigid body state at local frame %d with offset = %d"), LocalFrame, Target.FrameOffset);
 | 
			
		||||
		UE_LOG(LogTemp, Log, TEXT("Particle Position Error = %f | Should Trigger Resim = %s | Server Frame = %d | Client Frame = %d"), ErrorDistance, (ShouldTriggerResim ? TEXT("True") : TEXT("False")), Target.ServerFrame, LocalFrame);
 | 
			
		||||
		UE_LOG(LogTemp, Log, TEXT("Should Trigger Resim = %s | Server Frame = %d | Client Frame = %d"), (bShouldTriggerResim ? TEXT("True") : TEXT("False")), Target.ServerFrame, LocalFrame);
 | 
			
		||||
		UE_LOG(LogTemp, Log, TEXT("Particle Target Position = %s | Current Position = %s"), *Target.TargetState.Position.ToString(), *PastState.GetX().ToString());
 | 
			
		||||
		UE_LOG(LogTemp, Log, TEXT("Particle Target Velocity = %s | Current Velocity = %s"), *Target.TargetState.LinVel.ToString(), *PastState.GetV().ToString());
 | 
			
		||||
		UE_LOG(LogTemp, Log, TEXT("Particle Target Quaternion = %s | Current Quaternion = %s"), *Target.TargetState.Quaternion.ToString(), *PastState.GetR().ToString());
 | 
			
		||||
| 
						 | 
				
			
			@ -2032,7 +2314,7 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
 | 
			
		|||
	if (CVarResimDrawDebug->GetBool())
 | 
			
		||||
	{
 | 
			
		||||
		static constexpr float BoxSize = 5.0f;
 | 
			
		||||
		const float ColorLerp = ShouldTriggerResim ? 1.0f : 0.0f;
 | 
			
		||||
		const float ColorLerp = bShouldTriggerResim ? 1.0f : 0.0f;
 | 
			
		||||
		const FColor DebugColor = FLinearColor::LerpUsingHSV(FLinearColor::Green, FLinearColor::Red, ColorLerp).ToFColor(false);
 | 
			
		||||
 | 
			
		||||
		static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
 | 
			
		||||
| 
						 | 
				
			
			@ -2042,57 +2324,58 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
 | 
			
		|||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (LocalFrame > RewindData->GetBlockedResimFrame())
 | 
			
		||||
	// Wake up if is sleeping and should not sleep
 | 
			
		||||
	if (Handle->IsSleeping() && !bShouldSleep)
 | 
			
		||||
	{
 | 
			
		||||
		if (ShouldTriggerResim && Target.TickCount == 0)
 | 
			
		||||
		RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Dynamic);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bShouldTriggerResim && Target.TickCount == 0 && LocalFrame > RewindData->GetBlockedResimFrame())
 | 
			
		||||
	{
 | 
			
		||||
		// Trigger resimulation
 | 
			
		||||
		RigidsSolver->GetEvolution()->GetIslandManager().SetParticleResimFrame(Handle, LocalFrame);
 | 
			
		||||
 | 
			
		||||
		int32 ResimFrame = RewindData->GetResimFrame();
 | 
			
		||||
		ResimFrame = (ResimFrame == INDEX_NONE) ? LocalFrame : FMath::Min(ResimFrame, LocalFrame);
 | 
			
		||||
		RewindData->SetResimFrame(ResimFrame);
 | 
			
		||||
	}
 | 
			
		||||
	else if (SettingsCurrent.ResimulationSettings.GetRuntimeCorrectionEnabled())
 | 
			
		||||
	{
 | 
			
		||||
		const int32 NumPredictedFrames = RigidsSolver->GetCurrentFrame() - LocalFrame - Target.TickCount;
 | 
			
		||||
 | 
			
		||||
		if (Target.TickCount <= NumPredictedFrames && NumPredictedFrames > 0)
 | 
			
		||||
		{
 | 
			
		||||
			// Trigger resimulation
 | 
			
		||||
			RigidsSolver->GetEvolution()->GetIslandManager().SetParticleResimFrame(Handle, LocalFrame);
 | 
			
		||||
			const FVector ErrorOffset = (Target.TargetState.Position - PastState.GetX());
 | 
			
		||||
 | 
			
		||||
			int32 ResimFrame = RewindData->GetResimFrame();
 | 
			
		||||
			ResimFrame = (ResimFrame == INDEX_NONE) ? LocalFrame : FMath::Min(ResimFrame, LocalFrame);
 | 
			
		||||
			RewindData->SetResimFrame(ResimFrame);
 | 
			
		||||
		}
 | 
			
		||||
		else if (SettingsCurrent.ResimulationSettings.GetRuntimeCorrectionEnabled())
 | 
			
		||||
			// Positional Correction
 | 
			
		||||
			const float CorrectionAmountX = SettingsCurrent.ResimulationSettings.GetPosStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
			const FVector PosDiffCorrection = ErrorOffset * CorrectionAmountX; // Same result as (ErrorOffset / NumPredictedFrames) * PosStabilityMultiplier
 | 
			
		||||
			const FVector CorrectedX = Handle->GetX() + PosDiffCorrection;
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			const int32 NumPredictedFrames = RigidsSolver->GetCurrentFrame() - LocalFrame - Target.TickCount;
 | 
			
		||||
			// Rotational Correction
 | 
			
		||||
			const float CorrectionAmountR = SettingsCurrent.ResimulationSettings.GetRotStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
			const FQuat DeltaQuat = PastState.GetR().Inverse() * Target.TargetState.Quaternion;
 | 
			
		||||
			const FQuat TargetCorrectionR = Handle->GetR() * DeltaQuat;
 | 
			
		||||
			const FQuat CorrectedR = FQuat::Slerp(Handle->GetR(), TargetCorrectionR, CorrectionAmountR);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			if (Target.TickCount <= NumPredictedFrames && NumPredictedFrames > 0)
 | 
			
		||||
			if (SettingsCurrent.ResimulationSettings.GetRuntimeVelocityCorrectionEnabled())
 | 
			
		||||
			{
 | 
			
		||||
				// Positional Correction
 | 
			
		||||
				const float CorrectionAmountX = SettingsCurrent.ResimulationSettings.GetPosStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
				const FVector PosDiffCorrection = ErrorOffset * CorrectionAmountX; // Same result as (ErrorOffset / NumPredictedFrames) * PosStabilityMultiplier
 | 
			
		||||
				const FVector CorrectedX = Handle->GetX() + PosDiffCorrection;
 | 
			
		||||
				// Linear Velocity Correction
 | 
			
		||||
				const FVector LinVelDiff = Target.TargetState.LinVel - PastState.GetV(); // Velocity vector that the server covers but the client doesn't
 | 
			
		||||
				const float CorrectionAmountV = SettingsCurrent.ResimulationSettings.GetVelStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
				const FVector VelCorrection = LinVelDiff * CorrectionAmountV; // Same result as (LinVelDiff / NumPredictedFrames) * VelStabilityMultiplier
 | 
			
		||||
				const FVector CorrectedV = Handle->GetV() + VelCorrection;
 | 
			
		||||
 | 
			
		||||
				// Angular Velocity Correction
 | 
			
		||||
				const FVector AngVelDiff = FMath::DegreesToRadians(Target.TargetState.AngVel) - PastState.GetW(); // Angular velocity vector that the server covers but the client doesn't
 | 
			
		||||
				const float CorrectionAmountW = SettingsCurrent.ResimulationSettings.GetAngVelStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
				const FVector AngVelCorrection = AngVelDiff * CorrectionAmountW; // Same result as (AngVelDiff / NumPredictedFrames) * VelStabilityMultiplier
 | 
			
		||||
				const FVector CorrectedW = Handle->GetW() + AngVelCorrection;
 | 
			
		||||
 | 
			
		||||
				// Rotational Correction
 | 
			
		||||
				const float CorrectionAmountR = SettingsCurrent.ResimulationSettings.GetRotStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
 | 
			
		||||
				const FQuat DeltaQuat = PastState.GetR().Inverse() * Target.TargetState.Quaternion;
 | 
			
		||||
				const FQuat TargetCorrectionR = Handle->GetR() * DeltaQuat;
 | 
			
		||||
				const FQuat CorrectedR = FQuat::Slerp(Handle->GetR(), TargetCorrectionR, CorrectionAmountR);
 | 
			
		||||
 | 
			
		||||
				if (SettingsCurrent.ResimulationSettings.GetRuntimeVelocityCorrectionEnabled())
 | 
			
		||||
				{
 | 
			
		||||
					// Linear Velocity Correction
 | 
			
		||||
					const FVector LinVelDiff = Target.TargetState.LinVel - PastState.GetV(); // Velocity vector that the server covers but the client doesn't
 | 
			
		||||
					const float CorrectionAmountV = SettingsCurrent.ResimulationSettings.GetVelStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
					const FVector VelCorrection = LinVelDiff * CorrectionAmountV; // Same result as (LinVelDiff / NumPredictedFrames) * VelStabilityMultiplier
 | 
			
		||||
					const FVector CorrectedV = Handle->GetV() + VelCorrection;
 | 
			
		||||
 | 
			
		||||
					// Angular Velocity Correction
 | 
			
		||||
					const FVector AngVelDiff = Target.TargetState.AngVel - PastState.GetW(); // Angular velocity vector that the server covers but the client doesn't
 | 
			
		||||
					const float CorrectionAmountW = SettingsCurrent.ResimulationSettings.GetAngVelStabilityMultiplier() / NumPredictedFrames;
 | 
			
		||||
					const FVector AngVelCorrection = AngVelDiff * CorrectionAmountW; // Same result as (AngVelDiff / NumPredictedFrames) * VelStabilityMultiplier
 | 
			
		||||
					const FVector CorrectedW = Handle->GetW() + AngVelCorrection;
 | 
			
		||||
 | 
			
		||||
					// Apply correction to velocities
 | 
			
		||||
					Handle->SetV(CorrectedV);
 | 
			
		||||
					Handle->SetW(CorrectedW);
 | 
			
		||||
				}
 | 
			
		||||
				// Apply correction to velocities
 | 
			
		||||
				Handle->SetV(CorrectedV);
 | 
			
		||||
				Handle->SetW(CorrectedW);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
 | 
			
		||||
| 
						 | 
				
			
			@ -2103,14 +2386,27 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
 | 
			
		|||
				}
 | 
			
		||||
#endif
 | 
			
		||||
				// Apply correction to position and rotation
 | 
			
		||||
				static const auto CVarResimRuntimeCorrectConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.Resim.RuntimeCorrectConnectedBodies"));
 | 
			
		||||
				RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectedX, CorrectedR, CVarResimRuntimeCorrectConnectedBodies->GetBool());
 | 
			
		||||
			}
 | 
			
		||||
				RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectedX, CorrectedR, SettingsCurrent.ResimulationSettings.GetRuntimeCorrectConnectedBodies(), /*bInRecalculateFrictionOnConnectedBodies*/true, ReplicatedParticleIDs);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
			// Keep target for NumPredictedFrames time to perform runtime corrections with until a new target is received
 | 
			
		||||
			bClearTarget = Target.TickCount >= NumPredictedFrames;
 | 
			
		||||
		// Keep target for NumPredictedFrames time to perform runtime corrections with until a new target is received
 | 
			
		||||
		bClearTarget = Target.TickCount >= NumPredictedFrames;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set sleep state if we are about to clear the target from memory and the target is set to sleep
 | 
			
		||||
	if (bClearTarget && bShouldSleep)
 | 
			
		||||
	{
 | 
			
		||||
		// Snap object into correct state, it should already be at that state or very close to it
 | 
			
		||||
		RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, Target.TargetState.Position, Target.TargetState.Quaternion, /*bApplyToConnectedBodies*/true, /*bInRecalculateFrictionOnConnectedBodies*/true, ReplicatedParticleIDs);
 | 
			
		||||
 | 
			
		||||
		RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Sleeping);
 | 
			
		||||
		static const auto CVarSleepConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepConnectedBodies"));
 | 
			
		||||
		if (CVarSleepConnectedBodies->GetBool())
 | 
			
		||||
		{
 | 
			
		||||
			RigidsSolver->GetEvolution()->ApplySleepOnConnectedParticles(Handle);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bClearTarget;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,8 @@
 | 
			
		|||
#include "PhysicsReplication.h"
 | 
			
		||||
#include "Physics/Experimental/PhysScene_Chaos.h"
 | 
			
		||||
#include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding
 | 
			
		||||
#include "PhysicsEngine/BodySetup.h"
 | 
			
		||||
#include "PhysicsEngine/SkeletalBodySetup.h"
 | 
			
		||||
#if WITH_PUSH_MODEL
 | 
			
		||||
#include "Net/Core/PushModel/PushModel.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +65,7 @@ void UOptionalRepSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*>&
 | 
			
		|||
			OutWeldedBodies.Add(BI);
 | 
			
		||||
			if (PhysicsAsset)
 | 
			
		||||
			{
 | 
			
		||||
				if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx])
 | 
			
		||||
				if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx].Get())
 | 
			
		||||
				{
 | 
			
		||||
					OutLabels.Add(PhysicsAssetBodySetup->BoneName);
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +158,7 @@ AGrippableSkeletalMeshActor::AGrippableSkeletalMeshActor(const FObjectInitialize
 | 
			
		|||
 | 
			
		||||
	// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration
 | 
			
		||||
	// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
 | 
			
		||||
	MinNetUpdateFrequency = 30.0f;
 | 
			
		||||
	SetMinNetUpdateFrequency(30.0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AGrippableSkeletalMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
 | 
			
		||||
| 
						 | 
				
			
			@ -254,13 +256,23 @@ void AGrippableSkeletalMeshActor::GatherCurrentMovement()
 | 
			
		|||
			bool bFoundInCache = false;
 | 
			
		||||
 | 
			
		||||
			UWorld* World = GetWorld();
 | 
			
		||||
 | 
			
		||||
			const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
 | 
			
		||||
			int ServerFrame = 0;
 | 
			
		||||
			if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
 | 
			
		||||
 | 
			
		||||
			if (bShouldUsePhysicsReplicationCache)
 | 
			
		||||
			{
 | 
			
		||||
				if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
 | 
			
		||||
				if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
 | 
			
		||||
				{
 | 
			
		||||
					RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
 | 
			
		||||
					bFoundInCache = true;
 | 
			
		||||
					if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
 | 
			
		||||
					{
 | 
			
		||||
						if (RepMovement.ServerFrame != ServerFrame)
 | 
			
		||||
						{
 | 
			
		||||
							RepMovement.FillFrom(*FoundState, this, ServerFrame);
 | 
			
		||||
							bWasRepMovementModified = true;
 | 
			
		||||
						}
 | 
			
		||||
						bFoundInCache = true;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
#include "VRExpansionFunctionLibrary.h"
 | 
			
		||||
#include "GripScripts/VRGripScriptBase.h"
 | 
			
		||||
#include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding
 | 
			
		||||
#include "PhysicsEngine/SkeletalBodySetup.h"
 | 
			
		||||
#include "Net/UnrealNetwork.h"
 | 
			
		||||
#if WITH_PUSH_MODEL
 | 
			
		||||
#include "Net/Core/PushModel/PushModel.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,7 @@ AGrippableStaticMeshActor::AGrippableStaticMeshActor(const FObjectInitializer& O
 | 
			
		|||
 | 
			
		||||
	// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration
 | 
			
		||||
	// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
 | 
			
		||||
	MinNetUpdateFrequency = 30.0f;
 | 
			
		||||
	SetMinNetUpdateFrequency(30.0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AGrippableStaticMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
 | 
			
		||||
| 
						 | 
				
			
			@ -203,13 +203,23 @@ void AGrippableStaticMeshActor::GatherCurrentMovement()
 | 
			
		|||
			bool bFoundInCache = false;
 | 
			
		||||
 | 
			
		||||
			UWorld* World = GetWorld();
 | 
			
		||||
 | 
			
		||||
			const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
 | 
			
		||||
			int ServerFrame = 0;
 | 
			
		||||
			if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
 | 
			
		||||
 | 
			
		||||
			if (bShouldUsePhysicsReplicationCache)
 | 
			
		||||
			{
 | 
			
		||||
				if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
 | 
			
		||||
				if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
 | 
			
		||||
				{
 | 
			
		||||
					RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
 | 
			
		||||
					bFoundInCache = true;
 | 
			
		||||
					if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
 | 
			
		||||
					{
 | 
			
		||||
						if (RepMovement.ServerFrame != ServerFrame)
 | 
			
		||||
						{
 | 
			
		||||
							RepMovement.FillFrom(*FoundState, this, ServerFrame);
 | 
			
		||||
							bWasRepMovementModified = true;
 | 
			
		||||
						}
 | 
			
		||||
						bFoundInCache = true;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -243,20 +253,20 @@ void AGrippableStaticMeshActor::GatherCurrentMovement()
 | 
			
		|||
 | 
			
		||||
						// Technically, the values might have stayed the same, but we'll just assume they've changed.
 | 
			
		||||
						bWasAttachmentModified = true;
 | 
			
		||||
 | 
			
		||||
#if UE_WITH_IRIS
 | 
			
		||||
						// If RepPhysics has changed value then notify the ReplicationSystem
 | 
			
		||||
						if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
 | 
			
		||||
						{
 | 
			
		||||
							UpdateReplicatePhysicsCondition();
 | 
			
		||||
						}
 | 
			
		||||
#endif // UE_WITH_IRIS
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Technically, the values might have stayed the same, but we'll just assume they've changed.
 | 
			
		||||
			bWasRepMovementModified = true;
 | 
			
		||||
 | 
			
		||||
#if UE_WITH_IRIS
 | 
			
		||||
			// If RepPhysics has changed value then notify the ReplicationSystem
 | 
			
		||||
			if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
 | 
			
		||||
			{
 | 
			
		||||
				UpdateReplicatePhysicsCondition();
 | 
			
		||||
			}
 | 
			
		||||
#endif // UE_WITH_IRIS
 | 
			
		||||
		}
 | 
			
		||||
		else if (RootComponent != nullptr)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -272,7 +272,7 @@ void UVRDialComponent::OnGripRelease_Implementation(UGripMotionControllerCompone
 | 
			
		|||
		float AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle));
 | 
			
		||||
		float TargetSnap = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement));
 | 
			
		||||
 | 
			
		||||
		if (FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
 | 
			
		||||
		if (FMath::Abs(/*FRotator::ClampAxis(*/CurRotBackEnd/*)*/ - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
 | 
			
		||||
		{
 | 
			
		||||
			if (AngleOffsetCheck >= SnapAngleThreshold)//FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -592,7 +592,7 @@ void UVRDialComponent::AddDialAngle(float DialAngleDelta, bool bCallEvents, bool
 | 
			
		|||
		float AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle));
 | 
			
		||||
		float TargetSnap = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement));
 | 
			
		||||
 | 
			
		||||
		if (FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
 | 
			
		||||
		if (FMath::Abs(/*FRotator::ClampAxis(*/CurRotBackEnd/*)*/ - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
 | 
			
		||||
		{
 | 
			
		||||
			if (AngleOffsetCheck >= SnapAngleThreshold)//FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -810,7 +810,7 @@ FVector UVRSliderComponent::GetPerAxisSliderProgress()
 | 
			
		|||
	CalculatedLocation = bSlideDistanceIsInParentSpace ? CalculatedLocation * InitialRelativeTransform.GetScale3D() : CalculatedLocation;
 | 
			
		||||
 | 
			
		||||
	// Should need the clamp normally, but if someone is manually setting locations it could go out of bounds
 | 
			
		||||
	FVector Progress;
 | 
			
		||||
	FVector Progress = FVector::ZeroVector;
 | 
			
		||||
 | 
			
		||||
	if (bUseLegacyLogic)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@
 | 
			
		|||
 | 
			
		||||
//#include "Chaos/ParticleHandle.h"
 | 
			
		||||
#include "PhysicsEngine/PhysicsAsset.h"
 | 
			
		||||
#include "PhysicsEngine/PhysicsAsset.h"
 | 
			
		||||
#include "PhysicsEngine/SkeletalBodySetup.h"
 | 
			
		||||
#include "Physics/Experimental/PhysScene_Chaos.h"
 | 
			
		||||
#include "Chaos/KinematicGeometryParticles.h"
 | 
			
		||||
#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,8 @@
 | 
			
		|||
#include "Net/UnrealNetwork.h"
 | 
			
		||||
#include "PhysicsReplication.h"
 | 
			
		||||
#include "PhysicsEngine/PhysicsAsset.h"
 | 
			
		||||
#include "PhysicsEngine/BodySetup.h"
 | 
			
		||||
#include "PhysicsEngine/SkeletalBodySetup.h"
 | 
			
		||||
#if WITH_PUSH_MODEL
 | 
			
		||||
#include "Net/Core/PushModel/PushModel.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +271,7 @@ void UInversePhysicsSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*
 | 
			
		|||
			OutWeldedBodies.Add(BI);
 | 
			
		||||
			if (PhysicsAsset)
 | 
			
		||||
			{
 | 
			
		||||
				if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx])
 | 
			
		||||
				if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx].Get())
 | 
			
		||||
				{
 | 
			
		||||
					OutLabels.Add(PhysicsAssetBodySetup->BoneName);
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,7 +114,7 @@ FORCEINLINE_DEBUGGABLE bool CheckIsTargetInSightPie(const FPerceptionListener& L
 | 
			
		|||
const FAISightTargetVR::FTargetId FAISightTargetVR::InvalidTargetId = FAISystem::InvalidUnsignedID;
 | 
			
		||||
 | 
			
		||||
FAISightTargetVR::FAISightTargetVR(AActor* InTarget, FGenericTeamId InTeamId)
 | 
			
		||||
	: Target(InTarget), SightTargetInterface(nullptr), TeamId(InTeamId)
 | 
			
		||||
	: Target(InTarget), TeamId(InTeamId)
 | 
			
		||||
{
 | 
			
		||||
	if (InTarget)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -587,7 +587,7 @@ UAISense_Sight::EVisibilityResult UAISense_Sight_VR::ComputeVisibility(UWorld* W
 | 
			
		|||
		return UAISense_Sight::EVisibilityResult::NotVisible;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (Target.SightTargetInterface != nullptr)
 | 
			
		||||
	if (IAISightTargetInterface* SightTargetInterface = Target.WeakSightTargetInterface.Get())
 | 
			
		||||
	{
 | 
			
		||||
		const bool bWasVisible = SightQuery.GetLastResult();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -620,7 +620,7 @@ UAISense_Sight::EVisibilityResult UAISense_Sight_VR::ComputeVisibility(UWorld* W
 | 
			
		|||
		Context.IgnoreActor = ListenerActor;
 | 
			
		||||
		Context.bWasVisible = &bWasVisible;
 | 
			
		||||
 | 
			
		||||
		const UAISense_Sight::EVisibilityResult Result = Target.SightTargetInterface->CanBeSeenFrom(Context, OutSeenLocation, OutNumberOfLoSChecksPerformed, OutNumberOfAsyncLosCheckRequested, OutStimulusStrength, &SightQuery.UserData, &OnPendingCanBeSeenQueryProcessedDelegate);
 | 
			
		||||
		const UAISense_Sight::EVisibilityResult Result = SightTargetInterface->CanBeSeenFrom(Context, OutSeenLocation, OutNumberOfLoSChecksPerformed, OutNumberOfAsyncLosCheckRequested, OutStimulusStrength, &SightQuery.UserData, &OnPendingCanBeSeenQueryProcessedDelegate);
 | 
			
		||||
		if (Result == UAISense_Sight::EVisibilityResult::Pending)
 | 
			
		||||
		{
 | 
			
		||||
			// we need to clear the trace info value in order to avoid interfering with the engine processed asynchronous queries
 | 
			
		||||
| 
						 | 
				
			
			@ -842,38 +842,32 @@ void UAISense_Sight_VR::UnregisterSource(AActor& SourceActor)
 | 
			
		|||
	{
 | 
			
		||||
		AActor* TargetActor = AsTarget.Target.Get();
 | 
			
		||||
 | 
			
		||||
		if (TargetActor)
 | 
			
		||||
		{
 | 
			
		||||
			// notify all interested observers that this source is no longer
 | 
			
		||||
			// visible		
 | 
			
		||||
			AIPerception::FListenerMap& ListenersMap = *GetListeners();
 | 
			
		||||
			auto RemoveQuery = [this, &ListenersMap, &AsTargetId, &TargetActor](TArray<FAISightQueryVR>& SightQueries, const int32 QueryIndex)->EReverseForEachResult
 | 
			
		||||
		// notify all interested observers that this source is no longer
 | 
			
		||||
		// visible		
 | 
			
		||||
		AIPerception::FListenerMap& ListenersMap = *GetListeners();
 | 
			
		||||
		auto RemoveQuery = [this, &ListenersMap, &AsTargetId, &TargetActor](TArray<FAISightQueryVR>& SightQueries, const int32 QueryIndex)->EReverseForEachResult
 | 
			
		||||
			{
 | 
			
		||||
				FAISightQueryVR* SightQuery = &SightQueries[QueryIndex];
 | 
			
		||||
				if (SightQuery->TargetId == AsTargetId)
 | 
			
		||||
				{
 | 
			
		||||
					if (SightQuery->GetLastResult())
 | 
			
		||||
					if (SightQuery->GetLastResult() && TargetActor)
 | 
			
		||||
					{
 | 
			
		||||
						FPerceptionListener& Listener = ListenersMap[SightQuery->ObserverId];
 | 
			
		||||
						ensure(Listener.Listener.IsValid());
 | 
			
		||||
 | 
			
		||||
						Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 0.f, SightQuery->LastSeenLocation, Listener.CachedLocation, FAIStimulus::SensingFailed));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					SightQueries.RemoveAtSwap(QueryIndex, 1, EAllowShrinking::No);
 | 
			
		||||
					SightQueries.RemoveAtSwap(QueryIndex, EAllowShrinking::No);
 | 
			
		||||
					return EReverseForEachResult::Modified;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return EReverseForEachResult::UnTouched;
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			ReverseForEach(SightQueriesInRange, RemoveQuery);
 | 
			
		||||
			if (ReverseForEach(SightQueriesOutOfRange, RemoveQuery) == EReverseForEachResult::Modified)
 | 
			
		||||
			{
 | 
			
		||||
				bSightQueriesOutOfRangeDirty = true;
 | 
			
		||||
			}
 | 
			
		||||
			ReverseForEach(SightQueriesPending, RemoveQuery);
 | 
			
		||||
		ReverseForEach(SightQueriesInRange, RemoveQuery);
 | 
			
		||||
		if (ReverseForEach(SightQueriesOutOfRange, RemoveQuery) == EReverseForEachResult::Modified)
 | 
			
		||||
		{
 | 
			
		||||
			bSightQueriesOutOfRangeDirty = true;
 | 
			
		||||
		}
 | 
			
		||||
		ReverseForEach(SightQueriesPending, RemoveQuery);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -883,20 +877,24 @@ bool UAISense_Sight_VR::RegisterTarget(AActor& TargetActor, const TFunction<void
 | 
			
		|||
 | 
			
		||||
	FAISightTargetVR* SightTarget = ObservedTargets.Find(TargetActor.GetUniqueID());
 | 
			
		||||
 | 
			
		||||
	if (SightTarget != nullptr && SightTarget->GetTargetActor() != &TargetActor)
 | 
			
		||||
	// Check if the target is recycled OR new
 | 
			
		||||
	if (SightTarget == nullptr || SightTarget->GetTargetActor() != &TargetActor)
 | 
			
		||||
	{
 | 
			
		||||
		// this means given unique ID has already been recycled. 
 | 
			
		||||
 | 
			
		||||
		FAISightTargetVR NewSightTarget(&TargetActor);
 | 
			
		||||
 | 
			
		||||
		SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget));
 | 
			
		||||
		SightTarget->SightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
 | 
			
		||||
	}
 | 
			
		||||
	else if (SightTarget == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		FAISightTargetVR NewSightTarget(&TargetActor);
 | 
			
		||||
 | 
			
		||||
		SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget));
 | 
			
		||||
		SightTarget->SightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
 | 
			
		||||
		// we're looking at components first and only if nothing is found we proceed to check 
 | 
			
		||||
		// if the TargetActor implements IAISightTargetInterface. The advantage of doing it in 
 | 
			
		||||
		// this order is that you can have components override the original Actor's implementation
 | 
			
		||||
		if (IAISightTargetInterface* InterfaceComponent = TargetActor.FindComponentByInterface<IAISightTargetInterface>())
 | 
			
		||||
		{
 | 
			
		||||
			SightTarget->WeakSightTargetInterface = InterfaceComponent;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			SightTarget->WeakSightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set/update data
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
#include "SceneManagement.h"
 | 
			
		||||
#include "Components/SkeletalMeshComponent.h"
 | 
			
		||||
#include "PhysicsEngine/PhysicsAsset.h"
 | 
			
		||||
#include "PhysicsEngine/ShapeElem.h"
 | 
			
		||||
#include "PhysicsEngine/ConstraintInstance.h"
 | 
			
		||||
#include "ReferenceSkeleton.h"
 | 
			
		||||
#include "DrawDebugHelpers.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,7 +90,7 @@ namespace
 | 
			
		|||
 | 
			
		||||
			void RemoveWidget(UVRFullScreenUserWidget* InWidget)
 | 
			
		||||
			{
 | 
			
		||||
				WidgetsToHide.RemoveSingleSwap(InWidget, false);
 | 
			
		||||
				WidgetsToHide.RemoveSingleSwap(InWidget, EAllowShrinking::No);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1645,7 +1645,7 @@ bool RLE_Funcs::RLEEncodeBuffer(DataType* BufferToEncode, uint32 EncodeLength, T
 | 
			
		|||
 | 
			
		||||
	// Resize the out array to fit compressed contents
 | 
			
		||||
	uint32 Wrote = loc - EncodedLine->GetData();
 | 
			
		||||
	EncodedLine->RemoveAt(Wrote, EncodedLine->Num() - Wrote, true);
 | 
			
		||||
	EncodedLine->RemoveAt(Wrote, EncodedLine->Num() - Wrote, EAllowShrinking::Yes);
 | 
			
		||||
 | 
			
		||||
	// If the compression performed worse than the original file size, throw the results array and use the original instead.
 | 
			
		||||
	// This will almost never happen with voxels but can so should be accounted for.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -434,7 +434,7 @@ void UReplicatedVRCameraComponent::TickComponent(float DeltaTime, enum ELevelTic
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UReplicatedVRCameraComponent::HandleXRCamera()
 | 
			
		||||
void UReplicatedVRCameraComponent::HandleXRCamera(float DeltaTime)
 | 
			
		||||
{
 | 
			
		||||
	bool bIsLocallyControlled = IsLocallyControlled();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -462,7 +462,7 @@ void UReplicatedVRCameraComponent::HandleXRCamera()
 | 
			
		|||
				{
 | 
			
		||||
					FQuat Orientation;
 | 
			
		||||
					FVector Position;
 | 
			
		||||
					if (XRCamera->UpdatePlayerCamera(Orientation, Position))
 | 
			
		||||
					if (XRCamera->UpdatePlayerCamera(Orientation, Position, DeltaTime))
 | 
			
		||||
					{
 | 
			
		||||
						if (HasTrackingParameters())
 | 
			
		||||
						{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
#include "Net/UnrealNetwork.h"
 | 
			
		||||
#include "XRMotionControllerBase.h"
 | 
			
		||||
#include "NavFilters/NavigationQueryFilter.h"
 | 
			
		||||
#include "Misc/EngineNetworkCustomVersion.h"
 | 
			
		||||
//#include "Runtime/Engine/Private/EnginePrivate.h"
 | 
			
		||||
 | 
			
		||||
#if WITH_PUSH_MODEL
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +143,7 @@ AVRBaseCharacter::AVRBaseCharacter(const FObjectInitializer& ObjectInitializer)
 | 
			
		|||
 | 
			
		||||
	// Setting a minimum of every frame for replication consideration (UT uses this value for characters and projectiles).
 | 
			
		||||
	// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
 | 
			
		||||
	MinNetUpdateFrequency = 100.0f;
 | 
			
		||||
	SetMinNetUpdateFrequency(100.0f);
 | 
			
		||||
 | 
			
		||||
	// This is for smooth turning, we have more of a use for this than FPS characters do
 | 
			
		||||
	// Due to roll/pitch almost never being off 0 for VR the cost is just one byte so i'm fine defaulting it here
 | 
			
		||||
| 
						 | 
				
			
			@ -434,6 +435,11 @@ void AVRBaseCharacter::OnRep_ReplicatedMovement()
 | 
			
		|||
	ReppedMovement.Location = ReplicatedMovementVR.Location;
 | 
			
		||||
	ReppedMovement.Rotation = ReplicatedMovementVR.Rotation;
 | 
			
		||||
 | 
			
		||||
	ReppedMovement.ServerFrame = ReplicatedMovementVR.ServerFrame;
 | 
			
		||||
	ReppedMovement.ServerPhysicsHandle = ReplicatedMovementVR.ServerPhysicsHandle;
 | 
			
		||||
	ReppedMovement.bRepAcceleration = ReplicatedMovementVR.bRepAcceleration;
 | 
			
		||||
	ReppedMovement.Acceleration = ReplicatedMovementVR.Acceleration;
 | 
			
		||||
 | 
			
		||||
	Super::OnRep_ReplicatedMovement();
 | 
			
		||||
 | 
			
		||||
	if (!IsLocallyControlled())
 | 
			
		||||
| 
						 | 
				
			
			@ -469,8 +475,14 @@ void AVRBaseCharacter::GatherCurrentMovement()
 | 
			
		|||
	ReplicatedMovementVR.LinearVelocity = ReppedMovement.LinearVelocity;
 | 
			
		||||
	ReplicatedMovementVR.Location = ReppedMovement.Location;
 | 
			
		||||
	ReplicatedMovementVR.Rotation = ReppedMovement.Rotation;
 | 
			
		||||
	ReplicatedMovementVR.ServerFrame = ReppedMovement.ServerFrame;
 | 
			
		||||
	ReplicatedMovementVR.ServerPhysicsHandle = ReppedMovement.ServerPhysicsHandle;
 | 
			
		||||
	ReplicatedMovementVR.bRepAcceleration = ReppedMovement.bRepAcceleration;
 | 
			
		||||
	ReplicatedMovementVR.Acceleration = ReppedMovement.Acceleration;
 | 
			
		||||
 | 
			
		||||
	ReplicatedMovementVR.bJustTeleported = bFlagTeleported;
 | 
			
		||||
	ReplicatedMovementVR.bJustTeleportedGrips = bFlagTeleportedGrips;
 | 
			
		||||
 | 
			
		||||
	bFlagTeleported = false;
 | 
			
		||||
	bFlagTeleportedGrips = false;
 | 
			
		||||
	ReplicatedMovementVR.bPausedTracking = bTrackingPaused;
 | 
			
		||||
| 
						 | 
				
			
			@ -1001,7 +1013,7 @@ FVector AVRBaseCharacter::SetActorLocationAndRotationVR(FVector NewLoc, FRotator
 | 
			
		|||
FVector AVRBaseCharacter::SetActorLocationVR(FVector NewLoc, bool bTeleport, bool bSetCapsuleLocation)
 | 
			
		||||
{
 | 
			
		||||
	FVector NewLocation;
 | 
			
		||||
	FRotator NewRotation;
 | 
			
		||||
	//FRotator NewRotation;
 | 
			
		||||
	FVector PivotOffsetVal = (bSetCapsuleLocation ? GetVRLocation_Inline() : GetProjectedVRLocation()) - GetActorLocation();
 | 
			
		||||
	PivotOffsetVal.Z = 0.0f;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1243,4 +1255,103 @@ void AVRBaseCharacter::SetVRReplicateCapsuleHeight(bool bNewVRReplicateCapsuleHe
 | 
			
		|||
#if WITH_PUSH_MODEL
 | 
			
		||||
	MARK_PROPERTY_DIRTY_FROM_NAME(AVRBaseCharacter, VRReplicateCapsuleHeight, this);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool FRepMovementVRCharacter::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
 | 
			
		||||
{
 | 
			
		||||
	Ar.UsingCustomVersion(FEngineNetworkCustomVersion::Guid);
 | 
			
		||||
 | 
			
		||||
	FRepMovement BaseSettings = Owner ? Owner->GetReplicatedMovement() : FRepMovement();
 | 
			
		||||
 | 
			
		||||
	// pack bitfield with flags
 | 
			
		||||
	const bool bServerFrameAndHandleSupported = Ar.EngineNetVer() >= FEngineNetworkCustomVersion::RepMoveServerFrameAndHandle && Ar.EngineNetVer() != FEngineNetworkCustomVersion::Ver21AndViewPitchOnly_DONOTUSE;
 | 
			
		||||
	uint8 Flags = (bSimulatedPhysicSleep << 0) | (bRepPhysics << 1) | (bJustTeleported << 2) | (bJustTeleportedGrips << 3) | (bPausedTracking << 4);
 | 
			
		||||
	Ar.SerializeBits(&Flags, 5);
 | 
			
		||||
	bSimulatedPhysicSleep = (Flags & (1 << 0)) ? 1 : 0;
 | 
			
		||||
	bRepPhysics = (Flags & (1 << 1)) ? 1 : 0;
 | 
			
		||||
	const bool bRepServerFrame = (Flags & (1 << 2) && bServerFrameAndHandleSupported) ? 1 : 0;
 | 
			
		||||
	const bool bRepServerHandle = (Flags & (1 << 3) && bServerFrameAndHandleSupported) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	bJustTeleported = (Flags & (1 << 2)) ? 1 : 0;
 | 
			
		||||
	bJustTeleportedGrips = (Flags & (1 << 3)) ? 1 : 0;
 | 
			
		||||
	bPausedTracking = (Flags & (1 << 4)) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	bOutSuccess = true;
 | 
			
		||||
 | 
			
		||||
	if (bPausedTracking)
 | 
			
		||||
	{
 | 
			
		||||
		bOutSuccess &= PausedTrackingLoc.NetSerialize(Ar, Map, bOutSuccess);
 | 
			
		||||
 | 
			
		||||
		uint16 Yaw = 0;
 | 
			
		||||
		if (Ar.IsSaving())
 | 
			
		||||
		{
 | 
			
		||||
			Yaw = FRotator::CompressAxisToShort(PausedTrackingRot);
 | 
			
		||||
			Ar << Yaw;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			Ar << Yaw;
 | 
			
		||||
			PausedTrackingRot = Yaw;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// update location, rotation, linear velocity
 | 
			
		||||
	bOutSuccess &= SerializeQuantizedVector(Ar, Location, BaseSettings.LocationQuantizationLevel);
 | 
			
		||||
 | 
			
		||||
	switch (BaseSettings.RotationQuantizationLevel)
 | 
			
		||||
	{
 | 
			
		||||
	case ERotatorQuantization::ByteComponents:
 | 
			
		||||
	{
 | 
			
		||||
		Rotation.SerializeCompressed(Ar);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	case ERotatorQuantization::ShortComponents:
 | 
			
		||||
	{
 | 
			
		||||
		Rotation.SerializeCompressedShort(Ar);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bOutSuccess &= SerializeQuantizedVector(Ar, LinearVelocity, BaseSettings.VelocityQuantizationLevel);
 | 
			
		||||
 | 
			
		||||
	// update angular velocity if required
 | 
			
		||||
	if (bRepPhysics)
 | 
			
		||||
	{
 | 
			
		||||
		bOutSuccess &= SerializeQuantizedVector(Ar, AngularVelocity, BaseSettings.VelocityQuantizationLevel);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bRepServerFrame)
 | 
			
		||||
	{
 | 
			
		||||
		uint32 uServerFrame = (uint32)ServerFrame;
 | 
			
		||||
		Ar.SerializeIntPacked(uServerFrame);
 | 
			
		||||
		ServerFrame = (int32)uServerFrame;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bRepServerHandle)
 | 
			
		||||
	{
 | 
			
		||||
		uint32 uServerPhysicsHandle = (uint32)ServerPhysicsHandle;
 | 
			
		||||
		Ar.SerializeIntPacked(uServerPhysicsHandle);
 | 
			
		||||
		ServerPhysicsHandle = (int32)uServerPhysicsHandle;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (Ar.EngineNetVer() >= FEngineNetworkCustomVersion::RepMoveOptionalAcceleration)
 | 
			
		||||
	{
 | 
			
		||||
		uint8 AccelFlags = (bRepAcceleration << 0);
 | 
			
		||||
		Ar.SerializeBits(&AccelFlags, 1);
 | 
			
		||||
		bRepAcceleration = (AccelFlags & (1 << 0)) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
		if (bRepAcceleration)
 | 
			
		||||
		{
 | 
			
		||||
			// Note that we're using the same quantization as Velocity, since the units are commonly on the same order
 | 
			
		||||
			bOutSuccess &= SerializeQuantizedVector(Ar, Acceleration, VelocityQuantizationLevel);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else if (Ar.IsLoading())
 | 
			
		||||
	{
 | 
			
		||||
		bRepAcceleration = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -489,14 +489,32 @@ void UVRBaseCharacterMovementComponent::EndPushBackNotification()
 | 
			
		|||
 | 
			
		||||
FVector UVRBaseCharacterMovementComponent::GetActorFeetLocationVR() const
 | 
			
		||||
{
 | 
			
		||||
	if (AVRBaseCharacter * BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
 | 
			
		||||
 | 
			
		||||
	const UCapsuleComponent* const CapsuleComponent = CharacterOwner ? CharacterOwner->GetCapsuleComponent() : Cast<UCapsuleComponent>(UpdatedComponent);
 | 
			
		||||
	if (CapsuleComponent)
 | 
			
		||||
	{
 | 
			
		||||
		const float HalfHeight = CapsuleComponent->GetScaledCapsuleHalfHeight();
 | 
			
		||||
		if (AVRBaseCharacter* BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
 | 
			
		||||
		{
 | 
			
		||||
			return BaseCharacter->OffsetComponentToWorld.GetLocation() + HalfHeight * GetGravityDirection();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			return UpdatedComponent->GetComponentLocation() + HalfHeight * GetGravityDirection();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Super::GetActorFeetLocation();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/*if (AVRBaseCharacter* BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
 | 
			
		||||
	{
 | 
			
		||||
		return UpdatedComponent ? (BaseCharacter->OffsetComponentToWorld.GetLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		return UpdatedComponent ? (UpdatedComponent->GetComponentLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
 | 
			
		||||
	}
 | 
			
		||||
	}*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UVRBaseCharacterMovementComponent::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
 | 
			
		||||
| 
						 | 
				
			
			@ -533,7 +551,8 @@ bool UVRBaseCharacterMovementComponent::FloorSweepTest(
 | 
			
		|||
		const FCollisionShape BoxShape = FCollisionShape::MakeBox(FVector(CapsuleRadius * 0.707f, CapsuleRadius * 0.707f, CapsuleHeight));
 | 
			
		||||
 | 
			
		||||
		// First test with the box rotated so the corners are along the major axes (ie rotated 45 degrees).
 | 
			
		||||
		bBlockingHit = GetWorld()->SweepSingleByChannel(OutHit, Start, End, FQuat(RotateGravityToWorld(FVector(0.f, 0.f, -1.f)), UE_PI * 0.25f), TraceChannel, BoxShape, Params, ResponseParam);
 | 
			
		||||
		//bBlockingHit = GetWorld()->SweepSingleByChannel(OutHit, Start, End, FQuat(RotateGravityToWorld(FVector(0.f, 0.f, -1.f)), UE_PI * 0.25f), TraceChannel, BoxShape, Params, ResponseParam);
 | 
			
		||||
		bBlockingHit = GetWorld()->SweepSingleByChannel(OutHit, Start, End, FQuat(GetGravityDirection(), UE_PI * 0.25f), TraceChannel, BoxShape, Params, ResponseParam);
 | 
			
		||||
 | 
			
		||||
		if (!bBlockingHit)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -558,8 +577,8 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
 | 
			
		|||
	if (DownwardSweepResult != NULL && DownwardSweepResult->IsValidBlockingHit())
 | 
			
		||||
	{
 | 
			
		||||
		// Only if the supplied sweep was vertical and downward.
 | 
			
		||||
		const bool bIsDownward = RotateWorldToGravity(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).Z > 0;
 | 
			
		||||
		const bool bIsVertical = RotateWorldToGravity(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= UE_KINDA_SMALL_NUMBER;
 | 
			
		||||
		const bool bIsDownward = GetGravitySpaceZ(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd) > 0;
 | 
			
		||||
		const bool bIsVertical = ProjectToGravityFloor(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared() <= UE_KINDA_SMALL_NUMBER;
 | 
			
		||||
		if (bIsDownward && bIsVertical)
 | 
			
		||||
		{
 | 
			
		||||
			// Reject hits that are barely on the cusp of the radius of the capsule
 | 
			
		||||
| 
						 | 
				
			
			@ -569,7 +588,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
 | 
			
		|||
				bSkipSweep = true;
 | 
			
		||||
 | 
			
		||||
				const bool bIsWalkable = IsWalkable(*DownwardSweepResult);
 | 
			
		||||
				const float FloorDist = RotateWorldToGravity(CapsuleLocation - DownwardSweepResult->Location).Z;
 | 
			
		||||
				const float FloorDist = GetGravitySpaceZ(CapsuleLocation - DownwardSweepResult->Location);
 | 
			
		||||
				OutFloorResult.SetFromSweep(*DownwardSweepResult, FloorDist, bIsWalkable);
 | 
			
		||||
 | 
			
		||||
				if (bIsWalkable)
 | 
			
		||||
| 
						 | 
				
			
			@ -610,7 +629,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
 | 
			
		|||
		FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(SweepRadius, PawnHalfHeight - ShrinkHeight);
 | 
			
		||||
 | 
			
		||||
		FHitResult Hit(1.f);
 | 
			
		||||
		bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + RotateGravityToWorld(FVector(0.f, 0.f, -TraceDist)), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
 | 
			
		||||
		bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + TraceDist * GetGravityDirection(), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
 | 
			
		||||
 | 
			
		||||
		if (bBlockingHit)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -628,7 +647,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
 | 
			
		|||
					CapsuleShape.Capsule.HalfHeight = FMath::Max(PawnHalfHeight - ShrinkHeight, CapsuleShape.Capsule.Radius);
 | 
			
		||||
					Hit.Reset(1.f, false);
 | 
			
		||||
 | 
			
		||||
					bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + RotateGravityToWorld(FVector(0.f, 0.f, -TraceDist)), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
 | 
			
		||||
					bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + TraceDist * GetGravityDirection(), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -664,7 +683,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
 | 
			
		|||
		const float ShrinkHeight = PawnHalfHeight;
 | 
			
		||||
		const FVector LineTraceStart = CapsuleLocation;
 | 
			
		||||
		const float TraceDist = LineDistance + ShrinkHeight;
 | 
			
		||||
		const FVector Down = RotateGravityToWorld(FVector(0.f, 0.f, -TraceDist));
 | 
			
		||||
		const FVector Down = TraceDist * GetGravityDirection();
 | 
			
		||||
		QueryParams.TraceTag = SCENE_QUERY_STAT_NAME_ONLY(FloorLineTrace);
 | 
			
		||||
 | 
			
		||||
		FHitResult Hit(1.f);
 | 
			
		||||
| 
						 | 
				
			
			@ -703,31 +722,32 @@ float UVRBaseCharacterMovementComponent::SlideAlongSurface(const FVector& Delta,
 | 
			
		|||
		return 0.f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FVector Normal(RotateWorldToGravity(InNormal));
 | 
			
		||||
	FVector Normal(InNormal);
 | 
			
		||||
	const FVector::FReal NormalZ = GetGravitySpaceZ(Normal);
 | 
			
		||||
	if (IsMovingOnGround())
 | 
			
		||||
	{
 | 
			
		||||
		// We don't want to be pushed up an unwalkable surface.
 | 
			
		||||
		if (Normal.Z > 0.f)
 | 
			
		||||
		if (NormalZ > 0.f)
 | 
			
		||||
		{
 | 
			
		||||
			if (!IsWalkable(Hit))
 | 
			
		||||
			{
 | 
			
		||||
				Normal = Normal.GetSafeNormal2D();
 | 
			
		||||
				Normal = ProjectToGravityFloor(Normal).GetSafeNormal();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (Normal.Z < -UE_KINDA_SMALL_NUMBER)
 | 
			
		||||
		else if (NormalZ < -UE_KINDA_SMALL_NUMBER)
 | 
			
		||||
		{
 | 
			
		||||
			// Don't push down into the floor when the impact is on the upper portion of the capsule.
 | 
			
		||||
			if (CurrentFloor.FloorDist < MIN_FLOOR_DIST && CurrentFloor.bBlockingHit)
 | 
			
		||||
			{
 | 
			
		||||
				const FVector FloorNormal = RotateWorldToGravity(CurrentFloor.HitResult.Normal);
 | 
			
		||||
				const bool bFloorOpposedToMovement = (RotateWorldToGravity(Delta) | FloorNormal) < 0.f && (FloorNormal.Z < 1.f - UE_DELTA);
 | 
			
		||||
				const FVector FloorNormal = CurrentFloor.HitResult.Normal;
 | 
			
		||||
				const bool bFloorOpposedToMovement = (Delta | FloorNormal) < 0.f && (GetGravitySpaceZ(FloorNormal) < 1.f - UE_DELTA);
 | 
			
		||||
 | 
			
		||||
				if (bFloorOpposedToMovement)
 | 
			
		||||
				{
 | 
			
		||||
					Normal = FloorNormal;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Normal = Normal.GetSafeNormal2D();
 | 
			
		||||
				Normal = ProjectToGravityFloor(Normal).GetSafeNormal();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -744,9 +764,9 @@ float UVRBaseCharacterMovementComponent::SlideAlongSurface(const FVector& Delta,
 | 
			
		|||
	// that we have already validated the floor normal.
 | 
			
		||||
	// Otherwise just pass in as normal, either way skip the parents implementation as we are doing it now.
 | 
			
		||||
	if (IsMovingOnGround() || (MovementMode == MOVE_Custom && CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing))
 | 
			
		||||
		return Super::Super::SlideAlongSurface(Delta * VRWallSlideScaler, Time, RotateGravityToWorld(Normal), Hit, bHandleImpact);
 | 
			
		||||
		return Super::Super::SlideAlongSurface(Delta * VRWallSlideScaler, Time, Normal, Hit, bHandleImpact);
 | 
			
		||||
	else
 | 
			
		||||
		return Super::Super::SlideAlongSurface(Delta, Time, RotateGravityToWorld(Normal), Hit, bHandleImpact);
 | 
			
		||||
		return Super::Super::SlideAlongSurface(Delta, Time, Normal, Hit, bHandleImpact);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*void UVRBaseCharacterMovementComponent::SetCrouchedHalfHeight(float NewCrouchedHalfHeight)
 | 
			
		||||
| 
						 | 
				
			
			@ -2019,12 +2039,12 @@ void UVRBaseCharacterMovementComponent::SmoothCorrection(const FVector& OldLocat
 | 
			
		|||
 | 
			
		||||
		// The mesh doesn't move, but the capsule does so we have a new offset.
 | 
			
		||||
		FVector NewToOldVector = (OldWorldLocation - NewWorldLocation);
 | 
			
		||||
		if (bIsNavWalkingOnServer && FMath::Abs(NewToOldVector.Z) < NavWalkingFloorDistTolerance)
 | 
			
		||||
		if (bIsNavWalkingOnServer && FMath::Abs(GetGravitySpaceZ(NewToOldVector)) < NavWalkingFloorDistTolerance)
 | 
			
		||||
		{
 | 
			
		||||
			// ignore smoothing on Z axis
 | 
			
		||||
			// don't modify new location (local simulation result), since it's probably more accurate than server data
 | 
			
		||||
			// and shouldn't matter as long as difference is relatively small
 | 
			
		||||
			NewToOldVector.Z = 0;
 | 
			
		||||
			NewToOldVector = ProjectToGravityFloor(NewToOldVector);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const float DistSq = NewToOldVector.SizeSquared();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -173,7 +173,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
 | 
			
		|||
			else
 | 
			
		||||
				capLocation = UpdatedComponent->GetComponentLocation();
 | 
			
		||||
 | 
			
		||||
			const bool bEncroached = GetWorld()->OverlapBlockingTestByChannel(capLocation - FVector(0.f, 0.f, ScaledHalfHeightAdjust), FQuat::Identity,
 | 
			
		||||
			const bool bEncroached = GetWorld()->OverlapBlockingTestByChannel(capLocation - (ScaledHalfHeightAdjust * GetGravityDirection()), FQuat::Identity,
 | 
			
		||||
				UpdatedComponent->GetCollisionObjectType(), GetPawnCapsuleCollisionShape(SHRINK_None), CapsuleParams, ResponseParam);
 | 
			
		||||
 | 
			
		||||
			// If encroached, cancel
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +192,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
 | 
			
		|||
		if (bCrouchMaintainsBaseLocation)
 | 
			
		||||
		{
 | 
			
		||||
			// Intentionally not using MoveUpdatedComponent, where a horizontal plane constraint would prevent the base of the capsule from staying at the same spot.
 | 
			
		||||
			//UpdatedComponent->MoveComponent(FVector(0.f, 0.f, -ScaledHalfHeightAdjust), UpdatedComponent->GetComponentQuat(), true, nullptr, EMoveComponentFlags::MOVECOMP_NoFlags, ETeleportType::TeleportPhysics);
 | 
			
		||||
			//UpdatedComponent->MoveComponent(ScaledHalfHeightAdjust * GetGravityDirection(), UpdatedComponent->GetComponentQuat(), true, nullptr, EMoveComponentFlags::MOVECOMP_NoFlags, ETeleportType::TeleportPhysics);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		CharacterOwner->bIsCrouched = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -219,7 +219,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
 | 
			
		|||
 | 
			
		||||
			if (ClientData)
 | 
			
		||||
			{
 | 
			
		||||
				ClientData->MeshTranslationOffset -= FVector(0.f, 0.f, MeshAdjust);
 | 
			
		||||
				ClientData->MeshTranslationOffset -= MeshAdjust * -GetGravityDirection();
 | 
			
		||||
				ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +280,7 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
 | 
			
		|||
		if (!bCrouchMaintainsBaseLocation)
 | 
			
		||||
		{
 | 
			
		||||
			// Expand in place
 | 
			
		||||
			bEncroached = MyWorld->OverlapBlockingTestByChannel(PawnLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
			bEncroached = MyWorld->OverlapBlockingTestByChannel(PawnLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
 | 
			
		||||
			if (bEncroached)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -292,11 +292,11 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
 | 
			
		|||
					CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
 | 
			
		||||
					const float ShrinkHalfHeight = PawnHalfHeight - PawnRadius;
 | 
			
		||||
					const float TraceDist = PawnHalfHeight - ShrinkHalfHeight;
 | 
			
		||||
					const FVector Down = FVector(0.f, 0.f, -TraceDist);
 | 
			
		||||
					const FVector Down = TraceDist * GetGravityDirection();
 | 
			
		||||
 | 
			
		||||
					FHitResult Hit(1.f);
 | 
			
		||||
					const FCollisionShape ShortCapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_HeightCustom, ShrinkHalfHeight);
 | 
			
		||||
					const bool bBlockingHit = MyWorld->SweepSingleByChannel(Hit, PawnLocation, PawnLocation + Down, FQuat::Identity, CollisionChannel, ShortCapsuleShape, CapsuleParams);
 | 
			
		||||
					const bool bBlockingHit = MyWorld->SweepSingleByChannel(Hit, PawnLocation, PawnLocation + Down, GetWorldToGravityTransform(), CollisionChannel, ShortCapsuleShape, CapsuleParams);
 | 
			
		||||
					if (Hit.bStartPenetrating)
 | 
			
		||||
					{
 | 
			
		||||
						bEncroached = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -305,8 +305,9 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
 | 
			
		|||
					{
 | 
			
		||||
						// Compute where the base of the sweep ended up, and see if we can stand there
 | 
			
		||||
						const float DistanceToBase = (Hit.Time * TraceDist) + ShortCapsuleShape.Capsule.HalfHeight;
 | 
			
		||||
						const FVector NewLoc = FVector(PawnLocation.X, PawnLocation.Y, PawnLocation.Z - DistanceToBase + StandingCapsuleShape.Capsule.HalfHeight + SweepInflation + MIN_FLOOR_DIST / 2.f);
 | 
			
		||||
						bEncroached = MyWorld->OverlapBlockingTestByChannel(NewLoc, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
						const FVector Adjustment = (-DistanceToBase + StandingCapsuleShape.Capsule.HalfHeight + SweepInflation + MIN_FLOOR_DIST / 2.f) * -GetGravityDirection();
 | 
			
		||||
						const FVector NewLoc = PawnLocation + Adjustment;
 | 
			
		||||
						bEncroached = MyWorld->OverlapBlockingTestByChannel(NewLoc, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
						if (!bEncroached)
 | 
			
		||||
						{
 | 
			
		||||
							// Intentionally not using MoveUpdatedComponent, where a horizontal plane constraint would prevent the base of the capsule from staying at the same spot.
 | 
			
		||||
| 
						 | 
				
			
			@ -319,8 +320,8 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
 | 
			
		|||
		else
 | 
			
		||||
		{
 | 
			
		||||
			// Expand while keeping base location the same.
 | 
			
		||||
			FVector StandingLocation = PawnLocation + FVector(0.f, 0.f, StandingCapsuleShape.GetCapsuleHalfHeight() - CurrentCrouchedHalfHeight);
 | 
			
		||||
			bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
			FVector StandingLocation = PawnLocation + (StandingCapsuleShape.GetCapsuleHalfHeight() - CurrentCrouchedHalfHeight) * -GetGravityDirection();
 | 
			
		||||
			bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
 | 
			
		||||
			if (bEncroached)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -330,8 +331,8 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
 | 
			
		|||
					const float MinFloorDist = UE_KINDA_SMALL_NUMBER * 10.f;
 | 
			
		||||
					if (CurrentFloor.bBlockingHit && CurrentFloor.FloorDist > MinFloorDist)
 | 
			
		||||
					{
 | 
			
		||||
						StandingLocation.Z -= CurrentFloor.FloorDist - MinFloorDist;
 | 
			
		||||
						bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
						StandingLocation -= (CurrentFloor.FloorDist - MinFloorDist) * -GetGravityDirection();
 | 
			
		||||
						bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -375,7 +376,7 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
 | 
			
		|||
 | 
			
		||||
		if (ClientData)
 | 
			
		||||
		{
 | 
			
		||||
			ClientData->MeshTranslationOffset += FVector(0.f, 0.f, MeshAdjust);
 | 
			
		||||
			ClientData->MeshTranslationOffset += MeshAdjust * -GetGravityDirection();
 | 
			
		||||
			ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset;
 | 
			
		||||
		}
 | 
			
		||||
	}*/
 | 
			
		||||
| 
						 | 
				
			
			@ -856,7 +857,7 @@ bool UVRCharacterMovementComponent::ShouldCheckForValidLandingSpot(float DeltaTi
 | 
			
		|||
{
 | 
			
		||||
	// See if we hit an edge of a surface on the lower portion of the capsule.
 | 
			
		||||
	// In this case the normal will not equal the impact normal, and a downward sweep may find a walkable surface on top of the edge.
 | 
			
		||||
	if (Hit.Normal.Z > UE_KINDA_SMALL_NUMBER && !Hit.Normal.Equals(Hit.ImpactNormal))
 | 
			
		||||
	if (GetGravitySpaceZ(Hit.Normal) > UE_KINDA_SMALL_NUMBER && !Hit.Normal.Equals(Hit.ImpactNormal))
 | 
			
		||||
	{
 | 
			
		||||
		FVector PawnLocation = UpdatedComponent->GetComponentLocation();
 | 
			
		||||
		if (VRRootCapsule)
 | 
			
		||||
| 
						 | 
				
			
			@ -932,8 +933,12 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
 | 
			
		|||
		const FVector OldVelocity = Velocity;
 | 
			
		||||
		Acceleration = FVector::VectorPlaneProject(Acceleration, -GetGravityDirection());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		static const auto CVarLedgeMovementApplyDirectMove = IConsoleManager::Get().FindConsoleVariable(TEXT("p.LedgeMovement.ApplyDirectMove"));
 | 
			
		||||
		
 | 
			
		||||
		// Apply acceleration
 | 
			
		||||
		if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
 | 
			
		||||
		const bool bSkipForLedgeMove = bTriedLedgeMove && CVarLedgeMovementApplyDirectMove->GetBool();
 | 
			
		||||
		if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && !bSkipForLedgeMove)
 | 
			
		||||
		{
 | 
			
		||||
			CalcVelocity(timeTick, GroundFriction, false, GetMaxBrakingDeceleration());
 | 
			
		||||
			devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
 | 
			
		||||
| 
						 | 
				
			
			@ -999,7 +1004,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
 | 
			
		|||
				const float DesiredDist = Delta.Size();
 | 
			
		||||
				if (DesiredDist > UE_KINDA_SMALL_NUMBER)
 | 
			
		||||
				{
 | 
			
		||||
					const float ActualDist = (UpdatedComponent->GetComponentLocation() - OldLocation).Size2D();
 | 
			
		||||
					const float ActualDist = ProjectToGravityFloor(UpdatedComponent->GetComponentLocation() - OldLocation).Size();
 | 
			
		||||
					remainingTime += timeTick * (1.f - FMath::Min(1.f, ActualDist / DesiredDist));
 | 
			
		||||
				}
 | 
			
		||||
				RestorePreAdditiveVRMotionVelocity();
 | 
			
		||||
| 
						 | 
				
			
			@ -1024,8 +1029,10 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
 | 
			
		|||
		if (bCheckLedges && !CurrentFloor.IsWalkableFloor())
 | 
			
		||||
		{
 | 
			
		||||
			// calculate possible alternate movement
 | 
			
		||||
			const FVector GravDir = GetGravityDirection();
 | 
			
		||||
			const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldCapsuleLocation, Delta, GravDir);
 | 
			
		||||
			const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldLocation, Delta, OldFloor);
 | 
			
		||||
			// REMOVED 5.5 and replaced with above
 | 
			
		||||
			//const FVector GravDir = GetGravityDirection();
 | 
			
		||||
			//const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldCapsuleLocation, Delta, GravDir);
 | 
			
		||||
			if (!NewDelta.IsZero())
 | 
			
		||||
			{
 | 
			
		||||
				// first revert this move
 | 
			
		||||
| 
						 | 
				
			
			@ -1037,6 +1044,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
 | 
			
		|||
				// Try new movement direction
 | 
			
		||||
				Velocity = NewDelta / timeTick;
 | 
			
		||||
				remainingTime += timeTick;
 | 
			
		||||
				Iterations--;
 | 
			
		||||
				RestorePreAdditiveVRMotionVelocity();
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -1084,7 +1092,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
 | 
			
		|||
				// The floor check failed because it started in penetration
 | 
			
		||||
				// We do not want to try to move downward because the downward sweep failed, rather we'd like to try to pop out of the floor.
 | 
			
		||||
				FHitResult Hit(CurrentFloor.HitResult);
 | 
			
		||||
				Hit.TraceEnd = Hit.TraceStart + RotateGravityToWorld(FVector(0.f, 0.f, MAX_FLOOR_DIST));
 | 
			
		||||
				Hit.TraceEnd = Hit.TraceStart + MAX_FLOOR_DIST * -GetGravityDirection();
 | 
			
		||||
				const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
 | 
			
		||||
				ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
 | 
			
		||||
				bForceNextFloorCheck = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1168,8 +1176,10 @@ void UVRCharacterMovementComponent::CapsuleTouched(UPrimitiveComponent* Overlapp
 | 
			
		|||
		}
 | 
			
		||||
		
 | 
			
		||||
		const FVector Loc = VRRootCapsule->OffsetComponentToWorld.GetLocation();//UpdatedComponent->GetComponentLocation();
 | 
			
		||||
		FVector ImpulseDir = FVector(OtherLoc.X - Loc.X, OtherLoc.Y - Loc.Y, 0.25f).GetSafeNormal();
 | 
			
		||||
		ImpulseDir = (ImpulseDir + Velocity.GetSafeNormal2D()) * 0.5f;
 | 
			
		||||
 | 
			
		||||
		FVector ImpulseDir = OtherLoc - Loc;
 | 
			
		||||
		SetGravitySpaceZ(ImpulseDir, 0.25f);
 | 
			
		||||
		ImpulseDir = (ImpulseDir.GetSafeNormal() + ProjectToGravityFloor(Velocity).GetSafeNormal()) * 0.5f;
 | 
			
		||||
		ImpulseDir.Normalize();
 | 
			
		||||
 | 
			
		||||
		FName BoneName = NAME_None;
 | 
			
		||||
| 
						 | 
				
			
			@ -1186,7 +1196,7 @@ void UVRCharacterMovementComponent::CapsuleTouched(UPrimitiveComponent* Overlapp
 | 
			
		|||
			TouchForceFactorModified *= BI ? BI->GetBodyMass() : 1.0f;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		float ImpulseStrength = FMath::Clamp(Velocity.Size2D() * TouchForceFactorModified,
 | 
			
		||||
		float ImpulseStrength = FMath::Clamp<FVector::FReal>(ProjectToGravityFloor(Velocity).Size() * TouchForceFactorModified,
 | 
			
		||||
			MinTouchForce > 0.0f ? MinTouchForce : -FLT_MAX,
 | 
			
		||||
			MaxTouchForce > 0.0f ? MaxTouchForce : FLT_MAX);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1541,7 +1551,7 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
 | 
			
		|||
				// Trace to get the hit location on the capsule
 | 
			
		||||
				FHitResult Hit;
 | 
			
		||||
				bool bHasHit = UpdatedPrimitive->LineTraceComponent(Hit, BodyLocation,
 | 
			
		||||
					FVector(MyLocation.X, MyLocation.Y, BodyLocation.Z),
 | 
			
		||||
					ProjectToGravityFloor(MyLocation) + GetGravitySpaceComponentZ(BodyLocation),
 | 
			
		||||
					QueryParams);
 | 
			
		||||
 | 
			
		||||
				FVector HitLoc = Hit.ImpactPoint;
 | 
			
		||||
| 
						 | 
				
			
			@ -1554,12 +1564,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
 | 
			
		|||
					bIsPenetrating = true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				const float DistanceNow = (HitLoc - BodyLocation).SizeSquared2D();
 | 
			
		||||
				const float DistanceLater = (HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared2D();
 | 
			
		||||
				const float DistanceNow = ProjectToGravityFloor(HitLoc - BodyLocation).SizeSquared();
 | 
			
		||||
				const float DistanceLater = ProjectToGravityFloor(HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared();
 | 
			
		||||
 | 
			
		||||
				if (bHasHit && DistanceNow < StopBodyDistance && !bIsPenetrating)
 | 
			
		||||
				{
 | 
			
		||||
					OverlapBody->SetLinearVelocity(FVector(0.0f, 0.0f, 0.0f), false);
 | 
			
		||||
					OverlapBody->SetLinearVelocity(FVector::ZeroVector, false);
 | 
			
		||||
				}
 | 
			
		||||
				else if (DistanceLater <= DistanceNow || bIsPenetrating)
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			@ -1567,11 +1577,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
 | 
			
		|||
 | 
			
		||||
					if (bHasHit)
 | 
			
		||||
					{
 | 
			
		||||
						ForceCenter.Z = HitLoc.Z;
 | 
			
		||||
						SetGravitySpaceZ(ForceCenter, GetGravitySpaceZ(HitLoc));
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						ForceCenter.Z = FMath::Clamp(BodyLocation.Z, MyLocation.Z - CapsuleHalfHeight, MyLocation.Z + CapsuleHalfHeight);
 | 
			
		||||
						const FVector::FReal MyLocationZ = GetGravitySpaceZ(MyLocation);
 | 
			
		||||
						SetGravitySpaceZ(ForceCenter, FMath::Clamp(GetGravitySpaceZ(BodyLocation), MyLocationZ - CapsuleHalfHeight, MyLocationZ + CapsuleHalfHeight));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					OverlapBody->AddRadialForceToBody(ForceCenter, RepulsionForceRadius, RepulsionForce * Mass, ERadialImpulseFalloff::RIF_Constant);
 | 
			
		||||
| 
						 | 
				
			
			@ -1650,7 +1661,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Move along the current floor
 | 
			
		||||
	const FVector Delta = RotateGravityToWorld(RotateWorldToGravity(InVelocity) * FVector(1.0, 1.0, 0.0)) * DeltaSeconds;
 | 
			
		||||
	const FVector Delta = ProjectToGravityFloor(InVelocity) * DeltaSeconds;
 | 
			
		||||
	FHitResult Hit(1.f);
 | 
			
		||||
	FVector RampVector = ComputeGroundMovementDelta(Delta, CurrentFloor.HitResult, CurrentFloor.bLineTrace);
 | 
			
		||||
	SafeMoveUpdatedComponent(RampVector, UpdatedComponent->GetComponentQuat(), true, Hit);
 | 
			
		||||
| 
						 | 
				
			
			@ -1671,7 +1682,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
 | 
			
		|||
	{
 | 
			
		||||
		// We impacted something (most likely another ramp, but possibly a barrier).
 | 
			
		||||
		float PercentTimeApplied = Hit.Time;
 | 
			
		||||
		if ((Hit.Time > 0.f) && (Hit.Normal.Z > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit))
 | 
			
		||||
		if ((Hit.Time > 0.f) && (GetGravitySpaceZ(Hit.Normal) > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit))
 | 
			
		||||
		{
 | 
			
		||||
			// Another walkable ramp.
 | 
			
		||||
			const float InitialPercentRemaining = 1.f - PercentTimeApplied;
 | 
			
		||||
| 
						 | 
				
			
			@ -1691,8 +1702,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
 | 
			
		|||
				const FVector PreStepUpLocation = UpdatedComponent->GetComponentLocation();
 | 
			
		||||
				const FVector GravDir = GetGravityDirection();
 | 
			
		||||
 | 
			
		||||
				// I add in the HMD difference from last frame to the step up check to enforce it stepping up
 | 
			
		||||
				if (!StepUp(GravDir, (Delta * (1.f - PercentTimeApplied)) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, OutStepDownResult))
 | 
			
		||||
				if (!StepUp(GetGravityDirection(), Delta * (1.f - PercentTimeApplied), Hit, OutStepDownResult))
 | 
			
		||||
				{
 | 
			
		||||
					UE_LOG(LogVRCharacterMovement, Verbose, TEXT("- StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString());
 | 
			
		||||
					HandleImpact(Hit, LastMoveTimeSlice, RampVector);
 | 
			
		||||
| 
						 | 
				
			
			@ -1710,7 +1720,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
 | 
			
		|||
						if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && StepUpTimeSlice >= UE_KINDA_SMALL_NUMBER)
 | 
			
		||||
						{
 | 
			
		||||
							Velocity = (UpdatedComponent->GetComponentLocation() - PreStepUpLocation) / StepUpTimeSlice;
 | 
			
		||||
							Velocity = FVector::VectorPlaneProject(Velocity, -GravDir);
 | 
			
		||||
							Velocity = ProjectToGravityFloor(Velocity);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -1745,8 +1755,9 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
 | 
			
		|||
	CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
 | 
			
		||||
 | 
			
		||||
	// Don't bother stepping up if top of capsule is hitting something.
 | 
			
		||||
	const float InitialImpactZ = InHit.ImpactPoint.Z;
 | 
			
		||||
	if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius))
 | 
			
		||||
	const float InitialImpactZ = InHit.ImpactPoint | -GravDir;
 | 
			
		||||
	const float OldLocationZ = OldLocation | -GravDir;
 | 
			
		||||
	if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius))
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1762,7 +1773,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
 | 
			
		|||
	float StepTravelUpHeight = MaxStepHeight;
 | 
			
		||||
	float StepTravelDownHeight = StepTravelUpHeight;
 | 
			
		||||
	const float StepSideZ = -1.f * FVector::DotProduct(InHit.ImpactNormal, GravDir);//const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir);
 | 
			
		||||
	float PawnInitialFloorBaseZ = OldLocation.Z -PawnHalfHeight;
 | 
			
		||||
	float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight;
 | 
			
		||||
	float PawnFloorPointZ = PawnInitialFloorBaseZ;
 | 
			
		||||
 | 
			
		||||
	if (IsMovingOnGround() && CurrentFloor.IsWalkableFloor())
 | 
			
		||||
| 
						 | 
				
			
			@ -1776,7 +1787,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
 | 
			
		|||
		const bool bHitVerticalFace = !IsWithinEdgeTolerance(InHit.Location, InHit.ImpactPoint, PawnRadius);
 | 
			
		||||
		if (!CurrentFloor.bLineTrace && !bHitVerticalFace)
 | 
			
		||||
		{
 | 
			
		||||
			PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint.Z;
 | 
			
		||||
			PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint | -GravDir;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -1868,7 +1879,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
 | 
			
		|||
	if (Hit.IsValidBlockingHit())
 | 
			
		||||
	{
 | 
			
		||||
		// See if this step sequence would have allowed us to travel higher than our max step height allows.
 | 
			
		||||
		const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ;
 | 
			
		||||
		const float DeltaZ = (Hit.ImpactPoint | -GravDir) - PawnFloorPointZ;
 | 
			
		||||
		if (DeltaZ > MaxStepHeight)
 | 
			
		||||
		{
 | 
			
		||||
			//UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ);
 | 
			
		||||
| 
						 | 
				
			
			@ -1890,7 +1901,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
 | 
			
		|||
 | 
			
		||||
			// Also reject if we would end up being higher than our starting location by stepping down.
 | 
			
		||||
			// It's fine to step down onto an unwalkable normal below us, we will just slide off. Rejecting those moves would prevent us from being able to walk off the edge.
 | 
			
		||||
			if (Hit.Location.Z > OldLocation.Z)
 | 
			
		||||
			if ((Hit.Location | -GravDir) > OldLocationZ)
 | 
			
		||||
			{
 | 
			
		||||
				//UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString());
 | 
			
		||||
				ScopedStepUpMovement.RevertMove();
 | 
			
		||||
| 
						 | 
				
			
			@ -1921,7 +1932,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
 | 
			
		|||
 | 
			
		||||
			// Reject unwalkable normals if we end up higher than our initial height.
 | 
			
		||||
			// It's fine to walk down onto an unwalkable surface, don't reject those moves.
 | 
			
		||||
			if (Hit.Location.Z > OldLocation.Z)
 | 
			
		||||
			if ((Hit.Location | -GravDir) > OldLocationZ)
 | 
			
		||||
			{
 | 
			
		||||
				// We should reject the floor result if we are trying to step up an actual step where we are not able to perch (this is rare).
 | 
			
		||||
				// In those cases we should instead abort the step up and try to slide along the stair.
 | 
			
		||||
| 
						 | 
				
			
			@ -1990,8 +2001,9 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
 | 
			
		|||
	CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
 | 
			
		||||
 | 
			
		||||
	// Don't bother stepping up if top of capsule is hitting something.
 | 
			
		||||
	const float InitialImpactZ = InHit.ImpactPoint.Z;
 | 
			
		||||
	if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius))
 | 
			
		||||
	const float InitialImpactZ = InHit.ImpactPoint | -GravDir;
 | 
			
		||||
	const float OldLocationZ = OldLocation | -GravDir;
 | 
			
		||||
	if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius))
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -2013,7 +2025,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
 | 
			
		|||
	float StepTravelUpHeight = MaxStepHeight;
 | 
			
		||||
	float StepTravelDownHeight = StepTravelUpHeight;
 | 
			
		||||
	const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir);
 | 
			
		||||
	float PawnInitialFloorBaseZ = OldLocation.Z - PawnHalfHeight;
 | 
			
		||||
	float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight;
 | 
			
		||||
	float PawnFloorPointZ = PawnInitialFloorBaseZ;
 | 
			
		||||
 | 
			
		||||
	// Scope our movement updates, and do not apply them until all intermediate moves are completed.
 | 
			
		||||
| 
						 | 
				
			
			@ -2109,7 +2121,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
 | 
			
		|||
	if (Hit.IsValidBlockingHit())
 | 
			
		||||
	{
 | 
			
		||||
		// See if this step sequence would have allowed us to travel higher than our max step height allows.
 | 
			
		||||
		const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ;
 | 
			
		||||
		const float DeltaZ = (Hit.ImpactPoint | -GravDir) - PawnFloorPointZ;
 | 
			
		||||
		if (DeltaZ > MaxStepHeight)
 | 
			
		||||
		{
 | 
			
		||||
			UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ);
 | 
			
		||||
| 
						 | 
				
			
			@ -2131,7 +2143,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
 | 
			
		|||
 | 
			
		||||
			// Also reject if we would end up being higher than our starting location by stepping down.
 | 
			
		||||
			// It's fine to step down onto an unwalkable normal below us, we will just slide off. Rejecting those moves would prevent us from being able to walk off the edge.
 | 
			
		||||
			if (Hit.Location.Z > OldLocation.Z)
 | 
			
		||||
			if ((Hit.Location | -GravDir) > OldLocationZ)
 | 
			
		||||
			{
 | 
			
		||||
				UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString());
 | 
			
		||||
				ScopedStepUpMovement.RevertMove();
 | 
			
		||||
| 
						 | 
				
			
			@ -2162,7 +2174,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
 | 
			
		|||
 | 
			
		||||
			// Reject unwalkable normals if we end up higher than our initial height.
 | 
			
		||||
			// It's fine to walk down onto an unwalkable surface, don't reject those moves.
 | 
			
		||||
			if (Hit.Location.Z > OldLocation.Z)
 | 
			
		||||
			if ((Hit.Location | -GravDir) > OldLocationZ)
 | 
			
		||||
			{
 | 
			
		||||
				// We should reject the floor result if we are trying to step up an actual step where we are not able to perch (this is rare).
 | 
			
		||||
				// In those cases we should instead abort the step up and try to slide along the stair.
 | 
			
		||||
| 
						 | 
				
			
			@ -2195,6 +2207,12 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
 | 
			
		|||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FVector UVRCharacterMovementComponent::GetActorFeetLocation() const
 | 
			
		||||
{
 | 
			
		||||
	// Call into the VR version of it instead
 | 
			
		||||
	return GetActorFeetLocationVR();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -2259,8 +2277,12 @@ void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds)
 | 
			
		|||
				// @todo: This assumes only Yaw is used, currently a valid assumption. This is the only reason FaceRotation() is used above really, aside from being a virtual hook.
 | 
			
		||||
				if (bOrientRotationToMovement || (bUseControllerDesiredRotation && CharacterOwner->Controller))
 | 
			
		||||
				{
 | 
			
		||||
					TargetRotator.Pitch = 0.f;
 | 
			
		||||
					TargetRotator.Roll = 0.f;
 | 
			
		||||
					// Custom gravity automatically aligns the character to the gravity direction, so we shouldn't zero out pitch and roll.
 | 
			
		||||
					if (!HasCustomGravity())
 | 
			
		||||
					{
 | 
			
		||||
						TargetRotator.Pitch = 0.f;
 | 
			
		||||
						TargetRotator.Roll = 0.f;
 | 
			
		||||
					}
 | 
			
		||||
					MoveUpdatedComponent(FVector::ZeroVector, TargetRotator, false);
 | 
			
		||||
					FinalQuat = UpdatedComponent->GetComponentQuat();
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -2276,19 +2298,31 @@ void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds)
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We need to offset the base of the character here, not its origin, so offset by half height
 | 
			
		||||
		float HalfHeight, Radius;
 | 
			
		||||
		CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight);
 | 
			
		||||
 | 
			
		||||
		if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
 | 
			
		||||
		FVector NewWorldPos;
 | 
			
		||||
		if (HasCustomGravity())
 | 
			
		||||
		{
 | 
			
		||||
			HalfHeight = 0;
 | 
			
		||||
			const FVector RotationRadius = UpdatedComponent->GetComponentLocation() - NewBaseLocation;
 | 
			
		||||
			const FVector RotationDelta = DeltaQuat.RotateVector(RotationRadius) - RotationRadius;
 | 
			
		||||
			const FVector LinearDelta = NewBaseLocation - OldBaseLocation;
 | 
			
		||||
			NewWorldPos = ConstrainLocationToPlane(UpdatedComponent->GetComponentLocation() + RotationDelta + LinearDelta);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			// We need to offset the base of the character here, not its origin, so offset by half height
 | 
			
		||||
			float HalfHeight, Radius;
 | 
			
		||||
			CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight);
 | 
			
		||||
 | 
			
		||||
		FVector const BaseOffset(0.0f, 0.0f, HalfHeight);
 | 
			
		||||
			if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
 | 
			
		||||
			{
 | 
			
		||||
				HalfHeight = 0;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset);
 | 
			
		||||
		FVector const NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset);
 | 
			
		||||
			FVector const BaseOffset(0.0f, 0.0f, HalfHeight);
 | 
			
		||||
 | 
			
		||||
			FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset);
 | 
			
		||||
			NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset);
 | 
			
		||||
		}
 | 
			
		||||
			
 | 
			
		||||
		DeltaPosition = ConstrainDirectionToPlane(NewWorldPos - UpdatedComponent->GetComponentLocation());
 | 
			
		||||
 | 
			
		||||
		// move attached actor
 | 
			
		||||
| 
						 | 
				
			
			@ -2377,16 +2411,13 @@ FVector UVRCharacterMovementComponent::GetImpartedMovementBaseVelocity() const
 | 
			
		|||
			if (bImpartBaseAngularVelocity)
 | 
			
		||||
			{
 | 
			
		||||
				// Base position should be the bottom of the actor since I offset the capsule now
 | 
			
		||||
 | 
			
		||||
				float HalfHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
 | 
			
		||||
				if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
 | 
			
		||||
				if (BaseVRCharacterOwner && BaseVRCharacterOwner->bRetainRoomscale)
 | 
			
		||||
				{
 | 
			
		||||
					HalfHeight = 0.0f;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				const FVector CharacterBasePosition = (UpdatedComponent->GetComponentLocation() - FVector(0.f, 0.f, HalfHeight));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				
 | 
			
		||||
				const FVector CharacterBasePosition = (UpdatedComponent->GetComponentLocation() + HalfHeight * GetGravityDirection());
 | 
			
		||||
				const FVector BaseTangentialVel = MovementBaseUtility::GetMovementBaseTangentialVelocity(MovementBase, CharacterOwner->GetBasedMovement().BoneName, CharacterBasePosition);
 | 
			
		||||
				BaseVelocity += BaseTangentialVel;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -2562,13 +2593,13 @@ float UVRCharacterMovementComponent::ImmersionDepth() const
 | 
			
		|||
 | 
			
		||||
				if (VRRootCapsule)
 | 
			
		||||
				{
 | 
			
		||||
					TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + FVector(0.f, 0.f, CollisionHalfHeight);
 | 
			
		||||
					TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - FVector(0.f, 0.f, CollisionHalfHeight);
 | 
			
		||||
					TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + CollisionHalfHeight * -GetGravityDirection();
 | 
			
		||||
					TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - CollisionHalfHeight * -GetGravityDirection();
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					TraceStart = UpdatedComponent->GetComponentLocation() + FVector(0.f, 0.f, CollisionHalfHeight);
 | 
			
		||||
					TraceEnd = UpdatedComponent->GetComponentLocation() -FVector(0.f, 0.f, CollisionHalfHeight);
 | 
			
		||||
					TraceStart = UpdatedComponent->GetComponentLocation() + CollisionHalfHeight * -GetGravityDirection();
 | 
			
		||||
					TraceEnd = UpdatedComponent->GetComponentLocation() - CollisionHalfHeight * -GetGravityDirection();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				FCollisionQueryParams NewTraceParams(CharacterMovementComponentStatics::ImmersionDepthName, true);
 | 
			
		||||
| 
						 | 
				
			
			@ -2669,18 +2700,19 @@ void UVRCharacterMovementComponent::PhysFlying(float deltaTime, int32 Iterations
 | 
			
		|||
 | 
			
		||||
	if (Hit.Time < 1.f)
 | 
			
		||||
	{
 | 
			
		||||
		const FVector GravDir = FVector(0.f, 0.f, -1.f);
 | 
			
		||||
		const FVector VelDir = Velocity.GetSafeNormal();
 | 
			
		||||
		const float UpDown = GravDir | VelDir;
 | 
			
		||||
		const float UpDown = VelDir | GetGravityDirection();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		bool bSteppedUp = false;
 | 
			
		||||
		if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
 | 
			
		||||
		if ((FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
 | 
			
		||||
		{
 | 
			
		||||
			float stepZ = UpdatedComponent->GetComponentLocation().Z;
 | 
			
		||||
			bSteppedUp = StepUp(GravDir, (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, nullptr);
 | 
			
		||||
			const FVector::FReal StepZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation());
 | 
			
		||||
			bSteppedUp = StepUp(GetGravityDirection(), (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time), Hit);
 | 
			
		||||
			if (bSteppedUp)
 | 
			
		||||
			{
 | 
			
		||||
				OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
 | 
			
		||||
				const FVector::FReal LocationZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()) + (GetGravitySpaceZ(OldLocation) - StepZ);
 | 
			
		||||
				SetGravitySpaceZ(OldLocation, LocationZ);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2713,9 +2745,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FVector FallAcceleration = GetFallingLateralAcceleration(deltaTime);
 | 
			
		||||
	const FVector GravityRelativeFallAcceleration = RotateWorldToGravity(FallAcceleration);
 | 
			
		||||
	FallAcceleration = RotateGravityToWorld(FVector(GravityRelativeFallAcceleration.X, GravityRelativeFallAcceleration.Y, 0));
 | 
			
		||||
	const FVector FallAcceleration = ProjectToGravityFloor(GetFallingLateralAcceleration(deltaTime));
 | 
			
		||||
	const bool bHasLimitedAirControl = ShouldLimitAirControl(deltaTime, FallAcceleration);
 | 
			
		||||
 | 
			
		||||
	// Rewind the players position by the new capsule location
 | 
			
		||||
| 
						 | 
				
			
			@ -2751,7 +2781,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
				TGuardValue<FVector> RestoreAcceleration(Acceleration, FallAcceleration);
 | 
			
		||||
				if (HasCustomGravity())
 | 
			
		||||
				{
 | 
			
		||||
					Velocity = FVector::VectorPlaneProject(Velocity, RotateGravityToWorld(FVector::UpVector));
 | 
			
		||||
					Velocity = ProjectToGravityFloor(Velocity);
 | 
			
		||||
					const FVector GravityRelativeOffset = OldVelocity - Velocity;
 | 
			
		||||
					CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel);
 | 
			
		||||
					Velocity += GravityRelativeOffset;
 | 
			
		||||
| 
						 | 
				
			
			@ -2797,15 +2827,15 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
		DecayFormerBaseVelocity(timeTick);
 | 
			
		||||
 | 
			
		||||
		// See if we need to sub-step to exactly reach the apex. This is important for avoiding "cutting off the top" of the trajectory as framerate varies.
 | 
			
		||||
		const FVector GravityRelativeOldVelocityWithRootMotion = RotateWorldToGravity(OldVelocityWithRootMotion);
 | 
			
		||||
		const FVector::FReal GravityRelativeOldVelocityWithRootMotionZ = GetGravitySpaceZ(OldVelocityWithRootMotion);
 | 
			
		||||
		static const auto CVarForceJumpPeakSubstep = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ForceJumpPeakSubstep"));
 | 
			
		||||
		if (CVarForceJumpPeakSubstep->GetInt() != 0 && GravityRelativeOldVelocityWithRootMotion.Z > 0.f && RotateWorldToGravity(Velocity).Z <= 0.f && NumJumpApexAttempts < MaxJumpApexAttemptsPerSimulation)
 | 
			
		||||
		if (CVarForceJumpPeakSubstep->GetInt() && GravityRelativeOldVelocityWithRootMotionZ > 0.f && GetGravitySpaceZ(Velocity) <= 0.f && NumJumpApexAttempts < MaxJumpApexAttemptsPerSimulation)
 | 
			
		||||
		{
 | 
			
		||||
			const FVector DerivedAccel = (Velocity - OldVelocityWithRootMotion) / timeTick;
 | 
			
		||||
			const FVector GravityRelativeDerivedAccel = RotateWorldToGravity(DerivedAccel);
 | 
			
		||||
			if (!FMath::IsNearlyZero(GravityRelativeDerivedAccel.Z))
 | 
			
		||||
			const FVector::FReal GravityRelativeDerivedAccelZ = GetGravitySpaceZ(DerivedAccel);
 | 
			
		||||
			if (!FMath::IsNearlyZero(GravityRelativeDerivedAccelZ))
 | 
			
		||||
			{
 | 
			
		||||
				const float TimeToApex = -GravityRelativeOldVelocityWithRootMotion.Z / GravityRelativeDerivedAccel.Z;
 | 
			
		||||
				const float TimeToApex = -GravityRelativeOldVelocityWithRootMotionZ / GravityRelativeDerivedAccelZ;
 | 
			
		||||
 | 
			
		||||
				// The time-to-apex calculation should be precise, and we want to avoid adding a substep when we are basically already at the apex from the previous iteration's work.
 | 
			
		||||
				const float ApexTimeMinimum = 0.0001f;
 | 
			
		||||
| 
						 | 
				
			
			@ -2814,8 +2844,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
					const FVector ApexVelocity = OldVelocityWithRootMotion + (DerivedAccel * TimeToApex);
 | 
			
		||||
					if (HasCustomGravity())
 | 
			
		||||
					{
 | 
			
		||||
						const FVector GravityRelativeApexVelocity = RotateWorldToGravity(ApexVelocity);
 | 
			
		||||
						Velocity = RotateGravityToWorld(FVector(GravityRelativeApexVelocity.X, GravityRelativeApexVelocity.Y, 0)); // Should be nearly zero anyway, but this makes apex notifications consistent.
 | 
			
		||||
						Velocity = ProjectToGravityFloor(ApexVelocity); // Should be nearly zero anyway, but this makes apex notifications consistent.
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
| 
						 | 
				
			
			@ -2847,7 +2876,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
		ApplyRootMotionToVelocity(timeTick);
 | 
			
		||||
		//ApplyVRMotionToVelocity(deltaTime);
 | 
			
		||||
 | 
			
		||||
		if (bNotifyApex && (RotateWorldToGravity(Velocity).Z < 0.f))
 | 
			
		||||
		if (bNotifyApex && (GetGravitySpaceZ(Velocity) < 0.f))
 | 
			
		||||
		{
 | 
			
		||||
			// Just passed jump apex since now going down
 | 
			
		||||
			bNotifyApex = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -2912,7 +2941,9 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
 | 
			
		||||
					FFindFloorResult FloorResult;
 | 
			
		||||
					FindFloor(PawnLocation, FloorResult, false, NULL);
 | 
			
		||||
					if (FloorResult.IsWalkableFloor() && IsValidLandingSpot(PawnLocation, FloorResult.HitResult))
 | 
			
		||||
 | 
			
		||||
					// Note that we only care about capsule sweep floor results, since the line trace may detect a lower walkable surface that our falling capsule wouldn't actually reach yet.
 | 
			
		||||
					if (!FloorResult.bLineTrace && FloorResult.IsWalkableFloor() && IsValidLandingSpot(PawnLocation, FloorResult.HitResult))
 | 
			
		||||
					{
 | 
			
		||||
						//RestorePreAdditiveVRMotionVelocity();
 | 
			
		||||
						remainingTime += subTimeTickRemaining;
 | 
			
		||||
| 
						 | 
				
			
			@ -2950,7 +2981,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
						TGuardValue<FVector> RestoreVelocity(Velocity, OldVelocity);
 | 
			
		||||
						if (HasCustomGravity())
 | 
			
		||||
						{
 | 
			
		||||
							Velocity = FVector::VectorPlaneProject(Velocity, RotateGravityToWorld(FVector::UpVector));
 | 
			
		||||
							Velocity = ProjectToGravityFloor(Velocity);
 | 
			
		||||
							const FVector GravityRelativeOffset = OldVelocity - Velocity;
 | 
			
		||||
							CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel);
 | 
			
		||||
							VelocityNoAirControl = Velocity + GravityRelativeOffset;
 | 
			
		||||
| 
						 | 
				
			
			@ -2981,12 +3012,12 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
				{
 | 
			
		||||
					const FVector ContactVelocity = MovementBaseUtility::GetMovementBaseVelocity(HitComponent, NAME_None) + MovementBaseUtility::GetMovementBaseTangentialVelocity(HitComponent, NAME_None, Hit.ImpactPoint);
 | 
			
		||||
					const FVector NewVelocity = Velocity - Hit.ImpactNormal * FVector::DotProduct(Velocity - ContactVelocity, Hit.ImpactNormal);
 | 
			
		||||
					Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
 | 
			
		||||
					Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? ProjectToGravityFloor(Velocity) + GetGravitySpaceComponentZ(NewVelocity) : NewVelocity;
 | 
			
		||||
				}
 | 
			
		||||
				else if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported)
 | 
			
		||||
				{
 | 
			
		||||
					const FVector NewVelocity = (Delta / subTimeTickRemaining);
 | 
			
		||||
					Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
 | 
			
		||||
					Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? ProjectToGravityFloor(Velocity) + GetGravitySpaceComponentZ(NewVelocity) : NewVelocity;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && (Delta | Adjusted) > 0.f)
 | 
			
		||||
| 
						 | 
				
			
			@ -3019,7 +3050,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
						}
 | 
			
		||||
 | 
			
		||||
						// Act as if there was no air control on the last move when computing new deflection.
 | 
			
		||||
						if (bHasLimitedAirControl && RotateWorldToGravity(Hit.Normal).Z > CharacterMovementConstants::VERTICAL_SLOPE_NORMAL_ZVR)
 | 
			
		||||
						if (bHasLimitedAirControl && GetGravitySpaceZ(Hit.Normal) > CharacterMovementConstants::VERTICAL_SLOPE_NORMAL_ZVR)
 | 
			
		||||
						{
 | 
			
		||||
							const FVector LastMoveNoAirControl = VelocityNoAirControl * LastMoveTimeSlice;
 | 
			
		||||
							Delta = ComputeSlideVector(LastMoveNoAirControl, 1.f, OldHitNormal, Hit);
 | 
			
		||||
| 
						 | 
				
			
			@ -3045,19 +3076,27 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
						if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported)
 | 
			
		||||
						{
 | 
			
		||||
							const FVector NewVelocity = (Delta / subTimeTickRemaining);
 | 
			
		||||
							Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
 | 
			
		||||
							Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? ProjectToGravityFloor(Velocity) + GetGravitySpaceComponentZ(NewVelocity) : NewVelocity;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						// bDitch=true means that pawn is straddling two slopes, neither of which he can stand on
 | 
			
		||||
						bool bDitch = ((RotateWorldToGravity(OldHitImpactNormal).Z > 0.f) && (RotateWorldToGravity(Hit.ImpactNormal).Z > 0.f) && (FMath::Abs(Delta.Z) <= UE_KINDA_SMALL_NUMBER) && ((Hit.ImpactNormal | OldHitImpactNormal) < 0.f));
 | 
			
		||||
						bool bDitch = ((GetGravitySpaceZ(OldHitImpactNormal) > 0.f) && (GetGravitySpaceZ(Hit.ImpactNormal) > 0.f) && (FMath::Abs(GetGravitySpaceZ(Delta)) <= UE_KINDA_SMALL_NUMBER) && ((Hit.ImpactNormal | OldHitImpactNormal) < 0.f));
 | 
			
		||||
						SafeMoveUpdatedComponent(Delta, PawnRotation, true, Hit);
 | 
			
		||||
						if (Hit.Time == 0.f)
 | 
			
		||||
						{
 | 
			
		||||
							// if we are stuck then try to side step
 | 
			
		||||
							FVector SideDelta = (OldHitNormal + Hit.ImpactNormal).GetSafeNormal2D();
 | 
			
		||||
							FVector SideDelta = ProjectToGravityFloor(OldHitNormal + Hit.ImpactNormal).GetSafeNormal();
 | 
			
		||||
							if (SideDelta.IsNearlyZero())
 | 
			
		||||
							{
 | 
			
		||||
								SideDelta = FVector(OldHitNormal.Y, -OldHitNormal.X, 0).GetSafeNormal();
 | 
			
		||||
								if (HasCustomGravity())
 | 
			
		||||
								{
 | 
			
		||||
									const FVector GravityRelativeHitNormal = RotateWorldToGravity(OldHitNormal);
 | 
			
		||||
									SideDelta = RotateGravityToWorld(FVector(GravityRelativeHitNormal.Y, -GravityRelativeHitNormal.X, 0.f)).GetSafeNormal();
 | 
			
		||||
								}
 | 
			
		||||
								else
 | 
			
		||||
								{
 | 
			
		||||
									SideDelta = FVector(OldHitNormal.Y, -OldHitNormal.X, 0).GetSafeNormal();
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
							SafeMoveUpdatedComponent(SideDelta, PawnRotation, true, Hit);
 | 
			
		||||
						}
 | 
			
		||||
| 
						 | 
				
			
			@ -3069,13 +3108,13 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
							ProcessLanded(Hit, remainingTime, Iterations);
 | 
			
		||||
							return;
 | 
			
		||||
						}
 | 
			
		||||
						else if (GetPerchRadiusThreshold() > 0.f && Hit.Time == 1.f && RotateWorldToGravity(OldHitImpactNormal).Z >= GetWalkableFloorZ())
 | 
			
		||||
						else if (GetPerchRadiusThreshold() > 0.f && Hit.Time == 1.f && GetGravitySpaceZ(OldHitImpactNormal) >= GetWalkableFloorZ())
 | 
			
		||||
						{
 | 
			
		||||
							// We might be in a virtual 'ditch' within our perch radius. This is rare.
 | 
			
		||||
							const FVector PawnLocation = UpdatedComponent->GetComponentLocation();
 | 
			
		||||
							const float ZMovedDist = FMath::Abs(RotateWorldToGravity(PawnLocation - OldLocation).Z);
 | 
			
		||||
							const float MovedDist2DSq = FVector::VectorPlaneProject(PawnLocation - OldLocation, RotateGravityToWorld(FVector::UpVector)).Size2D();
 | 
			
		||||
							if (ZMovedDist <= 0.2f * timeTick && MovedDist2DSq <= 4.f * timeTick)
 | 
			
		||||
							const float ZMovedDist = FMath::Abs(GetGravitySpaceZ(PawnLocation - OldLocation));
 | 
			
		||||
							const float MovedDist2D = ProjectToGravityFloor(PawnLocation - OldLocation).Size();
 | 
			
		||||
							if (ZMovedDist <= 0.2f * timeTick && MovedDist2D <= 4.f * timeTick)
 | 
			
		||||
							{
 | 
			
		||||
								FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity);
 | 
			
		||||
								GravityRelativeVelocity.X += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f);
 | 
			
		||||
| 
						 | 
				
			
			@ -3135,12 +3174,10 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity);
 | 
			
		||||
		if (GravityRelativeVelocity.SizeSquared2D() <= UE_KINDA_SMALL_NUMBER * 10.f)
 | 
			
		||||
		const FVector GravityProjectedVelocity = ProjectToGravityFloor(Velocity);
 | 
			
		||||
		if (GravityProjectedVelocity.SizeSquared() <= UE_KINDA_SMALL_NUMBER * 10.f)
 | 
			
		||||
		{
 | 
			
		||||
			GravityRelativeVelocity.X = 0.f;
 | 
			
		||||
			GravityRelativeVelocity.Y = 0.f;
 | 
			
		||||
			Velocity = RotateGravityToWorld(GravityRelativeVelocity);
 | 
			
		||||
			Velocity = GetGravitySpaceComponentZ(Velocity);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		RestorePreAdditiveVRMotionVelocity();
 | 
			
		||||
| 
						 | 
				
			
			@ -3179,7 +3216,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
 | 
			
		|||
	devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysNavWalking: Velocity contains NaN before CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
 | 
			
		||||
 | 
			
		||||
	//bound acceleration
 | 
			
		||||
	Acceleration.Z = 0.f;
 | 
			
		||||
	Acceleration = ProjectToGravityFloor(Acceleration);
 | 
			
		||||
	//if (!HasRootMotion())
 | 
			
		||||
	//{
 | 
			
		||||
		CalcVelocity(deltaTime, GroundFriction, false, BrakingDecelerationWalking);
 | 
			
		||||
| 
						 | 
				
			
			@ -3198,8 +3235,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
 | 
			
		|||
 | 
			
		||||
	Iterations++;
 | 
			
		||||
 | 
			
		||||
	FVector DesiredMove = Velocity;
 | 
			
		||||
	DesiredMove.Z = 0.f;
 | 
			
		||||
	const FVector DesiredMove = ProjectToGravityFloor(Velocity);
 | 
			
		||||
 | 
			
		||||
	//const FVector OldPlayerLocation = GetActorFeetLocation();
 | 
			
		||||
	const FVector OldLocation = GetActorFeetLocationVR();
 | 
			
		||||
| 
						 | 
				
			
			@ -3214,11 +3250,11 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
 | 
			
		|||
	{
 | 
			
		||||
		if (bProjectNavMeshWalking)
 | 
			
		||||
		{
 | 
			
		||||
			const float DistSq2D = (OldLocation - CachedNavLocation.Location).SizeSquared2D();
 | 
			
		||||
			const float DistZ = FMath::Abs(OldLocation.Z - CachedNavLocation.Location.Z);
 | 
			
		||||
			const float DistSq2D = ProjectToGravityFloor(OldLocation - CachedNavLocation.Location).SizeSquared();
 | 
			
		||||
			const float DistZ = FMath::Abs(GetGravitySpaceZ(OldLocation - CachedNavLocation.Location));
 | 
			
		||||
 | 
			
		||||
			const float TotalCapsuleHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.0f;
 | 
			
		||||
			const float ProjectionScale = (OldLocation.Z > CachedNavLocation.Location.Z) ? NavMeshProjectionHeightScaleUp : NavMeshProjectionHeightScaleDown;
 | 
			
		||||
			const float ProjectionScale = (GetGravitySpaceZ(OldLocation) > GetGravitySpaceZ(CachedNavLocation.Location)) ? NavMeshProjectionHeightScaleUp : NavMeshProjectionHeightScaleDown;
 | 
			
		||||
			const float DistZThr = TotalCapsuleHeight * FMath::Max(0.f, ProjectionScale);
 | 
			
		||||
 | 
			
		||||
			bSameNavLocation = (DistSq2D <= UE_KINDA_SMALL_NUMBER) && (DistZ < DistZThr);
 | 
			
		||||
| 
						 | 
				
			
			@ -3255,7 +3291,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
 | 
			
		|||
		// we'll follow that geometry's plane out of range of valid navigation.
 | 
			
		||||
		if (bSameNavLocation && bProjectNavMeshWalking)
 | 
			
		||||
		{
 | 
			
		||||
			AdjustedDest.Z = CachedNavLocation.Location.Z;
 | 
			
		||||
			SetGravitySpaceZ(AdjustedDest, GetGravitySpaceZ(CachedNavLocation.Location));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Find the point on the NavMesh
 | 
			
		||||
| 
						 | 
				
			
			@ -3272,7 +3308,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
 | 
			
		|||
 | 
			
		||||
	if (DestNavLocation.NodeRef != INVALID_NAVNODEREF)
 | 
			
		||||
	{
 | 
			
		||||
		FVector NewLocation(AdjustedDest.X, AdjustedDest.Y, DestNavLocation.Location.Z);
 | 
			
		||||
		FVector NewLocation = ProjectToGravityFloor(AdjustedDest) + GetGravitySpaceComponentZ(DestNavLocation.Location);
 | 
			
		||||
		if (bProjectNavMeshWalking)
 | 
			
		||||
		{
 | 
			
		||||
			SCOPE_CYCLE_COUNTER(STAT_CharNavProjectLocation);
 | 
			
		||||
| 
						 | 
				
			
			@ -3330,18 +3366,18 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
	float NetFluidFriction = 0.f;
 | 
			
		||||
	float Depth = ImmersionDepth();
 | 
			
		||||
	float NetBuoyancy = Buoyancy * Depth;
 | 
			
		||||
	float OriginalAccelZ = Acceleration.Z;
 | 
			
		||||
	float OriginalAccelZ = GetGravitySpaceZ(Acceleration);
 | 
			
		||||
	bool bLimitedUpAccel = false;
 | 
			
		||||
 | 
			
		||||
	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (Velocity.Z > 0.33f * MaxSwimSpeed) && (NetBuoyancy != 0.f))
 | 
			
		||||
	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (GetGravitySpaceZ(Velocity) > 0.33f * MaxSwimSpeed) && (NetBuoyancy != 0.f))
 | 
			
		||||
	{
 | 
			
		||||
		//damp positive Z out of water
 | 
			
		||||
		Velocity.Z = FMath::Max<FVector::FReal>(0.33f * MaxSwimSpeed, Velocity.Z * Depth * Depth);
 | 
			
		||||
		SetGravitySpaceZ(Velocity, FMath::Max<FVector::FReal>(0.33f * MaxSwimSpeed, GetGravitySpaceZ(Velocity) * Depth * Depth));
 | 
			
		||||
	}
 | 
			
		||||
	else if (Depth < 0.65f)
 | 
			
		||||
	{
 | 
			
		||||
		bLimitedUpAccel = (Acceleration.Z > 0.f);
 | 
			
		||||
		Acceleration.Z = FMath::Min<FVector::FReal>(0.1f, Acceleration.Z);
 | 
			
		||||
		bLimitedUpAccel = (OriginalAccelZ > 0.f);
 | 
			
		||||
		SetGravitySpaceZ(Acceleration, FMath::Min<FVector::FReal>(0.1f, OriginalAccelZ));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iterations++;
 | 
			
		||||
| 
						 | 
				
			
			@ -3351,7 +3387,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
	{
 | 
			
		||||
		const float Friction = 0.5f * GetPhysicsVolume()->FluidFriction * Depth;
 | 
			
		||||
		CalcVelocity(deltaTime, Friction, true, GetMaxBrakingDeceleration());
 | 
			
		||||
		Velocity.Z += GetGravityZ() * deltaTime * (1.f - NetBuoyancy);
 | 
			
		||||
		Velocity += (GetGravityZ() * deltaTime * (1.f - NetBuoyancy)) * -GetGravityDirection();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ApplyRootMotionToVelocity(deltaTime);
 | 
			
		||||
| 
						 | 
				
			
			@ -3359,7 +3395,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
 | 
			
		||||
	FVector Adjusted = Velocity * deltaTime;
 | 
			
		||||
	FHitResult Hit(1.f);
 | 
			
		||||
	float remainingTime = deltaTime * SwimVR(Adjusted/* + AdditionalVRInputVector*/, Hit);
 | 
			
		||||
	const float remainingTime = deltaTime * Swim(Adjusted, Hit);
 | 
			
		||||
 | 
			
		||||
	//may have left water - if so, script might have set new physics mode
 | 
			
		||||
	if (!IsSwimming())
 | 
			
		||||
| 
						 | 
				
			
			@ -3372,10 +3408,10 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
	if (Hit.Time < 1.f && CharacterOwner)
 | 
			
		||||
	{
 | 
			
		||||
		HandleSwimmingWallHit(Hit, deltaTime);
 | 
			
		||||
		if (bLimitedUpAccel && (Velocity.Z >= 0.f))
 | 
			
		||||
		if (bLimitedUpAccel && (GetGravitySpaceZ(Velocity) >= 0.f))
 | 
			
		||||
		{
 | 
			
		||||
			// allow upward velocity at surface if against obstacle
 | 
			
		||||
			Velocity.Z += OriginalAccelZ * deltaTime;
 | 
			
		||||
			Velocity += OriginalAccelZ * deltaTime * -GetGravityDirection();
 | 
			
		||||
			Adjusted = Velocity * (1.f - Hit.Time)*deltaTime;
 | 
			
		||||
			SwimVR(Adjusted, Hit);
 | 
			
		||||
			if (!IsSwimming())
 | 
			
		||||
| 
						 | 
				
			
			@ -3386,17 +3422,16 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const FVector GravDir = FVector(0.f, 0.f, -1.f);
 | 
			
		||||
		const FVector VelDir = Velocity.GetSafeNormal();
 | 
			
		||||
		const float UpDown = GravDir | VelDir;
 | 
			
		||||
		const float UpDown = VelDir | GetGravityDirection();
 | 
			
		||||
 | 
			
		||||
		bool bSteppedUp = false;
 | 
			
		||||
		if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
 | 
			
		||||
		if ((FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
 | 
			
		||||
		{
 | 
			
		||||
			float stepZ = UpdatedComponent->GetComponentLocation().Z;
 | 
			
		||||
			const float StepZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation());
 | 
			
		||||
			const FVector RealVelocity = Velocity;
 | 
			
		||||
			Velocity.Z = 1.f;	// HACK: since will be moving up, in case pawn leaves the water
 | 
			
		||||
			bSteppedUp = StepUp(GravDir, (Adjusted/* + AdditionalVRInputVector*/) * (1.f - Hit.Time), Hit);
 | 
			
		||||
			SetGravitySpaceZ(Velocity, 1.f);	// HACK: since will be moving up, in case pawn leaves the water
 | 
			
		||||
			bSteppedUp = StepUp(GetGravityDirection(), Adjusted * (1.f - Hit.Time), Hit);
 | 
			
		||||
			if (bSteppedUp)
 | 
			
		||||
			{
 | 
			
		||||
				//may have left water - if so, script might have set new physics mode
 | 
			
		||||
| 
						 | 
				
			
			@ -3406,7 +3441,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
					StartNewPhysics(remainingTime, Iterations);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
 | 
			
		||||
				SetGravitySpaceZ(OldLocation, GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()) + (GetGravitySpaceZ(OldLocation) - StepZ));
 | 
			
		||||
			}
 | 
			
		||||
			Velocity = RealVelocity;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -3421,12 +3456,12 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
 | 
			
		|||
 | 
			
		||||
	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && !bJustTeleported && ((deltaTime - remainingTime) > UE_KINDA_SMALL_NUMBER) && CharacterOwner)
 | 
			
		||||
	{
 | 
			
		||||
		bool bWaterJump = !GetPhysicsVolume()->bWaterVolume;
 | 
			
		||||
		float velZ = Velocity.Z;
 | 
			
		||||
		const bool bWaterJump = !GetPhysicsVolume()->bWaterVolume;
 | 
			
		||||
		const FVector::FReal VelZ = GetGravitySpaceZ(Velocity);
 | 
			
		||||
		Velocity = ((UpdatedComponent->GetComponentLocation() - OldLocation)/* - AdditionalVRInputVector*/) / (deltaTime - remainingTime);
 | 
			
		||||
		if (bWaterJump)
 | 
			
		||||
		{
 | 
			
		||||
			Velocity.Z = velZ;
 | 
			
		||||
			SetGravitySpaceZ(Velocity, VelZ);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3472,9 +3507,10 @@ void UVRCharacterMovementComponent::StartSwimmingVR(FVector OldLocation, FVector
 | 
			
		|||
		}
 | 
			
		||||
		MoveUpdatedComponent(End - NewLocation, UpdatedComponent->GetComponentQuat(), true);
 | 
			
		||||
	}
 | 
			
		||||
	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (Velocity.Z > 2.f* CharacterMovementConstants::SWIMBOBSPEEDVR) && (Velocity.Z < 0.f)) //allow for falling out of water
 | 
			
		||||
	const FVector::FReal GravityRelativeVelocityZ = GetGravitySpaceZ(Velocity);
 | 
			
		||||
	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (GravityRelativeVelocityZ > 2.f * CharacterMovementConstants::SWIMBOBSPEEDVR) && (GravityRelativeVelocityZ < 0.f)) //allow for falling out of water
 | 
			
		||||
	{
 | 
			
		||||
		Velocity.Z = CharacterMovementConstants::SWIMBOBSPEEDVR - Velocity.Size2D() * 0.7f; //smooth bobbing
 | 
			
		||||
		SetGravitySpaceZ(Velocity, CharacterMovementConstants::SWIMBOBSPEEDVR - ProjectToGravityFloor(Velocity).Size() * 0.7f); //smooth bobbing
 | 
			
		||||
	}
 | 
			
		||||
	if ((remainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations))
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -3517,26 +3553,25 @@ bool UVRCharacterMovementComponent::CheckWaterJump(FVector CheckPoint, FVector&
 | 
			
		|||
	FVector currentLoc = VRRootCapsule ? VRRootCapsule->OffsetComponentToWorld.GetLocation() : UpdatedComponent->GetComponentLocation();
 | 
			
		||||
 | 
			
		||||
	// check if there is a wall directly in front of the swimming pawn
 | 
			
		||||
	CheckPoint.Z = 0.f;
 | 
			
		||||
	CheckPoint = ProjectToGravityFloor(CheckPoint);
 | 
			
		||||
	FVector CheckNorm = CheckPoint.GetSafeNormal();
 | 
			
		||||
	float PawnCapsuleRadius, PawnCapsuleHalfHeight;
 | 
			
		||||
	CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnCapsuleRadius, PawnCapsuleHalfHeight);
 | 
			
		||||
	CheckPoint = currentLoc + 1.2f * PawnCapsuleRadius * CheckNorm;
 | 
			
		||||
	FVector Extent(PawnCapsuleRadius, PawnCapsuleRadius, PawnCapsuleHalfHeight);
 | 
			
		||||
	FHitResult HitInfo(1.f);
 | 
			
		||||
	FCollisionQueryParams CapsuleParams(SCENE_QUERY_STAT(CheckWaterJump), false, CharacterOwner);
 | 
			
		||||
	FCollisionResponseParams ResponseParam;
 | 
			
		||||
	InitCollisionParams(CapsuleParams, ResponseParam);
 | 
			
		||||
	FCollisionShape CapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_None);
 | 
			
		||||
	const ECollisionChannel CollisionChannel = UpdatedComponent->GetCollisionObjectType();
 | 
			
		||||
	bool bHit = GetWorld()->SweepSingleByChannel(HitInfo, currentLoc, CheckPoint, FQuat::Identity, CollisionChannel, CapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
	bool bHit = GetWorld()->SweepSingleByChannel(HitInfo, UpdatedComponent->GetComponentLocation(), CheckPoint, GetWorldToGravityTransform(), CollisionChannel, CapsuleShape, CapsuleParams, ResponseParam);
 | 
			
		||||
 | 
			
		||||
	if (bHit && !HitInfo.HitObjectHandle.DoesRepresentClass(APawn::StaticClass()))
 | 
			
		||||
	{
 | 
			
		||||
		// hit a wall - check if it is low enough
 | 
			
		||||
		WallNormal = -1.f * HitInfo.ImpactNormal;
 | 
			
		||||
		FVector Start = currentLoc;//UpdatedComponent->GetComponentLocation();
 | 
			
		||||
		Start.Z += MaxOutOfWaterStepHeight;
 | 
			
		||||
		Start += MaxOutOfWaterStepHeight * -GetGravityDirection();
 | 
			
		||||
		CheckPoint = Start + 3.2f * PawnCapsuleRadius * WallNormal;
 | 
			
		||||
		FCollisionQueryParams LineParams(SCENE_QUERY_STAT(CheckWaterJump), true, CharacterOwner);
 | 
			
		||||
		FCollisionResponseParams LineResponseParam;
 | 
			
		||||
| 
						 | 
				
			
			@ -3725,11 +3760,7 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
 | 
			
		|||
			// find floor and check if falling
 | 
			
		||||
			if (IsMovingOnGround() || MovementMode == MOVE_Falling)
 | 
			
		||||
			{
 | 
			
		||||
				bool bShouldFindFloor = Velocity.Z <= 0.f;
 | 
			
		||||
				if (HasCustomGravity())
 | 
			
		||||
				{
 | 
			
		||||
					bShouldFindFloor = RotateWorldToGravity(Velocity).Z <= 0.0;
 | 
			
		||||
				}
 | 
			
		||||
				const bool bShouldFindFloor = GetGravitySpaceZ(Velocity) <= UE_KINDA_SMALL_NUMBER;
 | 
			
		||||
 | 
			
		||||
				if (StepDownResult.bComputedFloor)
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			@ -3750,14 +3781,8 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
 | 
			
		|||
				{
 | 
			
		||||
					// Follows PhysWalking approach for encroachment on floor tests
 | 
			
		||||
					FHitResult Hit(CurrentFloor.HitResult);
 | 
			
		||||
					if (HasCustomGravity())
 | 
			
		||||
					{
 | 
			
		||||
						Hit.TraceEnd = Hit.TraceStart - GetGravityDirection() * MAX_FLOOR_DIST;
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						Hit.TraceEnd = Hit.TraceStart + FVector(0.f, 0.f, MAX_FLOOR_DIST);
 | 
			
		||||
					}
 | 
			
		||||
					Hit.TraceEnd = Hit.TraceStart - GetGravityDirection() * MAX_FLOOR_DIST;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
					const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
 | 
			
		||||
					const bool bResolved = ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
 | 
			
		||||
| 
						 | 
				
			
			@ -3775,16 +3800,9 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
 | 
			
		|||
					if (!bSimGravityDisabled)
 | 
			
		||||
					{
 | 
			
		||||
						// No floor, must fall.
 | 
			
		||||
						if (HasCustomGravity())
 | 
			
		||||
						if (GetGravitySpaceZ(Velocity) <= UE_KINDA_SMALL_NUMBER || bApplyGravityWhileJumping || !CharacterOwner->IsJumpProvidingForce())
 | 
			
		||||
						{
 | 
			
		||||
							if (RotateWorldToGravity(Velocity).Z <= 0.f || bApplyGravityWhileJumping || !CharacterOwner->IsJumpProvidingForce())
 | 
			
		||||
							{
 | 
			
		||||
								Velocity = NewFallVelocity(Velocity, -GetGravityDirection() * GetGravityZ(), DeltaSeconds);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						else if (Velocity.Z <= 0.f || bApplyGravityWhileJumping || !CharacterOwner->IsJumpProvidingForce())
 | 
			
		||||
						{
 | 
			
		||||
							Velocity = NewFallVelocity(Velocity, FVector(0.f, 0.f, GetGravityZ()), DeltaSeconds);
 | 
			
		||||
							Velocity = NewFallVelocity(Velocity, -GetGravityDirection() * GetGravityZ(), DeltaSeconds);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3903,7 +3921,7 @@ void UVRCharacterMovementComponent::MoveSmooth(const FVector& InVelocity, const
 | 
			
		|||
				{
 | 
			
		||||
					OutStepDownResult = NULL; // No need for a floor when not walking.
 | 
			
		||||
					bool bShouldAttemptStepUp = false;
 | 
			
		||||
					bShouldAttemptStepUp = FMath::Abs(RotateWorldToGravity(Hit.ImpactNormal).Z) < 0.2;
 | 
			
		||||
					bShouldAttemptStepUp = FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2;
 | 
			
		||||
					if (bShouldAttemptStepUp)
 | 
			
		||||
					{
 | 
			
		||||
						const FVector GravDir = GetGravityDirection();
 | 
			
		||||
| 
						 | 
				
			
			@ -3946,7 +3964,7 @@ void UVRCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMov
 | 
			
		|||
					MoveResponse.RootMotionTrackPosition,
 | 
			
		||||
					MoveResponse.ClientAdjustment.NewLoc,
 | 
			
		||||
					MoveResponse.RootMotionRotation,
 | 
			
		||||
					MoveResponse.ClientAdjustment.NewVel.Z,
 | 
			
		||||
					GetGravitySpaceZ(MoveResponse.ClientAdjustment.NewVel),
 | 
			
		||||
					MoveResponse.ClientAdjustment.NewBase,
 | 
			
		||||
					MoveResponse.ClientAdjustment.NewBaseBoneName,
 | 
			
		||||
					MoveResponse.bHasBase,
 | 
			
		||||
| 
						 | 
				
			
			@ -3961,7 +3979,7 @@ void UVRCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMov
 | 
			
		|||
				MoveResponse.RootMotionTrackPosition,
 | 
			
		||||
				MoveResponse.ClientAdjustment.NewLoc,
 | 
			
		||||
				MoveResponse.RootMotionRotation,
 | 
			
		||||
				MoveResponse.ClientAdjustment.NewVel.Z,
 | 
			
		||||
				GetGravitySpaceZ(MoveResponse.ClientAdjustment.NewVel),
 | 
			
		||||
				MoveResponse.ClientAdjustment.NewBase,
 | 
			
		||||
				MoveResponse.ClientAdjustment.NewBaseBoneName,
 | 
			
		||||
				MoveResponse.bHasBase,
 | 
			
		||||
| 
						 | 
				
			
			@ -4452,7 +4470,7 @@ void UVRCharacterMovementComponent::ServerMoveHandleClientErrorVR(float ClientTi
 | 
			
		|||
			{
 | 
			
		||||
				const FVector LastBaseVelocity = MovementBaseUtility::GetMovementBaseVelocity(LastServerMovementBaseVRPtr, LastServerMovementBaseBoneName);
 | 
			
		||||
				RelativeVelocity = Velocity - LastBaseVelocity;
 | 
			
		||||
				const FVector BaseDirection = LastBaseVelocity.GetSafeNormal2D();
 | 
			
		||||
				const FVector BaseDirection = ProjectToGravityFloor(LastBaseVelocity).GetSafeNormal();
 | 
			
		||||
				const FVector RelativeDirection = RelativeVelocity * (1.f / MaxWalkSpeed);
 | 
			
		||||
 | 
			
		||||
				ClientForwardFactor = FMath::Clamp(FVector::DotProduct(BaseDirection, RelativeDirection), 0.f, 1.f);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -245,7 +245,7 @@ void UVRGestureComponent::CaptureGestureFrame()
 | 
			
		|||
		// Pop off oldest sample
 | 
			
		||||
		if (GestureLog.Samples.Num() >= RecordingBufferSize)
 | 
			
		||||
		{
 | 
			
		||||
			GestureLog.Samples.Pop(false);
 | 
			
		||||
			GestureLog.Samples.Pop(EAllowShrinking::No);
 | 
			
		||||
			bClearLatestSpline = true;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
| 
						 | 
				
			
			@ -600,8 +600,8 @@ bool UGesturesDatabase::ImportSplineAsGesture(USplineComponent * HostSplineCompo
 | 
			
		|||
 | 
			
		||||
	float LastDistance = 0.f;
 | 
			
		||||
	float ThisDistance = 0.f;
 | 
			
		||||
	FVector LastDistanceV;
 | 
			
		||||
	FVector ThisDistanceV;
 | 
			
		||||
	FVector LastDistanceV = FVector::ZeroVector;
 | 
			
		||||
	FVector ThisDistanceV = FVector::ZeroVector;
 | 
			
		||||
	FVector DistNormal;
 | 
			
		||||
	float DistAlongSegment = 0.f;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,9 @@
 | 
			
		|||
#include "VRPathFollowingComponent.h"
 | 
			
		||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRPathFollowingComponent)
 | 
			
		||||
 | 
			
		||||
#include "AI/Navigation/NavigationTypes.h"
 | 
			
		||||
#include "AI/Navigation/PathFollowingAgentInterface.h"
 | 
			
		||||
 | 
			
		||||
#include "CoreMinimal.h"
 | 
			
		||||
#include "Engine/World.h"
 | 
			
		||||
//#include "Runtime/Engine/Private/EnginePrivate.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +19,7 @@
 | 
			
		|||
 | 
			
		||||
DEFINE_LOG_CATEGORY(LogPathFollowingVR);
 | 
			
		||||
 | 
			
		||||
void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
 | 
			
		||||
/*void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
 | 
			
		||||
{
 | 
			
		||||
	Super::SetMovementComponent(MoveComp);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -26,8 +29,23 @@ void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* Move
 | 
			
		|||
	{
 | 
			
		||||
		OnRequestFinished.AddUObject(VRMovementComp, &UVRBaseCharacterMovementComponent::OnMoveCompleted);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
void UVRPathFollowingComponent::SetNavMovementInterface(INavMovementInterface* NavMoveInterface)
 | 
			
		||||
{
 | 
			
		||||
	Super::SetNavMovementInterface(NavMoveInterface);
 | 
			
		||||
 | 
			
		||||
	if (NavMovementInterface.IsValid() && NavMovementInterface->GetOwnerAsObject())
 | 
			
		||||
	{
 | 
			
		||||
		if (AVRBaseCharacter* VRMovement = Cast<AVRBaseCharacter>(NavMovementInterface->GetOwnerAsObject()))
 | 
			
		||||
		{ 
 | 
			
		||||
			if (IsValid(VRMovement->VRMovementReference))
 | 
			
		||||
			{
 | 
			
		||||
				OnRequestFinished.AddUObject(VRMovement->VRMovementReference, &UVRBaseCharacterMovementComponent::OnMoveCompleted);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UVRPathFollowingComponent::GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -82,52 +100,8 @@ void UVRPathFollowingComponent::GetDebugStringTokens(TArray<FString>& Tokens, TA
 | 
			
		|||
	Flags.Add(bFailedHeight ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UVRPathFollowingComponent::PauseMove(FAIRequestID RequestID, EPathFollowingVelocityMode VelocityMode)
 | 
			
		||||
{
 | 
			
		||||
	//UE_VLOG(GetOwner(), LogPathFollowing, Log, TEXT("PauseMove: RequestID(%u)"), RequestID);
 | 
			
		||||
	if (Status == EPathFollowingStatus::Paused)
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (RequestID.IsEquivalent(GetCurrentRequestId()))
 | 
			
		||||
	{
 | 
			
		||||
		if ((VelocityMode == EPathFollowingVelocityMode::Reset) && MovementComp && HasMovementAuthority())
 | 
			
		||||
		{
 | 
			
		||||
			MovementComp->StopMovementKeepPathing();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LocationWhenPaused = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector;
 | 
			
		||||
		PathTimeWhenPaused = Path.IsValid() ? Path->GetTimeStamp() : 0.;
 | 
			
		||||
		Status = EPathFollowingStatus::Paused;
 | 
			
		||||
 | 
			
		||||
		UpdateMoveFocus();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool UVRPathFollowingComponent::ShouldCheckPathOnResume() const
 | 
			
		||||
{
 | 
			
		||||
	bool bCheckPath = true;
 | 
			
		||||
	if (MovementComp != NULL)
 | 
			
		||||
	{
 | 
			
		||||
		float AgentRadius = 0.0f, AgentHalfHeight = 0.0f;
 | 
			
		||||
		MovementComp->GetOwner()->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight);
 | 
			
		||||
 | 
			
		||||
		const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocation() : MovementComp->GetActorFeetLocation());
 | 
			
		||||
		const FVector::FReal DeltaMove2DSq = (CurrentLocation - LocationWhenPaused).SizeSquared2D();
 | 
			
		||||
		const FVector::FReal DeltaZ = FMath::Abs(CurrentLocation.Z - LocationWhenPaused.Z);
 | 
			
		||||
		if (DeltaMove2DSq < FMath::Square(AgentRadius) && DeltaZ < (AgentHalfHeight * 0.5))
 | 
			
		||||
		{
 | 
			
		||||
			bCheckPath = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bCheckPath;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
 | 
			
		||||
/*int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
 | 
			
		||||
{
 | 
			
		||||
	int32 PickedPathPoint = INDEX_NONE;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +166,7 @@ bool UVRPathFollowingComponent::UpdateBlockDetection()
 | 
			
		|||
		{
 | 
			
		||||
			LocationSamples.AddZeroed(1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		LocationSamples[NextSampleIdx] = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationBased() : MovementComp->GetActorFeetLocationBased());
 | 
			
		||||
		NextSampleIdx = (NextSampleIdx + 1) % BlockDetectionSampleCount;
 | 
			
		||||
		return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -302,8 +276,8 @@ void UVRPathFollowingComponent::UpdatePathSegment()
 | 
			
		|||
			if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex))
 | 
			
		||||
			{
 | 
			
		||||
				//LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct,
 | 
			
		||||
					//*Path->GetPathPointLocation(MoveSegmentStartIndex),
 | 
			
		||||
					//*Path->GetPathPointLocation(MoveSegmentEndIndex));
 | 
			
		||||
					//Path->GetPathPointLocation(MoveSegmentStartIndex),
 | 
			
		||||
					//Path->GetPathPointLocation(MoveSegmentEndIndex));
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +429,7 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current
 | 
			
		|||
 | 
			
		||||
	const FVector ToGoal = (GoalLocation - AgentLocation);
 | 
			
		||||
	const FVector CurrentDirection = GetCurrentDirection();
 | 
			
		||||
	CurrentDot = FloatCastChecked<float>(FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection), /* Precision */ 1. / 128.);
 | 
			
		||||
	CurrentDot = FloatCastChecked<float>(FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection),  1. / 128.);
 | 
			
		||||
	bDotFailed = (CurrentDot < 0.0f) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
	// get cylinder of moving agent
 | 
			
		||||
| 
						 | 
				
			
			@ -472,3 +446,4 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current
 | 
			
		|||
	const float UseHeight = GoalHalfHeight + (AgentHalfHeight * MinAgentHalfHeightPct);
 | 
			
		||||
	bHeightFailed = (CurrentHeight > UseHeight) ? 1 : 0;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -862,7 +862,11 @@ public:
 | 
			
		|||
	{
 | 
			
		||||
		bWillEverBeLit = false;
 | 
			
		||||
		bCreateSceneProxy = InComponent->bShouldCreateProxy;
 | 
			
		||||
		MaterialRelevance = MaterialInstance->GetRelevance(GetScene().GetFeatureLevel());
 | 
			
		||||
 | 
			
		||||
		if (MaterialInstance)
 | 
			
		||||
		{
 | 
			
		||||
			MaterialRelevance = MaterialInstance->GetRelevance(GetScene().GetFeatureLevel());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// FPrimitiveSceneProxy interface.
 | 
			
		||||
| 
						 | 
				
			
			@ -886,12 +890,12 @@ public:
 | 
			
		|||
		{
 | 
			
		||||
			ParentMaterialProxy = WireframeMaterialInstance;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		else if (MaterialInstance != nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			ParentMaterialProxy = MaterialInstance->GetRenderProxy();
 | 
			
		||||
		}
 | 
			
		||||
#else
 | 
			
		||||
		FMaterialRenderProxy* ParentMaterialProxy = MaterialInstance->GetRenderProxy();
 | 
			
		||||
		FMaterialRenderProxy* ParentMaterialProxy = MaterialInstance ? MaterialInstance->GetRenderProxy() : nullptr;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		//FSpriteTextureOverrideRenderProxy* TextureOverrideMaterialProxy = new FSpriteTextureOverrideRenderProxy(ParentMaterialProxy,
 | 
			
		||||
| 
						 | 
				
			
			@ -912,10 +916,10 @@ public:
 | 
			
		|||
			{
 | 
			
		||||
				if (GeometryMode == EWidgetGeometryMode::Plane)
 | 
			
		||||
				{
 | 
			
		||||
					float U = -RenderTarget->SizeX * Pivot.X;
 | 
			
		||||
					float V = -RenderTarget->SizeY * Pivot.Y;
 | 
			
		||||
					float UL = RenderTarget->SizeX * (1.0f - Pivot.X);
 | 
			
		||||
					float VL = RenderTarget->SizeY * (1.0f - Pivot.Y);
 | 
			
		||||
					float U = -RenderTarget->SizeX * static_cast<float>(Pivot.X);
 | 
			
		||||
					float V = -RenderTarget->SizeY * static_cast<float>(Pivot.Y);
 | 
			
		||||
					float UL = RenderTarget->SizeX * (1.0f - static_cast<float>(Pivot.X));
 | 
			
		||||
					float VL = RenderTarget->SizeY * (1.0f - static_cast<float>(Pivot.Y));
 | 
			
		||||
 | 
			
		||||
					int32 VertexIndices[4];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -948,13 +952,13 @@ public:
 | 
			
		|||
					const int32 NumSegments = FMath::Lerp(4, 32, ArcAngle / PI);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
					const float Radius = RenderTarget->SizeX / ArcAngle;
 | 
			
		||||
					const float Apothem = Radius * FMath::Cos(0.5f*ArcAngle);
 | 
			
		||||
					const float ChordLength = 2.0f * Radius * FMath::Sin(0.5f*ArcAngle);
 | 
			
		||||
					const double Radius = RenderTarget->SizeX / ArcAngle;
 | 
			
		||||
					const double Apothem = Radius * FMath::Cos(0.5 * ArcAngle);
 | 
			
		||||
					const double ChordLength = 2.0f * Radius * FMath::Sin(0.5 * ArcAngle);
 | 
			
		||||
 | 
			
		||||
					const float PivotOffsetX = ChordLength * (0.5 - Pivot.X);
 | 
			
		||||
					const float V = -RenderTarget->SizeY * Pivot.Y;
 | 
			
		||||
					const float VL = RenderTarget->SizeY * (1.0f - Pivot.Y);
 | 
			
		||||
					const double PivotOffsetX = ChordLength * (0.5 - Pivot.X);
 | 
			
		||||
					const double V = -RenderTarget->SizeY * Pivot.Y;
 | 
			
		||||
					const double VL = RenderTarget->SizeY * (1.0 - Pivot.Y);
 | 
			
		||||
 | 
			
		||||
					int32 VertexIndices[4];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -964,7 +968,7 @@ public:
 | 
			
		|||
 | 
			
		||||
						if (VisibilityMap & (1 << ViewIndex))
 | 
			
		||||
						{
 | 
			
		||||
							const float RadiansPerStep = ArcAngle / NumSegments;
 | 
			
		||||
							const double RadiansPerStep = ArcAngle / NumSegments;
 | 
			
		||||
 | 
			
		||||
							FVector LastTangentX;
 | 
			
		||||
							FVector LastTangentY;
 | 
			
		||||
| 
						 | 
				
			
			@ -972,14 +976,14 @@ public:
 | 
			
		|||
 | 
			
		||||
							for (int32 Segment = 0; Segment < NumSegments; Segment++)
 | 
			
		||||
							{
 | 
			
		||||
								const float Angle = -ArcAngle / 2 + Segment * RadiansPerStep;
 | 
			
		||||
								const float NextAngle = Angle + RadiansPerStep;
 | 
			
		||||
								const double Angle = -ArcAngle / 2 + Segment * RadiansPerStep;
 | 
			
		||||
								const double NextAngle = Angle + RadiansPerStep;
 | 
			
		||||
 | 
			
		||||
								// Polar to Cartesian
 | 
			
		||||
								const float X0 = Radius * FMath::Cos(Angle) - Apothem;
 | 
			
		||||
								const float Y0 = Radius * FMath::Sin(Angle);
 | 
			
		||||
								const float X1 = Radius * FMath::Cos(NextAngle) - Apothem;
 | 
			
		||||
								const float Y1 = Radius * FMath::Sin(NextAngle);
 | 
			
		||||
								const double X0 = Radius * FMath::Cos(Angle) - Apothem;
 | 
			
		||||
								const double Y0 = Radius * FMath::Sin(Angle);
 | 
			
		||||
								const double X1 = Radius * FMath::Cos(NextAngle) - Apothem;
 | 
			
		||||
								const double Y1 = Radius * FMath::Sin(NextAngle);
 | 
			
		||||
 | 
			
		||||
								const float U0 = static_cast<float>(Segment) / NumSegments;
 | 
			
		||||
								const float U1 = static_cast<float>(Segment + 1) / NumSegments;
 | 
			
		||||
| 
						 | 
				
			
			@ -1123,7 +1127,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
 | 
			
		||||
 | 
			
		||||
	uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
 | 
			
		||||
	SIZE_T GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	FVector Origin;
 | 
			
		||||
| 
						 | 
				
			
			@ -1135,7 +1139,7 @@ private:
 | 
			
		|||
	UBodySetup* BodySetup;
 | 
			
		||||
	EWidgetBlendMode BlendMode;
 | 
			
		||||
	EWidgetGeometryMode GeometryMode;
 | 
			
		||||
	float ArcAngle;
 | 
			
		||||
	double ArcAngle;
 | 
			
		||||
	bool bCreateSceneProxy;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1149,10 +1153,13 @@ FPrimitiveSceneProxy* UVRStereoWidgetComponent::CreateSceneProxy()
 | 
			
		|||
 | 
			
		||||
	if (WidgetRenderer && GetSlateWindow() && GetSlateWindow()->GetContent() != SNullWidget::NullWidget)
 | 
			
		||||
	{
 | 
			
		||||
		RequestRenderUpdate();
 | 
			
		||||
		LastWidgetRenderTime = 0;
 | 
			
		||||
		if (ISlate3DRenderer* SlateRenderer = WidgetRenderer->GetSlateRenderer())
 | 
			
		||||
		{
 | 
			
		||||
			RequestRenderUpdate();
 | 
			
		||||
			LastWidgetRenderTime = 0;
 | 
			
		||||
 | 
			
		||||
		return new FStereoWidget3DSceneProxy(this, *WidgetRenderer->GetSlateRenderer());
 | 
			
		||||
			return new FStereoWidget3DSceneProxy(this, *SlateRenderer);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if WITH_EDITOR
 | 
			
		||||
| 
						 | 
				
			
			@ -1208,7 +1215,7 @@ FPrimitiveSceneProxy* UVRStereoWidgetComponent::CreateSceneProxy()
 | 
			
		|||
			return Result;
 | 
			
		||||
		}
 | 
			
		||||
		virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
 | 
			
		||||
		uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
 | 
			
		||||
		SIZE_T GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		const FVector	BoxExtents;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ public:
 | 
			
		|||
	UPROPERTY()
 | 
			
		||||
		uint8 MoveActionFlags;
 | 
			
		||||
	UPROPERTY()
 | 
			
		||||
		TArray<UObject*> MoveActionObjectReferences;
 | 
			
		||||
		TArray<TObjectPtr<UObject>> MoveActionObjectReferences;
 | 
			
		||||
	UPROPERTY()
 | 
			
		||||
		EVRMoveActionVelocityRetention VelRetentionSetting;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -516,7 +516,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	// Moved these here to avoid having to duplicate tons of properties
 | 
			
		||||
	UPROPERTY(Transient)
 | 
			
		||||
		UPrimitiveComponent* ClientMovementBase;
 | 
			
		||||
		TObjectPtr<UPrimitiveComponent> ClientMovementBase;
 | 
			
		||||
	UPROPERTY(Transient)
 | 
			
		||||
		FName ClientBaseBoneName;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -809,7 +809,7 @@ public:
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	UPROPERTY(BlueprintReadWrite, Category = "GripMotionController")
 | 
			
		||||
	TArray<UPrimitiveComponent *> AdditionalLateUpdateComponents;
 | 
			
		||||
	TArray<TObjectPtr<UPrimitiveComponent>> AdditionalLateUpdateComponents;
 | 
			
		||||
 | 
			
		||||
	//  Movement Replication
 | 
			
		||||
	// Actor needs to be replicated for this to work
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,8 +64,11 @@ private:
 | 
			
		|||
	TMap<Chaos::FConstPhysicsObjectHandle, FReplicatedPhysicsTargetAsync> ObjectToTarget;
 | 
			
		||||
	TMap<Chaos::FConstPhysicsObjectHandle, FNetworkPhysicsSettingsAsync> ObjectToSettings;
 | 
			
		||||
	TArray<int32> ParticlesInResimIslands;
 | 
			
		||||
	TArray<Chaos::FParticleID> ReplicatedParticleIDs;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	FReplicatedPhysicsTargetAsync* AddObjectToReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject);
 | 
			
		||||
	void RemoveObjectFromReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject);
 | 
			
		||||
	void UpdateAsyncTarget(const FPhysicsRepAsyncInputData& Input, Chaos::FPBDRigidsSolver* RigidsSolver);
 | 
			
		||||
	void UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input);
 | 
			
		||||
	void CacheResimInteractions();
 | 
			
		||||
| 
						 | 
				
			
			@ -105,6 +108,7 @@ public:
 | 
			
		|||
	TArray<FReplicatedPhysicsTarget> ReplicatedTargetsQueueVR;
 | 
			
		||||
	FPhysicsReplicationAsyncVR* PhysicsReplicationAsyncVR;
 | 
			
		||||
	FPhysicsReplicationAsyncInput* AsyncInputVR;	//async data being written into before we push into callback
 | 
			
		||||
	TWeakObjectPtr<UNetworkPhysicsSettingsComponent> SettingsCurrent;
 | 
			
		||||
 | 
			
		||||
	void PrepareAsyncData_ExternalVR(const FRigidBodyErrorCorrection& ErrorCorrection);	//prepare async data for writing. Call on external thread (i.e. game thread)
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,7 +113,7 @@ struct FAISightTargetVR
 | 
			
		|||
	static const FTargetId InvalidTargetId;
 | 
			
		||||
 | 
			
		||||
	TWeakObjectPtr<AActor> Target;
 | 
			
		||||
	IAISightTargetInterface* SightTargetInterface;
 | 
			
		||||
	TWeakInterfacePtr<IAISightTargetInterface> WeakSightTargetInterface;
 | 
			
		||||
	FGenericTeamId TeamId;
 | 
			
		||||
	FTargetId TargetId;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -333,7 +333,7 @@ protected:
 | 
			
		|||
	FOnPendingVisibilityQueryProcessedDelegateVR OnPendingCanBeSeenQueryProcessedDelegate;
 | 
			
		||||
	FTraceDelegate OnPendingTraceQueryProcessedDelegate;
 | 
			
		||||
 | 
			
		||||
	UE_MT_DECLARE_RW_ACCESS_DETECTOR(QueriesListAccessDetector);
 | 
			
		||||
	UE_MT_DECLARE_TS_RW_ACCESS_DETECTOR(QueriesListAccessDetector);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,17 +28,21 @@ public:
 | 
			
		|||
		void SetConstraintToForceBased(bool bUseForceConstraint)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		if (!ConstraintInstance.ConstraintHandle.IsValid())
 | 
			
		||||
		// #TODO: Double check on this in 5.5, but it should work
 | 
			
		||||
		this->SetLinearDriveAccelerationMode(!bUseForceConstraint);
 | 
			
		||||
		this->SetAngularDriveAccelerationMode(!bUseForceConstraint);
 | 
			
		||||
		/*if (!ConstraintInstance.ConstraintHandle.IsValid())
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		if (ConstraintInstance.ConstraintHandle->IsType(Chaos::EConstraintType::JointConstraintType))
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
			if (Chaos::FJointConstraint* Constraint = static_cast<Chaos::FJointConstraint*>(ConstraintInstance.ConstraintHandle.Constraint))
 | 
			
		||||
			{
 | 
			
		||||
				Constraint->SetLinearDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
 | 
			
		||||
				Constraint->SetAngularDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		}*/
 | 
			
		||||
 | 
			
		||||
		//#endif
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -388,7 +388,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	/** */
 | 
			
		||||
	UPROPERTY(VisibleAnywhere, Instanced, NoClear, Category = "User Interface", meta = (ShowOnlyInnerProperties))
 | 
			
		||||
		UVRFullScreenUserWidget* ScreenUserWidget;
 | 
			
		||||
		TObjectPtr<UVRFullScreenUserWidget> ScreenUserWidget;
 | 
			
		||||
 | 
			
		||||
	UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
 | 
			
		||||
		UVRFullScreenUserWidget* GetPreviewWidgetComp();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,7 +175,7 @@ protected:
 | 
			
		|||
			int numMessages = OutMessages.Num();
 | 
			
		||||
			if (numMessages > MaxStoredMessages)
 | 
			
		||||
			{
 | 
			
		||||
				OutMessages.RemoveAt(0, numMessages - MaxStoredMessages, true);
 | 
			
		||||
				OutMessages.RemoveAt(0, numMessages - MaxStoredMessages, EAllowShrinking::Yes);
 | 
			
		||||
			}
 | 
			
		||||
			if (OldNumMessages != numMessages)
 | 
			
		||||
				bIsDirty = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,40 @@
 | 
			
		|||
#include "VRWheeledVehicle.generated.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// This only exists to expose the blueprint properties of the transmission values
 | 
			
		||||
USTRUCT(BlueprintType, Category = "VRWheeledVehicle")
 | 
			
		||||
struct VREXPANSIONPLUGIN_API FBPVehicleTransmissionConfig : public FVehicleTransmissionConfig
 | 
			
		||||
{
 | 
			
		||||
	GENERATED_BODY()
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
	void SetFrom(FVehicleTransmissionConfig & Config)
 | 
			
		||||
	{
 | 
			
		||||
		bUseAutomaticGears = Config.bUseAutomaticGears;
 | 
			
		||||
		bUseAutoReverse = Config.bUseAutoReverse;
 | 
			
		||||
		FinalRatio = Config.FinalRatio;
 | 
			
		||||
		ForwardGearRatios = Config.ForwardGearRatios;
 | 
			
		||||
		ReverseGearRatios = Config.ReverseGearRatios;
 | 
			
		||||
		ChangeUpRPM = Config.ChangeUpRPM;
 | 
			
		||||
		ChangeDownRPM = Config.ChangeDownRPM;
 | 
			
		||||
		GearChangeTime = Config.GearChangeTime;
 | 
			
		||||
		TransmissionEfficiency = Config.TransmissionEfficiency;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void SetTo(FVehicleTransmissionConfig& Config)
 | 
			
		||||
	{
 | 
			
		||||
		Config.bUseAutomaticGears = bUseAutomaticGears;
 | 
			
		||||
		Config.bUseAutoReverse = bUseAutoReverse;
 | 
			
		||||
		Config.FinalRatio = FinalRatio;
 | 
			
		||||
		Config.ForwardGearRatios = ForwardGearRatios;
 | 
			
		||||
		Config.ReverseGearRatios = ReverseGearRatios;
 | 
			
		||||
		Config.ChangeUpRPM = ChangeUpRPM;
 | 
			
		||||
		Config.ChangeDownRPM = ChangeDownRPM;
 | 
			
		||||
		Config.GearChangeTime = GearChangeTime;
 | 
			
		||||
		Config.TransmissionEfficiency = TransmissionEfficiency;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* This override of the base wheeled vehicle allows for dual pawn usage in engine.
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +58,29 @@ class VREXPANSIONPLUGIN_API AVRWheeledVehicle : public AWheeledVehiclePawn
 | 
			
		|||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	UFUNCTION(BlueprintPure, Category = "Pawn")
 | 
			
		||||
	float GetFinalGearRatio()
 | 
			
		||||
	{
 | 
			
		||||
		float CurrentGearRatio = 0.0f;
 | 
			
		||||
		if (UChaosWheeledVehicleMovementComponent* MoveComp = Cast<UChaosWheeledVehicleMovementComponent>(this->GetMovementComponent()))
 | 
			
		||||
		{		
 | 
			
		||||
			CurrentGearRatio = MoveComp->TransmissionSetup.FinalRatio;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return CurrentGearRatio;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UFUNCTION(BlueprintCallable, Category = "Pawn")
 | 
			
		||||
	void SetFinalGearRatio(float NewFinalGearRatio)
 | 
			
		||||
	{
 | 
			
		||||
		if (UChaosWheeledVehicleMovementComponent* MoveComp = Cast<UChaosWheeledVehicleMovementComponent>(this->GetMovementComponent()))
 | 
			
		||||
		{
 | 
			
		||||
			MoveComp->TransmissionSetup.FinalRatio = NewFinalGearRatio;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Calls the movement components override controller function
 | 
			
		||||
	UFUNCTION(BlueprintCallable, Category = "Pawn")
 | 
			
		||||
		virtual bool SetBindToInput(AController * CController, bool bBindToInput)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,7 +101,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	// Get Camera View is no longer required, they finally broke the HMD logic out into its own section!!
 | 
			
		||||
	//virtual void GetCameraView(float DeltaTime, FMinimalViewInfo& DesiredView) override;
 | 
			
		||||
	virtual void HandleXRCamera() override;
 | 
			
		||||
	virtual void HandleXRCamera(float DeltaTime) override;
 | 
			
		||||
 | 
			
		||||
	UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedCameraTransform, Category = "ReplicatedCamera|Networking")
 | 
			
		||||
	FBPVRComponentPosRep ReplicatedCameraTransform;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,67 +50,7 @@ public:
 | 
			
		|||
	UPROPERTY(Transient)
 | 
			
		||||
		TObjectPtr<AActor> Owner;
 | 
			
		||||
 | 
			
		||||
	bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
 | 
			
		||||
	{
 | 
			
		||||
		FRepMovement BaseSettings = Owner ? Owner->GetReplicatedMovement() : FRepMovement();
 | 
			
		||||
 | 
			
		||||
		// pack bitfield with flags
 | 
			
		||||
		uint8 Flags = (bSimulatedPhysicSleep << 0) | (bRepPhysics << 1) | (bJustTeleported << 2) | (bJustTeleportedGrips << 3) | (bPausedTracking << 4);
 | 
			
		||||
		Ar.SerializeBits(&Flags, 5);
 | 
			
		||||
		bSimulatedPhysicSleep = (Flags & (1 << 0)) ? 1 : 0;
 | 
			
		||||
		bRepPhysics = (Flags & (1 << 1)) ? 1 : 0;
 | 
			
		||||
		bJustTeleported = (Flags & (1 << 2)) ? 1 : 0;
 | 
			
		||||
		bJustTeleportedGrips = (Flags & (1 << 3)) ? 1 : 0;
 | 
			
		||||
		bPausedTracking = (Flags & (1 << 4)) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
		bOutSuccess = true;
 | 
			
		||||
 | 
			
		||||
		if (bPausedTracking)
 | 
			
		||||
		{
 | 
			
		||||
			bOutSuccess &= PausedTrackingLoc.NetSerialize(Ar, Map, bOutSuccess);
 | 
			
		||||
 | 
			
		||||
			uint16 Yaw = 0;
 | 
			
		||||
			if (Ar.IsSaving())
 | 
			
		||||
			{
 | 
			
		||||
				Yaw = FRotator::CompressAxisToShort(PausedTrackingRot);
 | 
			
		||||
				Ar << Yaw;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				Ar << Yaw;
 | 
			
		||||
				PausedTrackingRot = Yaw;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// update location, rotation, linear velocity
 | 
			
		||||
		bOutSuccess &= SerializeQuantizedVector(Ar, Location, BaseSettings.LocationQuantizationLevel);
 | 
			
		||||
 | 
			
		||||
		switch (BaseSettings.RotationQuantizationLevel)
 | 
			
		||||
		{
 | 
			
		||||
		case ERotatorQuantization::ByteComponents:
 | 
			
		||||
		{
 | 
			
		||||
			Rotation.SerializeCompressed(Ar);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		case ERotatorQuantization::ShortComponents:
 | 
			
		||||
		{
 | 
			
		||||
			Rotation.SerializeCompressedShort(Ar);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bOutSuccess &= SerializeQuantizedVector(Ar, LinearVelocity, BaseSettings.VelocityQuantizationLevel);
 | 
			
		||||
 | 
			
		||||
		// update angular velocity if required
 | 
			
		||||
		if (bRepPhysics)
 | 
			
		||||
		{
 | 
			
		||||
			bOutSuccess &= SerializeQuantizedVector(Ar, AngularVelocity, BaseSettings.VelocityQuantizationLevel);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
| 
						 | 
				
			
			@ -276,7 +216,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	/** BaseVR Character movement component belongs to */
 | 
			
		||||
	UPROPERTY(Transient, DuplicateTransient)
 | 
			
		||||
		AVRPlayerController* OwningVRPlayerController;
 | 
			
		||||
		TObjectPtr<AVRPlayerController> OwningVRPlayerController;
 | 
			
		||||
 | 
			
		||||
	// If true then we will retain roomscale tracking in relative space of the character.
 | 
			
		||||
	// If false than the movement component will offset to the hmd tracking and the tracking will be nulled out
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ public:
 | 
			
		|||
	static const float CLIMB_SWEEP_EDGE_REJECT_DISTANCE;
 | 
			
		||||
	virtual bool IsWithinClimbingEdgeTolerance(const FVector& CapsuleLocation, const FVector& TestImpactPoint, const float CapsuleRadius) const;
 | 
			
		||||
	virtual bool VRClimbStepUp(const FVector& GravDir, const FVector& Delta, const FHitResult &InHit, FStepDownResult* OutStepDownResult = nullptr) override;
 | 
			
		||||
	virtual FVector GetActorFeetLocation() const override;
 | 
			
		||||
 | 
			
		||||
	virtual bool IsWithinEdgeTolerance(const FVector& CapsuleLocation, const FVector& TestImpactPoint, const float CapsuleRadius) const override;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
#include "Navigation/PathFollowingComponent.h"
 | 
			
		||||
#include "AbstractNavData.h"
 | 
			
		||||
#include "Runtime/Launch/Resources/Version.h"
 | 
			
		||||
 | 
			
		||||
#include "VRPathFollowingComponent.generated.h"
 | 
			
		||||
 | 
			
		||||
DECLARE_LOG_CATEGORY_EXTERN(LogPathFollowingVR, Warning, All);
 | 
			
		||||
| 
						 | 
				
			
			@ -18,8 +19,17 @@ class VREXPANSIONPLUGIN_API UVRPathFollowingComponent : public UPathFollowingCom
 | 
			
		|||
	GENERATED_BODY()
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	UPROPERTY(transient)
 | 
			
		||||
		UVRBaseCharacterMovementComponent* VRMovementComp;
 | 
			
		||||
 | 
			
		||||
	virtual void SetNavMovementInterface(INavMovementInterface* NavMoveInterface) override;
 | 
			
		||||
 | 
			
		||||
	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
			
		||||
	// Update with 5.5, they are now using the interface feet location so I don't think I need to override anything anymore.
 | 
			
		||||
	// Keeping the class intact in case people subclassed it.
 | 
			
		||||
	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/*UPROPERTY(transient)
 | 
			
		||||
		TObjectPtr<UVRBaseCharacterMovementComponent> VRMovementComp;
 | 
			
		||||
 | 
			
		||||
	// Add link to VRMovementComp
 | 
			
		||||
	void SetMovementComponent(UNavMovementComponent* MoveComp) override;
 | 
			
		||||
| 
						 | 
				
			
			@ -28,29 +38,29 @@ public:
 | 
			
		|||
	// Have to override this to call the correct HasReachCurrentTarget
 | 
			
		||||
	void UpdatePathSegment() override;
 | 
			
		||||
	bool HasReachedCurrentTarget(const FVector& CurrentLocation) const;
 | 
			
		||||
 | 
			
		||||
	*/
 | 
			
		||||
	// Had to override this to get the correct DebugReachTest
 | 
			
		||||
	virtual void GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const override;
 | 
			
		||||
	void DebugReachTest(float& CurrentDot, float& CurrentDistance, float& CurrentHeight, uint8& bDotFailed, uint8& bDistanceFailed, uint8& bHeightFailed) const;
 | 
			
		||||
	/*void DebugReachTest(float& CurrentDot, float& CurrentDistance, float& CurrentHeight, uint8& bDotFailed, uint8& bDistanceFailed, uint8& bHeightFailed) const;
 | 
			
		||||
 | 
			
		||||
	void FollowPathSegment(float DeltaTime) override;
 | 
			
		||||
	int32 DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const override;
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
	// This has a foot location when using meta paths, i'm not overriding it yet but this means that meta paths might have slightly bugged implementation.
 | 
			
		||||
	/** notify about finished movement */
 | 
			
		||||
	//virtual void OnPathFinished(const FPathFollowingResult& Result) override;
 | 
			
		||||
 | 
			
		||||
	/** pause path following
 | 
			
		||||
	// notify about finished movement 
 | 
			
		||||
	virtual void OnPathFinished(const FPathFollowingResult& Result) override;
 | 
			
		||||
	*/
 | 
			
		||||
	/* pause path following
 | 
			
		||||
	*  @param RequestID - request to pause, FAIRequestID::CurrentRequest means pause current request, regardless of its ID */
 | 
			
		||||
	void PauseMove(FAIRequestID RequestID = FAIRequestID::CurrentRequest, EPathFollowingVelocityMode VelocityMode = EPathFollowingVelocityMode::Reset) override;
 | 
			
		||||
	//void PauseMove(FAIRequestID RequestID = FAIRequestID::CurrentRequest, EPathFollowingVelocityMode VelocityMode = EPathFollowingVelocityMode::Reset) override;
 | 
			
		||||
	// Now has an actor feet call  .......Just a debug reference
 | 
			
		||||
	//virtual FAIRequestID RequestMove(FNavPathSharedPtr Path, FRequestCompletedSignature OnComplete, const AActor* DestinationActor = NULL, float AcceptanceRadius = UPathFollowingComponent::DefaultAcceptanceRadius, bool bStopOnOverlap = true, FCustomMoveSharedPtr GameData = NULL) override;
 | 
			
		||||
	/*virtual FAIRequestID RequestMove(FNavPathSharedPtr Path, FRequestCompletedSignature OnComplete, const AActor* DestinationActor = NULL, float AcceptanceRadius = UPathFollowingComponent::DefaultAcceptanceRadius, bool bStopOnOverlap = true, FCustomMoveSharedPtr GameData = NULL) override;
 | 
			
		||||
	// 
 | 
			
		||||
	// Fine in 4.13
 | 
			
		||||
	bool ShouldCheckPathOnResume() const override;
 | 
			
		||||
 | 
			
		||||
	// Fine in 4.13, had to change feet based for both
 | 
			
		||||
	bool UpdateBlockDetection() override;
 | 
			
		||||
	bool UpdateBlockDetection() override;*/
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
	"FileVersion": 3,
 | 
			
		||||
	"EngineAssociation": "5.4",
 | 
			
		||||
	"EngineAssociation": "5.5",
 | 
			
		||||
	"Category": "",
 | 
			
		||||
	"Description": "",
 | 
			
		||||
	"Modules": [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue