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:
Simeon "Waldo" Wallrath 2024-11-18 17:09:08 +01:00
parent 55d567e436
commit f086787883
40 changed files with 1044 additions and 564 deletions

View file

@ -2,9 +2,11 @@
"version": "1.0", "version": "1.0",
"components": [ "components": [
"Microsoft.Net.Component.4.6.2.TargetingPack", "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.14.38.17.8.x86.x64",
"Microsoft.VisualStudio.Component.VC.Tools.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.CoreEditor",
"Microsoft.VisualStudio.Workload.ManagedDesktop", "Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop", "Microsoft.VisualStudio.Workload.NativeDesktop",

@ -1 +1 @@
Subproject commit 006e2b4922eb4f7af0d6df1468a042a98eecf68f Subproject commit 968fe10c9f08859c64ea88768d5216140fc340d9

View file

@ -1,7 +1,7 @@
{ {
"FileVersion": 3, "FileVersion": 3,
"Version": 5.4, "Version": 5.5,
"VersionName": "5.4", "VersionName": "5.5",
"FriendlyName": "OpenXRExpansionPlugin", "FriendlyName": "OpenXRExpansionPlugin",
"Description": "An set of utility functions for OpenXR", "Description": "An set of utility functions for OpenXR",
"Category": "Virtual Reality", "Category": "Virtual Reality",
@ -39,10 +39,10 @@
"Name": "OpenXR", "Name": "OpenXR",
"Enabled": true, "Enabled": true,
"PlatformAllowList": [ "PlatformAllowList": [
"Win64", "Win64",
"Linux", "Linux",
"Android" "Android"
] ]
}, },
{ {
"Name": "XRBase", "Name": "XRBase",

View file

@ -4,6 +4,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "UObject/ObjectMacros.h" #include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h" #include "Kismet/BlueprintFunctionLibrary.h"
#include "Animation/BoneReference.h"
#include "UObject/Object.h" #include "UObject/Object.h"
#include "Engine/EngineTypes.h" #include "Engine/EngineTypes.h"

View file

@ -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 // 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 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(); const AActor* MyOwner = GetOwner();
return MyOwner->HasLocalNetOwner(); return MyOwner->HasLocalNetOwner();
#else //#else
// I like epics new authority check more than mine // I like epics new authority check more than mine
const AActor* MyOwner = GetOwner(); /* const AActor* MyOwner = GetOwner();
const APawn* MyPawn = Cast<APawn>(MyOwner); const APawn* MyPawn = Cast<APawn>(MyOwner);
return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority); return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);*/
#endif //#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 // Using tick and not timers because skeletal components tick anyway, kind of a waste to make another tick by adding a timer over that

View file

@ -7214,10 +7214,10 @@ void UGripMotionControllerComponent::ApplyTrackingParameters(FVector& OriginalPo
if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, curRot, curLoc)) if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, curRot, curLoc))
{ {
if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera) /*if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera)
{ {
AttachChar->VRReplicatedCamera->ApplyTrackingParameters(curLoc, true); AttachChar->VRReplicatedCamera->ApplyTrackingParameters(curLoc, true);
} }*/
//curLoc.Z = 0; //curLoc.Z = 0;
LastLocationForLateUpdate = curLoc; LastLocationForLateUpdate = curLoc;
@ -7234,7 +7234,7 @@ void UGripMotionControllerComponent::ApplyTrackingParameters(FVector& OriginalPo
if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera) if (IsValid(AttachChar) && AttachChar->VRReplicatedCamera)
{ {
// Sample camera location instead // Sample camera location instead
LastLocationForLateUpdate = AttachChar->VRReplicatedCamera->GetRelativeLocation(); LastLocationForLateUpdate = AttachChar->VRReplicatedCamera->ReplicatedCameraTransform.Position; //GetRelativeLocation();
if (!AttachChar->bRetainRoomscale && IsLocallyControlled()) if (!AttachChar->bRetainRoomscale && IsLocallyControlled())
{ {

View file

@ -54,7 +54,7 @@ AGrippableActor::AGrippableActor(const FObjectInitializer& ObjectInitializer)
// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration // 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 // 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 void AGrippableActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
@ -152,13 +152,23 @@ void AGrippableActor::GatherCurrentMovement()
bool bFoundInCache = false; bool bFoundInCache = false;
UWorld* World = GetWorld(); UWorld* World = GetWorld();
const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
int ServerFrame = 0; 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); if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
bFoundInCache = true; {
if (RepMovement.ServerFrame != ServerFrame)
{
RepMovement.FillFrom(*FoundState, this, ServerFrame);
bWasRepMovementModified = true;
}
bFoundInCache = true;
}
} }
} }

View file

@ -52,9 +52,16 @@ namespace PhysicsReplicationCVars
int32 EnableDefaultReplication = 0; int32 EnableDefaultReplication = 0;
namespace DefaultReplicationCVars
{
bool bHardsnapLegacyInPT = false;
bool bCorrectConnectedBodies = false;
bool bCorrectConnectedBodiesFriction = true;
}
namespace ResimulationCVars namespace ResimulationCVars
{ {
bool bRuntimeCorrectionEnabled = true; bool bRuntimeCorrectionEnabled = false;
bool bRuntimeVelocityCorrection = false; bool bRuntimeVelocityCorrection = false;
bool bRuntimeCorrectConnectedBodies = true; bool bRuntimeCorrectConnectedBodies = true;
float PosStabilityMultiplier = 0.5f; float PosStabilityMultiplier = 0.5f;
@ -62,6 +69,18 @@ namespace PhysicsReplicationCVars
float VelStabilityMultiplier = 0.5f; float VelStabilityMultiplier = 0.5f;
float AngVelStabilityMultiplier = 0.5f; float AngVelStabilityMultiplier = 0.5f;
bool bDrawDebug = false; 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 namespace PredictiveInterpolationCVars
@ -92,15 +111,24 @@ namespace PhysicsReplicationCVars
bool bSkipVelocityRepOnPosEarlyOut = true; bool bSkipVelocityRepOnPosEarlyOut = true;
bool bPostResimWaitForUpdate = false; bool bPostResimWaitForUpdate = false;
bool bVelocityBased = true; 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 bDisableSoftSnap = false;
bool bAlwaysHardSnap = false; bool bAlwaysHardSnap = false;
bool bSkipReplication = false; bool bSkipReplication = false;
bool bDontClearTarget = false; bool bDontClearTarget = false;
bool bDrawDebugTargets = false; bool bDrawDebugTargets = false;
bool bDrawDebugVectors = false; bool bDrawDebugVectors = false;
float DrawDebugZOffset = 50.0f;
float SleepSecondsClearTarget = 15.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; const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrection.AngularVelocityCoefficient;
static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance")); 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 // Get Current state
FRigidBodyState CurrentState; 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 // Too much error so just snap state here and be done with it
PhysicsTarget.AccumulatedErrorSeconds = 0.0f; PhysicsTarget.AccumulatedErrorSeconds = 0.0f;
bRestoredState = true; 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 // Set the new velocities
BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake); BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake);
@ -714,7 +795,8 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
int32 LocalFrameOffset = 0; // LocalFrame = ServerFrame + LocalFrameOffset; int32 LocalFrameOffset = 0; // LocalFrame = ServerFrame + LocalFrameOffset;
if (FPhysicsSolverBase::IsNetworkPhysicsPredictionEnabled()) bool LocalFrameOffsetAssigned = false;
if (UPhysicsSettings::Get()->PhysicsPrediction.bEnablePhysicsPrediction)
{ {
if (UWorld* World = GetOwningWorld()) if (UWorld* World = GetOwningWorld())
{ {
@ -722,6 +804,7 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
{ {
if (APlayerController* PlayerController = World->GetFirstPlayerController()) if (APlayerController* PlayerController = World->GetFirstPlayerController())
{ {
LocalFrameOffsetAssigned = PlayerController->GetNetworkPhysicsTickOffsetAssigned();
LocalFrameOffset = PlayerController->GetNetworkPhysicsTickOffset(); LocalFrameOffset = PlayerController->GetNetworkPhysicsTickOffset();
} }
} }
@ -753,6 +836,9 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
{ {
// Removed as this is server sided // Removed as this is server sided
/* /*
// Update actor replication settings overrides
SettingsCurrent = UNetworkPhysicsSettingsComponent::GetSettingsForActor(OwningActor);
const ENetRole OwnerRole = OwningActor->GetLocalRole(); const ENetRole OwnerRole = OwningActor->GetLocalRole();
const bool bIsSimulated = OwnerRole == ROLE_SimulatedProxy; const bool bIsSimulated = OwnerRole == ROLE_SimulatedProxy;
const bool bIsReplicatedAutonomous = OwnerRole == ROLE_AutonomousProxy && PrimComp->bReplicatePhysicsToAutonomousProxy; 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.RepMode = PhysicsTarget.ReplicationMode;
AsyncInputData.ServerFrame = PhysicsTarget.ServerFrame; AsyncInputData.ServerFrame = PhysicsTarget.ServerFrame;
AsyncInputData.FrameOffset = LocalFrameOffset;
AsyncInputData.LatencyOneWay = PingSecondsOneWay; AsyncInputData.LatencyOneWay = PingSecondsOneWay;
if (LocalFrameOffsetAssigned)
{
AsyncInputData.FrameOffset = LocalFrameOffset;
}
AsyncInputVR->InputData.Add(AsyncInputData); AsyncInputVR->InputData.Add(AsyncInputData);
} }
ReplicatedTargetsQueueVR.Reset(); ReplicatedTargetsQueueVR.Reset();
@ -919,7 +1010,9 @@ bool FRepMovementVR::GatherActorsMovement(AActor* OwningActor)
void FPhysicsReplicationAsyncVR::OnPhysicsObjectUnregistered_Internal(Chaos::FConstPhysicsObjectHandle PhysicsObject) 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); ObjectToSettings.Remove(PhysicsObject);
} }
@ -990,18 +1083,18 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal()
{ {
if (Input.TargetState.Flags == ERigidBodyFlags::None) if (Input.TargetState.Flags == ERigidBodyFlags::None)
{ {
// Remove replication target // Remove replication target
ObjectToTarget.Remove(Input.PhysicsObject); RemoveObjectFromReplication(Input.PhysicsObject);
continue; continue;
} }
if (!bRewindDataExist && Input.RepMode == EPhysicsReplicationMode::Resimulation) 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. // 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) void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input)
{ {
if (Input.PhysicsObject == nullptr) if (Input.PhysicsObject == nullptr)
@ -1025,6 +1153,12 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
return; 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()); Chaos::FPBDRigidsSolver* RigidsSolver = static_cast<Chaos::FPBDRigidsSolver*>(GetSolver());
if (RigidsSolver == nullptr) if (RigidsSolver == nullptr)
{ {
@ -1041,10 +1175,10 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(Input.PhysicsObject)) if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(Input.PhysicsObject))
{ {
// Cache all target states inside RewindData // 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, RewindData->SetTargetStateAtFrame(*Handle, LocalFrame, Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData,
Input.TargetState.Position, Input.TargetState.Quaternion, 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) if (bFirstTarget)
{ {
// First time we add a target, set previous state to current input // 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->PrevPos = Input.TargetState.Position;
Target->PrevPosTarget = Input.TargetState.Position; Target->PrevPosTarget = Input.TargetState.Position;
Target->PrevRotTarget = Input.TargetState.Quaternion; 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 // Cache if this target was previously allowed to be altered, before this update
const bool bPrevAllowTargetAltering = Target->bAllowTargetAltering; 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 // Set if the target is allowed to be altered after this update
Target->bAllowTargetAltering = !(Target->TargetState.Flags & ERigidBodyFlags::Sleeping) && !(Input.TargetState.Flags & ERigidBodyFlags::Sleeping); 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->ReceiveFrame = CurrentFrame;
Target->TargetState = Input.TargetState; Target->TargetState = Input.TargetState;
Target->RepMode = Input.RepMode; Target->RepMode = Input.RepMode;
Target->FrameOffset = Input.FrameOffset; Target->FrameOffset = Input.FrameOffset.IsSet() ? *Input.FrameOffset : 0;
Target->TickCount = 0; Target->TickCount = 0;
Target->AccumulatedSleepSeconds = 0.0f; Target->AccumulatedSleepSeconds = 0.0f;
@ -1145,6 +1282,8 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
if (CVarDrawDebugTargets->GetBool()) if (CVarDrawDebugTargets->GetBool())
{ {
const FVector Offset = FVector(0.0f, 0.0f, 50.0f); 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")); 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); 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 // 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")); static const auto CVarTargetTickAlignmentClampMultiplier = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.TargetTickAlignmentClampMultiplier"));
const int32 AdjustedAverageReceiveInterval = FMath::CeilToInt(Target->AverageReceiveInterval) * CVarTargetTickAlignmentClampMultiplier->GetInt(); 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) 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 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(); Chaos::FConstPhysicsObjectHandle& POHandle = Itr.Key();
if (FGeometryParticleHandle* Handle = Interface.GetParticle(POHandle)) if (FGeometryParticleHandle* Handle = Interface.GetParticle(POHandle))
@ -1266,6 +1406,8 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
if (FPBDRigidParticleHandle* RigidHandle = Handle->CastToRigidParticle()) if (FPBDRigidParticleHandle* RigidHandle = Handle->CastToRigidParticle())
{ {
ParticleID = RigidHandle->ParticleID();
// Cache custom settings for this object if there are any // Cache custom settings for this object if there are any
FetchObjectSettings(POHandle); FetchObjectSettings(POHandle);
@ -1290,6 +1432,7 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
if (bRemoveItr) if (bRemoveItr)
{ {
ReplicatedParticleIDs.Remove(ParticleID);
Itr.RemoveCurrent(); Itr.RemoveCurrent();
} }
} }
@ -1463,7 +1606,8 @@ bool FPhysicsReplicationAsyncVR::DefaultReplication(Chaos::FPBDRigidParticleHand
const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrectionDefault.AngularVelocityCoefficient; const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrectionDefault.AngularVelocityCoefficient;
static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance")); 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 // Get Current state
FRigidBodyState CurrentState; FRigidBodyState CurrentState;
@ -1582,8 +1726,11 @@ bool FPhysicsReplicationAsyncVR::DefaultReplication(Chaos::FPBDRigidParticleHand
// Too much error so just snap state here and be done with it // Too much error so just snap state here and be done with it
Target.AccumulatedErrorSeconds = 0.0f; Target.AccumulatedErrorSeconds = 0.0f;
bRestoredState = true; 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->SetV(NewState.LinVel);
Handle->SetW(FMath::DegreesToRadians(NewState.AngVel)); 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")); static const auto CVarDrawDebugTargets = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugTargets"));
if (CVarDrawDebugTargets->GetBool()) if (CVarDrawDebugTargets->GetBool())
{ {
// Needs updated post 5.5 for DrawDebugZ CVAR
const FVector Offset = FVector(0.0f, 0.0f, 50.0f); const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
const FVector StartPos = Target.TargetState.Position + Offset; const FVector StartPos = Target.TargetState.Position + Offset;
const int32 SizeMultiplier = FMath::Clamp(Target.TickCount, -4, 30); const int32 SizeMultiplier = FMath::Clamp(Target.TickCount, -4, 30);
@ -1675,15 +1824,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
if (bOkToClear && bShouldSleep && bCanSimulate) if (bOkToClear && bShouldSleep && bCanSimulate)
{ {
RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Sleeping); 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 CVarSleepSecondsClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepSecondsClearTarget"));
static const auto CVarDontClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DontClearTarget")); static const auto CVarDontClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DontClearTarget"));
// --- Should replication stop? --- // --- Should replication stop? ---
const bool bClearTarget = const bool bClearTarget =
(!bCanSimulate ((bOkToClear && bShouldSleep && Target.AccumulatedSleepSeconds >= CVarSleepSecondsClearTarget->GetFloat()) // Allow clearing the target due to sleeping after the object has been sleeping for n seconds
|| (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) // If replication say it's okay to clear the target and the object shouldn't replicate physics anymore, clear the target
|| (bOkToClear && !bReplicatingPhysics)) || (bOkToClear && !bCanSimulate)) // If replication say it's okay to clear the target and the object can't simulate, clear the target
&& !CVarDontClearTarget->GetBool(); && !CVarDontClearTarget->GetBool();
// --- Target Prediction --- // --- Target Prediction ---
@ -1694,10 +1849,16 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const int32 ExtrapolationTickLimit = FMath::Max( const int32 ExtrapolationTickLimit = FMath::Max(
FMath::CeilToInt(Target.AverageReceiveInterval * CVarExtrapolationTimeMultiplier->GetFloat()), // Extrapolate time based on receive interval * multiplier FMath::CeilToInt(Target.AverageReceiveInterval * CVarExtrapolationTimeMultiplier->GetFloat()), // Extrapolate time based on receive interval * multiplier
FMath::CeilToInt(CVarExtrapolationMinTime->GetFloat() / DeltaSeconds)); // At least extrapolate for N seconds FMath::CeilToInt(CVarExtrapolationMinTime->GetFloat() / DeltaSeconds)); // At least extrapolate for N seconds
if (Target.TickCount <= ExtrapolationTickLimit) if (Target.TickCount <= ExtrapolationTickLimit)
{ {
FPhysicsReplicationAsyncVR::ExtrapolateTarget(Target, 1, DeltaSeconds); FPhysicsReplicationAsyncVR::ExtrapolateTarget(Target, 1, DeltaSeconds);
} }
else
{
// If we reach the extrapolation limit, disable target from being altered
Target.bAllowTargetAltering = false;
}
} }
return bClearTarget; return bClearTarget;
@ -1716,26 +1877,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
// Get the rotational offset between the blended rotation target and the current rotation // Get the rotational offset between the blended rotation target and the current rotation
const FQuat TargetRotDelta = Target.TargetState.Quaternion * Handle->GetR().Inverse(); const FQuat TargetRotDelta = Target.TargetState.Quaternion * Handle->GetR().Inverse();
// Convert to angle axis // Convert to angle and axis
float Angle; float Angle;
FVector Axis; FVector Axis;
TargetRotDelta.ToAxisAndAngle(Axis, Angle); 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")); static const auto CVarEarlyOutAngle = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.EarlyOutAngle"));
if (Angle < FMath::DegreesToRadians(CVarEarlyOutAngle->GetFloat())) if (Angle < CVarEarlyOutAngle->GetFloat())
{ {
// Early Out // Early Out
return EndReplicationHelper(Target, true); 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")); static const auto CVarAverageReceiveIntervalSmoothing = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AverageReceiveIntervalSmoothing"));
// Update the AverageReceiveInterval if Target.ReceiveInterval has a valid value to update from // 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)); 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.Position = Handle->GetX();
CurrentState.Quaternion = Handle->GetR(); CurrentState.Quaternion = Handle->GetR();
CurrentState.LinVel = Handle->GetV(); CurrentState.LinVel = Handle->GetV();
CurrentState.AngVel = Handle->GetW(); // Note: Current angular velocity is in Radians CurrentState.AngVel = Handle->GetW(); // Radians
// NewState // NewState
const FVector TargetPos = FVector(Target.TargetState.Position); const FVector TargetPos = FVector(Target.TargetState.Position);
const FQuat TargetRot = Target.TargetState.Quaternion; const FQuat TargetRot = Target.TargetState.Quaternion;
const FVector TargetLinVel = FVector(Target.TargetState.LinVel); 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 --- /** --- Reconciliation ---
* If target velocities are low enough, check the traveled direction and distance from previous frame and compare with replicated linear velocity. * 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 CVarErrorAccumulationSeconds = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.ErrorAccumulationSeconds"));
static const auto CVarAlwaysHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AlwaysHardSnap")); static const auto CVarAlwaysHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AlwaysHardSnap"));
const bool bHardSnap = !bCanSimulate || static const auto CVarKinematicHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.KinematicHardSnap"));
Target.AccumulatedErrorSeconds > CVarErrorAccumulationSeconds->GetFloat() ||
CVarAlwaysHardSnap->GetBool(); const bool bHardSnap = (!bCanSimulate && CVarKinematicHardSnap->GetBool())
|| Target.AccumulatedErrorSeconds > CVarErrorAccumulationSeconds->GetFloat()
|| CVarAlwaysHardSnap->GetBool();
if (bHardSnap) if (bHardSnap)
{ {
@ -1815,13 +1973,13 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
} }
else else
{ {
// Set XPRQVW to hard snap dynamic object // Set XRVW to hard snap dynamic object and force recalculation of friction
Handle->SetX(Target.PrevPosTarget); const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
Handle->SetP(Target.PrevPosTarget); RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, Target.PrevPosTarget, Target.PrevRotTarget, bCorrectConnectedBodies, /*bInRecalculateFrictionOnConnectedBodies*/ true, ReplicatedParticleIDs);
Handle->SetR(Target.PrevRotTarget);
Handle->SetQ(Target.PrevRotTarget);
Handle->SetV(Target.TargetState.LinVel); Handle->SetV(TargetLinVel);
Handle->SetW(FMath::DegreesToRadians(Target.TargetState.AngVel)); Handle->SetW(TargetAngVel);
} }
// Cache data for next replication // Cache data for next replication
@ -1830,8 +1988,72 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
// End replication and go to sleep if that's requested // End replication and go to sleep if that's requested
return EndReplicationHelper(Target, true); 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 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 // Calculate interpolation time based on current average receive rate
const float AverageReceiveIntervalSeconds = Target.AverageReceiveInterval * DeltaSeconds; const float AverageReceiveIntervalSeconds = Target.AverageReceiveInterval * DeltaSeconds;
const float InterpolationTime = AverageReceiveIntervalSeconds * SettingsCurrent.PredictiveInterpolationSettings.GetPosInterpolationTimeMultiplier(); 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(), const float RotCorrectionTime = FMath::Max(SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeBase() + AverageReceiveIntervalSeconds + RTT * SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMultiplier(),
DeltaSeconds + SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMin()); DeltaSeconds + SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMin());
FVector CorrectionX = CurrentState.Position;
if ((bXCanEarlyOut && SettingsCurrent.PredictiveInterpolationSettings.GetSkipVelocityRepOnPosEarlyOut()) == false) if ((bXCanEarlyOut && SettingsCurrent.PredictiveInterpolationSettings.GetSkipVelocityRepOnPosEarlyOut()) == false)
{ // --- Velocity Replication --- { // --- Velocity Replication ---
@ -1853,10 +2076,10 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const FVector LinVelDiff = -CurrentState.LinVel + TargetLinVel; const FVector LinVelDiff = -CurrentState.LinVel + TargetLinVel;
// Calculate velocity blend amount for this tick as an alpha value // 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; 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()) if (CVarPosCorrectionAsVelocity->GetBool())
{ {
// Convert PosDiff to a velocity // Convert PosDiff to a velocity
@ -1866,20 +2089,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const FVector BlendedTargetVelocity = LinVelDiff + PosDiffVelocity; const FVector BlendedTargetVelocity = LinVelDiff + PosDiffVelocity;
// Add BlendedTargetVelocity onto current velocity // Add BlendedTargetVelocity onto current velocity
RepLinVel = CurrentState.LinVel + (BlendedTargetVelocity * VelocityAlpha); // Same as (BlendedTargetVelocity / InterpolationTime) * DeltaSeconds
RepLinVel = CurrentState.LinVel + (BlendedTargetVelocity * Alpha);
} }
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 // 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 // Calculate the PosDiff amount to correct this tick
Handle->SetX(Handle->GetX() + PosDiffVelocityDelta); const FVector PosDiffVelocityDelta = PosDiff * CorrectionAlpha; // Same as (PosDiff / PosCorrectionTime) * DeltaSeconds
// The new position after correction
CorrectionX = Handle->GetX() + PosDiffVelocityDelta;
} }
// Apply velocity replication // Apply velocity replication
@ -1890,7 +2114,8 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
static const auto CVarDrawDebugVectors = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugVectors")); static const auto CVarDrawDebugVectors = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugVectors"));
if (CVarDrawDebugVectors->GetBool()) 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 OffsetAdd = FVector(0.0f, 0.0f, 10.0f);
const FVector StartPos = TargetPos + Offset; const FVector StartPos = TargetPos + Offset;
FVector Direction = TargetLinVel; FVector Direction = TargetLinVel;
@ -1908,37 +2133,63 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
Target.PrevLinVel = FVector(RepLinVel); Target.PrevLinVel = FVector(RepLinVel);
} }
FQuat CorrectionR = CurrentState.Quaternion;
{ // --- Angular Velocity Replication --- { // --- Angular Velocity Replication ---
/* Todo, Implement InterpolationTime */
// Extrapolate current rotation along current angular velocity to see where we would end up // Get AngVelDiff by adding inverted CurrentState.AngVel to TargetAngVel
float CurAngVelSize; const FVector AngVelDiff = -CurrentState.AngVel + TargetAngVel;
FVector CurAngVelAxis;
CurrentState.AngVel.FVector::ToDirectionAndLength(CurAngVelAxis, CurAngVelSize);
const FQuat CurRotExtrapDelta = FQuat(CurAngVelAxis, CurAngVelSize * DeltaSeconds);
const FQuat CurRotExtrap = CurRotExtrapDelta * CurrentState.Quaternion;
// Slerp from the extrapolated current rotation towards the target rotation // Calculate velocity blend amount for this tick as an alpha value
// This takes current angular velocity into account const float VelocityAlpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
const float RotCorrectionAmount = FMath::Clamp(DeltaSeconds / RotCorrectionTime, 0.0f, 1.0f);
const FQuat TargetRotBlended = FQuat::Slerp(CurRotExtrap, TargetRot, RotCorrectionAmount);
// Get the rotational offset between the blended rotation target and the current rotation FVector RepAngVel;
const FQuat TargetRotDelta = TargetRotBlended * CurrentState.Quaternion.Inverse(); 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 // Convert RotDiff to a velocity
float WAngle; float WAngle;
FVector WAxis; FVector WAxis;
TargetRotDelta.ToAxisAndAngle(WAxis, WAngle); RotDiff.ToAxisAndAngle(WAxis, WAngle);
const FVector TargetRotDeltaBlend = FVector(WAxis * (WAngle / (DeltaSeconds * SettingsCurrent.PredictiveInterpolationSettings.GetRotInterpolationTimeMultiplier()))); WAngle = FMath::UnwindRadians(WAngle);
const FVector RepAngVel = FMath::DegreesToRadians(TargetAngVel) + TargetRotDeltaBlend; 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); Handle->SetW(RepAngVel);
} }
// Cache data for next replication // Cache data for next replication
Target.PrevPos = FVector(CurrentState.Position); 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) if (bSoftSnap)
{ {
const FVector SoftSnapPos = FMath::Lerp(FVector(CurrentState.Position), 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, SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapToSource() ? Target.PrevRotTarget : Target.TargetState.Quaternion,
FMath::Clamp(SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapRotStrength(), 0.0f, 1.0f)); FMath::Clamp(SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapRotStrength(), 0.0f, 1.0f));
Handle->SetX(SoftSnapPos); // Apply correction as a transform shift
Handle->SetP(SoftSnapPos); const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
Handle->SetR(SoftSnapRot); const bool bCorrectConnectedBodiesFriction = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodiesFriction();
Handle->SetQ(SoftSnapRot); RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, SoftSnapPos, SoftSnapRot, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
} }
} }
@ -2003,17 +2254,48 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
return true; return true;
} }
const bool bShouldSleep = (Target.TargetState.Flags & ERigidBodyFlags::Sleeping) != 0;
bool bClearTarget = true; bool bClearTarget = true;
static constexpr Chaos::FFrameAndPhase::EParticleHistoryPhase RewindPhase = Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData; 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 Chaos::FGeometryParticleState PastState = RewindData->GetPastStateAtFrame(*Handle, LocalFrame, RewindPhase);
const FVector ErrorOffset = (Target.TargetState.Position - PastState.GetX()); // Check which comparisons to perform to trigger resimulation from
const float ErrorDistance = ErrorOffset.Size(); const bool bCompareX = Chaos::FPhysicsSolverBase::GetResimulationErrorPositionThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorPositionThreshold;
const bool ShouldTriggerResim = ErrorDistance >= ResimErrorThreshold; 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) #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
@ -2021,7 +2303,7 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
if (Chaos::FPhysicsSolverBase::CanDebugNetworkPhysicsPrediction()) 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("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 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 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()); 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()) if (CVarResimDrawDebug->GetBool())
{ {
static constexpr float BoxSize = 5.0f; 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); const FColor DebugColor = FLinearColor::LerpUsingHSV(FLinearColor::Green, FLinearColor::Red, ColorLerp).ToFColor(false);
static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime")); static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
@ -2042,57 +2324,58 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
} }
#endif #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 const FVector ErrorOffset = (Target.TargetState.Position - PastState.GetX());
RigidsSolver->GetEvolution()->GetIslandManager().SetParticleResimFrame(Handle, LocalFrame);
int32 ResimFrame = RewindData->GetResimFrame(); // Positional Correction
ResimFrame = (ResimFrame == INDEX_NONE) ? LocalFrame : FMath::Min(ResimFrame, LocalFrame); const float CorrectionAmountX = SettingsCurrent.ResimulationSettings.GetPosStabilityMultiplier() / NumPredictedFrames;
RewindData->SetResimFrame(ResimFrame); const FVector PosDiffCorrection = ErrorOffset * CorrectionAmountX; // Same result as (ErrorOffset / NumPredictedFrames) * PosStabilityMultiplier
} const FVector CorrectedX = Handle->GetX() + PosDiffCorrection;
else if (SettingsCurrent.ResimulationSettings.GetRuntimeCorrectionEnabled())
{ // Rotational Correction
const int32 NumPredictedFrames = RigidsSolver->GetCurrentFrame() - LocalFrame - Target.TickCount; 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())
if (Target.TickCount <= NumPredictedFrames && NumPredictedFrames > 0)
{ {
// Positional Correction // Linear Velocity Correction
const float CorrectionAmountX = SettingsCurrent.ResimulationSettings.GetPosStabilityMultiplier() / NumPredictedFrames; const FVector LinVelDiff = Target.TargetState.LinVel - PastState.GetV(); // Velocity vector that the server covers but the client doesn't
const FVector PosDiffCorrection = ErrorOffset * CorrectionAmountX; // Same result as (ErrorOffset / NumPredictedFrames) * PosStabilityMultiplier const float CorrectionAmountV = SettingsCurrent.ResimulationSettings.GetVelStabilityMultiplier() / NumPredictedFrames;
const FVector CorrectedX = Handle->GetX() + PosDiffCorrection; 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 // Apply correction to velocities
const float CorrectionAmountR = SettingsCurrent.ResimulationSettings.GetRotStabilityMultiplier() / NumPredictedFrames; Handle->SetV(CorrectedV);
Handle->SetW(CorrectedW);
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);
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
@ -2103,14 +2386,27 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
} }
#endif #endif
// Apply correction to position and rotation // Apply correction to position and rotation
static const auto CVarResimRuntimeCorrectConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.Resim.RuntimeCorrectConnectedBodies")); RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectedX, CorrectedR, SettingsCurrent.ResimulationSettings.GetRuntimeCorrectConnectedBodies(), /*bInRecalculateFrictionOnConnectedBodies*/true, ReplicatedParticleIDs);
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectedX, CorrectedR, CVarResimRuntimeCorrectConnectedBodies->GetBool()); }
}
// Keep target for NumPredictedFrames time to perform runtime corrections with until a new target is received // Keep target for NumPredictedFrames time to perform runtime corrections with until a new target is received
bClearTarget = Target.TickCount >= NumPredictedFrames; 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; return bClearTarget;
} }

