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",
"components": [
"Microsoft.Net.Component.4.6.2.TargetingPack",
"Microsoft.VisualStudio.Component.Unreal.Workspace",
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL",
"Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.Windows10SDK.22621",
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop",

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

View file

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

View file

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

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

View file

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

View file

@ -54,7 +54,7 @@ AGrippableActor::AGrippableActor(const FObjectInitializer& ObjectInitializer)
// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration
// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
MinNetUpdateFrequency = 30.0f;
SetMinNetUpdateFrequency(30.0f);
}
void AGrippableActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
@ -152,13 +152,23 @@ void AGrippableActor::GatherCurrentMovement()
bool bFoundInCache = false;
UWorld* World = GetWorld();
const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
int ServerFrame = 0;
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
if (bShouldUsePhysicsReplicationCache)
{
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
{
RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
bFoundInCache = true;
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
{
if (RepMovement.ServerFrame != ServerFrame)
{
RepMovement.FillFrom(*FoundState, this, ServerFrame);
bWasRepMovementModified = true;
}
bFoundInCache = true;
}
}
}

View file

@ -52,9 +52,16 @@ namespace PhysicsReplicationCVars
int32 EnableDefaultReplication = 0;
namespace DefaultReplicationCVars
{
bool bHardsnapLegacyInPT = false;
bool bCorrectConnectedBodies = false;
bool bCorrectConnectedBodiesFriction = true;
}
namespace ResimulationCVars
{
bool bRuntimeCorrectionEnabled = true;
bool bRuntimeCorrectionEnabled = false;
bool bRuntimeVelocityCorrection = false;
bool bRuntimeCorrectConnectedBodies = true;
float PosStabilityMultiplier = 0.5f;
@ -62,6 +69,18 @@ namespace PhysicsReplicationCVars
float VelStabilityMultiplier = 0.5f;
float AngVelStabilityMultiplier = 0.5f;
bool bDrawDebug = false;
// Inside of NetworkPhysicsComponent - UPDATE AS CHANGE
int32 RedundantInputs = 2;
int32 RedundantStates = 0;
bool bAllowRewindToClosestState = true;
bool bCompareStateToTriggerRewind = false;
bool bCompareInputToTriggerRewind = false;
bool bEnableUnreliableFlow = true;
bool bEnableReliableFlow = false;
bool bApplyDataInsteadOfMergeData = false;
bool bAllowInputExtrapolation = true;
bool bValidateDataOnGameThread = false;
}
namespace PredictiveInterpolationCVars
@ -92,15 +111,24 @@ namespace PhysicsReplicationCVars
bool bSkipVelocityRepOnPosEarlyOut = true;
bool bPostResimWaitForUpdate = false;
bool bVelocityBased = true;
bool bPosCorrectionAsVelocity = false;
// New or re-named 5.5
bool bCorrectionAsVelocity = false;
bool bCorrectConnectedBodies = false;
bool bCorrectConnectedBodiesFriction = true;
bool bSleepConnectedBodies = true;
bool bKinematicPrediction = true;
bool bKinematicHardSnap = false;
bool bDisableSoftSnap = false;
bool bAlwaysHardSnap = false;
bool bSkipReplication = false;
bool bDontClearTarget = false;
bool bDrawDebugTargets = false;
bool bDrawDebugVectors = false;
float DrawDebugZOffset = 50.0f;
float SleepSecondsClearTarget = 15.0f;
int32 TargetTickAlignmentClampMultiplier = 1;
int32 TargetTickAlignmentClampMultiplier = 2;
}
}
@ -482,7 +510,25 @@ bool FPhysicsReplicationVR::ApplyRigidBodyState(float DeltaSeconds, FBodyInstanc
const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrection.AngularVelocityCoefficient;
static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance"));
const float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrection.MaxLinearHardSnapDistance;
float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrection.MaxLinearHardSnapDistance;
static const auto CVarHardsnapLegacyInPT = IConsoleManager::Get().FindConsoleVariable(TEXT("p.DefaultReplication.Legacy.HardsnapInPT"));
bool bHardsnapLegacyInPT = CVarHardsnapLegacyInPT->GetBool();
static const auto CVarCorrectConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("p.DefaultReplication.CorrectConnectedBodies"));
bool bCorrectConnectedBodies = CVarCorrectConnectedBodies->GetBool();
static const auto CVarCorrectConnectedBodiesFriction = IConsoleManager::Get().FindConsoleVariable(TEXT("p.DefaultReplication.CorrectConnectedBodiesFriction"));
bool bCorrectConnectedBodiesFriction = CVarCorrectConnectedBodiesFriction->GetBool();
// Assign per-actor settings from NetworkPhysicSettingsComponent if this actor has one
if (SettingsCurrent.Get())
{
MaxLinearHardSnapDistance = SettingsCurrent.Get()->DefaultReplicationSettings.GetMaxLinearHardSnapDistance(MaxLinearHardSnapDistance);
bHardsnapLegacyInPT = SettingsCurrent.Get()->DefaultReplicationSettings.GetHardsnapDefaultLegacyInPT();
bCorrectConnectedBodies = SettingsCurrent.Get()->DefaultReplicationSettings.GetCorrectConnectedBodies();
bCorrectConnectedBodiesFriction = SettingsCurrent.Get()->DefaultReplicationSettings.GetCorrectConnectedBodiesFriction();
}
// Get Current state
FRigidBodyState CurrentState;
@ -606,7 +652,42 @@ bool FPhysicsReplicationVR::ApplyRigidBodyState(float DeltaSeconds, FBodyInstanc
// Too much error so just snap state here and be done with it
PhysicsTarget.AccumulatedErrorSeconds = 0.0f;
bRestoredState = true;
BI->SetBodyTransform(IdealWorldTM, ETeleportType::ResetPhysics, bAutoWake);
// Hardsnap in physics thread
bool bPTHardSnapSuccess = false;
if (PhysicsReplicationAsyncVR != nullptr)
{
if (bHardsnapLegacyInPT)
{
if (Chaos::FSingleParticlePhysicsProxy* Proxy = static_cast<Chaos::FSingleParticlePhysicsProxy*>(BI->GetPhysicsActorHandle()))
{
if (Chaos::FPBDRigidsSolver* Solver = Proxy->GetSolver<Chaos::FPBDRigidsSolver>())
{
Solver->EnqueueCommandImmediate([Solver, Proxy, IdealWorldTM, NewState, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction]()
{
Chaos::FRigidBodyHandle_Internal* Handle = Proxy->GetPhysicsThreadAPI();
// Set XRVW to hard snap dynamic object and force recalculation of friction
Solver->GetEvolution()->ApplyParticleTransformCorrection(Proxy->GetHandle_LowLevel(), IdealWorldTM.GetLocation(), IdealWorldTM.GetRotation(), bCorrectConnectedBodies, bCorrectConnectedBodiesFriction);
Handle->SetV(NewState.LinVel);
Handle->SetW(FMath::DegreesToRadians(NewState.AngVel));
});
bPTHardSnapSuccess = true;
}
}
}
}
if (!bPTHardSnapSuccess)
{
BI->SetBodyTransform(IdealWorldTM, ETeleportType::ResetPhysics, bAutoWake);
// Set the new velocities
BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake);
BI->SetAngularVelocityInRadians(FMath::DegreesToRadians(NewState.AngVel), false, bAutoWake);
}
// Set the new velocities
BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake);
@ -714,7 +795,8 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
int32 LocalFrameOffset = 0; // LocalFrame = ServerFrame + LocalFrameOffset;
if (FPhysicsSolverBase::IsNetworkPhysicsPredictionEnabled())
bool LocalFrameOffsetAssigned = false;
if (UPhysicsSettings::Get()->PhysicsPrediction.bEnablePhysicsPrediction)
{
if (UWorld* World = GetOwningWorld())
{
@ -722,6 +804,7 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
{
if (APlayerController* PlayerController = World->GetFirstPlayerController())
{
LocalFrameOffsetAssigned = PlayerController->GetNetworkPhysicsTickOffsetAssigned();
LocalFrameOffset = PlayerController->GetNetworkPhysicsTickOffset();
}
}
@ -753,6 +836,9 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
{
// Removed as this is server sided
/*
// Update actor replication settings overrides
SettingsCurrent = UNetworkPhysicsSettingsComponent::GetSettingsForActor(OwningActor);
const ENetRole OwnerRole = OwningActor->GetLocalRole();
const bool bIsSimulated = OwnerRole == ROLE_SimulatedProxy;
const bool bIsReplicatedAutonomous = OwnerRole == ROLE_AutonomousProxy && PrimComp->bReplicatePhysicsToAutonomousProxy;
@ -821,9 +907,14 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrim
AsyncInputData.RepMode = PhysicsTarget.ReplicationMode;
AsyncInputData.ServerFrame = PhysicsTarget.ServerFrame;
AsyncInputData.FrameOffset = LocalFrameOffset;
AsyncInputData.LatencyOneWay = PingSecondsOneWay;
if (LocalFrameOffsetAssigned)
{
AsyncInputData.FrameOffset = LocalFrameOffset;
}
AsyncInputVR->InputData.Add(AsyncInputData);
}
ReplicatedTargetsQueueVR.Reset();
@ -919,7 +1010,9 @@ bool FRepMovementVR::GatherActorsMovement(AActor* OwningActor)
void FPhysicsReplicationAsyncVR::OnPhysicsObjectUnregistered_Internal(Chaos::FConstPhysicsObjectHandle PhysicsObject)
{
ObjectToTarget.Remove(PhysicsObject);
RemoveObjectFromReplication(PhysicsObject);
// Only clear Settings when PhysicsObject unregister (not when it stops replicating, hence why it's not baked into RemoveObjectFromReplication())
ObjectToSettings.Remove(PhysicsObject);
}
@ -991,17 +1084,17 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal()
if (Input.TargetState.Flags == ERigidBodyFlags::None)
{
// Remove replication target
ObjectToTarget.Remove(Input.PhysicsObject);
RemoveObjectFromReplication(Input.PhysicsObject);
continue;
}
if (!bRewindDataExist && Input.RepMode == EPhysicsReplicationMode::Resimulation)
{
// We don't have rewind data but an actor is set to replicate using resimulation; we need to enable rewind capture.
if (ensure(Chaos::FPBDRigidsSolver::IsNetworkPhysicsPredictionEnabled()))
if (ensure(Chaos::FPBDRigidsSolver::IsNetworkPhysicsPredictionEnabled() && RigidsSolver->IsUsingFixedDt()))
{
const int32 NumFrames = FMath::Max<int32>(1, Chaos::FPBDRigidsSolver::GetPhysicsHistoryCount());
RigidsSolver->EnableRewindCapture(NumFrames, true);
RigidsSolver->EnableRewindCapture();
}
}
@ -1018,6 +1111,41 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal()
}
}
FReplicatedPhysicsTargetAsync* FPhysicsReplicationAsyncVR::AddObjectToReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject)
{
if (ensure(PhysicsObject))
{
// Cache ParticleID in array of replicated objects
Chaos::FReadPhysicsObjectInterface_Internal Interface = Chaos::FPhysicsObjectInternalInterface::GetRead();
if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(PhysicsObject))
{
ReplicatedParticleIDs.Add(Handle->ParticleID());
}
// Add to Object-Target map
return &ObjectToTarget.Add(PhysicsObject, FReplicatedPhysicsTargetAsync());
}
return nullptr;
}
void FPhysicsReplicationAsyncVR::RemoveObjectFromReplication(Chaos::FConstPhysicsObjectHandle PhysicsObject)
{
if (PhysicsObject == nullptr)
{
return;
}
// Remove from Object-Target map
ObjectToTarget.Remove(PhysicsObject);
// Remove cached replicated ParticleID
Chaos::FReadPhysicsObjectInterface_Internal Interface = Chaos::FPhysicsObjectInternalInterface::GetRead();
if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(PhysicsObject))
{
ReplicatedParticleIDs.Remove(Handle->ParticleID());
}
}
void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input)
{
if (Input.PhysicsObject == nullptr)
@ -1025,6 +1153,12 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
return;
}
// If there is no FrameOffset set then we have not synced up physics ticks with the server yet so don't cache this data
if (Input.FrameOffset.IsSet() == false)
{
return;
}
Chaos::FPBDRigidsSolver* RigidsSolver = static_cast<Chaos::FPBDRigidsSolver*>(GetSolver());
if (RigidsSolver == nullptr)
{
@ -1041,10 +1175,10 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
if (Chaos::FGeometryParticleHandle* Handle = Interface.GetParticle(Input.PhysicsObject))
{
// Cache all target states inside RewindData
const int32 LocalFrame = Input.ServerFrame - Input.FrameOffset;
const int32 LocalFrame = Input.ServerFrame - *Input.FrameOffset;
RewindData->SetTargetStateAtFrame(*Handle, LocalFrame, Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData,
Input.TargetState.Position, Input.TargetState.Quaternion,
Input.TargetState.LinVel, Input.TargetState.AngVel, (Input.TargetState.Flags & ERigidBodyFlags::Sleeping));
Input.TargetState.LinVel, FMath::DegreesToRadians(Input.TargetState.AngVel), (Input.TargetState.Flags & ERigidBodyFlags::Sleeping));
}
}
@ -1060,7 +1194,7 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
if (bFirstTarget)
{
// First time we add a target, set previous state to current input
Target = &ObjectToTarget.Add(Input.PhysicsObject, FReplicatedPhysicsTargetAsync());
Target = AddObjectToReplication(Input.PhysicsObject);
Target->PrevPos = Input.TargetState.Position;
Target->PrevPosTarget = Input.TargetState.Position;
Target->PrevRotTarget = Input.TargetState.Quaternion;
@ -1107,6 +1241,9 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
// Cache if this target was previously allowed to be altered, before this update
const bool bPrevAllowTargetAltering = Target->bAllowTargetAltering;
// Cache if the physics frame offset has changed since last target
const bool bFrameOffsetCorrected = Target->FrameOffset != Input.FrameOffset;
// Set if the target is allowed to be altered after this update
Target->bAllowTargetAltering = !(Target->TargetState.Flags & ERigidBodyFlags::Sleeping) && !(Input.TargetState.Flags & ERigidBodyFlags::Sleeping);
@ -1130,7 +1267,7 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
Target->ReceiveFrame = CurrentFrame;
Target->TargetState = Input.TargetState;
Target->RepMode = Input.RepMode;
Target->FrameOffset = Input.FrameOffset;
Target->FrameOffset = Input.FrameOffset.IsSet() ? *Input.FrameOffset : 0;
Target->TickCount = 0;
Target->AccumulatedSleepSeconds = 0.0f;
@ -1145,6 +1282,8 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
if (CVarDrawDebugTargets->GetBool())
{
const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
// Port this?
//const FVector Offset = FVector(0.0f, 0.0f, PhysicsReplicationCVars::PredictiveInterpolationCVars::DrawDebugZOffset);
static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
Chaos::FDebugDrawQueue::GetInstance().DrawDebugBox(Input.TargetState.Position + Offset, FVector(15.0f, 15.0f, 15.0f), Input.TargetState.Quaternion, FColor::MakeRandomSeededColor(Input.ServerFrame), false, CVarNetCorrectionLifetime->GetFloat(), 0, 1.0f);
@ -1176,7 +1315,7 @@ void FPhysicsReplicationAsyncVR::UpdateAsyncTarget(const FPhysicsRepAsyncInputDa
*/
// Run target alignment if we have been allowed to alter the target during the last two target updates
if (!bFirstTarget && bPrevAllowTargetAltering && Target->bAllowTargetAltering)
if (!bFirstTarget && bPrevAllowTargetAltering && Target->bAllowTargetAltering && !bFrameOffsetCorrected)
{
static const auto CVarTargetTickAlignmentClampMultiplier = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.TargetTickAlignmentClampMultiplier"));
const int32 AdjustedAverageReceiveInterval = FMath::CeilToInt(Target->AverageReceiveInterval) * CVarTargetTickAlignmentClampMultiplier->GetInt();
@ -1257,6 +1396,7 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
for (auto Itr = ObjectToTarget.CreateIterator(); Itr; ++Itr)
{
bool bRemoveItr = true; // Remove current cached replication target unless replication logic tells us to store it for next tick
FParticleID ParticleID;
Chaos::FConstPhysicsObjectHandle& POHandle = Itr.Key();
if (FGeometryParticleHandle* Handle = Interface.GetParticle(POHandle))
@ -1266,6 +1406,8 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
if (FPBDRigidParticleHandle* RigidHandle = Handle->CastToRigidParticle())
{
ParticleID = RigidHandle->ParticleID();
// Cache custom settings for this object if there are any
FetchObjectSettings(POHandle);
@ -1290,6 +1432,7 @@ void FPhysicsReplicationAsyncVR::ApplyTargetStatesAsync(const float DeltaSeconds
if (bRemoveItr)
{
ReplicatedParticleIDs.Remove(ParticleID);
Itr.RemoveCurrent();
}
}
@ -1463,7 +1606,8 @@ bool FPhysicsReplicationAsyncVR::DefaultReplication(Chaos::FPBDRigidParticleHand
const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrectionDefault.AngularVelocityCoefficient;
static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance"));
const float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrectionDefault.MaxLinearHardSnapDistance;
float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrectionDefault.MaxLinearHardSnapDistance;
MaxLinearHardSnapDistance = SettingsCurrent.DefaultReplicationSettings.GetMaxLinearHardSnapDistance(MaxLinearHardSnapDistance);
// Get Current state
FRigidBodyState CurrentState;
@ -1582,8 +1726,11 @@ bool FPhysicsReplicationAsyncVR::DefaultReplication(Chaos::FPBDRigidParticleHand
// Too much error so just snap state here and be done with it
Target.AccumulatedErrorSeconds = 0.0f;
bRestoredState = true;
Handle->SetX(TargetPos);
Handle->SetR(TargetQuat);
// Set XRVW to hard snap dynamic object and force recalculation of friction
const bool bCorrectConnectedBodies = SettingsCurrent.DefaultReplicationSettings.GetCorrectConnectedBodies();
const bool bCorrectConnectedBodiesFriction = SettingsCurrent.DefaultReplicationSettings.GetCorrectConnectedBodiesFriction();
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, TargetPos, TargetQuat, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
Handle->SetV(NewState.LinVel);
Handle->SetW(FMath::DegreesToRadians(NewState.AngVel));
}
@ -1651,6 +1798,8 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
static const auto CVarDrawDebugTargets = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugTargets"));
if (CVarDrawDebugTargets->GetBool())
{
// Needs updated post 5.5 for DrawDebugZ CVAR
const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
const FVector StartPos = Target.TargetState.Position + Offset;
const int32 SizeMultiplier = FMath::Clamp(Target.TickCount, -4, 30);
@ -1675,15 +1824,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
if (bOkToClear && bShouldSleep && bCanSimulate)
{
RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Sleeping);
static const auto CVarSleepConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepConnectedBodies"));
if (CVarSleepConnectedBodies->GetBool())
{
RigidsSolver->GetEvolution()->ApplySleepOnConnectedParticles(Handle);
}
}
static const auto CVarSleepSecondsClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepSecondsClearTarget"));
static const auto CVarDontClearTarget = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DontClearTarget"));
// --- Should replication stop? ---
const bool bClearTarget =
(!bCanSimulate
|| (bOkToClear && bShouldSleep && Target.AccumulatedSleepSeconds >= CVarSleepSecondsClearTarget->GetFloat()) // Don't clear the target due to sleeping until the object both should sleep and is sleeping for n seconds
|| (bOkToClear && !bReplicatingPhysics))
((bOkToClear && bShouldSleep && Target.AccumulatedSleepSeconds >= CVarSleepSecondsClearTarget->GetFloat()) // Allow clearing the target due to sleeping after the object has been sleeping for n seconds
|| (bOkToClear && !bReplicatingPhysics) // If replication say it's okay to clear the target and the object shouldn't replicate physics anymore, clear the target
|| (bOkToClear && !bCanSimulate)) // If replication say it's okay to clear the target and the object can't simulate, clear the target
&& !CVarDontClearTarget->GetBool();
// --- Target Prediction ---
@ -1694,10 +1849,16 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const int32 ExtrapolationTickLimit = FMath::Max(
FMath::CeilToInt(Target.AverageReceiveInterval * CVarExtrapolationTimeMultiplier->GetFloat()), // Extrapolate time based on receive interval * multiplier
FMath::CeilToInt(CVarExtrapolationMinTime->GetFloat() / DeltaSeconds)); // At least extrapolate for N seconds
if (Target.TickCount <= ExtrapolationTickLimit)
{
FPhysicsReplicationAsyncVR::ExtrapolateTarget(Target, 1, DeltaSeconds);
}
else
{
// If we reach the extrapolation limit, disable target from being altered
Target.bAllowTargetAltering = false;
}
}
return bClearTarget;
@ -1716,26 +1877,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
// Get the rotational offset between the blended rotation target and the current rotation
const FQuat TargetRotDelta = Target.TargetState.Quaternion * Handle->GetR().Inverse();
// Convert to angle axis
// Convert to angle and axis
float Angle;
FVector Axis;
TargetRotDelta.ToAxisAndAngle(Axis, Angle);
Angle = FMath::Abs(FMath::UnwindRadians(Angle));
Angle = FMath::RadiansToDegrees(FMath::UnwindRadians(Angle));
Angle = FMath::Abs(Angle);
static const auto CVarEarlyOutAngle = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.EarlyOutAngle"));
if (Angle < FMath::DegreesToRadians(CVarEarlyOutAngle->GetFloat()))
if (Angle < CVarEarlyOutAngle->GetFloat())
{
// Early Out
return EndReplicationHelper(Target, true);
}
}
// Wake up if sleeping
if (bIsSleeping)
{
RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Dynamic);
}
static const auto CVarAverageReceiveIntervalSmoothing = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AverageReceiveIntervalSmoothing"));
// Update the AverageReceiveInterval if Target.ReceiveInterval has a valid value to update from
Target.AverageReceiveInterval = Target.ReceiveInterval == 0 ? Target.AverageReceiveInterval : FMath::Lerp(Target.AverageReceiveInterval, Target.ReceiveInterval, FMath::Clamp((1.0f / (Target.ReceiveInterval * CVarAverageReceiveIntervalSmoothing->GetFloat())), 0.0f, 1.0f));
@ -1745,13 +1901,13 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
CurrentState.Position = Handle->GetX();
CurrentState.Quaternion = Handle->GetR();
CurrentState.LinVel = Handle->GetV();
CurrentState.AngVel = Handle->GetW(); // Note: Current angular velocity is in Radians
CurrentState.AngVel = Handle->GetW(); // Radians
// NewState
const FVector TargetPos = FVector(Target.TargetState.Position);
const FQuat TargetRot = Target.TargetState.Quaternion;
const FVector TargetLinVel = FVector(Target.TargetState.LinVel);
const FVector TargetAngVel = FVector(Target.TargetState.AngVel); // Note: Target angular velocity is in Degrees
const FVector TargetAngVel = FVector(FMath::DegreesToRadians(Target.TargetState.AngVel)); // Radians
/** --- Reconciliation ---
* If target velocities are low enough, check the traveled direction and distance from previous frame and compare with replicated linear velocity.
@ -1799,9 +1955,11 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
static const auto CVarErrorAccumulationSeconds = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.ErrorAccumulationSeconds"));
static const auto CVarAlwaysHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.AlwaysHardSnap"));
const bool bHardSnap = !bCanSimulate ||
Target.AccumulatedErrorSeconds > CVarErrorAccumulationSeconds->GetFloat() ||
CVarAlwaysHardSnap->GetBool();
static const auto CVarKinematicHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.KinematicHardSnap"));
const bool bHardSnap = (!bCanSimulate && CVarKinematicHardSnap->GetBool())
|| Target.AccumulatedErrorSeconds > CVarErrorAccumulationSeconds->GetFloat()
|| CVarAlwaysHardSnap->GetBool();
if (bHardSnap)
{
@ -1815,13 +1973,13 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
}
else
{
// Set XPRQVW to hard snap dynamic object
Handle->SetX(Target.PrevPosTarget);
Handle->SetP(Target.PrevPosTarget);
Handle->SetR(Target.PrevRotTarget);
Handle->SetQ(Target.PrevRotTarget);
Handle->SetV(Target.TargetState.LinVel);
Handle->SetW(FMath::DegreesToRadians(Target.TargetState.AngVel));
// Set XRVW to hard snap dynamic object and force recalculation of friction
const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, Target.PrevPosTarget, Target.PrevRotTarget, bCorrectConnectedBodies, /*bInRecalculateFrictionOnConnectedBodies*/ true, ReplicatedParticleIDs);
Handle->SetV(TargetLinVel);
Handle->SetW(TargetAngVel);
}
// Cache data for next replication
@ -1830,8 +1988,72 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
// End replication and go to sleep if that's requested
return EndReplicationHelper(Target, true);
}
else if (Handle->IsKinematic()) // Smooth Kinematic Replication
{
static const auto CVarKinematicPrediction = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.KinematicPrediction"));
const bool bKinematicPrediction = CVarKinematicPrediction->GetBool();
const float InterpolationTicks = FMath::CeilToInt(Target.AverageReceiveInterval) - (RigidsSolver->GetCurrentFrame() - Target.ReceiveFrame);
if ((bKinematicPrediction && Target.bAllowTargetAltering) || InterpolationTicks > 0)
{
/* Calculate the Lerp value for a smooth interpolation
* ------------------------------------------------------------------------------
* bKinematicPrediction is True :: Interpolate towards the target that gets forward predicted each tick
* 1 / 4 = 0.25 = 25% interpolation each time (if AverageReceiveInterval is 4)
* ------------------------------------------------------------------------------
* bKinematicPrediction is False :: Interpolate from current position to the static source for the current target, we need to cover the same amount of distance but from a decaying distance
* | ---> | ------------------ |
* 0% 25% 100% (1 / 4 = 0.25)
* | ---> | ----------- |
* 0% 33% 100% (1 / 3 = 0.33)
* | ---> | ---- |
* 0% 50% 100% (1 / 2 = 0.5)
* | ---> |
* 0% 100% (1 / 1 = 1.0)
* ------------------------------------------------------------------------------
*/
const float Lerp = 1.f / (bKinematicPrediction ? Target.AverageReceiveInterval : InterpolationTicks);
// Interpolate position and rotation from current position towards target position based on either predicted target or source target
const FVector KinTargetPos = FMath::Lerp(CurrentState.Position,
(bKinematicPrediction ? Target.TargetState.Position : Target.PrevPosTarget),
Lerp);
const FQuat KinTargetRot = FQuat::Slerp(CurrentState.Quaternion,
(bKinematicPrediction ? Target.TargetState.Quaternion : Target.PrevRotTarget),
Lerp);
// Apply kinematic target
const Chaos::FKinematicTarget KinTarget = Chaos::FKinematicTarget::MakePositionTarget(KinTargetPos, KinTargetRot); // Uses EKinematicTargetMode::Position
RigidsSolver->GetEvolution()->SetParticleKinematicTarget(Handle, KinTarget);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
//static const auto CVarDrawDebugTargets = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugTargets"));
if (CVarDrawDebugTargets->GetBool())
{
static const auto CVarDrawDebugZOffset = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugZOffset"));
const FVector Offset = FVector(0.0f, 0.0f, CVarDrawDebugZOffset->GetFloat());
const FVector Pos = KinTargetPos + Offset;
const int32 SizeMultiplier = FMath::Clamp(Target.TickCount, -4, 30);
static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
Chaos::FDebugDrawQueue::GetInstance().DrawDebugSphere(Pos, 3.0f + SizeMultiplier * 0.75f, 8, FColor::MakeRandomSeededColor(Target.ServerFrame), false, CVarNetCorrectionLifetime->GetFloat(), 0, 1.0f);
}
#endif
}
else
{
// End replication and allow to clear target
return EndReplicationHelper(Target, true);
}
}
else // Velocity-based Replication
{
// Wake up if sleeping
if (bIsSleeping)
{
RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Dynamic);
}
// Calculate interpolation time based on current average receive rate
const float AverageReceiveIntervalSeconds = Target.AverageReceiveInterval * DeltaSeconds;
const float InterpolationTime = AverageReceiveIntervalSeconds * SettingsCurrent.PredictiveInterpolationSettings.GetPosInterpolationTimeMultiplier();
@ -1843,6 +2065,7 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const float RotCorrectionTime = FMath::Max(SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeBase() + AverageReceiveIntervalSeconds + RTT * SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMultiplier(),
DeltaSeconds + SettingsCurrent.PredictiveInterpolationSettings.GetRotCorrectionTimeMin());
FVector CorrectionX = CurrentState.Position;
if ((bXCanEarlyOut && SettingsCurrent.PredictiveInterpolationSettings.GetSkipVelocityRepOnPosEarlyOut()) == false)
{ // --- Velocity Replication ---
@ -1853,10 +2076,10 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const FVector LinVelDiff = -CurrentState.LinVel + TargetLinVel;
// Calculate velocity blend amount for this tick as an alpha value
const float Alpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
const float VelocityAlpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
FVector RepLinVel;
static const auto CVarPosCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.PosCorrectionAsVelocity"));
static const auto CVarPosCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.CorrectionAsVelocity"));
if (CVarPosCorrectionAsVelocity->GetBool())
{
// Convert PosDiff to a velocity
@ -1866,20 +2089,21 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
const FVector BlendedTargetVelocity = LinVelDiff + PosDiffVelocity;
// Add BlendedTargetVelocity onto current velocity
RepLinVel = CurrentState.LinVel + (BlendedTargetVelocity * Alpha);
RepLinVel = CurrentState.LinVel + (BlendedTargetVelocity * VelocityAlpha); // Same as (BlendedTargetVelocity / InterpolationTime) * DeltaSeconds
}
else // Positional correction as position shift
else // Positional correction as transform shift
{
// Calculate the PosDiff amount to correct this tick
const FVector PosDiffVelocityDelta = PosDiff * (DeltaSeconds / PosCorrectionTime); // Same as (PosDiff / PosCorrectionTime) * DeltaSeconds
// Add velocity diff onto current velocity
RepLinVel = CurrentState.LinVel + (LinVelDiff * Alpha);
RepLinVel = CurrentState.LinVel + (LinVelDiff * VelocityAlpha); // Same as (LinVelDiff / InterpolationTime) * DeltaSeconds
// Calculate correction blend amount for this tick as an alpha value
const float CorrectionAlpha = FMath::Clamp(DeltaSeconds / PosCorrectionTime, 0.0f, 1.0f);
// Apply positional correction
Handle->SetX(Handle->GetX() + PosDiffVelocityDelta);
// Calculate the PosDiff amount to correct this tick
const FVector PosDiffVelocityDelta = PosDiff * CorrectionAlpha; // Same as (PosDiff / PosCorrectionTime) * DeltaSeconds
// The new position after correction
CorrectionX = Handle->GetX() + PosDiffVelocityDelta;
}
// Apply velocity replication
@ -1890,7 +2114,8 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
static const auto CVarDrawDebugVectors = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugVectors"));
if (CVarDrawDebugVectors->GetBool())
{
const FVector Offset = FVector(0.0f, 0.0f, 50.0f);
static const auto CVarDrawDebugZOffset = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.DrawDebugZOffset"));
const FVector Offset = FVector(0.0f, 0.0f, CVarDrawDebugZOffset->GetFloat());
const FVector OffsetAdd = FVector(0.0f, 0.0f, 10.0f);
const FVector StartPos = TargetPos + Offset;
FVector Direction = TargetLinVel;
@ -1908,37 +2133,63 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
Target.PrevLinVel = FVector(RepLinVel);
}
FQuat CorrectionR = CurrentState.Quaternion;
{ // --- Angular Velocity Replication ---
/* Todo, Implement InterpolationTime */
// Extrapolate current rotation along current angular velocity to see where we would end up
float CurAngVelSize;
FVector CurAngVelAxis;
CurrentState.AngVel.FVector::ToDirectionAndLength(CurAngVelAxis, CurAngVelSize);
const FQuat CurRotExtrapDelta = FQuat(CurAngVelAxis, CurAngVelSize * DeltaSeconds);
const FQuat CurRotExtrap = CurRotExtrapDelta * CurrentState.Quaternion;
// Get AngVelDiff by adding inverted CurrentState.AngVel to TargetAngVel
const FVector AngVelDiff = -CurrentState.AngVel + TargetAngVel;
// Slerp from the extrapolated current rotation towards the target rotation
// This takes current angular velocity into account
const float RotCorrectionAmount = FMath::Clamp(DeltaSeconds / RotCorrectionTime, 0.0f, 1.0f);
const FQuat TargetRotBlended = FQuat::Slerp(CurRotExtrap, TargetRot, RotCorrectionAmount);
// Calculate velocity blend amount for this tick as an alpha value
const float VelocityAlpha = FMath::Clamp(DeltaSeconds / InterpolationTime, 0.0f, 1.0f);
// Get the rotational offset between the blended rotation target and the current rotation
const FQuat TargetRotDelta = TargetRotBlended * CurrentState.Quaternion.Inverse();
FVector RepAngVel;
static const auto CVarCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.CorrectionAsVelocity"));
if (CVarCorrectionAsVelocity->GetBool())
{
// Get RotDiff
const FQuat RotDiff = TargetRot * CurrentState.Quaternion.Inverse();
// Convert the rotational delta to angular velocity
float WAngle;
FVector WAxis;
TargetRotDelta.ToAxisAndAngle(WAxis, WAngle);
const FVector TargetRotDeltaBlend = FVector(WAxis * (WAngle / (DeltaSeconds * SettingsCurrent.PredictiveInterpolationSettings.GetRotInterpolationTimeMultiplier())));
const FVector RepAngVel = FMath::DegreesToRadians(TargetAngVel) + TargetRotDeltaBlend;
// Convert RotDiff to a velocity
float WAngle;
FVector WAxis;
RotDiff.ToAxisAndAngle(WAxis, WAngle);
WAngle = FMath::UnwindRadians(WAngle);
const FVector RotDiffVelocity = FVector(WAxis * (WAngle / RotCorrectionTime));
// Add RotDiffVelocity to AngVelDiff to get BlendedTargetVelocity
const FVector BlendedTargetVelocity = AngVelDiff + RotDiffVelocity;
// Add BlendedTargetVelocity to CurrentState.AngVel
RepAngVel = CurrentState.AngVel + (BlendedTargetVelocity * VelocityAlpha); // Same as (BlendedTargetVelocity / InterpolationTime) * DeltaSeconds
}
else // Positional correction as transform shift
{
// Add velocity diff onto current velocity
RepAngVel = CurrentState.AngVel + (AngVelDiff * VelocityAlpha); // Same as (AngVelDiff / InterpolationTime) * DeltaSeconds
// Calculate correction blend amount for this tick as an alpha value
const float CorrectionAlpha = FMath::Clamp(DeltaSeconds / RotCorrectionTime, 0.0f, 1.0f);
// The new position after correction
CorrectionR = FQuat::Slerp(Handle->GetR(), TargetRot, CorrectionAlpha);
}
// Apply velocity replication
Handle->SetW(RepAngVel);
}
// Cache data for next replication
Target.PrevPos = FVector(CurrentState.Position);
// Apply correction as a transform shift
static const auto CVarCorrectionAsVelocity = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.CorrectionAsVelocity"));
if (!CVarCorrectionAsVelocity->GetBool())
{
const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
const bool bCorrectConnectedBodiesFriction = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodiesFriction();
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectionX, CorrectionR, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
}
if (bSoftSnap)
{
const FVector SoftSnapPos = FMath::Lerp(FVector(CurrentState.Position),
@ -1949,10 +2200,10 @@ bool FPhysicsReplicationAsyncVR::PredictiveInterpolation(Chaos::FPBDRigidParticl
SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapToSource() ? Target.PrevRotTarget : Target.TargetState.Quaternion,
FMath::Clamp(SettingsCurrent.PredictiveInterpolationSettings.GetSoftSnapRotStrength(), 0.0f, 1.0f));
Handle->SetX(SoftSnapPos);
Handle->SetP(SoftSnapPos);
Handle->SetR(SoftSnapRot);
Handle->SetQ(SoftSnapRot);
// Apply correction as a transform shift
const bool bCorrectConnectedBodies = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodies();
const bool bCorrectConnectedBodiesFriction = SettingsCurrent.PredictiveInterpolationSettings.GetCorrectConnectedBodiesFriction();
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, SoftSnapPos, SoftSnapRot, bCorrectConnectedBodies, bCorrectConnectedBodiesFriction, ReplicatedParticleIDs);
}
}
@ -2003,17 +2254,48 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
return true;
}
const bool bShouldSleep = (Target.TargetState.Flags & ERigidBodyFlags::Sleeping) != 0;
bool bClearTarget = true;
static constexpr Chaos::FFrameAndPhase::EParticleHistoryPhase RewindPhase = Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData;
const float ResimErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorThreshold(Chaos::FPhysicsSolverBase::ResimulationErrorThreshold());
// Get state from locally cached history for frame corresponding to received data
const Chaos::FGeometryParticleState PastState = RewindData->GetPastStateAtFrame(*Handle, LocalFrame, RewindPhase);
const FVector ErrorOffset = (Target.TargetState.Position - PastState.GetX());
const float ErrorDistance = ErrorOffset.Size();
const bool ShouldTriggerResim = ErrorDistance >= ResimErrorThreshold;
// Check which comparisons to perform to trigger resimulation from
const bool bCompareX = Chaos::FPhysicsSolverBase::GetResimulationErrorPositionThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorPositionThreshold;
const bool bCompareR = Chaos::FPhysicsSolverBase::GetResimulationErrorRotationThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorRotationThreshold;
const bool bCompareV = Chaos::FPhysicsSolverBase::GetResimulationErrorLinearVelocityThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorLinearVelocityThreshold;
const bool bCompareW = Chaos::FPhysicsSolverBase::GetResimulationErrorAngularVelocityThresholdEnabled() || SettingsCurrent.ResimulationSettings.bOverrideResimulationErrorAngularVelocityThreshold;
bool bShouldTriggerResim = false;
// Check for positional discrepancy in Distance between client and server
if (bCompareX)
{
const float ResimPositionErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorPositionThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorPositionThreshold());
bShouldTriggerResim = Chaos::FRewindData::CheckVectorThreshold(Target.TargetState.Position, PastState.GetX(), ResimPositionErrorThreshold);
}
// Check for linear velocity discrepancy in Distance / s between client and server
if (!bShouldTriggerResim && bCompareV)
{
const float ResimLinVelocityErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorLinearVelocityThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorLinearVelocityThreshold());
bShouldTriggerResim = Chaos::FRewindData::CheckVectorThreshold(Target.TargetState.LinVel, PastState.GetV(), ResimLinVelocityErrorThreshold);
}
// Check for angular velocity discrepancy in Degrees / s between client and server
if (!bShouldTriggerResim && bCompareW)
{
const float ResimAngVelocityErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorAngularVelocityThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorAngularVelocityThreshold());
bShouldTriggerResim = Chaos::FRewindData::CheckVectorThreshold(FMath::DegreesToRadians(Target.TargetState.AngVel), PastState.GetW(), ResimAngVelocityErrorThreshold);
}
// Check for rotational discrepancy in Degrees between client and server
if (!bShouldTriggerResim && bCompareR)
{
const float ResimRotationErrorThreshold = SettingsCurrent.ResimulationSettings.GetResimulationErrorRotationThreshold(Chaos::FPhysicsSolverBase::GetResimulationErrorRotationThreshold());
bShouldTriggerResim = Chaos::FRewindData::CheckQuaternionThreshold(Target.TargetState.Quaternion, PastState.GetR(), ResimRotationErrorThreshold);
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
@ -2021,7 +2303,7 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
if (Chaos::FPhysicsSolverBase::CanDebugNetworkPhysicsPrediction())
{
UE_LOG(LogTemp, Log, TEXT("Apply Rigid body state at local frame %d with offset = %d"), LocalFrame, Target.FrameOffset);
UE_LOG(LogTemp, Log, TEXT("Particle Position Error = %f | Should Trigger Resim = %s | Server Frame = %d | Client Frame = %d"), ErrorDistance, (ShouldTriggerResim ? TEXT("True") : TEXT("False")), Target.ServerFrame, LocalFrame);
UE_LOG(LogTemp, Log, TEXT("Should Trigger Resim = %s | Server Frame = %d | Client Frame = %d"), (bShouldTriggerResim ? TEXT("True") : TEXT("False")), Target.ServerFrame, LocalFrame);
UE_LOG(LogTemp, Log, TEXT("Particle Target Position = %s | Current Position = %s"), *Target.TargetState.Position.ToString(), *PastState.GetX().ToString());
UE_LOG(LogTemp, Log, TEXT("Particle Target Velocity = %s | Current Velocity = %s"), *Target.TargetState.LinVel.ToString(), *PastState.GetV().ToString());
UE_LOG(LogTemp, Log, TEXT("Particle Target Quaternion = %s | Current Quaternion = %s"), *Target.TargetState.Quaternion.ToString(), *PastState.GetR().ToString());
@ -2032,7 +2314,7 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
if (CVarResimDrawDebug->GetBool())
{
static constexpr float BoxSize = 5.0f;
const float ColorLerp = ShouldTriggerResim ? 1.0f : 0.0f;
const float ColorLerp = bShouldTriggerResim ? 1.0f : 0.0f;
const FColor DebugColor = FLinearColor::LerpUsingHSV(FLinearColor::Green, FLinearColor::Red, ColorLerp).ToFColor(false);
static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
@ -2042,57 +2324,58 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
}
#endif
if (LocalFrame > RewindData->GetBlockedResimFrame())
// Wake up if is sleeping and should not sleep
if (Handle->IsSleeping() && !bShouldSleep)
{
if (ShouldTriggerResim && Target.TickCount == 0)
RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Dynamic);
}
if (bShouldTriggerResim && Target.TickCount == 0 && LocalFrame > RewindData->GetBlockedResimFrame())
{
// Trigger resimulation
RigidsSolver->GetEvolution()->GetIslandManager().SetParticleResimFrame(Handle, LocalFrame);
int32 ResimFrame = RewindData->GetResimFrame();
ResimFrame = (ResimFrame == INDEX_NONE) ? LocalFrame : FMath::Min(ResimFrame, LocalFrame);
RewindData->SetResimFrame(ResimFrame);
}
else if (SettingsCurrent.ResimulationSettings.GetRuntimeCorrectionEnabled())
{
const int32 NumPredictedFrames = RigidsSolver->GetCurrentFrame() - LocalFrame - Target.TickCount;
if (Target.TickCount <= NumPredictedFrames && NumPredictedFrames > 0)
{
// Trigger resimulation
RigidsSolver->GetEvolution()->GetIslandManager().SetParticleResimFrame(Handle, LocalFrame);
const FVector ErrorOffset = (Target.TargetState.Position - PastState.GetX());
int32 ResimFrame = RewindData->GetResimFrame();
ResimFrame = (ResimFrame == INDEX_NONE) ? LocalFrame : FMath::Min(ResimFrame, LocalFrame);
RewindData->SetResimFrame(ResimFrame);
}
else if (SettingsCurrent.ResimulationSettings.GetRuntimeCorrectionEnabled())
// Positional Correction
const float CorrectionAmountX = SettingsCurrent.ResimulationSettings.GetPosStabilityMultiplier() / NumPredictedFrames;
const FVector PosDiffCorrection = ErrorOffset * CorrectionAmountX; // Same result as (ErrorOffset / NumPredictedFrames) * PosStabilityMultiplier
const FVector CorrectedX = Handle->GetX() + PosDiffCorrection;
{
const int32 NumPredictedFrames = RigidsSolver->GetCurrentFrame() - LocalFrame - Target.TickCount;
// Rotational Correction
const float CorrectionAmountR = SettingsCurrent.ResimulationSettings.GetRotStabilityMultiplier() / NumPredictedFrames;
const FQuat DeltaQuat = PastState.GetR().Inverse() * Target.TargetState.Quaternion;
const FQuat TargetCorrectionR = Handle->GetR() * DeltaQuat;
const FQuat CorrectedR = FQuat::Slerp(Handle->GetR(), TargetCorrectionR, CorrectionAmountR);
if (Target.TickCount <= NumPredictedFrames && NumPredictedFrames > 0)
if (SettingsCurrent.ResimulationSettings.GetRuntimeVelocityCorrectionEnabled())
{
// Positional Correction
const float CorrectionAmountX = SettingsCurrent.ResimulationSettings.GetPosStabilityMultiplier() / NumPredictedFrames;
const FVector PosDiffCorrection = ErrorOffset * CorrectionAmountX; // Same result as (ErrorOffset / NumPredictedFrames) * PosStabilityMultiplier
const FVector CorrectedX = Handle->GetX() + PosDiffCorrection;
// Linear Velocity Correction
const FVector LinVelDiff = Target.TargetState.LinVel - PastState.GetV(); // Velocity vector that the server covers but the client doesn't
const float CorrectionAmountV = SettingsCurrent.ResimulationSettings.GetVelStabilityMultiplier() / NumPredictedFrames;
const FVector VelCorrection = LinVelDiff * CorrectionAmountV; // Same result as (LinVelDiff / NumPredictedFrames) * VelStabilityMultiplier
const FVector CorrectedV = Handle->GetV() + VelCorrection;
// Angular Velocity Correction
const FVector AngVelDiff = FMath::DegreesToRadians(Target.TargetState.AngVel) - PastState.GetW(); // Angular velocity vector that the server covers but the client doesn't
const float CorrectionAmountW = SettingsCurrent.ResimulationSettings.GetAngVelStabilityMultiplier() / NumPredictedFrames;
const FVector AngVelCorrection = AngVelDiff * CorrectionAmountW; // Same result as (AngVelDiff / NumPredictedFrames) * VelStabilityMultiplier
const FVector CorrectedW = Handle->GetW() + AngVelCorrection;
// Rotational Correction
const float CorrectionAmountR = SettingsCurrent.ResimulationSettings.GetRotStabilityMultiplier() / NumPredictedFrames;
const FQuat DeltaQuat = PastState.GetR().Inverse() * Target.TargetState.Quaternion;
const FQuat TargetCorrectionR = Handle->GetR() * DeltaQuat;
const FQuat CorrectedR = FQuat::Slerp(Handle->GetR(), TargetCorrectionR, CorrectionAmountR);
if (SettingsCurrent.ResimulationSettings.GetRuntimeVelocityCorrectionEnabled())
{
// Linear Velocity Correction
const FVector LinVelDiff = Target.TargetState.LinVel - PastState.GetV(); // Velocity vector that the server covers but the client doesn't
const float CorrectionAmountV = SettingsCurrent.ResimulationSettings.GetVelStabilityMultiplier() / NumPredictedFrames;
const FVector VelCorrection = LinVelDiff * CorrectionAmountV; // Same result as (LinVelDiff / NumPredictedFrames) * VelStabilityMultiplier
const FVector CorrectedV = Handle->GetV() + VelCorrection;
// Angular Velocity Correction
const FVector AngVelDiff = Target.TargetState.AngVel - PastState.GetW(); // Angular velocity vector that the server covers but the client doesn't
const float CorrectionAmountW = SettingsCurrent.ResimulationSettings.GetAngVelStabilityMultiplier() / NumPredictedFrames;
const FVector AngVelCorrection = AngVelDiff * CorrectionAmountW; // Same result as (AngVelDiff / NumPredictedFrames) * VelStabilityMultiplier
const FVector CorrectedW = Handle->GetW() + AngVelCorrection;
// Apply correction to velocities
Handle->SetV(CorrectedV);
Handle->SetW(CorrectedW);
}
// Apply correction to velocities
Handle->SetV(CorrectedV);
Handle->SetW(CorrectedW);
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
@ -2103,14 +2386,27 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
}
#endif
// Apply correction to position and rotation
static const auto CVarResimRuntimeCorrectConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.Resim.RuntimeCorrectConnectedBodies"));
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectedX, CorrectedR, CVarResimRuntimeCorrectConnectedBodies->GetBool());
}
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, CorrectedX, CorrectedR, SettingsCurrent.ResimulationSettings.GetRuntimeCorrectConnectedBodies(), /*bInRecalculateFrictionOnConnectedBodies*/true, ReplicatedParticleIDs);
}
// Keep target for NumPredictedFrames time to perform runtime corrections with until a new target is received
bClearTarget = Target.TickCount >= NumPredictedFrames;
// Keep target for NumPredictedFrames time to perform runtime corrections with until a new target is received
bClearTarget = Target.TickCount >= NumPredictedFrames;
}
// Set sleep state if we are about to clear the target from memory and the target is set to sleep
if (bClearTarget && bShouldSleep)
{
// Snap object into correct state, it should already be at that state or very close to it
RigidsSolver->GetEvolution()->ApplyParticleTransformCorrection(Handle, Target.TargetState.Position, Target.TargetState.Quaternion, /*bApplyToConnectedBodies*/true, /*bInRecalculateFrictionOnConnectedBodies*/true, ReplicatedParticleIDs);
RigidsSolver->GetEvolution()->SetParticleObjectState(Handle, Chaos::EObjectStateType::Sleeping);
static const auto CVarSleepConnectedBodies = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.PredictiveInterpolation.SleepConnectedBodies"));
if (CVarSleepConnectedBodies->GetBool())
{
RigidsSolver->GetEvolution()->ApplySleepOnConnectedParticles(Handle);
}
}
return bClearTarget;
}