View file

@ -13,6 +13,8 @@
#include "PhysicsReplication.h" #include "PhysicsReplication.h"
#include "Physics/Experimental/PhysScene_Chaos.h" #include "Physics/Experimental/PhysScene_Chaos.h"
#include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding #include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding
#include "PhysicsEngine/BodySetup.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#if WITH_PUSH_MODEL #if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h" #include "Net/Core/PushModel/PushModel.h"
#endif #endif
@ -63,7 +65,7 @@ void UOptionalRepSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*>&
OutWeldedBodies.Add(BI); OutWeldedBodies.Add(BI);
if (PhysicsAsset) if (PhysicsAsset)
{ {
if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx]) if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx].Get())
{ {
OutLabels.Add(PhysicsAssetBodySetup->BoneName); OutLabels.Add(PhysicsAssetBodySetup->BoneName);
} }
@ -156,7 +158,7 @@ AGrippableSkeletalMeshActor::AGrippableSkeletalMeshActor(const FObjectInitialize
// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration // 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 // 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 void AGrippableSkeletalMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
@ -254,13 +256,23 @@ void AGrippableSkeletalMeshActor::GatherCurrentMovement()
bool bFoundInCache = false; bool bFoundInCache = false;
UWorld* World = GetWorld(); UWorld* World = GetWorld();
const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
int ServerFrame = 0; 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); if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
bFoundInCache = true; {
if (RepMovement.ServerFrame != ServerFrame)
{
RepMovement.FillFrom(*FoundState, this, ServerFrame);
bWasRepMovementModified = true;
}
bFoundInCache = true;
}
} }
} }

View file

@ -7,6 +7,7 @@
#include "VRExpansionFunctionLibrary.h" #include "VRExpansionFunctionLibrary.h"
#include "GripScripts/VRGripScriptBase.h" #include "GripScripts/VRGripScriptBase.h"
#include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding #include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding
#include "PhysicsEngine/SkeletalBodySetup.h"
#include "Net/UnrealNetwork.h" #include "Net/UnrealNetwork.h"
#if WITH_PUSH_MODEL #if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h" #include "Net/Core/PushModel/PushModel.h"

View file

@ -103,7 +103,7 @@ AGrippableStaticMeshActor::AGrippableStaticMeshActor(const FObjectInitializer& O
// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration // 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 // 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 void AGrippableStaticMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
@ -203,13 +203,23 @@ void AGrippableStaticMeshActor::GatherCurrentMovement()
bool bFoundInCache = false; bool bFoundInCache = false;
UWorld* World = GetWorld(); UWorld* World = GetWorld();
const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
int ServerFrame = 0; 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); if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
bFoundInCache = true; {
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. // Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true; 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. // Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true; 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) else if (RootComponent != nullptr)
{ {

View file

@ -272,7 +272,7 @@ void UVRDialComponent::OnGripRelease_Implementation(UGripMotionControllerCompone
float AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle)); float AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle));
float TargetSnap = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement)); 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)) 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 AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle));
float TargetSnap = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement)); 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)) if (AngleOffsetCheck >= SnapAngleThreshold)//FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{ {

View file

@ -810,7 +810,7 @@ FVector UVRSliderComponent::GetPerAxisSliderProgress()
CalculatedLocation = bSlideDistanceIsInParentSpace ? CalculatedLocation * InitialRelativeTransform.GetScale3D() : CalculatedLocation; CalculatedLocation = bSlideDistanceIsInParentSpace ? CalculatedLocation * InitialRelativeTransform.GetScale3D() : CalculatedLocation;
// Should need the clamp normally, but if someone is manually setting locations it could go out of bounds // 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) if (bUseLegacyLogic)
{ {

View file

@ -10,7 +10,7 @@
//#include "Chaos/ParticleHandle.h" //#include "Chaos/ParticleHandle.h"
#include "PhysicsEngine/PhysicsAsset.h" #include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/PhysicsAsset.h" #include "PhysicsEngine/SkeletalBodySetup.h"
#include "Physics/Experimental/PhysScene_Chaos.h" #include "Physics/Experimental/PhysScene_Chaos.h"
#include "Chaos/KinematicGeometryParticles.h" #include "Chaos/KinematicGeometryParticles.h"
#include "PhysicsProxy/SingleParticlePhysicsProxy.h" #include "PhysicsProxy/SingleParticlePhysicsProxy.h"

View file

@ -7,6 +7,8 @@
#include "Net/UnrealNetwork.h" #include "Net/UnrealNetwork.h"
#include "PhysicsReplication.h" #include "PhysicsReplication.h"
#include "PhysicsEngine/PhysicsAsset.h" #include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/BodySetup.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#if WITH_PUSH_MODEL #if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h" #include "Net/Core/PushModel/PushModel.h"
#endif #endif
@ -269,7 +271,7 @@ void UInversePhysicsSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*
OutWeldedBodies.Add(BI); OutWeldedBodies.Add(BI);
if (PhysicsAsset) if (PhysicsAsset)
{ {
if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx]) if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx].Get())
{ {
OutLabels.Add(PhysicsAssetBodySetup->BoneName); OutLabels.Add(PhysicsAssetBodySetup->BoneName);
} }

View file

@ -114,7 +114,7 @@ FORCEINLINE_DEBUGGABLE bool CheckIsTargetInSightPie(const FPerceptionListener& L
const FAISightTargetVR::FTargetId FAISightTargetVR::InvalidTargetId = FAISystem::InvalidUnsignedID; const FAISightTargetVR::FTargetId FAISightTargetVR::InvalidTargetId = FAISystem::InvalidUnsignedID;
FAISightTargetVR::FAISightTargetVR(AActor* InTarget, FGenericTeamId InTeamId) FAISightTargetVR::FAISightTargetVR(AActor* InTarget, FGenericTeamId InTeamId)
: Target(InTarget), SightTargetInterface(nullptr), TeamId(InTeamId) : Target(InTarget), TeamId(InTeamId)
{ {
if (InTarget) if (InTarget)
{ {
@ -587,7 +587,7 @@ UAISense_Sight::EVisibilityResult UAISense_Sight_VR::ComputeVisibility(UWorld* W
return UAISense_Sight::EVisibilityResult::NotVisible; return UAISense_Sight::EVisibilityResult::NotVisible;
} }
if (Target.SightTargetInterface != nullptr) if (IAISightTargetInterface* SightTargetInterface = Target.WeakSightTargetInterface.Get())
{ {
const bool bWasVisible = SightQuery.GetLastResult(); const bool bWasVisible = SightQuery.GetLastResult();
@ -620,7 +620,7 @@ UAISense_Sight::EVisibilityResult UAISense_Sight_VR::ComputeVisibility(UWorld* W
Context.IgnoreActor = ListenerActor; Context.IgnoreActor = ListenerActor;
Context.bWasVisible = &bWasVisible; 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) 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 // 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(); AActor* TargetActor = AsTarget.Target.Get();
if (TargetActor) // notify all interested observers that this source is no longer
{ // visible
// notify all interested observers that this source is no longer AIPerception::FListenerMap& ListenersMap = *GetListeners();
// visible auto RemoveQuery = [this, &ListenersMap, &AsTargetId, &TargetActor](TArray<FAISightQueryVR>& SightQueries, const int32 QueryIndex)->EReverseForEachResult
AIPerception::FListenerMap& ListenersMap = *GetListeners();
auto RemoveQuery = [this, &ListenersMap, &AsTargetId, &TargetActor](TArray<FAISightQueryVR>& SightQueries, const int32 QueryIndex)->EReverseForEachResult
{ {
FAISightQueryVR* SightQuery = &SightQueries[QueryIndex]; FAISightQueryVR* SightQuery = &SightQueries[QueryIndex];
if (SightQuery->TargetId == AsTargetId) if (SightQuery->TargetId == AsTargetId)
{ {
if (SightQuery->GetLastResult()) if (SightQuery->GetLastResult() && TargetActor)
{ {
FPerceptionListener& Listener = ListenersMap[SightQuery->ObserverId]; FPerceptionListener& Listener = ListenersMap[SightQuery->ObserverId];
ensure(Listener.Listener.IsValid()); ensure(Listener.Listener.IsValid());
Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 0.f, SightQuery->LastSeenLocation, Listener.CachedLocation, FAIStimulus::SensingFailed)); 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::Modified;
} }
return EReverseForEachResult::UnTouched; return EReverseForEachResult::UnTouched;
}; };
ReverseForEach(SightQueriesInRange, RemoveQuery);
ReverseForEach(SightQueriesInRange, RemoveQuery); if (ReverseForEach(SightQueriesOutOfRange, RemoveQuery) == EReverseForEachResult::Modified)
if (ReverseForEach(SightQueriesOutOfRange, RemoveQuery) == EReverseForEachResult::Modified) {
{ bSightQueriesOutOfRangeDirty = true;
bSightQueriesOutOfRangeDirty = true;
}
ReverseForEach(SightQueriesPending, RemoveQuery);
} }
ReverseForEach(SightQueriesPending, RemoveQuery);
} }
} }
@ -883,20 +877,24 @@ bool UAISense_Sight_VR::RegisterTarget(AActor& TargetActor, const TFunction<void
FAISightTargetVR* SightTarget = ObservedTargets.Find(TargetActor.GetUniqueID()); 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); FAISightTargetVR NewSightTarget(&TargetActor);
SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget)); 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
else if (SightTarget == nullptr) // this order is that you can have components override the original Actor's implementation
{ if (IAISightTargetInterface* InterfaceComponent = TargetActor.FindComponentByInterface<IAISightTargetInterface>())
FAISightTargetVR NewSightTarget(&TargetActor); {
SightTarget->WeakSightTargetInterface = InterfaceComponent;
SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget)); }
SightTarget->SightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor); else
{
SightTarget->WeakSightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
}
} }
// set/update data // set/update data

View file

@ -4,6 +4,7 @@
#include "SceneManagement.h" #include "SceneManagement.h"
#include "Components/SkeletalMeshComponent.h" #include "Components/SkeletalMeshComponent.h"
#include "PhysicsEngine/PhysicsAsset.h" #include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/ShapeElem.h"
#include "PhysicsEngine/ConstraintInstance.h" #include "PhysicsEngine/ConstraintInstance.h"
#include "ReferenceSkeleton.h" #include "ReferenceSkeleton.h"
#include "DrawDebugHelpers.h" #include "DrawDebugHelpers.h"

View file

@ -90,7 +90,7 @@ namespace
void RemoveWidget(UVRFullScreenUserWidget* InWidget) void RemoveWidget(UVRFullScreenUserWidget* InWidget)
{ {
WidgetsToHide.RemoveSingleSwap(InWidget, false); WidgetsToHide.RemoveSingleSwap(InWidget, EAllowShrinking::No);
} }
private: private:

View file