View file

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

View file

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

View file

@ -103,7 +103,7 @@ AGrippableStaticMeshActor::AGrippableStaticMeshActor(const FObjectInitializer& O
// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration
// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
MinNetUpdateFrequency = 30.0f;
SetMinNetUpdateFrequency(30.0f);
}
void AGrippableStaticMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
@ -203,13 +203,23 @@ void AGrippableStaticMeshActor::GatherCurrentMovement()
bool bFoundInCache = false;
UWorld* World = GetWorld();
const bool bShouldUsePhysicsReplicationCache = GetPhysicsReplicationMode() != EPhysicsReplicationMode::Default;
int ServerFrame = 0;
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
if (bShouldUsePhysicsReplicationCache)
{
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
{
RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
bFoundInCache = true;
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, /*OUT*/ServerFrame))
{
if (RepMovement.ServerFrame != ServerFrame)
{
RepMovement.FillFrom(*FoundState, this, ServerFrame);
bWasRepMovementModified = true;
}
bFoundInCache = true;
}
}
}
@ -243,20 +253,20 @@ void AGrippableStaticMeshActor::GatherCurrentMovement()
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
#if UE_WITH_IRIS
// If RepPhysics has changed value then notify the ReplicationSystem
if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
{
UpdateReplicatePhysicsCondition();
}
#endif // UE_WITH_IRIS
}
}
}
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
#if UE_WITH_IRIS
// If RepPhysics has changed value then notify the ReplicationSystem
if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
{
UpdateReplicatePhysicsCondition();
}
#endif // UE_WITH_IRIS
}
else if (RootComponent != nullptr)
{

View file

@ -272,7 +272,7 @@ void UVRDialComponent::OnGripRelease_Implementation(UGripMotionControllerCompone
float AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle));
float TargetSnap = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement));
if (FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
if (FMath::Abs(/*FRotator::ClampAxis(*/CurRotBackEnd/*)*/ - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{
if (AngleOffsetCheck >= SnapAngleThreshold)//FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{
@ -592,7 +592,7 @@ void UVRDialComponent::AddDialAngle(float DialAngleDelta, bool bCallEvents, bool
float AngleOffsetCheck = FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - FRotator::ClampAxis(LastSnapAngle));
float TargetSnap = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement));
if (FMath::Abs(FRotator::ClampAxis(CurRotBackEnd) - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
if (FMath::Abs(/*FRotator::ClampAxis(*/CurRotBackEnd/*)*/ - TargetSnap) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{
if (AngleOffsetCheck >= SnapAngleThreshold)//FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{

View file

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

View file

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

View file

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

View file

@ -114,7 +114,7 @@ FORCEINLINE_DEBUGGABLE bool CheckIsTargetInSightPie(const FPerceptionListener& L
const FAISightTargetVR::FTargetId FAISightTargetVR::InvalidTargetId = FAISystem::InvalidUnsignedID;
FAISightTargetVR::FAISightTargetVR(AActor* InTarget, FGenericTeamId InTeamId)
: Target(InTarget), SightTargetInterface(nullptr), TeamId(InTeamId)
: Target(InTarget), TeamId(InTeamId)
{
if (InTarget)
{
@ -587,7 +587,7 @@ UAISense_Sight::EVisibilityResult UAISense_Sight_VR::ComputeVisibility(UWorld* W
return UAISense_Sight::EVisibilityResult::NotVisible;
}
if (Target.SightTargetInterface != nullptr)
if (IAISightTargetInterface* SightTargetInterface = Target.WeakSightTargetInterface.Get())
{
const bool bWasVisible = SightQuery.GetLastResult();
@ -620,7 +620,7 @@ UAISense_Sight::EVisibilityResult UAISense_Sight_VR::ComputeVisibility(UWorld* W
Context.IgnoreActor = ListenerActor;
Context.bWasVisible = &bWasVisible;
const UAISense_Sight::EVisibilityResult Result = Target.SightTargetInterface->CanBeSeenFrom(Context, OutSeenLocation, OutNumberOfLoSChecksPerformed, OutNumberOfAsyncLosCheckRequested, OutStimulusStrength, &SightQuery.UserData, &OnPendingCanBeSeenQueryProcessedDelegate);
const UAISense_Sight::EVisibilityResult Result = SightTargetInterface->CanBeSeenFrom(Context, OutSeenLocation, OutNumberOfLoSChecksPerformed, OutNumberOfAsyncLosCheckRequested, OutStimulusStrength, &SightQuery.UserData, &OnPendingCanBeSeenQueryProcessedDelegate);
if (Result == UAISense_Sight::EVisibilityResult::Pending)
{
// we need to clear the trace info value in order to avoid interfering with the engine processed asynchronous queries
@ -842,38 +842,32 @@ void UAISense_Sight_VR::UnregisterSource(AActor& SourceActor)
{
AActor* TargetActor = AsTarget.Target.Get();
if (TargetActor)
{
// notify all interested observers that this source is no longer
// visible
AIPerception::FListenerMap& ListenersMap = *GetListeners();
auto RemoveQuery = [this, &ListenersMap, &AsTargetId, &TargetActor](TArray<FAISightQueryVR>& SightQueries, const int32 QueryIndex)->EReverseForEachResult
// notify all interested observers that this source is no longer
// visible
AIPerception::FListenerMap& ListenersMap = *GetListeners();
auto RemoveQuery = [this, &ListenersMap, &AsTargetId, &TargetActor](TArray<FAISightQueryVR>& SightQueries, const int32 QueryIndex)->EReverseForEachResult
{
FAISightQueryVR* SightQuery = &SightQueries[QueryIndex];
if (SightQuery->TargetId == AsTargetId)
{
if (SightQuery->GetLastResult())
if (SightQuery->GetLastResult() && TargetActor)
{
FPerceptionListener& Listener = ListenersMap[SightQuery->ObserverId];
ensure(Listener.Listener.IsValid());
Listener.RegisterStimulus(TargetActor, FAIStimulus(*this, 0.f, SightQuery->LastSeenLocation, Listener.CachedLocation, FAIStimulus::SensingFailed));
}
SightQueries.RemoveAtSwap(QueryIndex, 1, EAllowShrinking::No);
SightQueries.RemoveAtSwap(QueryIndex, EAllowShrinking::No);
return EReverseForEachResult::Modified;
}
return EReverseForEachResult::UnTouched;
};
ReverseForEach(SightQueriesInRange, RemoveQuery);
if (ReverseForEach(SightQueriesOutOfRange, RemoveQuery) == EReverseForEachResult::Modified)
{
bSightQueriesOutOfRangeDirty = true;
}
ReverseForEach(SightQueriesPending, RemoveQuery);
ReverseForEach(SightQueriesInRange, RemoveQuery);
if (ReverseForEach(SightQueriesOutOfRange, RemoveQuery) == EReverseForEachResult::Modified)
{
bSightQueriesOutOfRangeDirty = true;
}
ReverseForEach(SightQueriesPending, RemoveQuery);
}
}
@ -883,20 +877,24 @@ bool UAISense_Sight_VR::RegisterTarget(AActor& TargetActor, const TFunction<void
FAISightTargetVR* SightTarget = ObservedTargets.Find(TargetActor.GetUniqueID());
if (SightTarget != nullptr && SightTarget->GetTargetActor() != &TargetActor)
// Check if the target is recycled OR new
if (SightTarget == nullptr || SightTarget->GetTargetActor() != &TargetActor)
{
// this means given unique ID has already been recycled.
FAISightTargetVR NewSightTarget(&TargetActor);
SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget));
SightTarget->SightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
}
else if (SightTarget == nullptr)
{
FAISightTargetVR NewSightTarget(&TargetActor);
SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget));
SightTarget->SightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
// we're looking at components first and only if nothing is found we proceed to check
// if the TargetActor implements IAISightTargetInterface. The advantage of doing it in
// this order is that you can have components override the original Actor's implementation
if (IAISightTargetInterface* InterfaceComponent = TargetActor.FindComponentByInterface<IAISightTargetInterface>())
{
SightTarget->WeakSightTargetInterface = InterfaceComponent;
}
else
{
SightTarget->WeakSightTargetInterface = Cast<IAISightTargetInterface>(&TargetActor);
}
}
// set/update data

View file

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

View file

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

View file

@ -1645,7 +1645,7 @@ bool RLE_Funcs::RLEEncodeBuffer(DataType* BufferToEncode, uint32 EncodeLength, T
// Resize the out array to fit compressed contents
uint32 Wrote = loc - EncodedLine->GetData();
EncodedLine->RemoveAt(Wrote, EncodedLine->Num() - Wrote, true);
EncodedLine->RemoveAt(Wrote, EncodedLine->Num() - Wrote, EAllowShrinking::Yes);
// If the compression performed worse than the original file size, throw the results array and use the original instead.
// This will almost never happen with voxels but can so should be accounted for.

View file

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

View file

@ -15,6 +15,7 @@
#include "Net/UnrealNetwork.h"
#include "XRMotionControllerBase.h"
#include "NavFilters/NavigationQueryFilter.h"
#include "Misc/EngineNetworkCustomVersion.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
#if WITH_PUSH_MODEL
@ -142,7 +143,7 @@ AVRBaseCharacter::AVRBaseCharacter(const FObjectInitializer& ObjectInitializer)
// Setting a minimum of every frame for replication consideration (UT uses this value for characters and projectiles).
// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
MinNetUpdateFrequency = 100.0f;
SetMinNetUpdateFrequency(100.0f);
// This is for smooth turning, we have more of a use for this than FPS characters do
// Due to roll/pitch almost never being off 0 for VR the cost is just one byte so i'm fine defaulting it here
@ -434,6 +435,11 @@ void AVRBaseCharacter::OnRep_ReplicatedMovement()
ReppedMovement.Location = ReplicatedMovementVR.Location;
ReppedMovement.Rotation = ReplicatedMovementVR.Rotation;
ReppedMovement.ServerFrame = ReplicatedMovementVR.ServerFrame;
ReppedMovement.ServerPhysicsHandle = ReplicatedMovementVR.ServerPhysicsHandle;
ReppedMovement.bRepAcceleration = ReplicatedMovementVR.bRepAcceleration;
ReppedMovement.Acceleration = ReplicatedMovementVR.Acceleration;
Super::OnRep_ReplicatedMovement();
if (!IsLocallyControlled())
@ -469,8 +475,14 @@ void AVRBaseCharacter::GatherCurrentMovement()
ReplicatedMovementVR.LinearVelocity = ReppedMovement.LinearVelocity;
ReplicatedMovementVR.Location = ReppedMovement.Location;
ReplicatedMovementVR.Rotation = ReppedMovement.Rotation;
ReplicatedMovementVR.ServerFrame = ReppedMovement.ServerFrame;
ReplicatedMovementVR.ServerPhysicsHandle = ReppedMovement.ServerPhysicsHandle;
ReplicatedMovementVR.bRepAcceleration = ReppedMovement.bRepAcceleration;
ReplicatedMovementVR.Acceleration = ReppedMovement.Acceleration;
ReplicatedMovementVR.bJustTeleported = bFlagTeleported;
ReplicatedMovementVR.bJustTeleportedGrips = bFlagTeleportedGrips;
bFlagTeleported = false;
bFlagTeleportedGrips = false;
ReplicatedMovementVR.bPausedTracking = bTrackingPaused;
@ -1001,7 +1013,7 @@ FVector AVRBaseCharacter::SetActorLocationAndRotationVR(FVector NewLoc, FRotator
FVector AVRBaseCharacter::SetActorLocationVR(FVector NewLoc, bool bTeleport, bool bSetCapsuleLocation)
{
FVector NewLocation;
FRotator NewRotation;
//FRotator NewRotation;
FVector PivotOffsetVal = (bSetCapsuleLocation ? GetVRLocation_Inline() : GetProjectedVRLocation()) - GetActorLocation();
PivotOffsetVal.Z = 0.0f;
@ -1244,3 +1256,102 @@ void AVRBaseCharacter::SetVRReplicateCapsuleHeight(bool bNewVRReplicateCapsuleHe
MARK_PROPERTY_DIRTY_FROM_NAME(AVRBaseCharacter, VRReplicateCapsuleHeight, this);
#endif
}
bool FRepMovementVRCharacter::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
Ar.UsingCustomVersion(FEngineNetworkCustomVersion::Guid);
FRepMovement BaseSettings = Owner ? Owner->GetReplicatedMovement() : FRepMovement();
// pack bitfield with flags
const bool bServerFrameAndHandleSupported = Ar.EngineNetVer() >= FEngineNetworkCustomVersion::RepMoveServerFrameAndHandle && Ar.EngineNetVer() != FEngineNetworkCustomVersion::Ver21AndViewPitchOnly_DONOTUSE;
uint8 Flags = (bSimulatedPhysicSleep << 0) | (bRepPhysics << 1) | (bJustTeleported << 2) | (bJustTeleportedGrips << 3) | (bPausedTracking << 4);
Ar.SerializeBits(&Flags, 5);
bSimulatedPhysicSleep = (Flags & (1 << 0)) ? 1 : 0;
bRepPhysics = (Flags & (1 << 1)) ? 1 : 0;
const bool bRepServerFrame = (Flags & (1 << 2) && bServerFrameAndHandleSupported) ? 1 : 0;
const bool bRepServerHandle = (Flags & (1 << 3) && bServerFrameAndHandleSupported) ? 1 : 0;
bJustTeleported = (Flags & (1 << 2)) ? 1 : 0;
bJustTeleportedGrips = (Flags & (1 << 3)) ? 1 : 0;
bPausedTracking = (Flags & (1 << 4)) ? 1 : 0;
bOutSuccess = true;
if (bPausedTracking)
{
bOutSuccess &= PausedTrackingLoc.NetSerialize(Ar, Map, bOutSuccess);
uint16 Yaw = 0;
if (Ar.IsSaving())
{
Yaw = FRotator::CompressAxisToShort(PausedTrackingRot);
Ar << Yaw;
}
else
{
Ar << Yaw;
PausedTrackingRot = Yaw;
}
}
// update location, rotation, linear velocity
bOutSuccess &= SerializeQuantizedVector(Ar, Location, BaseSettings.LocationQuantizationLevel);
switch (BaseSettings.RotationQuantizationLevel)
{
case ERotatorQuantization::ByteComponents:
{
Rotation.SerializeCompressed(Ar);
break;
}
case ERotatorQuantization::ShortComponents:
{
Rotation.SerializeCompressedShort(Ar);
break;
}
}
bOutSuccess &= SerializeQuantizedVector(Ar, LinearVelocity, BaseSettings.VelocityQuantizationLevel);
// update angular velocity if required
if (bRepPhysics)
{
bOutSuccess &= SerializeQuantizedVector(Ar, AngularVelocity, BaseSettings.VelocityQuantizationLevel);
}
if (bRepServerFrame)
{
uint32 uServerFrame = (uint32)ServerFrame;
Ar.SerializeIntPacked(uServerFrame);
ServerFrame = (int32)uServerFrame;
}
if (bRepServerHandle)
{
uint32 uServerPhysicsHandle = (uint32)ServerPhysicsHandle;
Ar.SerializeIntPacked(uServerPhysicsHandle);
ServerPhysicsHandle = (int32)uServerPhysicsHandle;
}
if (Ar.EngineNetVer() >= FEngineNetworkCustomVersion::RepMoveOptionalAcceleration)
{
uint8 AccelFlags = (bRepAcceleration << 0);
Ar.SerializeBits(&AccelFlags, 1);
bRepAcceleration = (AccelFlags & (1 << 0)) ? 1 : 0;
if (bRepAcceleration)
{
// Note that we're using the same quantization as Velocity, since the units are commonly on the same order
bOutSuccess &= SerializeQuantizedVector(Ar, Acceleration, VelocityQuantizationLevel);
}
}
else if (Ar.IsLoading())
{
bRepAcceleration = false;
}
return true;
}

View file

@ -489,14 +489,32 @@ void UVRBaseCharacterMovementComponent::EndPushBackNotification()
FVector UVRBaseCharacterMovementComponent::GetActorFeetLocationVR() const
{
if (AVRBaseCharacter * BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
const UCapsuleComponent* const CapsuleComponent = CharacterOwner ? CharacterOwner->GetCapsuleComponent() : Cast<UCapsuleComponent>(UpdatedComponent);
if (CapsuleComponent)
{
const float HalfHeight = CapsuleComponent->GetScaledCapsuleHalfHeight();
if (AVRBaseCharacter* BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
{
return BaseCharacter->OffsetComponentToWorld.GetLocation() + HalfHeight * GetGravityDirection();
}
else
{
return UpdatedComponent->GetComponentLocation() + HalfHeight * GetGravityDirection();
}
}
return Super::GetActorFeetLocation();
/*if (AVRBaseCharacter* BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
{
return UpdatedComponent ? (BaseCharacter->OffsetComponentToWorld.GetLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
}
else
{
return UpdatedComponent ? (UpdatedComponent->GetComponentLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
}
}*/
}
void UVRBaseCharacterMovementComponent::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
@ -533,7 +551,8 @@ bool UVRBaseCharacterMovementComponent::FloorSweepTest(
const FCollisionShape BoxShape = FCollisionShape::MakeBox(FVector(CapsuleRadius * 0.707f, CapsuleRadius * 0.707f, CapsuleHeight));
// First test with the box rotated so the corners are along the major axes (ie rotated 45 degrees).
bBlockingHit = GetWorld()->SweepSingleByChannel(OutHit, Start, End, FQuat(RotateGravityToWorld(FVector(0.f, 0.f, -1.f)), UE_PI * 0.25f), TraceChannel, BoxShape, Params, ResponseParam);
//bBlockingHit = GetWorld()->SweepSingleByChannel(OutHit, Start, End, FQuat(RotateGravityToWorld(FVector(0.f, 0.f, -1.f)), UE_PI * 0.25f), TraceChannel, BoxShape, Params, ResponseParam);
bBlockingHit = GetWorld()->SweepSingleByChannel(OutHit, Start, End, FQuat(GetGravityDirection(), UE_PI * 0.25f), TraceChannel, BoxShape, Params, ResponseParam);
if (!bBlockingHit)
{
@ -558,8 +577,8 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
if (DownwardSweepResult != NULL && DownwardSweepResult->IsValidBlockingHit())
{
// Only if the supplied sweep was vertical and downward.
const bool bIsDownward = RotateWorldToGravity(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).Z > 0;
const bool bIsVertical = RotateWorldToGravity(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= UE_KINDA_SMALL_NUMBER;
const bool bIsDownward = GetGravitySpaceZ(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd) > 0;
const bool bIsVertical = ProjectToGravityFloor(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared() <= UE_KINDA_SMALL_NUMBER;
if (bIsDownward && bIsVertical)
{
// Reject hits that are barely on the cusp of the radius of the capsule
@ -569,7 +588,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
bSkipSweep = true;
const bool bIsWalkable = IsWalkable(*DownwardSweepResult);
const float FloorDist = RotateWorldToGravity(CapsuleLocation - DownwardSweepResult->Location).Z;
const float FloorDist = GetGravitySpaceZ(CapsuleLocation - DownwardSweepResult->Location);
OutFloorResult.SetFromSweep(*DownwardSweepResult, FloorDist, bIsWalkable);
if (bIsWalkable)
@ -610,7 +629,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(SweepRadius, PawnHalfHeight - ShrinkHeight);
FHitResult Hit(1.f);
bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + RotateGravityToWorld(FVector(0.f, 0.f, -TraceDist)), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + TraceDist * GetGravityDirection(), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
if (bBlockingHit)
{
@ -628,7 +647,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
CapsuleShape.Capsule.HalfHeight = FMath::Max(PawnHalfHeight - ShrinkHeight, CapsuleShape.Capsule.Radius);
Hit.Reset(1.f, false);
bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + RotateGravityToWorld(FVector(0.f, 0.f, -TraceDist)), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + TraceDist * GetGravityDirection(), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
}
}
@ -664,7 +683,7 @@ void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleL
const float ShrinkHeight = PawnHalfHeight;
const FVector LineTraceStart = CapsuleLocation;
const float TraceDist = LineDistance + ShrinkHeight;
const FVector Down = RotateGravityToWorld(FVector(0.f, 0.f, -TraceDist));
const FVector Down = TraceDist * GetGravityDirection();
QueryParams.TraceTag = SCENE_QUERY_STAT_NAME_ONLY(FloorLineTrace);
FHitResult Hit(1.f);
@ -703,31 +722,32 @@ float UVRBaseCharacterMovementComponent::SlideAlongSurface(const FVector& Delta,
return 0.f;
}
FVector Normal(RotateWorldToGravity(InNormal));
FVector Normal(InNormal);
const FVector::FReal NormalZ = GetGravitySpaceZ(Normal);
if (IsMovingOnGround())
{
// We don't want to be pushed up an unwalkable surface.
if (Normal.Z > 0.f)
if (NormalZ > 0.f)
{
if (!IsWalkable(Hit))
{
Normal = Normal.GetSafeNormal2D();
Normal = ProjectToGravityFloor(Normal).GetSafeNormal();
}
}
else if (Normal.Z < -UE_KINDA_SMALL_NUMBER)
else if (NormalZ < -UE_KINDA_SMALL_NUMBER)
{
// Don't push down into the floor when the impact is on the upper portion of the capsule.
if (CurrentFloor.FloorDist < MIN_FLOOR_DIST && CurrentFloor.bBlockingHit)
{
const FVector FloorNormal = RotateWorldToGravity(CurrentFloor.HitResult.Normal);
const bool bFloorOpposedToMovement = (RotateWorldToGravity(Delta) | FloorNormal) < 0.f && (FloorNormal.Z < 1.f - UE_DELTA);
const FVector FloorNormal = CurrentFloor.HitResult.Normal;
const bool bFloorOpposedToMovement = (Delta | FloorNormal) < 0.f && (GetGravitySpaceZ(FloorNormal) < 1.f - UE_DELTA);
if (bFloorOpposedToMovement)
{
Normal = FloorNormal;
}
Normal = Normal.GetSafeNormal2D();
Normal = ProjectToGravityFloor(Normal).GetSafeNormal();
}
}
}
@ -744,9 +764,9 @@ float UVRBaseCharacterMovementComponent::SlideAlongSurface(const FVector& Delta,
// that we have already validated the floor normal.
// Otherwise just pass in as normal, either way skip the parents implementation as we are doing it now.
if (IsMovingOnGround() || (MovementMode == MOVE_Custom && CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing))
return Super::Super::SlideAlongSurface(Delta * VRWallSlideScaler, Time, RotateGravityToWorld(Normal), Hit, bHandleImpact);
return Super::Super::SlideAlongSurface(Delta * VRWallSlideScaler, Time, Normal, Hit, bHandleImpact);
else
return Super::Super::SlideAlongSurface(Delta, Time, RotateGravityToWorld(Normal), Hit, bHandleImpact);
return Super::Super::SlideAlongSurface(Delta, Time, Normal, Hit, bHandleImpact);
}
/*void UVRBaseCharacterMovementComponent::SetCrouchedHalfHeight(float NewCrouchedHalfHeight)
@ -2019,12 +2039,12 @@ void UVRBaseCharacterMovementComponent::SmoothCorrection(const FVector& OldLocat
// The mesh doesn't move, but the capsule does so we have a new offset.
FVector NewToOldVector = (OldWorldLocation - NewWorldLocation);
if (bIsNavWalkingOnServer && FMath::Abs(NewToOldVector.Z) < NavWalkingFloorDistTolerance)
if (bIsNavWalkingOnServer && FMath::Abs(GetGravitySpaceZ(NewToOldVector)) < NavWalkingFloorDistTolerance)
{
// ignore smoothing on Z axis
// don't modify new location (local simulation result), since it's probably more accurate than server data
// and shouldn't matter as long as difference is relatively small
NewToOldVector.Z = 0;
NewToOldVector = ProjectToGravityFloor(NewToOldVector);
}
const float DistSq = NewToOldVector.SizeSquared();

View file

@ -173,7 +173,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
else
capLocation = UpdatedComponent->GetComponentLocation();
const bool bEncroached = GetWorld()->OverlapBlockingTestByChannel(capLocation - FVector(0.f, 0.f, ScaledHalfHeightAdjust), FQuat::Identity,
const bool bEncroached = GetWorld()->OverlapBlockingTestByChannel(capLocation - (ScaledHalfHeightAdjust * GetGravityDirection()), FQuat::Identity,
UpdatedComponent->GetCollisionObjectType(), GetPawnCapsuleCollisionShape(SHRINK_None), CapsuleParams, ResponseParam);
// If encroached, cancel
@ -192,7 +192,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
if (bCrouchMaintainsBaseLocation)
{
// Intentionally not using MoveUpdatedComponent, where a horizontal plane constraint would prevent the base of the capsule from staying at the same spot.
//UpdatedComponent->MoveComponent(FVector(0.f, 0.f, -ScaledHalfHeightAdjust), UpdatedComponent->GetComponentQuat(), true, nullptr, EMoveComponentFlags::MOVECOMP_NoFlags, ETeleportType::TeleportPhysics);
//UpdatedComponent->MoveComponent(ScaledHalfHeightAdjust * GetGravityDirection(), UpdatedComponent->GetComponentQuat(), true, nullptr, EMoveComponentFlags::MOVECOMP_NoFlags, ETeleportType::TeleportPhysics);
}
CharacterOwner->bIsCrouched = true;
@ -219,7 +219,7 @@ void UVRCharacterMovementComponent::Crouch(bool bClientSimulation)
if (ClientData)
{
ClientData->MeshTranslationOffset -= FVector(0.f, 0.f, MeshAdjust);
ClientData->MeshTranslationOffset -= MeshAdjust * -GetGravityDirection();
ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset;
}
}
@ -280,7 +280,7 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
if (!bCrouchMaintainsBaseLocation)
{
// Expand in place
bEncroached = MyWorld->OverlapBlockingTestByChannel(PawnLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
bEncroached = MyWorld->OverlapBlockingTestByChannel(PawnLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
if (bEncroached)
{
@ -292,11 +292,11 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
const float ShrinkHalfHeight = PawnHalfHeight - PawnRadius;
const float TraceDist = PawnHalfHeight - ShrinkHalfHeight;
const FVector Down = FVector(0.f, 0.f, -TraceDist);
const FVector Down = TraceDist * GetGravityDirection();
FHitResult Hit(1.f);
const FCollisionShape ShortCapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_HeightCustom, ShrinkHalfHeight);
const bool bBlockingHit = MyWorld->SweepSingleByChannel(Hit, PawnLocation, PawnLocation + Down, FQuat::Identity, CollisionChannel, ShortCapsuleShape, CapsuleParams);
const bool bBlockingHit = MyWorld->SweepSingleByChannel(Hit, PawnLocation, PawnLocation + Down, GetWorldToGravityTransform(), CollisionChannel, ShortCapsuleShape, CapsuleParams);
if (Hit.bStartPenetrating)
{
bEncroached = true;
@ -305,8 +305,9 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
{
// Compute where the base of the sweep ended up, and see if we can stand there
const float DistanceToBase = (Hit.Time * TraceDist) + ShortCapsuleShape.Capsule.HalfHeight;
const FVector NewLoc = FVector(PawnLocation.X, PawnLocation.Y, PawnLocation.Z - DistanceToBase + StandingCapsuleShape.Capsule.HalfHeight + SweepInflation + MIN_FLOOR_DIST / 2.f);
bEncroached = MyWorld->OverlapBlockingTestByChannel(NewLoc, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
const FVector Adjustment = (-DistanceToBase + StandingCapsuleShape.Capsule.HalfHeight + SweepInflation + MIN_FLOOR_DIST / 2.f) * -GetGravityDirection();
const FVector NewLoc = PawnLocation + Adjustment;
bEncroached = MyWorld->OverlapBlockingTestByChannel(NewLoc, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
if (!bEncroached)
{
// Intentionally not using MoveUpdatedComponent, where a horizontal plane constraint would prevent the base of the capsule from staying at the same spot.
@ -319,8 +320,8 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
else
{
// Expand while keeping base location the same.
FVector StandingLocation = PawnLocation + FVector(0.f, 0.f, StandingCapsuleShape.GetCapsuleHalfHeight() - CurrentCrouchedHalfHeight);
bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
FVector StandingLocation = PawnLocation + (StandingCapsuleShape.GetCapsuleHalfHeight() - CurrentCrouchedHalfHeight) * -GetGravityDirection();
bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
if (bEncroached)
{
@ -330,8 +331,8 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
const float MinFloorDist = UE_KINDA_SMALL_NUMBER * 10.f;
if (CurrentFloor.bBlockingHit && CurrentFloor.FloorDist > MinFloorDist)
{
StandingLocation.Z -= CurrentFloor.FloorDist - MinFloorDist;
bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, FQuat::Identity, CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
StandingLocation -= (CurrentFloor.FloorDist - MinFloorDist) * -GetGravityDirection();
bEncroached = MyWorld->OverlapBlockingTestByChannel(StandingLocation, GetWorldToGravityTransform(), CollisionChannel, StandingCapsuleShape, CapsuleParams, ResponseParam);
}
}
}
@ -375,7 +376,7 @@ void UVRCharacterMovementComponent::UnCrouch(bool bClientSimulation)
if (ClientData)
{
ClientData->MeshTranslationOffset += FVector(0.f, 0.f, MeshAdjust);
ClientData->MeshTranslationOffset += MeshAdjust * -GetGravityDirection();
ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset;
}
}*/
@ -856,7 +857,7 @@ bool UVRCharacterMovementComponent::ShouldCheckForValidLandingSpot(float DeltaTi
{
// See if we hit an edge of a surface on the lower portion of the capsule.
// In this case the normal will not equal the impact normal, and a downward sweep may find a walkable surface on top of the edge.
if (Hit.Normal.Z > UE_KINDA_SMALL_NUMBER && !Hit.Normal.Equals(Hit.ImpactNormal))
if (GetGravitySpaceZ(Hit.Normal) > UE_KINDA_SMALL_NUMBER && !Hit.Normal.Equals(Hit.ImpactNormal))
{
FVector PawnLocation = UpdatedComponent->GetComponentLocation();
if (VRRootCapsule)
@ -932,8 +933,12 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
const FVector OldVelocity = Velocity;
Acceleration = FVector::VectorPlaneProject(Acceleration, -GetGravityDirection());
static const auto CVarLedgeMovementApplyDirectMove = IConsoleManager::Get().FindConsoleVariable(TEXT("p.LedgeMovement.ApplyDirectMove"));
// Apply acceleration
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
const bool bSkipForLedgeMove = bTriedLedgeMove && CVarLedgeMovementApplyDirectMove->GetBool();
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && !bSkipForLedgeMove)
{
CalcVelocity(timeTick, GroundFriction, false, GetMaxBrakingDeceleration());
devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
@ -999,7 +1004,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
const float DesiredDist = Delta.Size();
if (DesiredDist > UE_KINDA_SMALL_NUMBER)
{
const float ActualDist = (UpdatedComponent->GetComponentLocation() - OldLocation).Size2D();
const float ActualDist = ProjectToGravityFloor(UpdatedComponent->GetComponentLocation() - OldLocation).Size();
remainingTime += timeTick * (1.f - FMath::Min(1.f, ActualDist / DesiredDist));
}
RestorePreAdditiveVRMotionVelocity();
@ -1024,8 +1029,10 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
if (bCheckLedges && !CurrentFloor.IsWalkableFloor())
{
// calculate possible alternate movement
const FVector GravDir = GetGravityDirection();
const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldCapsuleLocation, Delta, GravDir);
const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldLocation, Delta, OldFloor);
// REMOVED 5.5 and replaced with above
//const FVector GravDir = GetGravityDirection();
//const FVector NewDelta = bTriedLedgeMove ? FVector::ZeroVector : GetLedgeMove(OldCapsuleLocation, Delta, GravDir);
if (!NewDelta.IsZero())
{
// first revert this move
@ -1037,6 +1044,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
// Try new movement direction
Velocity = NewDelta / timeTick;
remainingTime += timeTick;
Iterations--;
RestorePreAdditiveVRMotionVelocity();
continue;
}
@ -1084,7 +1092,7 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
// The floor check failed because it started in penetration
// We do not want to try to move downward because the downward sweep failed, rather we'd like to try to pop out of the floor.
FHitResult Hit(CurrentFloor.HitResult);
Hit.TraceEnd = Hit.TraceStart + RotateGravityToWorld(FVector(0.f, 0.f, MAX_FLOOR_DIST));
Hit.TraceEnd = Hit.TraceStart + MAX_FLOOR_DIST * -GetGravityDirection();
const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
bForceNextFloorCheck = true;
@ -1168,8 +1176,10 @@ void UVRCharacterMovementComponent::CapsuleTouched(UPrimitiveComponent* Overlapp
}
const FVector Loc = VRRootCapsule->OffsetComponentToWorld.GetLocation();//UpdatedComponent->GetComponentLocation();
FVector ImpulseDir = FVector(OtherLoc.X - Loc.X, OtherLoc.Y - Loc.Y, 0.25f).GetSafeNormal();
ImpulseDir = (ImpulseDir + Velocity.GetSafeNormal2D()) * 0.5f;
FVector ImpulseDir = OtherLoc - Loc;
SetGravitySpaceZ(ImpulseDir, 0.25f);
ImpulseDir = (ImpulseDir.GetSafeNormal() + ProjectToGravityFloor(Velocity).GetSafeNormal()) * 0.5f;
ImpulseDir.Normalize();
FName BoneName = NAME_None;
@ -1186,7 +1196,7 @@ void UVRCharacterMovementComponent::CapsuleTouched(UPrimitiveComponent* Overlapp
TouchForceFactorModified *= BI ? BI->GetBodyMass() : 1.0f;
}
float ImpulseStrength = FMath::Clamp(Velocity.Size2D() * TouchForceFactorModified,
float ImpulseStrength = FMath::Clamp<FVector::FReal>(ProjectToGravityFloor(Velocity).Size() * TouchForceFactorModified,
MinTouchForce > 0.0f ? MinTouchForce : -FLT_MAX,
MaxTouchForce > 0.0f ? MaxTouchForce : FLT_MAX);
@ -1541,7 +1551,7 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
// Trace to get the hit location on the capsule
FHitResult Hit;
bool bHasHit = UpdatedPrimitive->LineTraceComponent(Hit, BodyLocation,
FVector(MyLocation.X, MyLocation.Y, BodyLocation.Z),
ProjectToGravityFloor(MyLocation) + GetGravitySpaceComponentZ(BodyLocation),
QueryParams);
FVector HitLoc = Hit.ImpactPoint;
@ -1554,12 +1564,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
bIsPenetrating = true;
}
const float DistanceNow = (HitLoc - BodyLocation).SizeSquared2D();
const float DistanceLater = (HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared2D();
const float DistanceNow = ProjectToGravityFloor(HitLoc - BodyLocation).SizeSquared();
const float DistanceLater = ProjectToGravityFloor(HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared();
if (bHasHit && DistanceNow < StopBodyDistance && !bIsPenetrating)
{
OverlapBody->SetLinearVelocity(FVector(0.0f, 0.0f, 0.0f), false);
OverlapBody->SetLinearVelocity(FVector::ZeroVector, false);
}
else if (DistanceLater <= DistanceNow || bIsPenetrating)
{
@ -1567,11 +1577,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds)
if (bHasHit)
{
ForceCenter.Z = HitLoc.Z;
SetGravitySpaceZ(ForceCenter, GetGravitySpaceZ(HitLoc));
}
else
{
ForceCenter.Z = FMath::Clamp(BodyLocation.Z, MyLocation.Z - CapsuleHalfHeight, MyLocation.Z + CapsuleHalfHeight);
const FVector::FReal MyLocationZ = GetGravitySpaceZ(MyLocation);
SetGravitySpaceZ(ForceCenter, FMath::Clamp(GetGravitySpaceZ(BodyLocation), MyLocationZ - CapsuleHalfHeight, MyLocationZ + CapsuleHalfHeight));
}
OverlapBody->AddRadialForceToBody(ForceCenter, RepulsionForceRadius, RepulsionForce * Mass, ERadialImpulseFalloff::RIF_Constant);
@ -1650,7 +1661,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
}
// Move along the current floor
const FVector Delta = RotateGravityToWorld(RotateWorldToGravity(InVelocity) * FVector(1.0, 1.0, 0.0)) * DeltaSeconds;
const FVector Delta = ProjectToGravityFloor(InVelocity) * DeltaSeconds;
FHitResult Hit(1.f);
FVector RampVector = ComputeGroundMovementDelta(Delta, CurrentFloor.HitResult, CurrentFloor.bLineTrace);
SafeMoveUpdatedComponent(RampVector, UpdatedComponent->GetComponentQuat(), true, Hit);
@ -1671,7 +1682,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
{
// We impacted something (most likely another ramp, but possibly a barrier).
float PercentTimeApplied = Hit.Time;
if ((Hit.Time > 0.f) && (Hit.Normal.Z > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit))
if ((Hit.Time > 0.f) && (GetGravitySpaceZ(Hit.Normal) > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit))
{
// Another walkable ramp.
const float InitialPercentRemaining = 1.f - PercentTimeApplied;
@ -1691,8 +1702,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
const FVector PreStepUpLocation = UpdatedComponent->GetComponentLocation();
const FVector GravDir = GetGravityDirection();
// I add in the HMD difference from last frame to the step up check to enforce it stepping up
if (!StepUp(GravDir, (Delta * (1.f - PercentTimeApplied)) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, OutStepDownResult))
if (!StepUp(GetGravityDirection(), Delta * (1.f - PercentTimeApplied), Hit, OutStepDownResult))
{
UE_LOG(LogVRCharacterMovement, Verbose, TEXT("- StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString());
HandleImpact(Hit, LastMoveTimeSlice, RampVector);
@ -1710,7 +1720,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && StepUpTimeSlice >= UE_KINDA_SMALL_NUMBER)
{
Velocity = (UpdatedComponent->GetComponentLocation() - PreStepUpLocation) / StepUpTimeSlice;
Velocity = FVector::VectorPlaneProject(Velocity, -GravDir);
Velocity = ProjectToGravityFloor(Velocity);
}
}
}
@ -1745,8 +1755,9 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
// Don't bother stepping up if top of capsule is hitting something.
const float InitialImpactZ = InHit.ImpactPoint.Z;
if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius))
const float InitialImpactZ = InHit.ImpactPoint | -GravDir;
const float OldLocationZ = OldLocation | -GravDir;
if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius))
{
return false;
}
@ -1762,7 +1773,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
float StepTravelUpHeight = MaxStepHeight;
float StepTravelDownHeight = StepTravelUpHeight;
const float StepSideZ = -1.f * FVector::DotProduct(InHit.ImpactNormal, GravDir);//const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir);
float PawnInitialFloorBaseZ = OldLocation.Z -PawnHalfHeight;
float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight;
float PawnFloorPointZ = PawnInitialFloorBaseZ;
if (IsMovingOnGround() && CurrentFloor.IsWalkableFloor())
@ -1776,7 +1787,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
const bool bHitVerticalFace = !IsWithinEdgeTolerance(InHit.Location, InHit.ImpactPoint, PawnRadius);
if (!CurrentFloor.bLineTrace && !bHitVerticalFace)
{
PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint.Z;
PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint | -GravDir;
}
else
{
@ -1868,7 +1879,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
if (Hit.IsValidBlockingHit())
{
// See if this step sequence would have allowed us to travel higher than our max step height allows.
const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ;
const float DeltaZ = (Hit.ImpactPoint | -GravDir) - PawnFloorPointZ;
if (DeltaZ > MaxStepHeight)
{
//UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ);
@ -1890,7 +1901,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
// Also reject if we would end up being higher than our starting location by stepping down.
// It's fine to step down onto an unwalkable normal below us, we will just slide off. Rejecting those moves would prevent us from being able to walk off the edge.
if (Hit.Location.Z > OldLocation.Z)
if ((Hit.Location | -GravDir) > OldLocationZ)
{
//UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString());
ScopedStepUpMovement.RevertMove();
@ -1921,7 +1932,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector
// Reject unwalkable normals if we end up higher than our initial height.
// It's fine to walk down onto an unwalkable surface, don't reject those moves.
if (Hit.Location.Z > OldLocation.Z)
if ((Hit.Location | -GravDir) > OldLocationZ)
{
// We should reject the floor result if we are trying to step up an actual step where we are not able to perch (this is rare).
// In those cases we should instead abort the step up and try to slide along the stair.
@ -1990,8 +2001,9 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
// Don't bother stepping up if top of capsule is hitting something.
const float InitialImpactZ = InHit.ImpactPoint.Z;
if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius))
const float InitialImpactZ = InHit.ImpactPoint | -GravDir;
const float OldLocationZ = OldLocation | -GravDir;
if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius))
{
return false;
}
@ -2013,7 +2025,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
float StepTravelUpHeight = MaxStepHeight;
float StepTravelDownHeight = StepTravelUpHeight;
const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir);
float PawnInitialFloorBaseZ = OldLocation.Z - PawnHalfHeight;
float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight;
float PawnFloorPointZ = PawnInitialFloorBaseZ;
// Scope our movement updates, and do not apply them until all intermediate moves are completed.
@ -2109,7 +2121,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
if (Hit.IsValidBlockingHit())
{
// See if this step sequence would have allowed us to travel higher than our max step height allows.
const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ;
const float DeltaZ = (Hit.ImpactPoint | -GravDir) - PawnFloorPointZ;
if (DeltaZ > MaxStepHeight)
{
UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ);
@ -2131,7 +2143,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
// Also reject if we would end up being higher than our starting location by stepping down.
// It's fine to step down onto an unwalkable normal below us, we will just slide off. Rejecting those moves would prevent us from being able to walk off the edge.
if (Hit.Location.Z > OldLocation.Z)
if ((Hit.Location | -GravDir) > OldLocationZ)
{
UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString());
ScopedStepUpMovement.RevertMove();
@ -2162,7 +2174,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
// Reject unwalkable normals if we end up higher than our initial height.
// It's fine to walk down onto an unwalkable surface, don't reject those moves.
if (Hit.Location.Z > OldLocation.Z)
if ((Hit.Location | -GravDir) > OldLocationZ)
{
// We should reject the floor result if we are trying to step up an actual step where we are not able to perch (this is rare).
// In those cases we should instead abort the step up and try to slide along the stair.
@ -2195,6 +2207,12 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const
return true;
}
FVector UVRCharacterMovementComponent::GetActorFeetLocation() const
{
// Call into the VR version of it instead
return GetActorFeetLocationVR();
}
void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds)
{
@ -2259,8 +2277,12 @@ void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds)
// @todo: This assumes only Yaw is used, currently a valid assumption. This is the only reason FaceRotation() is used above really, aside from being a virtual hook.
if (bOrientRotationToMovement || (bUseControllerDesiredRotation && CharacterOwner->Controller))
{
TargetRotator.Pitch = 0.f;
TargetRotator.Roll = 0.f;
// Custom gravity automatically aligns the character to the gravity direction, so we shouldn't zero out pitch and roll.
if (!HasCustomGravity())
{
TargetRotator.Pitch = 0.f;
TargetRotator.Roll = 0.f;
}
MoveUpdatedComponent(FVector::ZeroVector, TargetRotator, false);
FinalQuat = UpdatedComponent->GetComponentQuat();
}
@ -2276,19 +2298,31 @@ void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds)
}
}
// We need to offset the base of the character here, not its origin, so offset by half height
float HalfHeight, Radius;
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight);
if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
FVector NewWorldPos;
if (HasCustomGravity())
{
HalfHeight = 0;
const FVector RotationRadius = UpdatedComponent->GetComponentLocation() - NewBaseLocation;
const FVector RotationDelta = DeltaQuat.RotateVector(RotationRadius) - RotationRadius;
const FVector LinearDelta = NewBaseLocation - OldBaseLocation;
NewWorldPos = ConstrainLocationToPlane(UpdatedComponent->GetComponentLocation() + RotationDelta + LinearDelta);
}
else
{
// We need to offset the base of the character here, not its origin, so offset by half height
float HalfHeight, Radius;
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight);
if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
{
HalfHeight = 0;
}
FVector const BaseOffset(0.0f, 0.0f, HalfHeight);
FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset);
NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset);
}
FVector const BaseOffset(0.0f, 0.0f, HalfHeight);
FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset);
FVector const NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset);
DeltaPosition = ConstrainDirectionToPlane(NewWorldPos - UpdatedComponent->GetComponentLocation());
// move attached actor
@ -2377,16 +2411,13 @@ FVector UVRCharacterMovementComponent::GetImpartedMovementBaseVelocity() const
if (bImpartBaseAngularVelocity)
{
// Base position should be the bottom of the actor since I offset the capsule now
float HalfHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale)
if (BaseVRCharacterOwner && BaseVRCharacterOwner->bRetainRoomscale)
{
HalfHeight = 0.0f;
}
const FVector CharacterBasePosition = (UpdatedComponent->GetComponentLocation() - FVector(0.f, 0.f, HalfHeight));
const FVector CharacterBasePosition = (UpdatedComponent->GetComponentLocation() + HalfHeight * GetGravityDirection());
const FVector BaseTangentialVel = MovementBaseUtility::GetMovementBaseTangentialVelocity(MovementBase, CharacterOwner->GetBasedMovement().BoneName, CharacterBasePosition);
BaseVelocity += BaseTangentialVel;
}
@ -2562,13 +2593,13 @@ float UVRCharacterMovementComponent::ImmersionDepth() const
if (VRRootCapsule)
{
TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + FVector(0.f, 0.f, CollisionHalfHeight);
TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - FVector(0.f, 0.f, CollisionHalfHeight);
TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + CollisionHalfHeight * -GetGravityDirection();
TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - CollisionHalfHeight * -GetGravityDirection();
}
else
{
TraceStart = UpdatedComponent->GetComponentLocation() + FVector(0.f, 0.f, CollisionHalfHeight);
TraceEnd = UpdatedComponent->GetComponentLocation() -FVector(0.f, 0.f, CollisionHalfHeight);
TraceStart = UpdatedComponent->GetComponentLocation() + CollisionHalfHeight * -GetGravityDirection();
TraceEnd = UpdatedComponent->GetComponentLocation() - CollisionHalfHeight * -GetGravityDirection();
}
FCollisionQueryParams NewTraceParams(CharacterMovementComponentStatics::ImmersionDepthName, true);
@ -2669,18 +2700,19 @@ void UVRCharacterMovementComponent::PhysFlying(float deltaTime, int32 Iterations
if (Hit.Time < 1.f)
{
const FVector GravDir = FVector(0.f, 0.f, -1.f);
const FVector VelDir = Velocity.GetSafeNormal();
const float UpDown = GravDir | VelDir;
const float UpDown = VelDir | GetGravityDirection();
bool bSteppedUp = false;
if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
if ((FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
{
float stepZ = UpdatedComponent->GetComponentLocation().Z;
bSteppedUp = StepUp(GravDir, (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, nullptr);
const FVector::FReal StepZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation());
bSteppedUp = StepUp(GetGravityDirection(), (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time), Hit);
if (bSteppedUp)
{
OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
const FVector::FReal LocationZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()) + (GetGravitySpaceZ(OldLocation) - StepZ);
SetGravitySpaceZ(OldLocation, LocationZ);
}
}
@ -2713,9 +2745,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
return;
}
FVector FallAcceleration = GetFallingLateralAcceleration(deltaTime);
const FVector GravityRelativeFallAcceleration = RotateWorldToGravity(FallAcceleration);
FallAcceleration = RotateGravityToWorld(FVector(GravityRelativeFallAcceleration.X, GravityRelativeFallAcceleration.Y, 0));
const FVector FallAcceleration = ProjectToGravityFloor(GetFallingLateralAcceleration(deltaTime));
const bool bHasLimitedAirControl = ShouldLimitAirControl(deltaTime, FallAcceleration);
// Rewind the players position by the new capsule location
@ -2751,7 +2781,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
TGuardValue<FVector> RestoreAcceleration(Acceleration, FallAcceleration);
if (HasCustomGravity())
{
Velocity = FVector::VectorPlaneProject(Velocity, RotateGravityToWorld(FVector::UpVector));
Velocity = ProjectToGravityFloor(Velocity);
const FVector GravityRelativeOffset = OldVelocity - Velocity;
CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel);
Velocity += GravityRelativeOffset;
@ -2797,15 +2827,15 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
DecayFormerBaseVelocity(timeTick);
// See if we need to sub-step to exactly reach the apex. This is important for avoiding "cutting off the top" of the trajectory as framerate varies.
const FVector GravityRelativeOldVelocityWithRootMotion = RotateWorldToGravity(OldVelocityWithRootMotion);
const FVector::FReal GravityRelativeOldVelocityWithRootMotionZ = GetGravitySpaceZ(OldVelocityWithRootMotion);
static const auto CVarForceJumpPeakSubstep = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ForceJumpPeakSubstep"));
if (CVarForceJumpPeakSubstep->GetInt() != 0 && GravityRelativeOldVelocityWithRootMotion.Z > 0.f && RotateWorldToGravity(Velocity).Z <= 0.f && NumJumpApexAttempts < MaxJumpApexAttemptsPerSimulation)
if (CVarForceJumpPeakSubstep->GetInt() && GravityRelativeOldVelocityWithRootMotionZ > 0.f && GetGravitySpaceZ(Velocity) <= 0.f && NumJumpApexAttempts < MaxJumpApexAttemptsPerSimulation)
{
const FVector DerivedAccel = (Velocity - OldVelocityWithRootMotion) / timeTick;
const FVector GravityRelativeDerivedAccel = RotateWorldToGravity(DerivedAccel);
if (!FMath::IsNearlyZero(GravityRelativeDerivedAccel.Z))
const FVector::FReal GravityRelativeDerivedAccelZ = GetGravitySpaceZ(DerivedAccel);
if (!FMath::IsNearlyZero(GravityRelativeDerivedAccelZ))
{
const float TimeToApex = -GravityRelativeOldVelocityWithRootMotion.Z / GravityRelativeDerivedAccel.Z;
const float TimeToApex = -GravityRelativeOldVelocityWithRootMotionZ / GravityRelativeDerivedAccelZ;
// The time-to-apex calculation should be precise, and we want to avoid adding a substep when we are basically already at the apex from the previous iteration's work.
const float ApexTimeMinimum = 0.0001f;
@ -2814,8 +2844,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
const FVector ApexVelocity = OldVelocityWithRootMotion + (DerivedAccel * TimeToApex);
if (HasCustomGravity())
{
const FVector GravityRelativeApexVelocity = RotateWorldToGravity(ApexVelocity);
Velocity = RotateGravityToWorld(FVector(GravityRelativeApexVelocity.X, GravityRelativeApexVelocity.Y, 0)); // Should be nearly zero anyway, but this makes apex notifications consistent.
Velocity = ProjectToGravityFloor(ApexVelocity); // Should be nearly zero anyway, but this makes apex notifications consistent.
}
else
{
@ -2847,7 +2876,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
ApplyRootMotionToVelocity(timeTick);
//ApplyVRMotionToVelocity(deltaTime);
if (bNotifyApex && (RotateWorldToGravity(Velocity).Z < 0.f))
if (bNotifyApex && (GetGravitySpaceZ(Velocity) < 0.f))
{
// Just passed jump apex since now going down
bNotifyApex = false;
@ -2912,7 +2941,9 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
FFindFloorResult FloorResult;
FindFloor(PawnLocation, FloorResult, false, NULL);
if (FloorResult.IsWalkableFloor() && IsValidLandingSpot(PawnLocation, FloorResult.HitResult))
// Note that we only care about capsule sweep floor results, since the line trace may detect a lower walkable surface that our falling capsule wouldn't actually reach yet.
if (!FloorResult.bLineTrace && FloorResult.IsWalkableFloor() && IsValidLandingSpot(PawnLocation, FloorResult.HitResult))
{
//RestorePreAdditiveVRMotionVelocity();
remainingTime += subTimeTickRemaining;
@ -2950,7 +2981,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
TGuardValue<FVector> RestoreVelocity(Velocity, OldVelocity);
if (HasCustomGravity())
{
Velocity = FVector::VectorPlaneProject(Velocity, RotateGravityToWorld(FVector::UpVector));
Velocity = ProjectToGravityFloor(Velocity);
const FVector GravityRelativeOffset = OldVelocity - Velocity;
CalcVelocity(timeTick, FallingLateralFriction, false, MaxDecel);
VelocityNoAirControl = Velocity + GravityRelativeOffset;
@ -2981,12 +3012,12 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
{
const FVector ContactVelocity = MovementBaseUtility::GetMovementBaseVelocity(HitComponent, NAME_None) + MovementBaseUtility::GetMovementBaseTangentialVelocity(HitComponent, NAME_None, Hit.ImpactPoint);
const FVector NewVelocity = Velocity - Hit.ImpactNormal * FVector::DotProduct(Velocity - ContactVelocity, Hit.ImpactNormal);
Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? ProjectToGravityFloor(Velocity) + GetGravitySpaceComponentZ(NewVelocity) : NewVelocity;
}
else if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported)
{
const FVector NewVelocity = (Delta / subTimeTickRemaining);
Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? ProjectToGravityFloor(Velocity) + GetGravitySpaceComponentZ(NewVelocity) : NewVelocity;
}
if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && (Delta | Adjusted) > 0.f)
@ -3019,7 +3050,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
}
// Act as if there was no air control on the last move when computing new deflection.
if (bHasLimitedAirControl && RotateWorldToGravity(Hit.Normal).Z > CharacterMovementConstants::VERTICAL_SLOPE_NORMAL_ZVR)
if (bHasLimitedAirControl && GetGravitySpaceZ(Hit.Normal) > CharacterMovementConstants::VERTICAL_SLOPE_NORMAL_ZVR)
{
const FVector LastMoveNoAirControl = VelocityNoAirControl * LastMoveTimeSlice;
Delta = ComputeSlideVector(LastMoveNoAirControl, 1.f, OldHitNormal, Hit);
@ -3045,19 +3076,27 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
if (subTimeTickRemaining > UE_KINDA_SMALL_NUMBER && !bJustTeleported)
{
const FVector NewVelocity = (Delta / subTimeTickRemaining);
Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? FVector(Velocity.X, Velocity.Y, NewVelocity.Z) : NewVelocity;
Velocity = HasAnimRootMotion() || CurrentRootMotion.HasOverrideVelocityWithIgnoreZAccumulate() ? ProjectToGravityFloor(Velocity) + GetGravitySpaceComponentZ(NewVelocity) : NewVelocity;
}
// bDitch=true means that pawn is straddling two slopes, neither of which he can stand on
bool bDitch = ((RotateWorldToGravity(OldHitImpactNormal).Z > 0.f) && (RotateWorldToGravity(Hit.ImpactNormal).Z > 0.f) && (FMath::Abs(Delta.Z) <= UE_KINDA_SMALL_NUMBER) && ((Hit.ImpactNormal | OldHitImpactNormal) < 0.f));
bool bDitch = ((GetGravitySpaceZ(OldHitImpactNormal) > 0.f) && (GetGravitySpaceZ(Hit.ImpactNormal) > 0.f) && (FMath::Abs(GetGravitySpaceZ(Delta)) <= UE_KINDA_SMALL_NUMBER) && ((Hit.ImpactNormal | OldHitImpactNormal) < 0.f));
SafeMoveUpdatedComponent(Delta, PawnRotation, true, Hit);
if (Hit.Time == 0.f)
{
// if we are stuck then try to side step
FVector SideDelta = (OldHitNormal + Hit.ImpactNormal).GetSafeNormal2D();
FVector SideDelta = ProjectToGravityFloor(OldHitNormal + Hit.ImpactNormal).GetSafeNormal();
if (SideDelta.IsNearlyZero())
{
SideDelta = FVector(OldHitNormal.Y, -OldHitNormal.X, 0).GetSafeNormal();
if (HasCustomGravity())
{
const FVector GravityRelativeHitNormal = RotateWorldToGravity(OldHitNormal);
SideDelta = RotateGravityToWorld(FVector(GravityRelativeHitNormal.Y, -GravityRelativeHitNormal.X, 0.f)).GetSafeNormal();
}
else
{
SideDelta = FVector(OldHitNormal.Y, -OldHitNormal.X, 0).GetSafeNormal();
}
}
SafeMoveUpdatedComponent(SideDelta, PawnRotation, true, Hit);
}
@ -3069,13 +3108,13 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
ProcessLanded(Hit, remainingTime, Iterations);
return;
}
else if (GetPerchRadiusThreshold() > 0.f && Hit.Time == 1.f && RotateWorldToGravity(OldHitImpactNormal).Z >= GetWalkableFloorZ())
else if (GetPerchRadiusThreshold() > 0.f && Hit.Time == 1.f && GetGravitySpaceZ(OldHitImpactNormal) >= GetWalkableFloorZ())
{
// We might be in a virtual 'ditch' within our perch radius. This is rare.
const FVector PawnLocation = UpdatedComponent->GetComponentLocation();
const float ZMovedDist = FMath::Abs(RotateWorldToGravity(PawnLocation - OldLocation).Z);
const float MovedDist2DSq = FVector::VectorPlaneProject(PawnLocation - OldLocation, RotateGravityToWorld(FVector::UpVector)).Size2D();
if (ZMovedDist <= 0.2f * timeTick && MovedDist2DSq <= 4.f * timeTick)
const float ZMovedDist = FMath::Abs(GetGravitySpaceZ(PawnLocation - OldLocation));
const float MovedDist2D = ProjectToGravityFloor(PawnLocation - OldLocation).Size();
if (ZMovedDist <= 0.2f * timeTick && MovedDist2D <= 4.f * timeTick)
{
FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity);
GravityRelativeVelocity.X += 0.25f * GetMaxSpeed() * (RandomStream.FRand() - 0.5f);
@ -3135,12 +3174,10 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration
}
}
FVector GravityRelativeVelocity = RotateWorldToGravity(Velocity);
if (GravityRelativeVelocity.SizeSquared2D() <= UE_KINDA_SMALL_NUMBER * 10.f)
const FVector GravityProjectedVelocity = ProjectToGravityFloor(Velocity);
if (GravityProjectedVelocity.SizeSquared() <= UE_KINDA_SMALL_NUMBER * 10.f)
{
GravityRelativeVelocity.X = 0.f;
GravityRelativeVelocity.Y = 0.f;
Velocity = RotateGravityToWorld(GravityRelativeVelocity);
Velocity = GetGravitySpaceComponentZ(Velocity);
}
RestorePreAdditiveVRMotionVelocity();
@ -3179,7 +3216,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysNavWalking: Velocity contains NaN before CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
//bound acceleration
Acceleration.Z = 0.f;
Acceleration = ProjectToGravityFloor(Acceleration);
//if (!HasRootMotion())
//{
CalcVelocity(deltaTime, GroundFriction, false, BrakingDecelerationWalking);
@ -3198,8 +3235,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
Iterations++;
FVector DesiredMove = Velocity;
DesiredMove.Z = 0.f;
const FVector DesiredMove = ProjectToGravityFloor(Velocity);
//const FVector OldPlayerLocation = GetActorFeetLocation();
const FVector OldLocation = GetActorFeetLocationVR();
@ -3214,11 +3250,11 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
{
if (bProjectNavMeshWalking)
{
const float DistSq2D = (OldLocation - CachedNavLocation.Location).SizeSquared2D();
const float DistZ = FMath::Abs(OldLocation.Z - CachedNavLocation.Location.Z);
const float DistSq2D = ProjectToGravityFloor(OldLocation - CachedNavLocation.Location).SizeSquared();
const float DistZ = FMath::Abs(GetGravitySpaceZ(OldLocation - CachedNavLocation.Location));
const float TotalCapsuleHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.0f;
const float ProjectionScale = (OldLocation.Z > CachedNavLocation.Location.Z) ? NavMeshProjectionHeightScaleUp : NavMeshProjectionHeightScaleDown;
const float ProjectionScale = (GetGravitySpaceZ(OldLocation) > GetGravitySpaceZ(CachedNavLocation.Location)) ? NavMeshProjectionHeightScaleUp : NavMeshProjectionHeightScaleDown;
const float DistZThr = TotalCapsuleHeight * FMath::Max(0.f, ProjectionScale);
bSameNavLocation = (DistSq2D <= UE_KINDA_SMALL_NUMBER) && (DistZ < DistZThr);
@ -3255,7 +3291,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
// we'll follow that geometry's plane out of range of valid navigation.
if (bSameNavLocation && bProjectNavMeshWalking)
{
AdjustedDest.Z = CachedNavLocation.Location.Z;
SetGravitySpaceZ(AdjustedDest, GetGravitySpaceZ(CachedNavLocation.Location));
}
// Find the point on the NavMesh
@ -3272,7 +3308,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
if (DestNavLocation.NodeRef != INVALID_NAVNODEREF)
{
FVector NewLocation(AdjustedDest.X, AdjustedDest.Y, DestNavLocation.Location.Z);
FVector NewLocation = ProjectToGravityFloor(AdjustedDest) + GetGravitySpaceComponentZ(DestNavLocation.Location);
if (bProjectNavMeshWalking)
{
SCOPE_CYCLE_COUNTER(STAT_CharNavProjectLocation);
@ -3330,18 +3366,18 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
float NetFluidFriction = 0.f;
float Depth = ImmersionDepth();
float NetBuoyancy = Buoyancy * Depth;
float OriginalAccelZ = Acceleration.Z;
float OriginalAccelZ = GetGravitySpaceZ(Acceleration);
bool bLimitedUpAccel = false;
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (Velocity.Z > 0.33f * MaxSwimSpeed) && (NetBuoyancy != 0.f))
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (GetGravitySpaceZ(Velocity) > 0.33f * MaxSwimSpeed) && (NetBuoyancy != 0.f))
{
//damp positive Z out of water
Velocity.Z = FMath::Max<FVector::FReal>(0.33f * MaxSwimSpeed, Velocity.Z * Depth * Depth);
SetGravitySpaceZ(Velocity, FMath::Max<FVector::FReal>(0.33f * MaxSwimSpeed, GetGravitySpaceZ(Velocity) * Depth * Depth));
}
else if (Depth < 0.65f)
{
bLimitedUpAccel = (Acceleration.Z > 0.f);
Acceleration.Z = FMath::Min<FVector::FReal>(0.1f, Acceleration.Z);
bLimitedUpAccel = (OriginalAccelZ > 0.f);
SetGravitySpaceZ(Acceleration, FMath::Min<FVector::FReal>(0.1f, OriginalAccelZ));
}
Iterations++;
@ -3351,7 +3387,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
{
const float Friction = 0.5f * GetPhysicsVolume()->FluidFriction * Depth;
CalcVelocity(deltaTime, Friction, true, GetMaxBrakingDeceleration());
Velocity.Z += GetGravityZ() * deltaTime * (1.f - NetBuoyancy);
Velocity += (GetGravityZ() * deltaTime * (1.f - NetBuoyancy)) * -GetGravityDirection();
}
ApplyRootMotionToVelocity(deltaTime);
@ -3359,7 +3395,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
FVector Adjusted = Velocity * deltaTime;
FHitResult Hit(1.f);
float remainingTime = deltaTime * SwimVR(Adjusted/* + AdditionalVRInputVector*/, Hit);
const float remainingTime = deltaTime * Swim(Adjusted, Hit);
//may have left water - if so, script might have set new physics mode
if (!IsSwimming())
@ -3372,10 +3408,10 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
if (Hit.Time < 1.f && CharacterOwner)
{
HandleSwimmingWallHit(Hit, deltaTime);
if (bLimitedUpAccel && (Velocity.Z >= 0.f))
if (bLimitedUpAccel && (GetGravitySpaceZ(Velocity) >= 0.f))
{
// allow upward velocity at surface if against obstacle
Velocity.Z += OriginalAccelZ * deltaTime;
Velocity += OriginalAccelZ * deltaTime * -GetGravityDirection();
Adjusted = Velocity * (1.f - Hit.Time)*deltaTime;
SwimVR(Adjusted, Hit);
if (!IsSwimming())
@ -3386,17 +3422,16 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
}
}
const FVector GravDir = FVector(0.f, 0.f, -1.f);
const FVector VelDir = Velocity.GetSafeNormal();
const float UpDown = GravDir | VelDir;
const float UpDown = VelDir | GetGravityDirection();
bool bSteppedUp = false;
if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
if ((FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
{
float stepZ = UpdatedComponent->GetComponentLocation().Z;
const float StepZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation());
const FVector RealVelocity = Velocity;
Velocity.Z = 1.f; // HACK: since will be moving up, in case pawn leaves the water
bSteppedUp = StepUp(GravDir, (Adjusted/* + AdditionalVRInputVector*/) * (1.f - Hit.Time), Hit);
SetGravitySpaceZ(Velocity, 1.f); // HACK: since will be moving up, in case pawn leaves the water
bSteppedUp = StepUp(GetGravityDirection(), Adjusted * (1.f - Hit.Time), Hit);
if (bSteppedUp)
{
//may have left water - if so, script might have set new physics mode
@ -3406,7 +3441,7 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
StartNewPhysics(remainingTime, Iterations);
return;
}
OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
SetGravitySpaceZ(OldLocation, GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()) + (GetGravitySpaceZ(OldLocation) - StepZ));
}
Velocity = RealVelocity;
}
@ -3421,12 +3456,12 @@ void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iteratio
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && !bJustTeleported && ((deltaTime - remainingTime) > UE_KINDA_SMALL_NUMBER) && CharacterOwner)
{
bool bWaterJump = !GetPhysicsVolume()->bWaterVolume;
float velZ = Velocity.Z;
const bool bWaterJump = !GetPhysicsVolume()->bWaterVolume;
const FVector::FReal VelZ = GetGravitySpaceZ(Velocity);
Velocity = ((UpdatedComponent->GetComponentLocation() - OldLocation)/* - AdditionalVRInputVector*/) / (deltaTime - remainingTime);
if (bWaterJump)
{
Velocity.Z = velZ;
SetGravitySpaceZ(Velocity, VelZ);
}
}
@ -3472,9 +3507,10 @@ void UVRCharacterMovementComponent::StartSwimmingVR(FVector OldLocation, FVector
}
MoveUpdatedComponent(End - NewLocation, UpdatedComponent->GetComponentQuat(), true);
}
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (Velocity.Z > 2.f* CharacterMovementConstants::SWIMBOBSPEEDVR) && (Velocity.Z < 0.f)) //allow for falling out of water
const FVector::FReal GravityRelativeVelocityZ = GetGravitySpaceZ(Velocity);
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && (GravityRelativeVelocityZ > 2.f * CharacterMovementConstants::SWIMBOBSPEEDVR) && (GravityRelativeVelocityZ < 0.f)) //allow for falling out of water
{
Velocity.Z = CharacterMovementConstants::SWIMBOBSPEEDVR - Velocity.Size2D() * 0.7f; //smooth bobbing
SetGravitySpaceZ(Velocity, CharacterMovementConstants::SWIMBOBSPEEDVR - ProjectToGravityFloor(Velocity).Size() * 0.7f); //smooth bobbing
}
if ((remainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations))
{
@ -3517,26 +3553,25 @@ bool UVRCharacterMovementComponent::CheckWaterJump(FVector CheckPoint, FVector&
FVector currentLoc = VRRootCapsule ? VRRootCapsule->OffsetComponentToWorld.GetLocation() : UpdatedComponent->GetComponentLocation();
// check if there is a wall directly in front of the swimming pawn
CheckPoint.Z = 0.f;
CheckPoint = ProjectToGravityFloor(CheckPoint);
FVector CheckNorm = CheckPoint.GetSafeNormal();
float PawnCapsuleRadius, PawnCapsuleHalfHeight;
CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnCapsuleRadius, PawnCapsuleHalfHeight);
CheckPoint = currentLoc + 1.2f * PawnCapsuleRadius * CheckNorm;
FVector Extent(PawnCapsuleRadius, PawnCapsuleRadius, PawnCapsuleHalfHeight);
FHitResult HitInfo(1.f);
FCollisionQueryParams CapsuleParams(SCENE_QUERY_STAT(CheckWaterJump), false, CharacterOwner);
FCollisionResponseParams ResponseParam;
InitCollisionParams(CapsuleParams, ResponseParam);
FCollisionShape CapsuleShape = GetPawnCapsuleCollisionShape(SHRINK_None);
const ECollisionChannel CollisionChannel = UpdatedComponent->GetCollisionObjectType();
bool bHit = GetWorld()->SweepSingleByChannel(HitInfo, currentLoc, CheckPoint, FQuat::Identity, CollisionChannel, CapsuleShape, CapsuleParams, ResponseParam);
bool bHit = GetWorld()->SweepSingleByChannel(HitInfo, UpdatedComponent->GetComponentLocation(), CheckPoint, GetWorldToGravityTransform(), CollisionChannel, CapsuleShape, CapsuleParams, ResponseParam);
if (bHit && !HitInfo.HitObjectHandle.DoesRepresentClass(APawn::StaticClass()))
{
// hit a wall - check if it is low enough
WallNormal = -1.f * HitInfo.ImpactNormal;
FVector Start = currentLoc;//UpdatedComponent->GetComponentLocation();
Start.Z += MaxOutOfWaterStepHeight;
Start += MaxOutOfWaterStepHeight * -GetGravityDirection();
CheckPoint = Start + 3.2f * PawnCapsuleRadius * WallNormal;
FCollisionQueryParams LineParams(SCENE_QUERY_STAT(CheckWaterJump), true, CharacterOwner);
FCollisionResponseParams LineResponseParam;
@ -3725,11 +3760,7 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
// find floor and check if falling
if (IsMovingOnGround() || MovementMode == MOVE_Falling)
{
bool bShouldFindFloor = Velocity.Z <= 0.f;
if (HasCustomGravity())
{
bShouldFindFloor = RotateWorldToGravity(Velocity).Z <= 0.0;
}
const bool bShouldFindFloor = GetGravitySpaceZ(Velocity) <= UE_KINDA_SMALL_NUMBER;
if (StepDownResult.bComputedFloor)
{
@ -3750,14 +3781,8 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
{
// Follows PhysWalking approach for encroachment on floor tests
FHitResult Hit(CurrentFloor.HitResult);
if (HasCustomGravity())
{
Hit.TraceEnd = Hit.TraceStart - GetGravityDirection() * MAX_FLOOR_DIST;
}
else
{
Hit.TraceEnd = Hit.TraceStart + FVector(0.f, 0.f, MAX_FLOOR_DIST);
}
Hit.TraceEnd = Hit.TraceStart - GetGravityDirection() * MAX_FLOOR_DIST;
const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
const bool bResolved = ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
@ -3775,16 +3800,9 @@ void UVRCharacterMovementComponent::SimulateMovement(float DeltaSeconds)
if (!bSimGravityDisabled)
{
// No floor, must fall.
if (HasCustomGravity())
if (GetGravitySpaceZ(Velocity) <= UE_KINDA_SMALL_NUMBER || bApplyGravityWhileJumping || !CharacterOwner->IsJumpProvidingForce())
{
if (RotateWorldToGravity(Velocity).Z <= 0.f || bApplyGravityWhileJumping || !CharacterOwner->IsJumpProvidingForce())
{
Velocity = NewFallVelocity(Velocity, -GetGravityDirection() * GetGravityZ(), DeltaSeconds);
}
}
else if (Velocity.Z <= 0.f || bApplyGravityWhileJumping || !CharacterOwner->IsJumpProvidingForce())
{
Velocity = NewFallVelocity(Velocity, FVector(0.f, 0.f, GetGravityZ()), DeltaSeconds);
Velocity = NewFallVelocity(Velocity, -GetGravityDirection() * GetGravityZ(), DeltaSeconds);
}
}
@ -3903,7 +3921,7 @@ void UVRCharacterMovementComponent::MoveSmooth(const FVector& InVelocity, const
{
OutStepDownResult = NULL; // No need for a floor when not walking.
bool bShouldAttemptStepUp = false;
bShouldAttemptStepUp = FMath::Abs(RotateWorldToGravity(Hit.ImpactNormal).Z) < 0.2;
bShouldAttemptStepUp = FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2;
if (bShouldAttemptStepUp)
{
const FVector GravDir = GetGravityDirection();
@ -3946,7 +3964,7 @@ void UVRCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMov
MoveResponse.RootMotionTrackPosition,
MoveResponse.ClientAdjustment.NewLoc,
MoveResponse.RootMotionRotation,
MoveResponse.ClientAdjustment.NewVel.Z,
GetGravitySpaceZ(MoveResponse.ClientAdjustment.NewVel),
MoveResponse.ClientAdjustment.NewBase,
MoveResponse.ClientAdjustment.NewBaseBoneName,
MoveResponse.bHasBase,
@ -3961,7 +3979,7 @@ void UVRCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMov
MoveResponse.RootMotionTrackPosition,
MoveResponse.ClientAdjustment.NewLoc,
MoveResponse.RootMotionRotation,
MoveResponse.ClientAdjustment.NewVel.Z,
GetGravitySpaceZ(MoveResponse.ClientAdjustment.NewVel),
MoveResponse.ClientAdjustment.NewBase,
MoveResponse.ClientAdjustment.NewBaseBoneName,
MoveResponse.bHasBase,
@ -4452,7 +4470,7 @@ void UVRCharacterMovementComponent::ServerMoveHandleClientErrorVR(float ClientTi
{
const FVector LastBaseVelocity = MovementBaseUtility::GetMovementBaseVelocity(LastServerMovementBaseVRPtr, LastServerMovementBaseBoneName);
RelativeVelocity = Velocity - LastBaseVelocity;
const FVector BaseDirection = LastBaseVelocity.GetSafeNormal2D();
const FVector BaseDirection = ProjectToGravityFloor(LastBaseVelocity).GetSafeNormal();
const FVector RelativeDirection = RelativeVelocity * (1.f / MaxWalkSpeed);
ClientForwardFactor = FMath::Clamp(FVector::DotProduct(BaseDirection, RelativeDirection), 0.f, 1.f);

View file

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

View file

@ -3,6 +3,9 @@
#include "VRPathFollowingComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRPathFollowingComponent)
#include "AI/Navigation/NavigationTypes.h"
#include "AI/Navigation/PathFollowingAgentInterface.h"
#include "CoreMinimal.h"
#include "Engine/World.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
@ -16,7 +19,7 @@
DEFINE_LOG_CATEGORY(LogPathFollowingVR);
void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
/*void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
{
Super::SetMovementComponent(MoveComp);
@ -26,8 +29,23 @@ void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* Move
{
OnRequestFinished.AddUObject(VRMovementComp, &UVRBaseCharacterMovementComponent::OnMoveCompleted);
}
}
}*/
void UVRPathFollowingComponent::SetNavMovementInterface(INavMovementInterface* NavMoveInterface)
{
Super::SetNavMovementInterface(NavMoveInterface);
if (NavMovementInterface.IsValid() && NavMovementInterface->GetOwnerAsObject())
{
if (AVRBaseCharacter* VRMovement = Cast<AVRBaseCharacter>(NavMovementInterface->GetOwnerAsObject()))
{
if (IsValid(VRMovement->VRMovementReference))
{
OnRequestFinished.AddUObject(VRMovement->VRMovementReference, &UVRBaseCharacterMovementComponent::OnMoveCompleted);
}
}
}
}
void UVRPathFollowingComponent::GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const
{
@ -82,52 +100,8 @@ void UVRPathFollowingComponent::GetDebugStringTokens(TArray<FString>& Tokens, TA
Flags.Add(bFailedHeight ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
}
void UVRPathFollowingComponent::PauseMove(FAIRequestID RequestID, EPathFollowingVelocityMode VelocityMode)
{
//UE_VLOG(GetOwner(), LogPathFollowing, Log, TEXT("PauseMove: RequestID(%u)"), RequestID);
if (Status == EPathFollowingStatus::Paused)
{
return;
}
if (RequestID.IsEquivalent(GetCurrentRequestId()))
{
if ((VelocityMode == EPathFollowingVelocityMode::Reset) && MovementComp && HasMovementAuthority())
{
MovementComp->StopMovementKeepPathing();
}
LocationWhenPaused = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector;
PathTimeWhenPaused = Path.IsValid() ? Path->GetTimeStamp() : 0.;
Status = EPathFollowingStatus::Paused;
UpdateMoveFocus();
}
}
bool UVRPathFollowingComponent::ShouldCheckPathOnResume() const
{
bool bCheckPath = true;
if (MovementComp != NULL)
{
float AgentRadius = 0.0f, AgentHalfHeight = 0.0f;
MovementComp->GetOwner()->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight);
const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocation() : MovementComp->GetActorFeetLocation());
const FVector::FReal DeltaMove2DSq = (CurrentLocation - LocationWhenPaused).SizeSquared2D();
const FVector::FReal DeltaZ = FMath::Abs(CurrentLocation.Z - LocationWhenPaused.Z);
if (DeltaMove2DSq < FMath::Square(AgentRadius) && DeltaZ < (AgentHalfHeight * 0.5))
{
bCheckPath = false;
}
}
return bCheckPath;
}
int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
/*int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
{
int32 PickedPathPoint = INDEX_NONE;
@ -302,8 +276,8 @@ void UVRPathFollowingComponent::UpdatePathSegment()
if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex))
{
//LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct,
//*Path->GetPathPointLocation(MoveSegmentStartIndex),
//*Path->GetPathPointLocation(MoveSegmentEndIndex));
//Path->GetPathPointLocation(MoveSegmentStartIndex),
//Path->GetPathPointLocation(MoveSegmentEndIndex));
}
else
{
@ -455,7 +429,7 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current
const FVector ToGoal = (GoalLocation - AgentLocation);
const FVector CurrentDirection = GetCurrentDirection();
CurrentDot = FloatCastChecked<float>(FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection), /* Precision */ 1. / 128.);
CurrentDot = FloatCastChecked<float>(FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection), 1. / 128.);
bDotFailed = (CurrentDot < 0.0f) ? 1 : 0;
// get cylinder of moving agent
@ -472,3 +446,4 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current
const float UseHeight = GoalHalfHeight + (AgentHalfHeight * MinAgentHalfHeightPct);
bHeightFailed = (CurrentHeight > UseHeight) ? 1 : 0;
}
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,17 +28,21 @@ public:
void SetConstraintToForceBased(bool bUseForceConstraint)
{
if (!ConstraintInstance.ConstraintHandle.IsValid())
// #TODO: Double check on this in 5.5, but it should work
this->SetLinearDriveAccelerationMode(!bUseForceConstraint);
this->SetAngularDriveAccelerationMode(!bUseForceConstraint);
/*if (!ConstraintInstance.ConstraintHandle.IsValid())
return;
if (ConstraintInstance.ConstraintHandle->IsType(Chaos::EConstraintType::JointConstraintType))
{
if (Chaos::FJointConstraint* Constraint = static_cast<Chaos::FJointConstraint*>(ConstraintInstance.ConstraintHandle.Constraint))
{
Constraint->SetLinearDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
Constraint->SetAngularDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
}
}
}*/
//#endif
}

View file

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

View file

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

View file

@ -13,6 +13,40 @@
#include "VRWheeledVehicle.generated.h"
// This only exists to expose the blueprint properties of the transmission values
USTRUCT(BlueprintType, Category = "VRWheeledVehicle")
struct VREXPANSIONPLUGIN_API FBPVehicleTransmissionConfig : public FVehicleTransmissionConfig
{
GENERATED_BODY()
public:
void SetFrom(FVehicleTransmissionConfig & Config)
{
bUseAutomaticGears = Config.bUseAutomaticGears;
bUseAutoReverse = Config.bUseAutoReverse;
FinalRatio = Config.FinalRatio;
ForwardGearRatios = Config.ForwardGearRatios;
ReverseGearRatios = Config.ReverseGearRatios;
ChangeUpRPM = Config.ChangeUpRPM;
ChangeDownRPM = Config.ChangeDownRPM;
GearChangeTime = Config.GearChangeTime;
TransmissionEfficiency = Config.TransmissionEfficiency;
}
void SetTo(FVehicleTransmissionConfig& Config)
{
Config.bUseAutomaticGears = bUseAutomaticGears;
Config.bUseAutoReverse = bUseAutoReverse;
Config.FinalRatio = FinalRatio;
Config.ForwardGearRatios = ForwardGearRatios;
Config.ReverseGearRatios = ReverseGearRatios;
Config.ChangeUpRPM = ChangeUpRPM;
Config.ChangeDownRPM = ChangeDownRPM;
Config.GearChangeTime = GearChangeTime;
Config.TransmissionEfficiency = TransmissionEfficiency;
}
};
/**
* This override of the base wheeled vehicle allows for dual pawn usage in engine.
*/
@ -24,6 +58,29 @@ class VREXPANSIONPLUGIN_API AVRWheeledVehicle : public AWheeledVehiclePawn
public:
UFUNCTION(BlueprintPure, Category = "Pawn")
float GetFinalGearRatio()
{
float CurrentGearRatio = 0.0f;
if (UChaosWheeledVehicleMovementComponent* MoveComp = Cast<UChaosWheeledVehicleMovementComponent>(this->GetMovementComponent()))
{
CurrentGearRatio = MoveComp->TransmissionSetup.FinalRatio;
}
return CurrentGearRatio;
}
UFUNCTION(BlueprintCallable, Category = "Pawn")
void SetFinalGearRatio(float NewFinalGearRatio)
{
if (UChaosWheeledVehicleMovementComponent* MoveComp = Cast<UChaosWheeledVehicleMovementComponent>(this->GetMovementComponent()))
{
MoveComp->TransmissionSetup.FinalRatio = NewFinalGearRatio;
}
}
// Calls the movement components override controller function
UFUNCTION(BlueprintCallable, Category = "Pawn")
virtual bool SetBindToInput(AController * CController, bool bBindToInput)
{

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!!
//virtual void GetCameraView(float DeltaTime, FMinimalViewInfo& DesiredView) override;
virtual void HandleXRCamera() override;
virtual void HandleXRCamera(float DeltaTime) override;
UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedCameraTransform, Category = "ReplicatedCamera|Networking")
FBPVRComponentPosRep ReplicatedCameraTransform;

View file

@ -50,67 +50,7 @@ public:
UPROPERTY(Transient)
TObjectPtr<AActor> Owner;
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
FRepMovement BaseSettings = Owner ? Owner->GetReplicatedMovement() : FRepMovement();
// pack bitfield with flags
uint8 Flags = (bSimulatedPhysicSleep << 0) | (bRepPhysics << 1) | (bJustTeleported << 2) | (bJustTeleportedGrips << 3) | (bPausedTracking << 4);
Ar.SerializeBits(&Flags, 5);
bSimulatedPhysicSleep = (Flags & (1 << 0)) ? 1 : 0;
bRepPhysics = (Flags & (1 << 1)) ? 1 : 0;
bJustTeleported = (Flags & (1 << 2)) ? 1 : 0;
bJustTeleportedGrips = (Flags & (1 << 3)) ? 1 : 0;
bPausedTracking = (Flags & (1 << 4)) ? 1 : 0;
bOutSuccess = true;
if (bPausedTracking)
{
bOutSuccess &= PausedTrackingLoc.NetSerialize(Ar, Map, bOutSuccess);
uint16 Yaw = 0;
if (Ar.IsSaving())
{
Yaw = FRotator::CompressAxisToShort(PausedTrackingRot);
Ar << Yaw;
}
else
{
Ar << Yaw;
PausedTrackingRot = Yaw;
}
}
// update location, rotation, linear velocity
bOutSuccess &= SerializeQuantizedVector(Ar, Location, BaseSettings.LocationQuantizationLevel);
switch (BaseSettings.RotationQuantizationLevel)
{
case ERotatorQuantization::ByteComponents:
{
Rotation.SerializeCompressed(Ar);
break;
}
case ERotatorQuantization::ShortComponents:
{
Rotation.SerializeCompressedShort(Ar);
break;
}
}
bOutSuccess &= SerializeQuantizedVector(Ar, LinearVelocity, BaseSettings.VelocityQuantizationLevel);
// update angular velocity if required
if (bRepPhysics)
{
bOutSuccess &= SerializeQuantizedVector(Ar, AngularVelocity, BaseSettings.VelocityQuantizationLevel);
}
return true;
}
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};
template<>
@ -276,7 +216,7 @@ public:
/** BaseVR Character movement component belongs to */
UPROPERTY(Transient, DuplicateTransient)
AVRPlayerController* OwningVRPlayerController;
TObjectPtr<AVRPlayerController> OwningVRPlayerController;
// If true then we will retain roomscale tracking in relative space of the character.
// If false than the movement component will offset to the hmd tracking and the tracking will be nulled out

View file

@ -54,6 +54,7 @@ public:
static const float CLIMB_SWEEP_EDGE_REJECT_DISTANCE;
virtual bool IsWithinClimbingEdgeTolerance(const FVector& CapsuleLocation, const FVector& TestImpactPoint, const float CapsuleRadius) const;
virtual bool VRClimbStepUp(const FVector& GravDir, const FVector& Delta, const FHitResult &InHit, FStepDownResult* OutStepDownResult = nullptr) override;
virtual FVector GetActorFeetLocation() const override;
virtual bool IsWithinEdgeTolerance(const FVector& CapsuleLocation, const FVector& TestImpactPoint, const float CapsuleRadius) const override;

View file

@ -8,6 +8,7 @@
#include "Navigation/PathFollowingComponent.h"
#include "AbstractNavData.h"
#include "Runtime/Launch/Resources/Version.h"
#include "VRPathFollowingComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogPathFollowingVR, Warning, All);
@ -18,8 +19,17 @@ class VREXPANSIONPLUGIN_API UVRPathFollowingComponent : public UPathFollowingCom
GENERATED_BODY()
public:
UPROPERTY(transient)
UVRBaseCharacterMovementComponent* VRMovementComp;
virtual void SetNavMovementInterface(INavMovementInterface* NavMoveInterface) override;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Update with 5.5, they are now using the interface feet location so I don't think I need to override anything anymore.
// Keeping the class intact in case people subclassed it.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/*UPROPERTY(transient)
TObjectPtr<UVRBaseCharacterMovementComponent> VRMovementComp;
// Add link to VRMovementComp
void SetMovementComponent(UNavMovementComponent* MoveComp) override;
@ -28,29 +38,29 @@ public:
// Have to override this to call the correct HasReachCurrentTarget
void UpdatePathSegment() override;
bool HasReachedCurrentTarget(const FVector& CurrentLocation) const;
*/
// Had to override this to get the correct DebugReachTest
virtual void GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const override;
void DebugReachTest(float& CurrentDot, float& CurrentDistance, float& CurrentHeight, uint8& bDotFailed, uint8& bDistanceFailed, uint8& bHeightFailed) const;
/*void DebugReachTest(float& CurrentDot, float& CurrentDistance, float& CurrentHeight, uint8& bDotFailed, uint8& bDistanceFailed, uint8& bHeightFailed) const;
void FollowPathSegment(float DeltaTime) override;
int32 DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const override;
// This has a foot location when using meta paths, i'm not overriding it yet but this means that meta paths might have slightly bugged implementation.
/** notify about finished movement */
//virtual void OnPathFinished(const FPathFollowingResult& Result) override;
/** pause path following
// notify about finished movement
virtual void OnPathFinished(const FPathFollowingResult& Result) override;
*/
/* pause path following
* @param RequestID - request to pause, FAIRequestID::CurrentRequest means pause current request, regardless of its ID */
void PauseMove(FAIRequestID RequestID = FAIRequestID::CurrentRequest, EPathFollowingVelocityMode VelocityMode = EPathFollowingVelocityMode::Reset) override;
//void PauseMove(FAIRequestID RequestID = FAIRequestID::CurrentRequest, EPathFollowingVelocityMode VelocityMode = EPathFollowingVelocityMode::Reset) override;
// Now has an actor feet call .......Just a debug reference
//virtual FAIRequestID RequestMove(FNavPathSharedPtr Path, FRequestCompletedSignature OnComplete, const AActor* DestinationActor = NULL, float AcceptanceRadius = UPathFollowingComponent::DefaultAcceptanceRadius, bool bStopOnOverlap = true, FCustomMoveSharedPtr GameData = NULL) override;
/*virtual FAIRequestID RequestMove(FNavPathSharedPtr Path, FRequestCompletedSignature OnComplete, const AActor* DestinationActor = NULL, float AcceptanceRadius = UPathFollowingComponent::DefaultAcceptanceRadius, bool bStopOnOverlap = true, FCustomMoveSharedPtr GameData = NULL) override;
//
// Fine in 4.13
bool ShouldCheckPathOnResume() const override;
// Fine in 4.13, had to change feet based for both
bool UpdateBlockDetection() override;
bool UpdateBlockDetection() override;*/
};

View file

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