@ -1645,7 +1645,7 @@ bool RLE_Funcs::RLEEncodeBuffer(DataType* BufferToEncode, uint32 EncodeLength, T
// Resize the out array to fit compressed contents // Resize the out array to fit compressed contents
uint32 Wrote = loc - EncodedLine->GetData(); 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. // 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. // This will almost never happen with voxels but can so should be accounted for.

View file

@ -434,7 +434,7 @@ void UReplicatedVRCameraComponent::TickComponent(float DeltaTime, enum ELevelTic
} }
} }
void UReplicatedVRCameraComponent::HandleXRCamera() void UReplicatedVRCameraComponent::HandleXRCamera(float DeltaTime)
{ {
bool bIsLocallyControlled = IsLocallyControlled(); bool bIsLocallyControlled = IsLocallyControlled();
@ -462,7 +462,7 @@ void UReplicatedVRCameraComponent::HandleXRCamera()
{ {
FQuat Orientation; FQuat Orientation;
FVector Position; FVector Position;
if (XRCamera->UpdatePlayerCamera(Orientation, Position)) if (XRCamera->UpdatePlayerCamera(Orientation, Position, DeltaTime))
{ {
if (HasTrackingParameters()) if (HasTrackingParameters())
{ {

View file

@ -15,6 +15,7 @@
#include "Net/UnrealNetwork.h" #include "Net/UnrealNetwork.h"
#include "XRMotionControllerBase.h" #include "XRMotionControllerBase.h"
#include "NavFilters/NavigationQueryFilter.h" #include "NavFilters/NavigationQueryFilter.h"
#include "Misc/EngineNetworkCustomVersion.h"
//#include "Runtime/Engine/Private/EnginePrivate.h" //#include "Runtime/Engine/Private/EnginePrivate.h"
#if WITH_PUSH_MODEL #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). // 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 // 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 // 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 // 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.Location = ReplicatedMovementVR.Location;
ReppedMovement.Rotation = ReplicatedMovementVR.Rotation; ReppedMovement.Rotation = ReplicatedMovementVR.Rotation;
ReppedMovement.ServerFrame = ReplicatedMovementVR.ServerFrame;
ReppedMovement.ServerPhysicsHandle = ReplicatedMovementVR.ServerPhysicsHandle;
ReppedMovement.bRepAcceleration = ReplicatedMovementVR.bRepAcceleration;
ReppedMovement.Acceleration = ReplicatedMovementVR.Acceleration;
Super::OnRep_ReplicatedMovement(); Super::OnRep_ReplicatedMovement();
if (!IsLocallyControlled()) if (!IsLocallyControlled())
@ -469,8 +475,14 @@ void AVRBaseCharacter::GatherCurrentMovement()
ReplicatedMovementVR.LinearVelocity = ReppedMovement.LinearVelocity; ReplicatedMovementVR.LinearVelocity = ReppedMovement.LinearVelocity;
ReplicatedMovementVR.Location = ReppedMovement.Location; ReplicatedMovementVR.Location = ReppedMovement.Location;
ReplicatedMovementVR.Rotation = ReppedMovement.Rotation; ReplicatedMovementVR.Rotation = ReppedMovement.Rotation;
ReplicatedMovementVR.ServerFrame = ReppedMovement.ServerFrame;
ReplicatedMovementVR.ServerPhysicsHandle = ReppedMovement.ServerPhysicsHandle;
ReplicatedMovementVR.bRepAcceleration = ReppedMovement.bRepAcceleration;
ReplicatedMovementVR.Acceleration = ReppedMovement.Acceleration;
ReplicatedMovementVR.bJustTeleported = bFlagTeleported; ReplicatedMovementVR.bJustTeleported = bFlagTeleported;
ReplicatedMovementVR.bJustTeleportedGrips = bFlagTeleportedGrips; ReplicatedMovementVR.bJustTeleportedGrips = bFlagTeleportedGrips;
bFlagTeleported = false; bFlagTeleported = false;
bFlagTeleportedGrips = false; bFlagTeleportedGrips = false;
ReplicatedMovementVR.bPausedTracking = bTrackingPaused; ReplicatedMovementVR.bPausedTracking = bTrackingPaused;
@ -1001,7 +1013,7 @@ FVector AVRBaseCharacter::SetActorLocationAndRotationVR(FVector NewLoc, FRotator
FVector AVRBaseCharacter::SetActorLocationVR(FVector NewLoc, bool bTeleport, bool bSetCapsuleLocation) FVector AVRBaseCharacter::SetActorLocationVR(FVector NewLoc, bool bTeleport, bool bSetCapsuleLocation)
{ {
FVector NewLocation; FVector NewLocation;
FRotator NewRotation; //FRotator NewRotation;
FVector PivotOffsetVal = (bSetCapsuleLocation ? GetVRLocation_Inline() : GetProjectedVRLocation()) - GetActorLocation(); FVector PivotOffsetVal = (bSetCapsuleLocation ? GetVRLocation_Inline() : GetProjectedVRLocation()) - GetActorLocation();
PivotOffsetVal.Z = 0.0f; PivotOffsetVal.Z = 0.0f;
@ -1243,4 +1255,103 @@ void AVRBaseCharacter::SetVRReplicateCapsuleHeight(bool bNewVRReplicateCapsuleHe
#if WITH_PUSH_MODEL #if WITH_PUSH_MODEL
MARK_PROPERTY_DIRTY_FROM_NAME(AVRBaseCharacter, VRReplicateCapsuleHeight, this); MARK_PROPERTY_DIRTY_FROM_NAME(AVRBaseCharacter, VRReplicateCapsuleHeight, this);
#endif #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;
} }

View file

@ -489,14 +489,32 @@ void UVRBaseCharacterMovementComponent::EndPushBackNotification()
FVector UVRBaseCharacterMovementComponent::GetActorFeetLocationVR() const 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; return UpdatedComponent ? (BaseCharacter->OffsetComponentToWorld.GetLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
} }
else else
{ {
return UpdatedComponent ? (UpdatedComponent->GetComponentLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation; return UpdatedComponent ? (UpdatedComponent->GetComponentLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
} }*/
} }
void UVRBaseCharacterMovementComponent::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result) 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)); 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). // 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) if (!bBlockingHit)
{ {
@ -558,8 +577,8 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
if (DownwardSweepResult != NULL && DownwardSweepResult->IsValidBlockingHit()) if (DownwardSweepResult != NULL && DownwardSweepResult->IsValidBlockingHit())
{ {
// Only if the supplied sweep was vertical and downward. // Only if the supplied sweep was vertical and downward.
const bool bIsDownward = RotateWorldToGravity(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).Z > 0; const bool bIsDownward = GetGravitySpaceZ(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd) > 0;
const bool bIsVertical = RotateWorldToGravity(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= UE_KINDA_SMALL_NUMBER; const bool bIsVertical = ProjectToGravityFloor(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared() <= UE_KINDA_SMALL_NUMBER;
if (bIsDownward && bIsVertical) if (bIsDownward && bIsVertical)
{ {
// Reject hits that are barely on the cusp of the radius of the capsule // 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; bSkipSweep = true;
const bool bIsWalkable = IsWalkable(*DownwardSweepResult); 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); OutFloorResult.SetFromSweep(*DownwardSweepResult, FloorDist, bIsWalkable);
if (bIsWalkable) if (bIsWalkable)
@ -610,7 +629,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(SweepRadius, PawnHalfHeight - ShrinkHeight); FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(SweepRadius, PawnHalfHeight - ShrinkHeight);
FHitResult Hit(1.f); 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) if (bBlockingHit)
{ {
@ -628,7 +647,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
CapsuleShape.Capsule.HalfHeight = FMath::Max(PawnHalfHeight - ShrinkHeight, CapsuleShape.Capsule.Radius); CapsuleShape.Capsule.HalfHeight = FMath::Max(PawnHalfHeight - ShrinkHeight, CapsuleShape.Capsule.Radius);
Hit.Reset(1.f, false); 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 float ShrinkHeight = PawnHalfHeight;
const FVector LineTraceStart = CapsuleLocation; const FVector LineTraceStart = CapsuleLocation;
const float TraceDist = LineDistance + ShrinkHeight; 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); QueryParams.TraceTag = SCENE_QUERY_STAT_NAME_ONLY(FloorLineTrace);
FHitResult Hit(1.f); FHitResult Hit(1.f);
@ -703,31 +722,32 @@ float UVRBaseCharacterMovementComponent::SlideAlongSurface(const FVector& Delta,
return 0.f; return 0.f;
} }
FVector Normal(RotateWorldToGravity(InNormal)); FVector Normal(InNormal);
const FVector::FReal NormalZ = GetGravitySpaceZ(Normal);
if (IsMovingOnGround()) if (IsMovingOnGround())
{ {
// We don't want to be pushed up an unwalkable surface. // We don't want to be pushed up an unwalkable surface.
if (Normal.Z > 0.f) if (NormalZ > 0.f)
{ {
if (!IsWalkable(Hit)) 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. // 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) if (CurrentFloor.FloorDist < MIN_FLOOR_DIST && CurrentFloor.bBlockingHit)
{ {
const FVector FloorNormal = RotateWorldToGravity(CurrentFloor.HitResult.Normal); const FVector FloorNormal = CurrentFloor.HitResult.Normal;
const bool bFloorOpposedToMovement = (RotateWorldToGravity(Delta) | FloorNormal) < 0.f && (FloorNormal.Z < 1.f - UE_DELTA); const bool bFloorOpposedToMovement = (Delta | FloorNormal) < 0.f && (GetGravitySpaceZ(FloorNormal) < 1.f - UE_DELTA);
if (bFloorOpposedToMovement) if (bFloorOpposedToMovement)
{ {
Normal = FloorNormal; 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. // 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. // 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)) 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 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) /*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. // The mesh doesn't move, but the capsule does so we have a new offset.
FVector NewToOldVector = (OldWorldLocation - NewWorldLocation); FVector NewToOldVector = (OldWorldLocation - NewWorldLocation);
if (bIsNavWalkingOnServer && FMath::Abs(NewToOldVector.Z) < NavWalkingFloorDistTolerance) if (bIsNavWalkingOnServer && FMath::Abs(GetGravitySpaceZ(NewToOldVector)) < NavWalkingFloorDistTolerance)
{ {
// ignore smoothing on Z axis // ignore smoothing on Z axis
// don't modify new location (local simulation result), since it's probably more accurate than server data // 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 // and shouldn't matter as long as difference is relatively small
NewToOldVector.Z = 0; NewToOldVector = ProjectToGravityFloor(NewToOldVector);
} }
const float DistSq = NewToOldVector.SizeSquared(); const float DistSq = NewToOldVector.SizeSquared();

View file

@ -173,7 +173,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
else else
capLocation = UpdatedComponent->GetComponentLocation(); 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); UpdatedComponent->GetCollisionObjectType(), GetPawnCapsuleCollisionShape(SHRINK_None), CapsuleParams, ResponseParam);
// If encroached, cancel // If encroached, cancel
@ -192,7 +192,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
if (bCrouchMaintainsBaseLocation) if (bCrouchMaintainsBaseLocation)
{ {
// Intentionally not using MoveUpdatedComponent, where a horizontal plane constraint would prevent the base of the capsule from staying at the same spot. // 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; CharacterOwner->bIsCrouched = true;
@ -219,7 +219,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
if (ClientData) if (ClientData)
{ {
ClientData->MeshTranslationOffset -= FVector(0.f, 0.f, MeshAdjust); ClientData->MeshTranslationOffset -= MeshAdjust * -GetGravityDirection();
ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset; ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset;
} }
} }
@ -280,7 +280,7 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
if (!bCrouchMaintainsBaseLocation) if (!bCrouchMaintainsBaseLocation)
{ {
// Expand in place // Expand in place
bEncroached = MyWorld->OverlapBlockingTestByChannel(PawnLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam); bEncroached = MyWorld->OverlapBlockingTestByChannel(PawnLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
if (bEncroached) if (bEncroached)
{ {
@ -292,11 +292,11 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight); CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
const float ShrinkHalfHeight = PawnHalfHeight - PawnRadius; const float ShrinkHalfHeight = PawnHalfHeight - PawnRadius;
const float TraceDist = PawnHalfHeight - ShrinkHalfHeight; const float TraceDist = PawnHalfHeight - ShrinkHalfHeight;
const FVector Down = FVector(0.f, 0.f, -TraceDist); const FVector Down = TraceDist * GetGravityDirection();
FHitResult Hit(1.f); FHitResult Hit(1.f);
const FCollisionShape ShortCapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_HeightCustom, ShrinkHalfHeight); 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) if (Hit.bStartPenetrating)
{ {
bEncroached = true; 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 // 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 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); const FVector Adjustment = (-DistanceToBase + StandingCapsuleShape.Capsule.HalfHeight + SweepInflation + MIN_FLOOR_DIST / 2.f) * -GetGravityDirection();
bEncroached = MyWorld->OverlapBlockingTestByChannel(NewLoc, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam); const FVector NewLoc = PawnLocation + Adjustment;
bEncroached = MyWorld->OverlapBlockingTestByChannel(NewLoc, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
if (!bEncroached) if (!bEncroached)
{ {
// Intentionally not using MoveUpdatedComponent, where a horizontal plane constraint would prevent the base of the capsule from staying at the same spot. // 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 else
{ {
// Expand while keeping base location the same. // Expand while keeping base location the same.
FVector StandingLocation = PawnLocation + FVector(0.f, 0.f, StandingCapsuleShape.GetCapsuleHalfHeight() - CurrentCrouchedHalfHeight); FVector StandingLocation = PawnLocation + (StandingCapsuleShape.GetCapsuleHalfHeight() - CurrentCrouchedHalfHeight) * -GetGravityDirection();
bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam); bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
if (bEncroached) if (bEncroached)
{ {
@ -330,8 +331,8 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
const float MinFloorDist = UE_KINDA_SMALL_NUMBER * 10.f; const float MinFloorDist = UE_KINDA_SMALL_NUMBER * 10.f;
if (CurrentFloor.bBlockingHit && CurrentFloor.FloorDist > MinFloorDist) if (CurrentFloor.bBlockingHit && CurrentFloor.FloorDist > MinFloorDist)
{ {
StandingLocation.Z -= CurrentFloor.FloorDist - MinFloorDist; StandingLocation -= (CurrentFloor.FloorDist - MinFloorDist) * -GetGravityDirection();
bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam); bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
} }
} }
} }
@ -375,7 +376,7 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
if (ClientData) if (ClientData)
{ {
ClientData->MeshTranslationOffset += FVector(0.f, 0.f, MeshAdjust); ClientData->MeshTranslationOffset += MeshAdjust * -GetGravityDirection();
ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset; 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. // 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. // 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(); FVector PawnLocation = UpdatedComponent->GetComponentLocation();
if (VRRootCapsule) if (VRRootCapsule)
@ -932,8 +933,12 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
const FVector OldVelocity = Velocity; const FVector OldVelocity = Velocity;
Acceleration = FVector::VectorPlaneProject(Acceleration, -GetGravityDirection()); Acceleration = FVector::VectorPlaneProject(Acceleration, -GetGravityDirection());
static const auto CVarLedgeMovementApplyDirectMove = IConsoleManager::Get().FindConsoleVariable(TEXT("p.LedgeMovement.ApplyDirectMove"));
// Apply acceleration // Apply acceleration
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity()) const bool bSkipForLedgeMove = bTriedLedgeMove && CVarLedgeMovementApplyDirectMove->GetBool();
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && !bSkipForLedgeMove)
{ {
CalcVelocity(timeTick, GroundFriction, false, GetMaxBrakingDeceleration()); CalcVelocity(timeTick, GroundFriction, false, GetMaxBrakingDeceleration());
devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString())); 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(); const float DesiredDist = Delta.Size();
if (DesiredDist > UE_KINDA_SMALL_NUMBER) 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)); remainingTime += timeTick * (1.f - FMath::Min(1.f, ActualDist / DesiredDist));
} }
RestorePreAdditiveVRMotionVelocity(); RestorePreAdditiveVRMotionVelocity();
@ -1024,8 +1029,10 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
if (bCheckLedges && !CurrentFloor.IsWalkableFloor()) if (bCheckLedges && !CurrentFloor.IsWalkableFloor())
{ {
// calculate possible alternate movement // calculate possible alternate movement
const FVector GravDir = GetGravityDirection(); const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldLocation, Delta, OldFloor);
const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldCapsuleLocation, Delta, GravDir); // REMOVED 5.5 and replaced with above
//const FVector GravDir = GetGravityDirection();
//const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldCapsuleLocation, Delta, GravDir);
if (!NewDelta.IsZero()) if (!NewDelta.IsZero())
{ {
// first revert this move // first revert this move
@ -1037,6 +1044,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
// Try new movement direction // Try new movement direction
Velocity = NewDelta / timeTick; Velocity = NewDelta / timeTick;
remainingTime += timeTick; remainingTime += timeTick;
Iterations--;
RestorePreAdditiveVRMotionVelocity(); RestorePreAdditiveVRMotionVelocity();
continue; continue;
} }
@ -1084,7 +1092,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
// The floor check failed because it started in penetration // 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. // 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); 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); const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat()); ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
bForceNextFloorCheck = true; bForceNextFloorCheck = true;
@ -1168,8 +1176,10 @@ void UVRCharacterMovementComponent::CapsuleTouched(UPrimitiveComponent* Overlapp
} }
const FVector Loc = VRRootCapsule->OffsetComponentToWorld.GetLocation();//UpdatedComponent->GetComponentLocation(); 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(); ImpulseDir.Normalize();
FName BoneName = NAME_None; FName BoneName = NAME_None;
@ -1186,7 +1196,7 @@ void UVRCharacterMovementComponent::CapsuleTouched(UPrimitiveComponent* Overlapp
TouchForceFactorModified *= BI ? BI->GetBodyMass() : 1.0f; 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, MinTouchForce > 0.0f ? MinTouchForce : -FLT_MAX,
MaxTouchForce > 0.0f ? MaxTouchForce : 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 // Trace to get the hit location on the capsule
FHitResult Hit; FHitResult Hit;
bool bHasHit = UpdatedPrimitive->LineTraceComponent(Hit, BodyLocation, bool bHasHit = UpdatedPrimitive->LineTraceComponent(Hit, BodyLocation,
FVector(MyLocation.X, MyLocation.Y, BodyLocation.Z), ProjectToGravityFloor(MyLocation) + GetGravitySpaceComponentZ(BodyLocation),
QueryParams); QueryParams);
FVector HitLoc = Hit.ImpactPoint; FVector HitLoc = Hit.ImpactPoint;
@ -1554,12 +1564,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
bIsPenetrating = true; bIsPenetrating = true;
} }
const float DistanceNow = (HitLoc - BodyLocation).SizeSquared2D(); const float DistanceNow = ProjectToGravityFloor(HitLoc - BodyLocation).SizeSquared();
const float DistanceLater = (HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared2D(); const float DistanceLater = ProjectToGravityFloor(HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared();
if (bHasHit && DistanceNow < StopBodyDistance && !bIsPenetrating) 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) else if (DistanceLater <= DistanceNow || bIsPenetrating)
{ {
@ -1567,11 +1577,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
if (bHasHit) if (bHasHit)
{ {
ForceCenter.Z = HitLoc.Z; SetGravitySpaceZ(ForceCenter, GetGravitySpaceZ(HitLoc));
} }
else 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); 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 // 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); FHitResult Hit(1.f);
FVector RampVector = ComputeGroundMovementDelta(Delta, CurrentFloor.HitResult, CurrentFloor.bLineTrace); FVector RampVector = ComputeGroundMovementDelta(Delta, CurrentFloor.HitResult, CurrentFloor.bLineTrace);
SafeMoveUpdatedComponent(RampVector, UpdatedComponent->GetComponentQuat(), true, Hit); 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). // We impacted something (most likely another ramp, but possibly a barrier).
float PercentTimeApplied = Hit.Time; 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. // Another walkable ramp.
const float InitialPercentRemaining = 1.f - PercentTimeApplied; const float InitialPercentRemaining = 1.f - PercentTimeApplied;
@ -1691,8 +1702,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
const FVector PreStepUpLocation = UpdatedComponent->GetComponentLocation(); const FVector PreStepUpLocation = UpdatedComponent->GetComponentLocation();
const FVector GravDir = GetGravityDirection(); 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(GetGravityDirection(), Delta * (1.f - PercentTimeApplied), Hit, OutStepDownResult))
if (!StepUp(GravDir, (Delta * (1.f - PercentTimeApplied)) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, OutStepDownResult))
{ {
UE_LOG(LogVRCharacterMovement, Verbose, TEXT("- StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString()); UE_LOG(LogVRCharacterMovement, Verbose, TEXT("- StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString());
HandleImpact(Hit, LastMoveTimeSlice, RampVector); HandleImpact(Hit, LastMoveTimeSlice, RampVector);
@ -1710,7 +1720,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && StepUpTimeSlice >= UE_KINDA_SMALL_NUMBER) if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && StepUpTimeSlice >= UE_KINDA_SMALL_NUMBER)
{ {
Velocity = (UpdatedComponent->GetComponentLocation() - PreStepUpLocation) / StepUpTimeSlice; 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); CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
// Don't bother stepping up if top of capsule is hitting something. // Don't bother stepping up if top of capsule is hitting something.
const float InitialImpactZ = InHit.ImpactPoint.Z; const float InitialImpactZ = InHit.ImpactPoint | -GravDir;
if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius)) const float OldLocationZ = OldLocation | -GravDir;
if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius))
{ {
return false; return false;
} }
@ -1762,7 +1773,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
float StepTravelUpHeight = MaxStepHeight; float StepTravelUpHeight = MaxStepHeight;
float StepTravelDownHeight = StepTravelUpHeight; float StepTravelDownHeight = StepTravelUpHeight;
const float StepSideZ = -1.f * FVector::DotProduct(InHit.ImpactNormal, GravDir);//const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir); 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; float PawnFloorPointZ = PawnInitialFloorBaseZ;
if (IsMovingOnGround() && CurrentFloor.IsWalkableFloor()) 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); const bool bHitVerticalFace = !IsWithinEdgeTolerance(InHit.Location, InHit.ImpactPoint, PawnRadius);
if (!CurrentFloor.bLineTrace && !bHitVerticalFace) if (!CurrentFloor.bLineTrace && !bHitVerticalFace)
{ {
PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint.Z; PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint | -GravDir;
} }
else else
{ {
@ -1868,7 +1879,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
if (Hit.IsValidBlockingHit()) if (Hit.IsValidBlockingHit())
{ {
// See if this step sequence would have allowed us to travel higher than our max step height allows. // 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) if (DeltaZ > MaxStepHeight)
{ {
//UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ); //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. // 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. // 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()); //UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString());
ScopedStepUpMovement.RevertMove(); 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. // 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. // 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). // 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. // 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); CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
// Don't bother stepping up if top of capsule is hitting something. // Don't bother stepping up if top of capsule is hitting something.
const float InitialImpactZ = InHit.ImpactPoint.Z; const float InitialImpactZ = InHit.ImpactPoint | -GravDir;
if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius)) const float OldLocationZ = OldLocation | -GravDir;
if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius))
{ {
return false; return false;
} }
@ -2013,7 +2025,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
float StepTravelUpHeight = MaxStepHeight; float StepTravelUpHeight = MaxStepHeight;
float StepTravelDownHeight = StepTravelUpHeight; float StepTravelDownHeight = StepTravelUpHeight;
const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir); const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir);
float PawnInitialFloorBaseZ = OldLocation.Z - PawnHalfHeight; float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight;
float PawnFloorPointZ = PawnInitialFloorBaseZ; float PawnFloorPointZ = PawnInitialFloorBaseZ;
// Scope our movement updates, and do not apply them until all intermediate moves are completed. // 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()) if (Hit.IsValidBlockingHit())
{ {
// See if this step sequence would have allowed us to travel higher than our max step height allows. // 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) if (DeltaZ > MaxStepHeight)
{ {
UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ); 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. // 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. // 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()); UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString());
ScopedStepUpMovement.RevertMove(); 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. // 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. // 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). // 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. // 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; return true;
} }
FVector UVRCharacterMovementComponent::GetActorFeetLocation() const
{
// Call into the VR version of it instead
return GetActorFeetLocationVR();
}
void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds) 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. // @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)) if (bOrientRotationToMovement || (bUseControllerDesiredRotation && CharacterOwner->Controller))
{ {
TargetRotator.Pitch = 0.f; // Custom gravity automatically aligns the character to the gravity direction, so we shouldn't zero out pitch and roll.
TargetRotator.Roll = 0.f; if (!HasCustomGravity())
{
TargetRotator.Pitch = 0.f;
TargetRotator.Roll = 0.f;
}
MoveUpdatedComponent(FVector::ZeroVector, TargetRotator, false); MoveUpdatedComponent(FVector::ZeroVector, TargetRotator, false);
FinalQuat = UpdatedComponent->GetComponentQuat(); 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 FVector NewWorldPos;
float HalfHeight, Radius; if (HasCustomGravity())
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight);
if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
{ {
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 BaseOffset(0.0f, 0.0f, HalfHeight);
FVector const NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset);
FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset);
NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset);
}
DeltaPosition = ConstrainDirectionToPlane(NewWorldPos - UpdatedComponent->GetComponentLocation()); DeltaPosition = ConstrainDirectionToPlane(NewWorldPos - UpdatedComponent->GetComponentLocation());
// move attached actor // move attached actor
@ -2377,16 +2411,13 @@ FVector UVRCharacterMovementComponent::GetImpartedMovementBaseVelocity() const
if (bImpartBaseAngularVelocity) if (bImpartBaseAngularVelocity)
{ {
// Base position should be the bottom of the actor since I offset the capsule now // Base position should be the bottom of the actor since I offset the capsule now
float HalfHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); float HalfHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale) if (BaseVRCharacterOwner && BaseVRCharacterOwner->bRetainRoomscale)
{ {
HalfHeight = 0.0f; 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); const FVector BaseTangentialVel = MovementBaseUtility::GetMovementBaseTangentialVelocity(MovementBase, CharacterOwner->GetBasedMovement().BoneName, CharacterBasePosition);
BaseVelocity += BaseTangentialVel; BaseVelocity += BaseTangentialVel;
} }
@ -2562,13 +2593,13 @@ float UVRCharacterMovementComponent::ImmersionDepth() const
if (VRRootCapsule) if (VRRootCapsule)
{ {
TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + FVector(0.f, 0.f, CollisionHalfHeight); TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + CollisionHalfHeight * -GetGravityDirection();
TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - FVector(0.f, 0.f, CollisionHalfHeight); TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - CollisionHalfHeight * -GetGravityDirection();
} }
else else
{ {
TraceStart = UpdatedComponent->GetComponentLocation() + FVector(0.f, 0.f, CollisionHalfHeight); TraceStart = UpdatedComponent->GetComponentLocation() + CollisionHalfHeight * -GetGravityDirection();
TraceEnd = UpdatedComponent->GetComponentLocation() -FVector(0.f, 0.f, CollisionHalfHeight); TraceEnd = UpdatedComponent->GetComponentLocation() - CollisionHalfHeight * -GetGravityDirection();
} }
FCollisionQueryParams NewTraceParams(CharacterMovementComponentStatics::ImmersionDepthName, true); FCollisionQueryParams NewTraceParams(CharacterMovementComponentStatics::ImmersionDepthName, true);
@ -2669,18 +2700,19 @@ void UVRCharacterMovementComponent::PhysFlying(float deltaTime, int32 Iterations
if (Hit.Time < 1.f) if (Hit.Time < 1.f)
{ {
const FVector GravDir = FVector(0.f, 0.f, -1.f);
const FVector VelDir = Velocity.GetSafeNormal(); const FVector VelDir = Velocity.GetSafeNormal();
const float UpDown = GravDir | VelDir; const float UpDown = VelDir | GetGravityDirection();
bool bSteppedUp = false; 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 FVector::FReal StepZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation());
bSteppedUp = StepUp(GravDir, (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, nullptr); bSteppedUp = StepUp(GetGravityDirection(), (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time), Hit);
if (bSteppedUp) 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; return;
} }
FVector FallAcceleration = GetFallingLateralAcceleration(deltaTime); const FVector FallAcceleration = ProjectToGravityFloor(GetFallingLateralAcceleration(deltaTime));
const FVector GravityRelativeFallAcceleration = RotateWorldToGravity(FallAcceleration);
FallAcceleration = RotateGravityToWorld(FVector(GravityRelativeFallAcceleration.X, GravityRelativeFallAcceleration.Y, 0));
const bool bHasLimitedAirControl = ShouldLimitAirControl(deltaTime, FallAcceleration); const bool bHasLimitedAirControl = ShouldLimitAirControl(deltaTime, FallAcceleration);
// Rewind the players position by the new capsule location // 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); TGuardValue<FVector> RestoreAcceleration(Acceleration, FallAcceleration);
if (HasCustomGravity()) if (HasCustomGravity())
{ {
Velocity = FVector::VectorPlaneProject(Velocity, RotateGravityToWorld(FVector::UpVector)); Velocity = ProjectToGravityFloor(Velocity);
const FVector GravityRelativeOffset = OldVelocity - Velocity; const FVector GravityRelativeOffset = OldVelocity - Velocity;
CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel); CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel);
Velocity += GravityRelativeOffset; Velocity += GravityRelativeOffset;
@ -2797,15 +2827,15 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
DecayFormerBaseVelocity(timeTick); 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. // 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")); 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 DerivedAccel = (Velocity - OldVelocityWithRootMotion) / timeTick;
const FVector GravityRelativeDerivedAccel = RotateWorldToGravity(DerivedAccel); const FVector::FReal GravityRelativeDerivedAccelZ = GetGravitySpaceZ(DerivedAccel);
if (!FMath::IsNearlyZero(GravityRelativeDerivedAccel.Z)) 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. // 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; const float ApexTimeMinimum = 0.0001f;
@ -2814,8 +2844,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
const FVector ApexVelocity = OldVelocityWithRootMotion + (DerivedAccel * TimeToApex); const FVector ApexVelocity = OldVelocityWithRootMotion + (DerivedAccel * TimeToApex);
if (HasCustomGravity()) if (HasCustomGravity())
{ {
const FVector GravityRelativeApexVelocity = RotateWorldToGravity(ApexVelocity); Velocity = ProjectToGravityFloor(ApexVelocity); // Should be nearly zero anyway, but this makes apex notifications consistent.
Velocity = RotateGravityToWorld(FVector(GravityRelativeApexVelocity.X, GravityRelativeApexVelocity.Y, 0)); // Should be nearly zero anyway, but this makes apex notifications consistent.
} }
else else
{ {
@ -2847,7 +2876,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
ApplyRootMotionToVelocity(timeTick); ApplyRootMotionToVelocity(timeTick);
//ApplyVRMotionToVelocity(deltaTime); //ApplyVRMotionToVelocity(deltaTime);
if (bNotifyApex && (RotateWorldToGravity(Velocity).Z < 0.f)) if (bNotifyApex && (GetGravitySpaceZ(Velocity) < 0.f))
{ {
// Just passed jump apex since now going down // Just passed jump apex since now going down
bNotifyApex = false; bNotifyApex = false;
@ -2912,7 +2941,9 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
FFindFloorResult FloorResult; FFindFloorResult FloorResult;
FindFloor(PawnLocation, FloorResult, false, NULL); 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(); //RestorePreAdditiveVRMotionVelocity();
remainingTime += subTimeTickRemaining; remainingTime += subTimeTickRemaining;
@ -2950,7 +2981,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
TGuardValue<FVector> RestoreVelocity(Velocity, OldVelocity); TGuardValue<FVector> RestoreVelocity(Velocity, OldVelocity);
if (HasCustomGravity()) if (HasCustomGravity())
{ {
Velocity = FVector::VectorPlaneProject(Velocity, RotateGravityToWorld(FVector::UpVector)); Velocity = ProjectToGravityFloor(Velocity);
const FVector GravityRelativeOffset = OldVelocity - Velocity; const FVector GravityRelativeOffset = OldVelocity - Velocity;
CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel); CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel);
VelocityNoAirControl = Velocity + GravityRelativeOffset; 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 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); 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) else if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported)
{ {
const FVector NewVelocity = (Delta / subTimeTickRemaining); 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) 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. // 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; const FVector LastMoveNoAirControl = VelocityNoAirControl * LastMoveTimeSlice;
Delta = ComputeSlideVector(LastMoveNoAirControl, 1.f, OldHitNormal, Hit); 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) if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported)
{ {
const FVector NewVelocity = (Delta / subTimeTickRemaining); 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 // 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); SafeMoveUpdatedComponent(Delta, PawnRotation, true, Hit);
if (Hit.Time == 0.f) if (Hit.Time == 0.f)
{ {
// if we are stuck then try to side step // 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()) 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); SafeMoveUpdatedComponent(SideDelta, PawnRotation, true, Hit);
} }
@ -3069,13 +3108,13 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
ProcessLanded(Hit, remainingTime, Iterations); ProcessLanded(Hit, remainingTime, Iterations);
return; 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. // We might be in a virtual 'ditch' within our perch radius. This is rare.
const FVector PawnLocation = UpdatedComponent->GetComponentLocation(); const FVector PawnLocation = UpdatedComponent->GetComponentLocation();
const float ZMovedDist = FMath::Abs(RotateWorldToGravity(PawnLocation - OldLocation).Z); const float ZMovedDist = FMath::Abs(GetGravitySpaceZ(PawnLocation - OldLocation));
const float MovedDist2DSq = FVector::VectorPlaneProject(PawnLocation - OldLocation, RotateGravityToWorld(FVector::UpVector)).Size2D(); const float MovedDist2D = ProjectToGravityFloor(PawnLocation - OldLocation).Size();
if (ZMovedDist <= 0.2f * timeTick && MovedDist2DSq <= 4.f * timeTick) if (ZMovedDist <= 0.2f * timeTick && MovedDist2D <= 4.f * timeTick)
{ {
FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity); FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity);
GravityRelativeVelocity.X += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f); GravityRelativeVelocity.X += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f);
@ -3135,12 +3174,10 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
} }
} }
FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity); const FVector GravityProjectedVelocity = ProjectToGravityFloor(Velocity);
if (GravityRelativeVelocity.SizeSquared2D() <= UE_KINDA_SMALL_NUMBER * 10.f) if (GravityProjectedVelocity.SizeSquared() <= UE_KINDA_SMALL_NUMBER * 10.f)
{ {
GravityRelativeVelocity.X = 0.f; Velocity = GetGravitySpaceComponentZ(Velocity);
GravityRelativeVelocity.Y = 0.f;
Velocity = RotateGravityToWorld(GravityRelativeVelocity);
} }
RestorePreAdditiveVRMotionVelocity(); 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())); devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysNavWalking: Velocity contains NaN before CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
//bound acceleration //bound acceleration
Acceleration.Z = 0.f; Acceleration = ProjectToGravityFloor(Acceleration);
//if (!HasRootMotion()) //if (!HasRootMotion())
//{ //{
CalcVelocity(deltaTime, GroundFriction, false, BrakingDecelerationWalking); CalcVelocity(deltaTime, GroundFriction, false, BrakingDecelerationWalking);
@ -3198,8 +3235,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
Iterations++; Iterations++;
FVector DesiredMove = Velocity; const FVector DesiredMove = ProjectToGravityFloor(Velocity);
DesiredMove.Z = 0.f;
//const FVector OldPlayerLocation = GetActorFeetLocation(); //const FVector OldPlayerLocation = GetActorFeetLocation();
const FVector OldLocation = GetActorFeetLocationVR(); const FVector OldLocation = GetActorFeetLocationVR();
@ -3214,11 +3250,11 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
{ {
if (bProjectNavMeshWalking) if (bProjectNavMeshWalking)
{ {
const float DistSq2D = (OldLocation - CachedNavLocation.Location).SizeSquared2D(); const float DistSq2D = ProjectToGravityFloor(OldLocation - CachedNavLocation.Location).SizeSquared();
const float DistZ = FMath::Abs(OldLocation.Z - CachedNavLocation.Location.Z); const float DistZ = FMath::Abs(GetGravitySpaceZ(OldLocation - CachedNavLocation.Location));
const float TotalCapsuleHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.0f; 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); const float DistZThr = TotalCapsuleHeight * FMath::Max(0.f, ProjectionScale);
bSameNavLocation = (DistSq2D <= UE_KINDA_SMALL_NUMBER) && (DistZ < DistZThr); 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. // we'll follow that geometry's plane out of range of valid navigation.
if (bSameNavLocation && bProjectNavMeshWalking) if (bSameNavLocation && bProjectNavMeshWalking)
{ {
AdjustedDest.Z = CachedNavLocation.Location.Z; SetGravitySpaceZ(AdjustedDest, GetGravitySpaceZ(CachedNavLocation.Location));
} }
// Find the point on the NavMesh // Find the point on the NavMesh
@ -3272,7 +3308,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
if (DestNavLocation.NodeRef != INVALID_NAVNODEREF) if (DestNavLocation.NodeRef != INVALID_NAVNODEREF)
{ {
FVector NewLocation(AdjustedDest.X, AdjustedDest.Y, DestNavLocation.Location.Z); FVector NewLocation = ProjectToGravityFloor(AdjustedDest) + GetGravitySpaceComponentZ(DestNavLocation.Location);
if (bProjectNavMeshWalking) if (bProjectNavMeshWalking)
{ {
SCOPE_CYCLE_COUNTER(STAT_CharNavProjectLocation); SCOPE_CYCLE_COUNTER(STAT_CharNavProjectLocation);
@ -3330,18 +3366,18 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
float NetFluidFriction = 0.f; float NetFluidFriction = 0.f;
float Depth = ImmersionDepth(); float Depth = ImmersionDepth();
float NetBuoyancy = Buoyancy * Depth; float NetBuoyancy = Buoyancy * Depth;
float OriginalAccelZ = Acceleration.Z; float OriginalAccelZ = GetGravitySpaceZ(Acceleration);
bool bLimitedUpAccel = false; 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 //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) else if (Depth < 0.65f)
{ {
bLimitedUpAccel = (Acceleration.Z > 0.f); bLimitedUpAccel = (OriginalAccelZ > 0.f);
Acceleration.Z = FMath::Min<FVector::FReal>(0.1f, Acceleration.Z); SetGravitySpaceZ(Acceleration, FMath::Min<FVector::FReal>(0.1f, OriginalAccelZ));
} }
Iterations++; Iterations++;
@ -3351,7 +3387,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
{ {
const float Friction = 0.5f * GetPhysicsVolume()->FluidFriction * Depth; const float Friction = 0.5f * GetPhysicsVolume()->FluidFriction * Depth;
CalcVelocity(deltaTime, Friction, true, GetMaxBrakingDeceleration()); CalcVelocity(deltaTime, Friction, true, GetMaxBrakingDeceleration());
Velocity.Z += GetGravityZ() * deltaTime * (1.f - NetBuoyancy); Velocity += (GetGravityZ() * deltaTime * (1.f - NetBuoyancy)) * -GetGravityDirection();
} }
ApplyRootMotionToVelocity(deltaTime); ApplyRootMotionToVelocity(deltaTime);
@ -3359,7 +3395,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
FVector Adjusted = Velocity * deltaTime; FVector Adjusted = Velocity * deltaTime;
FHitResult Hit(1.f); 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 //may have left water - if so, script might have set new physics mode
if (!IsSwimming()) if (!IsSwimming())
@ -3372,10 +3408,10 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
if (Hit.Time < 1.f && CharacterOwner) if (Hit.Time < 1.f && CharacterOwner)
{ {
HandleSwimmingWallHit(Hit, deltaTime); HandleSwimmingWallHit(Hit, deltaTime);
if (bLimitedUpAccel && (Velocity.Z >= 0.f)) if (bLimitedUpAccel && (GetGravitySpaceZ(Velocity) >= 0.f))
{ {
// allow upward velocity at surface if against obstacle // allow upward velocity at surface if against obstacle
Velocity.Z += OriginalAccelZ * deltaTime; Velocity += OriginalAccelZ * deltaTime * -GetGravityDirection();
Adjusted = Velocity * (1.f - Hit.Time)*deltaTime; Adjusted = Velocity * (1.f - Hit.Time)*deltaTime;
SwimVR(Adjusted, Hit); SwimVR(Adjusted, Hit);
if (!IsSwimming()) 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 FVector VelDir = Velocity.GetSafeNormal();
const float UpDown = GravDir | VelDir; const float UpDown = VelDir | GetGravityDirection();
bool bSteppedUp = false; 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; const FVector RealVelocity = Velocity;
Velocity.Z = 1.f; // HACK: since will be moving up, in case pawn leaves the water SetGravitySpaceZ(Velocity, 1.f); // HACK: since will be moving up, in case pawn leaves the water
bSteppedUp = StepUp(GravDir, (Adjusted/* + AdditionalVRInputVector*/) * (1.f - Hit.Time), Hit); bSteppedUp = StepUp(GetGravityDirection(), Adjusted * (1.f - Hit.Time), Hit);
if (bSteppedUp) if (bSteppedUp)
{ {
//may have left water - if so, script might have set new physics mode //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); StartNewPhysics(remainingTime, Iterations);
return; return;
} }
OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ); SetGravitySpaceZ(OldLocation, GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()) + (GetGravitySpaceZ(OldLocation) - StepZ));
} }
Velocity = RealVelocity; 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) if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && !bJustTeleported && ((deltaTime - remainingTime) > UE_KINDA_SMALL_NUMBER) && CharacterOwner)
{ {
bool bWaterJump = !GetPhysicsVolume()->bWaterVolume; const bool bWaterJump = !GetPhysicsVolume()->bWaterVolume;
float velZ = Velocity.Z; const FVector::FReal VelZ = GetGravitySpaceZ(Velocity);
Velocity = ((UpdatedComponent->GetComponentLocation() - OldLocation)/* - AdditionalVRInputVector*/) / (deltaTime - remainingTime); Velocity = ((UpdatedComponent->GetComponentLocation() - OldLocation)/* - AdditionalVRInputVector*/) / (deltaTime - remainingTime);
if (bWaterJump) if (bWaterJump)
{ {
Velocity.Z = velZ; SetGravitySpaceZ(Velocity, VelZ);
} }
} }
@ -3472,9 +3507,10 @@ void UVRCharacterMovementComponent::StartSwimmingVR(FVector OldLocation, FVector
} }
MoveUpdatedComponent(End - NewLocation, UpdatedComponent->GetComponentQuat(), true); 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)) 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(); FVector currentLoc = VRRootCapsule ? VRRootCapsule->OffsetComponentToWorld.GetLocation() : UpdatedComponent->GetComponentLocation();
// check if there is a wall directly in front of the swimming pawn // check if there is a wall directly in front of the swimming pawn
CheckPoint.Z = 0.f; CheckPoint = ProjectToGravityFloor(CheckPoint);
FVector CheckNorm = CheckPoint.GetSafeNormal(); FVector CheckNorm = CheckPoint.GetSafeNormal();
float PawnCapsuleRadius, PawnCapsuleHalfHeight; float PawnCapsuleRadius, PawnCapsuleHalfHeight;
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnCapsuleRadius, PawnCapsuleHalfHeight); CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnCapsuleRadius, PawnCapsuleHalfHeight);
CheckPoint = currentLoc + 1.2f * PawnCapsuleRadius * CheckNorm; CheckPoint = currentLoc + 1.2f * PawnCapsuleRadius * CheckNorm;
FVector Extent(PawnCapsuleRadius, PawnCapsuleRadius, PawnCapsuleHalfHeight);
FHitResult HitInfo(1.f); FHitResult HitInfo(1.f);
FCollisionQueryParams CapsuleParams(SCENE_QUERY_STAT(CheckWaterJump), false, CharacterOwner); FCollisionQueryParams CapsuleParams(SCENE_QUERY_STAT(CheckWaterJump), false, CharacterOwner);
FCollisionResponseParams ResponseParam; FCollisionResponseParams ResponseParam;
InitCollisionParams(CapsuleParams, ResponseParam); InitCollisionParams(CapsuleParams, ResponseParam);
FCollisionShape CapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_None); FCollisionShape CapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_None);
const ECollisionChannel CollisionChannel = UpdatedComponent->GetCollisionObjectType(); 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())) if (bHit && !HitInfo.HitObjectHandle.DoesRepresentClass(APawn::StaticClass()))
{ {
// hit a wall - check if it is low enough // hit a wall - check if it is low enough
WallNormal = -1.f * HitInfo.ImpactNormal; WallNormal = -1.f * HitInfo.ImpactNormal;
FVector Start = currentLoc;//UpdatedComponent->GetComponentLocation(); FVector Start = currentLoc;//UpdatedComponent->GetComponentLocation();
Start.Z += MaxOutOfWaterStepHeight; Start += MaxOutOfWaterStepHeight * -GetGravityDirection();
CheckPoint = Start + 3.2f * PawnCapsuleRadius * WallNormal; CheckPoint = Start + 3.2f * PawnCapsuleRadius * WallNormal;
FCollisionQueryParams LineParams(SCENE_QUERY_STAT(CheckWaterJump), true, CharacterOwner); FCollisionQueryParams LineParams(SCENE_QUERY_STAT(CheckWaterJump), true, CharacterOwner);
FCollisionResponseParams LineResponseParam; FCollisionResponseParams LineResponseParam;
@ -3725,11 +3760,7 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
// find floor and check if falling // find floor and check if falling
if (IsMovingOnGround() || MovementMode == MOVE_Falling) if (IsMovingOnGround() || MovementMode == MOVE_Falling)
{ {
bool bShouldFindFloor = Velocity.Z <= 0.f; const bool bShouldFindFloor = GetGravitySpaceZ(Velocity) <= UE_KINDA_SMALL_NUMBER;
if (HasCustomGravity())
{
bShouldFindFloor = RotateWorldToGravity(Velocity).Z <= 0.0;
}
if (StepDownResult.bComputedFloor) if (StepDownResult.bComputedFloor)
{ {
@ -3750,14 +3781,8 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
{ {
// Follows PhysWalking approach for encroachment on floor tests // Follows PhysWalking approach for encroachment on floor tests
FHitResult Hit(CurrentFloor.HitResult); FHitResult Hit(CurrentFloor.HitResult);
if (HasCustomGravity()) Hit.TraceEnd = Hit.TraceStart - GetGravityDirection() * MAX_FLOOR_DIST;
{
Hit.TraceEnd = Hit.TraceStart - GetGravityDirection() * MAX_FLOOR_DIST;
}
else
{
Hit.TraceEnd = Hit.TraceStart + FVector(0.f, 0.f, MAX_FLOOR_DIST);
}
const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit); const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
const bool bResolved = ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat()); const bool bResolved = ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
@ -3775,16 +3800,9 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
if (!bSimGravityDisabled) if (!bSimGravityDisabled)
{ {
// No floor, must fall. // 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);
{
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);
} }
} }
@ -3903,7 +3921,7 @@ void UVRCharacterMovementComponent::MoveSmooth(const FVector& InVelocity, const
{ {
OutStepDownResult = NULL; // No need for a floor when not walking. OutStepDownResult = NULL; // No need for a floor when not walking.
bool bShouldAttemptStepUp = false; bool bShouldAttemptStepUp = false;
bShouldAttemptStepUp = FMath::Abs(RotateWorldToGravity(Hit.ImpactNormal).Z) < 0.2; bShouldAttemptStepUp = FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2;
if (bShouldAttemptStepUp) if (bShouldAttemptStepUp)
{ {
const FVector GravDir = GetGravityDirection(); const FVector GravDir = GetGravityDirection();
@ -3946,7 +3964,7 @@ void UVRCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMov
MoveResponse.RootMotionTrackPosition, MoveResponse.RootMotionTrackPosition,
MoveResponse.ClientAdjustment.NewLoc, MoveResponse.ClientAdjustment.NewLoc,
MoveResponse.RootMotionRotation, MoveResponse.RootMotionRotation,
MoveResponse.ClientAdjustment.NewVel.Z, GetGravitySpaceZ(MoveResponse.ClientAdjustment.NewVel),
MoveResponse.ClientAdjustment.NewBase, MoveResponse.ClientAdjustment.NewBase,
MoveResponse.ClientAdjustment.NewBaseBoneName, MoveResponse.ClientAdjustment.NewBaseBoneName,
MoveResponse.bHasBase, MoveResponse.bHasBase,
@ -3961,7 +3979,7 @@ void UVRCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMov
MoveResponse.RootMotionTrackPosition, MoveResponse.RootMotionTrackPosition,
MoveResponse.ClientAdjustment.NewLoc, MoveResponse.ClientAdjustment.NewLoc,
MoveResponse.RootMotionRotation, MoveResponse.RootMotionRotation,
MoveResponse.ClientAdjustment.NewVel.Z, GetGravitySpaceZ(MoveResponse.ClientAdjustment.NewVel),
MoveResponse.ClientAdjustment.NewBase, MoveResponse.ClientAdjustment.NewBase,
MoveResponse.ClientAdjustment.NewBaseBoneName, MoveResponse.ClientAdjustment.NewBaseBoneName,
MoveResponse.bHasBase, MoveResponse.bHasBase,
@ -4452,7 +4470,7 @@ void UVRCharacterMovementComponent::ServerMoveHandleClientErrorVR(float ClientTi
{ {
const FVector LastBaseVelocity = MovementBaseUtility::GetMovementBaseVelocity(LastServerMovementBaseVRPtr, LastServerMovementBaseBoneName); const FVector LastBaseVelocity = MovementBaseUtility::GetMovementBaseVelocity(LastServerMovementBaseVRPtr, LastServerMovementBaseBoneName);
RelativeVelocity = Velocity - LastBaseVelocity; RelativeVelocity = Velocity - LastBaseVelocity;
const FVector BaseDirection = LastBaseVelocity.GetSafeNormal2D(); const FVector BaseDirection = ProjectToGravityFloor(LastBaseVelocity).GetSafeNormal();
const FVector RelativeDirection = RelativeVelocity * (1.f / MaxWalkSpeed); const FVector RelativeDirection = RelativeVelocity * (1.f / MaxWalkSpeed);
ClientForwardFactor = FMath::Clamp(FVector::DotProduct(BaseDirection, RelativeDirection), 0.f, 1.f); ClientForwardFactor = FMath::Clamp(FVector::DotProduct(BaseDirection, RelativeDirection), 0.f, 1.f);

View file

@ -245,7 +245,7 @@ void UVRGestureComponent::CaptureGestureFrame()
// Pop off oldest sample // Pop off oldest sample
if (GestureLog.Samples.Num() >= RecordingBufferSize) if (GestureLog.Samples.Num() >= RecordingBufferSize)
{ {
GestureLog.Samples.Pop(false); GestureLog.Samples.Pop(EAllowShrinking::No);
bClearLatestSpline = true; bClearLatestSpline = true;
} }
@ -600,8 +600,8 @@ bool UGesturesDatabase::ImportSplineAsGesture(USplineComponent * HostSplineCompo
float LastDistance = 0.f; float LastDistance = 0.f;
float ThisDistance = 0.f; float ThisDistance = 0.f;
FVector LastDistanceV; FVector LastDistanceV = FVector::ZeroVector;
FVector ThisDistanceV; FVector ThisDistanceV = FVector::ZeroVector;
FVector DistNormal; FVector DistNormal;
float DistAlongSegment = 0.f; float DistAlongSegment = 0.f;

View file

@ -3,6 +3,9 @@
#include "VRPathFollowingComponent.h" #include "VRPathFollowingComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRPathFollowingComponent) #include UE_INLINE_GENERATED_CPP_BY_NAME(VRPathFollowingComponent)
#include "AI/Navigation/NavigationTypes.h"
#include "AI/Navigation/PathFollowingAgentInterface.h"
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Engine/World.h" #include "Engine/World.h"
//#include "Runtime/Engine/Private/EnginePrivate.h" //#include "Runtime/Engine/Private/EnginePrivate.h"
@ -16,7 +19,7 @@
DEFINE_LOG_CATEGORY(LogPathFollowingVR); DEFINE_LOG_CATEGORY(LogPathFollowingVR);
void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp) /*void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
{ {
Super::SetMovementComponent(MoveComp); Super::SetMovementComponent(MoveComp);
@ -26,8 +29,23 @@ void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* Move
{ {
OnRequestFinished.AddUObject(VRMovementComp, &UVRBaseCharacterMovementComponent::OnMoveCompleted); 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 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); 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())) /*int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
{
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 PickedPathPoint = INDEX_NONE; int32 PickedPathPoint = INDEX_NONE;
@ -192,7 +166,7 @@ bool UVRPathFollowingComponent::UpdateBlockDetection()
{ {
LocationSamples.AddZeroed(1); LocationSamples.AddZeroed(1);
} }
LocationSamples[NextSampleIdx] = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationBased() : MovementComp->GetActorFeetLocationBased()); LocationSamples[NextSampleIdx] = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationBased() : MovementComp->GetActorFeetLocationBased());
NextSampleIdx = (NextSampleIdx + 1) % BlockDetectionSampleCount; NextSampleIdx = (NextSampleIdx + 1) % BlockDetectionSampleCount;
return true; return true;
@ -302,8 +276,8 @@ void UVRPathFollowingComponent::UpdatePathSegment()
if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex)) if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex))
{ {
//LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct, //LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct,
//*Path->GetPathPointLocation(MoveSegmentStartIndex), //Path->GetPathPointLocation(MoveSegmentStartIndex),
//*Path->GetPathPointLocation(MoveSegmentEndIndex)); //Path->GetPathPointLocation(MoveSegmentEndIndex));
} }
else else
{ {
@ -455,7 +429,7 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current
const FVector ToGoal = (GoalLocation - AgentLocation); const FVector ToGoal = (GoalLocation - AgentLocation);
const FVector CurrentDirection = GetCurrentDirection(); 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; bDotFailed = (CurrentDot < 0.0f) ? 1 : 0;
// get cylinder of moving agent // get cylinder of moving agent
@ -472,3 +446,4 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current
const float UseHeight = GoalHalfHeight + (AgentHalfHeight * MinAgentHalfHeightPct); const float UseHeight = GoalHalfHeight + (AgentHalfHeight * MinAgentHalfHeightPct);
bHeightFailed = (CurrentHeight > UseHeight) ? 1 : 0; bHeightFailed = (CurrentHeight > UseHeight) ? 1 : 0;
} }
*/

View file

@ -862,7 +862,11 @@ public:
{ {
bWillEverBeLit = false; bWillEverBeLit = false;
bCreateSceneProxy = InComponent->bShouldCreateProxy; bCreateSceneProxy = InComponent->bShouldCreateProxy;
MaterialRelevance = MaterialInstance->GetRelevance(GetScene().GetFeatureLevel());
if (MaterialInstance)
{
MaterialRelevance = MaterialInstance->GetRelevance(GetScene().GetFeatureLevel());
}
} }
// FPrimitiveSceneProxy interface. // FPrimitiveSceneProxy interface.
@ -886,12 +890,12 @@ public:
{ {
ParentMaterialProxy = WireframeMaterialInstance; ParentMaterialProxy = WireframeMaterialInstance;
} }
else else if (MaterialInstance != nullptr)
{ {
ParentMaterialProxy = MaterialInstance->GetRenderProxy(); ParentMaterialProxy = MaterialInstance->GetRenderProxy();
} }
#else #else
FMaterialRenderProxy* ParentMaterialProxy = MaterialInstance->GetRenderProxy(); FMaterialRenderProxy* ParentMaterialProxy = MaterialInstance ? MaterialInstance->GetRenderProxy() : nullptr;
#endif #endif
//FSpriteTextureOverrideRenderProxy* TextureOverrideMaterialProxy = new FSpriteTextureOverrideRenderProxy(ParentMaterialProxy, //FSpriteTextureOverrideRenderProxy* TextureOverrideMaterialProxy = new FSpriteTextureOverrideRenderProxy(ParentMaterialProxy,
@ -912,10 +916,10 @@ public:
{ {
if (GeometryMode == EWidgetGeometryMode::Plane) if (GeometryMode == EWidgetGeometryMode::Plane)
{ {
float U = -RenderTarget->SizeX * Pivot.X; float U = -RenderTarget->SizeX * static_cast<float>(Pivot.X);
float V = -RenderTarget->SizeY * Pivot.Y; float V = -RenderTarget->SizeY * static_cast<float>(Pivot.Y);
float UL = RenderTarget->SizeX * (1.0f - Pivot.X); float UL = RenderTarget->SizeX * (1.0f - static_cast<float>(Pivot.X));
float VL = RenderTarget->SizeY * (1.0f - Pivot.Y); float VL = RenderTarget->SizeY * (1.0f - static_cast<float>(Pivot.Y));
int32 VertexIndices[4]; int32 VertexIndices[4];
@ -948,13 +952,13 @@ public:
const int32 NumSegments = FMath::Lerp(4, 32, ArcAngle / PI); const int32 NumSegments = FMath::Lerp(4, 32, ArcAngle / PI);
const float Radius = RenderTarget->SizeX / ArcAngle; const double Radius = RenderTarget->SizeX / ArcAngle;
const float Apothem = Radius * FMath::Cos(0.5f*ArcAngle); const double Apothem = Radius * FMath::Cos(0.5 * ArcAngle);
const float ChordLength = 2.0f * Radius * FMath::Sin(0.5f*ArcAngle); const double ChordLength = 2.0f * Radius * FMath::Sin(0.5 * ArcAngle);
const float PivotOffsetX = ChordLength * (0.5 - Pivot.X); const double PivotOffsetX = ChordLength * (0.5 - Pivot.X);
const float V = -RenderTarget->SizeY * Pivot.Y; const double V = -RenderTarget->SizeY * Pivot.Y;
const float VL = RenderTarget->SizeY * (1.0f - Pivot.Y); const double VL = RenderTarget->SizeY * (1.0 - Pivot.Y);
int32 VertexIndices[4]; int32 VertexIndices[4];
@ -964,7 +968,7 @@ public:
if (VisibilityMap & (1 << ViewIndex)) if (VisibilityMap & (1 << ViewIndex))
{ {
const float RadiansPerStep = ArcAngle / NumSegments; const double RadiansPerStep = ArcAngle / NumSegments;
FVector LastTangentX; FVector LastTangentX;
FVector LastTangentY; FVector LastTangentY;
@ -972,14 +976,14 @@ public:
for (int32 Segment = 0; Segment < NumSegments; Segment++) for (int32 Segment = 0; Segment < NumSegments; Segment++)
{ {
const float Angle = -ArcAngle / 2 + Segment * RadiansPerStep; const double Angle = -ArcAngle / 2 + Segment * RadiansPerStep;
const float NextAngle = Angle + RadiansPerStep; const double NextAngle = Angle + RadiansPerStep;
// Polar to Cartesian // Polar to Cartesian
const float X0 = Radius * FMath::Cos(Angle) - Apothem; const double X0 = Radius * FMath::Cos(Angle) - Apothem;
const float Y0 = Radius * FMath::Sin(Angle); const double Y0 = Radius * FMath::Sin(Angle);
const float X1 = Radius * FMath::Cos(NextAngle) - Apothem; const double X1 = Radius * FMath::Cos(NextAngle) - Apothem;
const float Y1 = Radius * FMath::Sin(NextAngle); const double Y1 = Radius * FMath::Sin(NextAngle);
const float U0 = static_cast<float>(Segment) / NumSegments; const float U0 = static_cast<float>(Segment) / NumSegments;
const float U1 = static_cast<float>(Segment + 1) / 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()); } 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: private:
FVector Origin; FVector Origin;
@ -1135,7 +1139,7 @@ private:
UBodySetup* BodySetup; UBodySetup* BodySetup;
EWidgetBlendMode BlendMode; EWidgetBlendMode BlendMode;
EWidgetGeometryMode GeometryMode; EWidgetGeometryMode GeometryMode;
float ArcAngle; double ArcAngle;
bool bCreateSceneProxy; bool bCreateSceneProxy;
}; };
@ -1149,10 +1153,13 @@ FPrimitiveSceneProxy* UVRStereoWidgetComponent::CreateSceneProxy()
if (WidgetRenderer && GetSlateWindow() && GetSlateWindow()->GetContent() != SNullWidget::NullWidget) if (WidgetRenderer && GetSlateWindow() && GetSlateWindow()->GetContent() != SNullWidget::NullWidget)
{ {
RequestRenderUpdate(); if (ISlate3DRenderer* SlateRenderer = WidgetRenderer->GetSlateRenderer())
LastWidgetRenderTime = 0; {
RequestRenderUpdate();
LastWidgetRenderTime = 0;
return new FStereoWidget3DSceneProxy(this, *WidgetRenderer->GetSlateRenderer()); return new FStereoWidget3DSceneProxy(this, *SlateRenderer);
}
} }
#if WITH_EDITOR #if WITH_EDITOR
@ -1208,7 +1215,7 @@ FPrimitiveSceneProxy* UVRStereoWidgetComponent::CreateSceneProxy()
return Result; return Result;
} }
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); } 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: private:
const FVector BoxExtents; const FVector BoxExtents;

View file

@ -88,7 +88,7 @@ public:
UPROPERTY() UPROPERTY()
uint8 MoveActionFlags; uint8 MoveActionFlags;
UPROPERTY() UPROPERTY()
TArray<UObject*> MoveActionObjectReferences; TArray<TObjectPtr<UObject>> MoveActionObjectReferences;
UPROPERTY() UPROPERTY()
EVRMoveActionVelocityRetention VelRetentionSetting; EVRMoveActionVelocityRetention VelRetentionSetting;
@ -516,7 +516,7 @@ public:
// Moved these here to avoid having to duplicate tons of properties // Moved these here to avoid having to duplicate tons of properties
UPROPERTY(Transient) UPROPERTY(Transient)
UPrimitiveComponent* ClientMovementBase; TObjectPtr<UPrimitiveComponent> ClientMovementBase;
UPROPERTY(Transient) UPROPERTY(Transient)
FName ClientBaseBoneName; FName ClientBaseBoneName;

View file

@ -809,7 +809,7 @@ public:
} }
UPROPERTY(BlueprintReadWrite, Category = "GripMotionController") UPROPERTY(BlueprintReadWrite, Category = "GripMotionController")
TArray<UPrimitiveComponent *> AdditionalLateUpdateComponents; TArray<TObjectPtr<UPrimitiveComponent>> AdditionalLateUpdateComponents;
// Movement Replication // Movement Replication
// Actor needs to be replicated for this to work // Actor needs to be replicated for this to work

View file

@ -64,8 +64,11 @@ private:
TMap<Chaos::FConstPhysicsObjectHandle, FReplicatedPhysicsTargetAsync> ObjectToTarget; TMap<Chaos::FConstPhysicsObjectHandle, FReplicatedPhysicsTargetAsync> ObjectToTarget;
TMap<Chaos::FConstPhysicsObjectHandle, FNetworkPhysicsSettingsAsync> ObjectToSettings; TMap<Chaos::FConstPhysicsObjectHandle, FNetworkPhysicsSettingsAsync> ObjectToSettings;
TArray<int32> ParticlesInResimIslands; TArray<int32> ParticlesInResimIslands;
TArray<Chaos::FParticleID> ReplicatedParticleIDs;
private: private:
FReplicatedPhysicsTargetAsync* AddObjectToReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject);
void RemoveObjectFromReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject);
void UpdateAsyncTarget(const FPhysicsRepAsyncInputData& Input, Chaos::FPBDRigidsSolver* RigidsSolver); void UpdateAsyncTarget(const FPhysicsRepAsyncInputData& Input, Chaos::FPBDRigidsSolver* RigidsSolver);
void UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input); void UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input);
void CacheResimInteractions(); void CacheResimInteractions();
@ -105,6 +108,7 @@ public:
TArray<FReplicatedPhysicsTarget> ReplicatedTargetsQueueVR; TArray<FReplicatedPhysicsTarget> ReplicatedTargetsQueueVR;
FPhysicsReplicationAsyncVR* PhysicsReplicationAsyncVR; FPhysicsReplicationAsyncVR* PhysicsReplicationAsyncVR;
FPhysicsReplicationAsyncInput* AsyncInputVR; //async data being written into before we push into callback 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) void PrepareAsyncData_ExternalVR(const FRigidBodyErrorCorrection& ErrorCorrection); //prepare async data for writing. Call on external thread (i.e. game thread)
}; };

View file

@ -113,7 +113,7 @@ struct FAISightTargetVR
static const FTargetId InvalidTargetId; static const FTargetId InvalidTargetId;
TWeakObjectPtr<AActor> Target; TWeakObjectPtr<AActor> Target;
IAISightTargetInterface* SightTargetInterface; TWeakInterfacePtr<IAISightTargetInterface> WeakSightTargetInterface;
FGenericTeamId TeamId; FGenericTeamId TeamId;
FTargetId TargetId; FTargetId TargetId;
@ -333,7 +333,7 @@ protected:
FOnPendingVisibilityQueryProcessedDelegateVR OnPendingCanBeSeenQueryProcessedDelegate; FOnPendingVisibilityQueryProcessedDelegateVR OnPendingCanBeSeenQueryProcessedDelegate;
FTraceDelegate OnPendingTraceQueryProcessedDelegate; FTraceDelegate OnPendingTraceQueryProcessedDelegate;
UE_MT_DECLARE_RW_ACCESS_DETECTOR(QueriesListAccessDetector); UE_MT_DECLARE_TS_RW_ACCESS_DETECTOR(QueriesListAccessDetector);
public: public:

View file

@ -28,17 +28,21 @@ public:
void SetConstraintToForceBased(bool bUseForceConstraint) 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; return;
if (ConstraintInstance.ConstraintHandle->IsType(Chaos::EConstraintType::JointConstraintType)) if (ConstraintInstance.ConstraintHandle->IsType(Chaos::EConstraintType::JointConstraintType))
{ {
if (Chaos::FJointConstraint* Constraint = static_cast<Chaos::FJointConstraint*>(ConstraintInstance.ConstraintHandle.Constraint)) if (Chaos::FJointConstraint* Constraint = static_cast<Chaos::FJointConstraint*>(ConstraintInstance.ConstraintHandle.Constraint))
{ {
Constraint->SetLinearDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration); Constraint->SetLinearDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
Constraint->SetAngularDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration); Constraint->SetAngularDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
} }
} }*/
//#endif //#endif
} }

View file

@ -388,7 +388,7 @@ public:
/** */ /** */
UPROPERTY(VisibleAnywhere, Instanced, NoClear, Category = "User Interface", meta = (ShowOnlyInnerProperties)) UPROPERTY(VisibleAnywhere, Instanced, NoClear, Category = "User Interface", meta = (ShowOnlyInnerProperties))
UVRFullScreenUserWidget* ScreenUserWidget; TObjectPtr<UVRFullScreenUserWidget> ScreenUserWidget;
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor") UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
UVRFullScreenUserWidget* GetPreviewWidgetComp(); UVRFullScreenUserWidget* GetPreviewWidgetComp();

View file

@ -175,7 +175,7 @@ protected:
int numMessages = OutMessages.Num(); int numMessages = OutMessages.Num();
if (numMessages > MaxStoredMessages) if (numMessages > MaxStoredMessages)
{ {
OutMessages.RemoveAt(0, numMessages - MaxStoredMessages, true); OutMessages.RemoveAt(0, numMessages - MaxStoredMessages, EAllowShrinking::Yes);
} }
if (OldNumMessages != numMessages) if (OldNumMessages != numMessages)
bIsDirty = true; bIsDirty = true;

View file

@ -13,6 +13,40 @@
#include "VRWheeledVehicle.generated.h" #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. * 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: 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") UFUNCTION(BlueprintCallable, Category = "Pawn")
virtual bool SetBindToInput(AController * CController, bool bBindToInput) virtual bool SetBindToInput(AController * CController, bool bBindToInput)
{ {

View file

@ -101,7 +101,7 @@ public:
// Get Camera View is no longer required, they finally broke the HMD logic out into its own section!! // 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 GetCameraView(float DeltaTime, FMinimalViewInfo& DesiredView) override;
virtual void HandleXRCamera() override; virtual void HandleXRCamera(float DeltaTime) override;
UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedCameraTransform, Category = "ReplicatedCamera|Networking") UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedCameraTransform, Category = "ReplicatedCamera|Networking")
FBPVRComponentPosRep ReplicatedCameraTransform; FBPVRComponentPosRep ReplicatedCameraTransform;

View file

@ -50,67 +50,7 @@ public:
UPROPERTY(Transient) UPROPERTY(Transient)
TObjectPtr<AActor> Owner; TObjectPtr<AActor> Owner;
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) 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;
}
}; };
template<> template<>
@ -276,7 +216,7 @@ public:
/** BaseVR Character movement component belongs to */ /** BaseVR Character movement component belongs to */
UPROPERTY(Transient, DuplicateTransient) UPROPERTY(Transient, DuplicateTransient)
AVRPlayerController* OwningVRPlayerController; TObjectPtr<AVRPlayerController> OwningVRPlayerController;
// If true then we will retain roomscale tracking in relative space of the character. // 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 // If false than the movement component will offset to the hmd tracking and the tracking will be nulled out

View file

@ -54,6 +54,7 @@ public:
static const float CLIMB_SWEEP_EDGE_REJECT_DISTANCE; static const float CLIMB_SWEEP_EDGE_REJECT_DISTANCE;
virtual bool IsWithinClimbingEdgeTolerance(const FVector& CapsuleLocation, const FVector& TestImpactPoint, const float CapsuleRadius) const; 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 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; virtual bool IsWithinEdgeTolerance(const FVector& CapsuleLocation, const FVector& TestImpactPoint, const float CapsuleRadius) const override;

View file

@ -8,6 +8,7 @@
#include "Navigation/PathFollowingComponent.h" #include "Navigation/PathFollowingComponent.h"
#include "AbstractNavData.h" #include "AbstractNavData.h"
#include "Runtime/Launch/Resources/Version.h" #include "Runtime/Launch/Resources/Version.h"
#include "VRPathFollowingComponent.generated.h" #include "VRPathFollowingComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogPathFollowingVR, Warning, All); DECLARE_LOG_CATEGORY_EXTERN(LogPathFollowingVR, Warning, All);
@ -18,8 +19,17 @@ class VREXPANSIONPLUGIN_API UVRPathFollowingComponent : public UPathFollowingCom
GENERATED_BODY() GENERATED_BODY()
public: 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 // Add link to VRMovementComp
void SetMovementComponent(UNavMovementComponent* MoveComp) override; void SetMovementComponent(UNavMovementComponent* MoveComp) override;
@ -28,29 +38,29 @@ public:
// Have to override this to call the correct HasReachCurrentTarget // Have to override this to call the correct HasReachCurrentTarget
void UpdatePathSegment() override; void UpdatePathSegment() override;
bool HasReachedCurrentTarget(const FVector& CurrentLocation) const; bool HasReachedCurrentTarget(const FVector& CurrentLocation) const;
*/
// Had to override this to get the correct DebugReachTest // Had to override this to get the correct DebugReachTest
virtual void GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const override; 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; void FollowPathSegment(float DeltaTime) override;
int32 DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const 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. // 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 */ // notify about finished movement
//virtual void OnPathFinished(const FPathFollowingResult& Result) override; virtual void OnPathFinished(const FPathFollowingResult& Result) override;
*/
/** pause path following /* pause path following
* @param RequestID - request to pause, FAIRequestID::CurrentRequest means pause current request, regardless of its ID */ * @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 // 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 // Fine in 4.13
bool ShouldCheckPathOnResume() const override; bool ShouldCheckPathOnResume() const override;
// Fine in 4.13, had to change feet based for both // Fine in 4.13, had to change feet based for both
bool UpdateBlockDetection() override; bool UpdateBlockDetection() override;*/
}; };

View file

@ -1,6 +1,6 @@
{ {
"FileVersion": 3, "FileVersion": 3,
"EngineAssociation": "5.4", "EngineAssociation": "5.5",
"Category": "", "Category": "",
"Description": "", "Description": "",
"Modules": [ "Modules": [