diff --git a/VIRTUOS_ExpansionPluginTests/.vsconfig b/VIRTUOS_ExpansionPluginTests/.vsconfig index 7f5f4b3..b3c233d 100644 --- a/VIRTUOS_ExpansionPluginTests/.vsconfig +++ b/VIRTUOS_ExpansionPluginTests/.vsconfig @@ -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", diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap index 3b309e6..5b839b2 100644 --- a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap +++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5aab6f41e292d60b10ff7cd066a3d379e792019d79ec6fb488f2532a84987ad -size 965258 +oid sha256:2b83e4e8b5b780a863df9625d9c4790b57bb829b0ed61b3035c275e0d403952f +size 1110477 diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/SPUD b/VIRTUOS_ExpansionPluginTests/Plugins/SPUD index 006e2b4..968fe10 160000 --- a/VIRTUOS_ExpansionPluginTests/Plugins/SPUD +++ b/VIRTUOS_ExpansionPluginTests/Plugins/SPUD @@ -1 +1 @@ -Subproject commit 006e2b4922eb4f7af0d6df1468a042a98eecf68f +Subproject commit 968fe10c9f08859c64ea88768d5216140fc340d9 diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin index 7232e3d..f04ed51 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin @@ -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", diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h index 6da369d..c064148 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h @@ -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" diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h index b27ff0f..3e78598 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h @@ -189,16 +189,16 @@ public: // Need this as I can't think of another way for an actor component to make sure it isn't on the server inline bool IsLocallyControlled() const { -#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22) +//#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22) const AActor* MyOwner = GetOwner(); return MyOwner->HasLocalNetOwner(); -#else +//#else // I like epics new authority check more than mine - const AActor* MyOwner = GetOwner(); + /* const AActor* MyOwner = GetOwner(); const APawn* MyPawn = Cast(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 diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/GripMotionControllerComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/GripMotionControllerComponent.cpp index 5bcf7aa..6f56b35 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/GripMotionControllerComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/GripMotionControllerComponent.cpp @@ -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()) { diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableActor.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableActor.cpp index 57d69df..8e388ad 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableActor.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableActor.cpp @@ -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(World->GetPhysicsScene())) + + if (bShouldUsePhysicsReplicationCache) { - if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame)) + if (FPhysScene_Chaos* Scene = static_cast(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; + } } } diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippablePhysicsReplication.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippablePhysicsReplication.cpp index b06da07..1f34a92 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippablePhysicsReplication.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippablePhysicsReplication.cpp @@ -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(BI->GetPhysicsActorHandle())) + { + if (Chaos::FPBDRigidsSolver* Solver = Proxy->GetSolver()) + { + 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, TMapPhysicsPrediction.bEnablePhysicsPrediction) { if (UWorld* World = GetOwningWorld()) { @@ -722,6 +804,7 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMapGetFirstPlayerController()) { + LocalFrameOffsetAssigned = PlayerController->GetNetworkPhysicsTickOffsetAssigned(); LocalFrameOffset = PlayerController->GetNetworkPhysicsTickOffset(); } } @@ -753,6 +836,9 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMapGetLocalRole(); const bool bIsSimulated = OwnerRole == ROLE_SimulatedProxy; const bool bIsReplicatedAutonomous = OwnerRole == ROLE_AutonomousProxy && PrimComp->bReplicatePhysicsToAutonomousProxy; @@ -821,9 +907,14 @@ void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMapInputData.Add(AsyncInputData); } ReplicatedTargetsQueueVR.Reset(); @@ -919,7 +1010,9 @@ bool FRepMovementVR::GatherActorsMovement(AActor* OwningActor) void FPhysicsReplicationAsyncVR::OnPhysicsObjectUnregistered_Internal(Chaos::FConstPhysicsObjectHandle PhysicsObject) { - ObjectToTarget.Remove(PhysicsObject); + RemoveObjectFromReplication(PhysicsObject); + + // Only clear Settings when PhysicsObject unregister (not when it stops replicating, hence why it's not baked into RemoveObjectFromReplication()) ObjectToSettings.Remove(PhysicsObject); } @@ -990,18 +1083,18 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal() { if (Input.TargetState.Flags == ERigidBodyFlags::None) { - // Remove replication target - ObjectToTarget.Remove(Input.PhysicsObject); + // Remove replication target + RemoveObjectFromReplication(Input.PhysicsObject); continue; } if (!bRewindDataExist && Input.RepMode == EPhysicsReplicationMode::Resimulation) { // We don't have rewind data but an actor is set to replicate using resimulation; we need to enable rewind capture. - if (ensure(Chaos::FPBDRigidsSolver::IsNetworkPhysicsPredictionEnabled())) + if (ensure(Chaos::FPBDRigidsSolver::IsNetworkPhysicsPredictionEnabled() && RigidsSolver->IsUsingFixedDt())) { - const int32 NumFrames = FMath::Max(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(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; } diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshActor.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshActor.cpp index b1a36b3..2af2f6f 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshActor.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshActor.cpp @@ -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& 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(World->GetPhysicsScene())) + + if (bShouldUsePhysicsReplicationCache) { - if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame)) + if (FPhysScene_Chaos* Scene = static_cast(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; + } } } diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshComponent.cpp index 4059705..78f3fcd 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableSkeletalMeshComponent.cpp @@ -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" diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableStaticMeshActor.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableStaticMeshActor.cpp index 74dec5c..3a65f6e 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableStaticMeshActor.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Grippables/GrippableStaticMeshActor.cpp @@ -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(World->GetPhysicsScene())) + + if (bShouldUsePhysicsReplicationCache) { - if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame)) + if (FPhysScene_Chaos* Scene = static_cast(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) { diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRDialComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRDialComponent.cpp index 91b1359..d6ec172 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRDialComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRDialComponent.cpp @@ -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)) { diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRSliderComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRSliderComponent.cpp index 9113d33..acc881d 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRSliderComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Interactibles/VRSliderComponent.cpp @@ -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) { diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/CollisionIgnoreSubsystem.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/CollisionIgnoreSubsystem.cpp index 0308f80..b619e7b 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/CollisionIgnoreSubsystem.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/CollisionIgnoreSubsystem.cpp @@ -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" diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/OptionalRepSkeletalMeshActor.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/OptionalRepSkeletalMeshActor.cpp index 480b36f..1ec865c 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/OptionalRepSkeletalMeshActor.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/OptionalRepSkeletalMeshActor.cpp @@ -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(TArraySkeletalBodySetups[BodyIdx]) + if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx].Get()) { OutLabels.Add(PhysicsAssetBodySetup->BoneName); } diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRAIPerceptionOverrides.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRAIPerceptionOverrides.cpp index 8dcd672..0e932b3 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRAIPerceptionOverrides.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRAIPerceptionOverrides.cpp @@ -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& 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& 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 TFunctionGetTargetActor() != &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(&TargetActor); - } - else if (SightTarget == nullptr) - { - FAISightTargetVR NewSightTarget(&TargetActor); - - SightTarget = &(ObservedTargets.Add(NewSightTarget.TargetId, NewSightTarget)); - SightTarget->SightTargetInterface = Cast(&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()) + { + SightTarget->WeakSightTargetInterface = InterfaceComponent; + } + else + { + SightTarget->WeakSightTargetInterface = Cast(&TargetActor); + } } // set/update data diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VREPhysicalAnimationComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VREPhysicalAnimationComponent.cpp index f109a22..6f0c6e3 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VREPhysicalAnimationComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VREPhysicalAnimationComponent.cpp @@ -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" diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRFullScreenUserWidget.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRFullScreenUserWidget.cpp index b9592a2..014b349 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRFullScreenUserWidget.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRFullScreenUserWidget.cpp @@ -90,7 +90,7 @@ namespace void RemoveWidget(UVRFullScreenUserWidget* InWidget) { - WidgetsToHide.RemoveSingleSwap(InWidget, false); + WidgetsToHide.RemoveSingleSwap(InWidget, EAllowShrinking::No); } private: diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRRenderTargetManager.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRRenderTargetManager.cpp index 901c0f0..fbe5a35 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRRenderTargetManager.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/Misc/VRRenderTargetManager.cpp @@ -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. diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/ReplicatedVRCameraComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/ReplicatedVRCameraComponent.cpp index c592ee2..346d4ec 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/ReplicatedVRCameraComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/ReplicatedVRCameraComponent.cpp @@ -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()) { diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacter.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacter.cpp index 43efdf8..13d7a03 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacter.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacter.cpp @@ -15,6 +15,7 @@ #include "Net/UnrealNetwork.h" #include "XRMotionControllerBase.h" #include "NavFilters/NavigationQueryFilter.h" +#include "Misc/EngineNetworkCustomVersion.h" //#include "Runtime/Engine/Private/EnginePrivate.h" #if WITH_PUSH_MODEL @@ -142,7 +143,7 @@ AVRBaseCharacter::AVRBaseCharacter(const FObjectInitializer& ObjectInitializer) // Setting a minimum of every frame for replication consideration (UT uses this value for characters and projectiles). // Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default - MinNetUpdateFrequency = 100.0f; + SetMinNetUpdateFrequency(100.0f); // This is for smooth turning, we have more of a use for this than FPS characters do // Due to roll/pitch almost never being off 0 for VR the cost is just one byte so i'm fine defaulting it here @@ -434,6 +435,11 @@ void AVRBaseCharacter::OnRep_ReplicatedMovement() ReppedMovement.Location = ReplicatedMovementVR.Location; ReppedMovement.Rotation = ReplicatedMovementVR.Rotation; + ReppedMovement.ServerFrame = ReplicatedMovementVR.ServerFrame; + ReppedMovement.ServerPhysicsHandle = ReplicatedMovementVR.ServerPhysicsHandle; + ReppedMovement.bRepAcceleration = ReplicatedMovementVR.bRepAcceleration; + ReppedMovement.Acceleration = ReplicatedMovementVR.Acceleration; + Super::OnRep_ReplicatedMovement(); if (!IsLocallyControlled()) @@ -469,8 +475,14 @@ void AVRBaseCharacter::GatherCurrentMovement() ReplicatedMovementVR.LinearVelocity = ReppedMovement.LinearVelocity; ReplicatedMovementVR.Location = ReppedMovement.Location; ReplicatedMovementVR.Rotation = ReppedMovement.Rotation; + ReplicatedMovementVR.ServerFrame = ReppedMovement.ServerFrame; + ReplicatedMovementVR.ServerPhysicsHandle = ReppedMovement.ServerPhysicsHandle; + ReplicatedMovementVR.bRepAcceleration = ReppedMovement.bRepAcceleration; + ReplicatedMovementVR.Acceleration = ReppedMovement.Acceleration; + ReplicatedMovementVR.bJustTeleported = bFlagTeleported; ReplicatedMovementVR.bJustTeleportedGrips = bFlagTeleportedGrips; + bFlagTeleported = false; bFlagTeleportedGrips = false; ReplicatedMovementVR.bPausedTracking = bTrackingPaused; @@ -1001,7 +1013,7 @@ FVector AVRBaseCharacter::SetActorLocationAndRotationVR(FVector NewLoc, FRotator FVector AVRBaseCharacter::SetActorLocationVR(FVector NewLoc, bool bTeleport, bool bSetCapsuleLocation) { FVector NewLocation; - FRotator NewRotation; + //FRotator NewRotation; FVector PivotOffsetVal = (bSetCapsuleLocation ? GetVRLocation_Inline() : GetProjectedVRLocation()) - GetActorLocation(); PivotOffsetVal.Z = 0.0f; @@ -1243,4 +1255,103 @@ void AVRBaseCharacter::SetVRReplicateCapsuleHeight(bool bNewVRReplicateCapsuleHe #if WITH_PUSH_MODEL MARK_PROPERTY_DIRTY_FROM_NAME(AVRBaseCharacter, VRReplicateCapsuleHeight, this); #endif +} + +bool FRepMovementVRCharacter::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) +{ + Ar.UsingCustomVersion(FEngineNetworkCustomVersion::Guid); + + FRepMovement BaseSettings = Owner ? Owner->GetReplicatedMovement() : FRepMovement(); + + // pack bitfield with flags + const bool bServerFrameAndHandleSupported = Ar.EngineNetVer() >= FEngineNetworkCustomVersion::RepMoveServerFrameAndHandle && Ar.EngineNetVer() != FEngineNetworkCustomVersion::Ver21AndViewPitchOnly_DONOTUSE; + uint8 Flags = (bSimulatedPhysicSleep << 0) | (bRepPhysics << 1) | (bJustTeleported << 2) | (bJustTeleportedGrips << 3) | (bPausedTracking << 4); + Ar.SerializeBits(&Flags, 5); + bSimulatedPhysicSleep = (Flags & (1 << 0)) ? 1 : 0; + bRepPhysics = (Flags & (1 << 1)) ? 1 : 0; + const bool bRepServerFrame = (Flags & (1 << 2) && bServerFrameAndHandleSupported) ? 1 : 0; + const bool bRepServerHandle = (Flags & (1 << 3) && bServerFrameAndHandleSupported) ? 1 : 0; + + bJustTeleported = (Flags & (1 << 2)) ? 1 : 0; + bJustTeleportedGrips = (Flags & (1 << 3)) ? 1 : 0; + bPausedTracking = (Flags & (1 << 4)) ? 1 : 0; + + bOutSuccess = true; + + if (bPausedTracking) + { + bOutSuccess &= PausedTrackingLoc.NetSerialize(Ar, Map, bOutSuccess); + + uint16 Yaw = 0; + if (Ar.IsSaving()) + { + Yaw = FRotator::CompressAxisToShort(PausedTrackingRot); + Ar << Yaw; + } + else + { + Ar << Yaw; + PausedTrackingRot = Yaw; + } + + } + + // update location, rotation, linear velocity + bOutSuccess &= SerializeQuantizedVector(Ar, Location, BaseSettings.LocationQuantizationLevel); + + switch (BaseSettings.RotationQuantizationLevel) + { + case ERotatorQuantization::ByteComponents: + { + Rotation.SerializeCompressed(Ar); + break; + } + + case ERotatorQuantization::ShortComponents: + { + Rotation.SerializeCompressedShort(Ar); + break; + } + } + + bOutSuccess &= SerializeQuantizedVector(Ar, LinearVelocity, BaseSettings.VelocityQuantizationLevel); + + // update angular velocity if required + if (bRepPhysics) + { + bOutSuccess &= SerializeQuantizedVector(Ar, AngularVelocity, BaseSettings.VelocityQuantizationLevel); + } + + if (bRepServerFrame) + { + uint32 uServerFrame = (uint32)ServerFrame; + Ar.SerializeIntPacked(uServerFrame); + ServerFrame = (int32)uServerFrame; + } + + if (bRepServerHandle) + { + uint32 uServerPhysicsHandle = (uint32)ServerPhysicsHandle; + Ar.SerializeIntPacked(uServerPhysicsHandle); + ServerPhysicsHandle = (int32)uServerPhysicsHandle; + } + + if (Ar.EngineNetVer() >= FEngineNetworkCustomVersion::RepMoveOptionalAcceleration) + { + uint8 AccelFlags = (bRepAcceleration << 0); + Ar.SerializeBits(&AccelFlags, 1); + bRepAcceleration = (AccelFlags & (1 << 0)) ? 1 : 0; + + if (bRepAcceleration) + { + // Note that we're using the same quantization as Velocity, since the units are commonly on the same order + bOutSuccess &= SerializeQuantizedVector(Ar, Acceleration, VelocityQuantizationLevel); + } + } + else if (Ar.IsLoading()) + { + bRepAcceleration = false; + } + + return true; } \ No newline at end of file diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacterMovementComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacterMovementComponent.cpp index db3bddd..581bc8b 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacterMovementComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRBaseCharacterMovementComponent.cpp @@ -489,14 +489,32 @@ void UVRBaseCharacterMovementComponent::EndPushBackNotification() FVector UVRBaseCharacterMovementComponent::GetActorFeetLocationVR() const { - if (AVRBaseCharacter * BaseCharacter = Cast(GetCharacterOwner())) + + const UCapsuleComponent* const CapsuleComponent = CharacterOwner ? CharacterOwner->GetCapsuleComponent() : Cast(UpdatedComponent); + if (CapsuleComponent) + { + const float HalfHeight = CapsuleComponent->GetScaledCapsuleHalfHeight(); + if (AVRBaseCharacter* BaseCharacter = Cast(GetCharacterOwner())) + { + return BaseCharacter->OffsetComponentToWorld.GetLocation() + HalfHeight * GetGravityDirection(); + } + else + { + return UpdatedComponent->GetComponentLocation() + HalfHeight * GetGravityDirection(); + } + } + + return Super::GetActorFeetLocation(); + + + /*if (AVRBaseCharacter* BaseCharacter = Cast(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(); diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRCharacterMovementComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRCharacterMovementComponent.cpp index 504ad79..65cc5a3 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRCharacterMovementComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRCharacterMovementComponent.cpp @@ -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(ProjectToGravityFloor(Velocity).Size() * TouchForceFactorModified, MinTouchForce > 0.0f ? MinTouchForce : -FLT_MAX, MaxTouchForce > 0.0f ? MaxTouchForce : FLT_MAX); @@ -1541,7 +1551,7 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds) // Trace to get the hit location on the capsule FHitResult Hit; bool bHasHit = UpdatedPrimitive->LineTraceComponent(Hit, BodyLocation, - FVector(MyLocation.X, MyLocation.Y, BodyLocation.Z), + ProjectToGravityFloor(MyLocation) + GetGravitySpaceComponentZ(BodyLocation), QueryParams); FVector HitLoc = Hit.ImpactPoint; @@ -1554,12 +1564,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds) bIsPenetrating = true; } - const float DistanceNow = (HitLoc - BodyLocation).SizeSquared2D(); - const float DistanceLater = (HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared2D(); + const float DistanceNow = ProjectToGravityFloor(HitLoc - BodyLocation).SizeSquared(); + const float DistanceLater = ProjectToGravityFloor(HitLoc - (BodyLocation + BodyVelocity * DeltaSeconds)).SizeSquared(); if (bHasHit && DistanceNow < StopBodyDistance && !bIsPenetrating) { - OverlapBody->SetLinearVelocity(FVector(0.0f, 0.0f, 0.0f), false); + OverlapBody->SetLinearVelocity(FVector::ZeroVector, false); } else if (DistanceLater <= DistanceNow || bIsPenetrating) { @@ -1567,11 +1577,12 @@ void UVRCharacterMovementComponent::ApplyRepulsionForce(float DeltaSeconds) if (bHasHit) { - ForceCenter.Z = HitLoc.Z; + SetGravitySpaceZ(ForceCenter, GetGravitySpaceZ(HitLoc)); } else { - ForceCenter.Z = FMath::Clamp(BodyLocation.Z, MyLocation.Z - CapsuleHalfHeight, MyLocation.Z + CapsuleHalfHeight); + const FVector::FReal MyLocationZ = GetGravitySpaceZ(MyLocation); + SetGravitySpaceZ(ForceCenter, FMath::Clamp(GetGravitySpaceZ(BodyLocation), MyLocationZ - CapsuleHalfHeight, MyLocationZ + CapsuleHalfHeight)); } OverlapBody->AddRadialForceToBody(ForceCenter, RepulsionForceRadius, RepulsionForce * Mass, ERadialImpulseFalloff::RIF_Constant); @@ -1650,7 +1661,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl } // Move along the current floor - const FVector Delta = RotateGravityToWorld(RotateWorldToGravity(InVelocity) * FVector(1.0, 1.0, 0.0)) * DeltaSeconds; + const FVector Delta = ProjectToGravityFloor(InVelocity) * DeltaSeconds; FHitResult Hit(1.f); FVector RampVector = ComputeGroundMovementDelta(Delta, CurrentFloor.HitResult, CurrentFloor.bLineTrace); SafeMoveUpdatedComponent(RampVector, UpdatedComponent->GetComponentQuat(), true, Hit); @@ -1671,7 +1682,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl { // We impacted something (most likely another ramp, but possibly a barrier). float PercentTimeApplied = Hit.Time; - if ((Hit.Time > 0.f) && (Hit.Normal.Z > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit)) + if ((Hit.Time > 0.f) && (GetGravitySpaceZ(Hit.Normal) > UE_KINDA_SMALL_NUMBER) && IsWalkable(Hit)) { // Another walkable ramp. const float InitialPercentRemaining = 1.f - PercentTimeApplied; @@ -1691,8 +1702,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl const FVector PreStepUpLocation = UpdatedComponent->GetComponentLocation(); const FVector GravDir = GetGravityDirection(); - // I add in the HMD difference from last frame to the step up check to enforce it stepping up - if (!StepUp(GravDir, (Delta * (1.f - PercentTimeApplied)) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, OutStepDownResult)) + if (!StepUp(GetGravityDirection(), Delta * (1.f - PercentTimeApplied), Hit, OutStepDownResult)) { UE_LOG(LogVRCharacterMovement, Verbose, TEXT("- StepUp (ImpactNormal %s, Normal %s"), *Hit.ImpactNormal.ToString(), *Hit.Normal.ToString()); HandleImpact(Hit, LastMoveTimeSlice, RampVector); @@ -1710,7 +1720,7 @@ void UVRCharacterMovementComponent::MoveAlongFloor(const FVector& InVelocity, fl if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity() && StepUpTimeSlice >= UE_KINDA_SMALL_NUMBER) { Velocity = (UpdatedComponent->GetComponentLocation() - PreStepUpLocation) / StepUpTimeSlice; - Velocity = FVector::VectorPlaneProject(Velocity, -GravDir); + Velocity = ProjectToGravityFloor(Velocity); } } } @@ -1745,8 +1755,9 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight); // Don't bother stepping up if top of capsule is hitting something. - const float InitialImpactZ = InHit.ImpactPoint.Z; - if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius)) + const float InitialImpactZ = InHit.ImpactPoint | -GravDir; + const float OldLocationZ = OldLocation | -GravDir; + if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius)) { return false; } @@ -1762,7 +1773,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector float StepTravelUpHeight = MaxStepHeight; float StepTravelDownHeight = StepTravelUpHeight; const float StepSideZ = -1.f * FVector::DotProduct(InHit.ImpactNormal, GravDir);//const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir); - float PawnInitialFloorBaseZ = OldLocation.Z -PawnHalfHeight; + float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight; float PawnFloorPointZ = PawnInitialFloorBaseZ; if (IsMovingOnGround() && CurrentFloor.IsWalkableFloor()) @@ -1776,7 +1787,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector const bool bHitVerticalFace = !IsWithinEdgeTolerance(InHit.Location, InHit.ImpactPoint, PawnRadius); if (!CurrentFloor.bLineTrace && !bHitVerticalFace) { - PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint.Z; + PawnFloorPointZ = CurrentFloor.HitResult.ImpactPoint | -GravDir; } else { @@ -1868,7 +1879,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector if (Hit.IsValidBlockingHit()) { // See if this step sequence would have allowed us to travel higher than our max step height allows. - const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ; + const float DeltaZ = (Hit.ImpactPoint | -GravDir) - PawnFloorPointZ; if (DeltaZ > MaxStepHeight) { //UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ); @@ -1890,7 +1901,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector // Also reject if we would end up being higher than our starting location by stepping down. // It's fine to step down onto an unwalkable normal below us, we will just slide off. Rejecting those moves would prevent us from being able to walk off the edge. - if (Hit.Location.Z > OldLocation.Z) + if ((Hit.Location | -GravDir) > OldLocationZ) { //UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString()); ScopedStepUpMovement.RevertMove(); @@ -1921,7 +1932,7 @@ bool UVRCharacterMovementComponent::StepUp(const FVector& GravDir, const FVector // Reject unwalkable normals if we end up higher than our initial height. // It's fine to walk down onto an unwalkable surface, don't reject those moves. - if (Hit.Location.Z > OldLocation.Z) + if ((Hit.Location | -GravDir) > OldLocationZ) { // We should reject the floor result if we are trying to step up an actual step where we are not able to perch (this is rare). // In those cases we should instead abort the step up and try to slide along the stair. @@ -1990,8 +2001,9 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight); // Don't bother stepping up if top of capsule is hitting something. - const float InitialImpactZ = InHit.ImpactPoint.Z; - if (InitialImpactZ > OldLocation.Z + (PawnHalfHeight - PawnRadius)) + const float InitialImpactZ = InHit.ImpactPoint | -GravDir; + const float OldLocationZ = OldLocation | -GravDir; + if (InitialImpactZ > OldLocationZ + (PawnHalfHeight - PawnRadius)) { return false; } @@ -2013,7 +2025,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const float StepTravelUpHeight = MaxStepHeight; float StepTravelDownHeight = StepTravelUpHeight; const float StepSideZ = -1.f * (InHit.ImpactNormal | GravDir); - float PawnInitialFloorBaseZ = OldLocation.Z - PawnHalfHeight; + float PawnInitialFloorBaseZ = OldLocationZ - PawnHalfHeight; float PawnFloorPointZ = PawnInitialFloorBaseZ; // Scope our movement updates, and do not apply them until all intermediate moves are completed. @@ -2109,7 +2121,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const if (Hit.IsValidBlockingHit()) { // See if this step sequence would have allowed us to travel higher than our max step height allows. - const float DeltaZ = Hit.ImpactPoint.Z - PawnFloorPointZ; + const float DeltaZ = (Hit.ImpactPoint | -GravDir) - PawnFloorPointZ; if (DeltaZ > MaxStepHeight) { UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (too high Height %.3f) up from floor base %f"), DeltaZ, PawnInitialFloorBaseZ); @@ -2131,7 +2143,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const // Also reject if we would end up being higher than our starting location by stepping down. // It's fine to step down onto an unwalkable normal below us, we will just slide off. Rejecting those moves would prevent us from being able to walk off the edge. - if (Hit.Location.Z > OldLocation.Z) + if ((Hit.Location | -GravDir) > OldLocationZ) { UE_LOG(LogVRCharacterMovement, VeryVerbose, TEXT("- Reject StepUp (unwalkable normal %s above old position)"), *Hit.ImpactNormal.ToString()); ScopedStepUpMovement.RevertMove(); @@ -2162,7 +2174,7 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const // Reject unwalkable normals if we end up higher than our initial height. // It's fine to walk down onto an unwalkable surface, don't reject those moves. - if (Hit.Location.Z > OldLocation.Z) + if ((Hit.Location | -GravDir) > OldLocationZ) { // We should reject the floor result if we are trying to step up an actual step where we are not able to perch (this is rare). // In those cases we should instead abort the step up and try to slide along the stair. @@ -2195,6 +2207,12 @@ bool UVRCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const return true; } +FVector UVRCharacterMovementComponent::GetActorFeetLocation() const +{ + // Call into the VR version of it instead + return GetActorFeetLocationVR(); +} + void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds) { @@ -2259,8 +2277,12 @@ void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds) // @todo: This assumes only Yaw is used, currently a valid assumption. This is the only reason FaceRotation() is used above really, aside from being a virtual hook. if (bOrientRotationToMovement || (bUseControllerDesiredRotation && CharacterOwner->Controller)) { - TargetRotator.Pitch = 0.f; - TargetRotator.Roll = 0.f; + // Custom gravity automatically aligns the character to the gravity direction, so we shouldn't zero out pitch and roll. + if (!HasCustomGravity()) + { + TargetRotator.Pitch = 0.f; + TargetRotator.Roll = 0.f; + } MoveUpdatedComponent(FVector::ZeroVector, TargetRotator, false); FinalQuat = UpdatedComponent->GetComponentQuat(); } @@ -2276,19 +2298,31 @@ void UVRCharacterMovementComponent::UpdateBasedMovement(float DeltaSeconds) } } - // We need to offset the base of the character here, not its origin, so offset by half height - float HalfHeight, Radius; - CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight); - - if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale) + FVector NewWorldPos; + if (HasCustomGravity()) { - HalfHeight = 0; + const FVector RotationRadius = UpdatedComponent->GetComponentLocation() - NewBaseLocation; + const FVector RotationDelta = DeltaQuat.RotateVector(RotationRadius) - RotationRadius; + const FVector LinearDelta = NewBaseLocation - OldBaseLocation; + NewWorldPos = ConstrainLocationToPlane(UpdatedComponent->GetComponentLocation() + RotationDelta + LinearDelta); } + else + { + // We need to offset the base of the character here, not its origin, so offset by half height + float HalfHeight, Radius; + CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(Radius, HalfHeight); - FVector const BaseOffset(0.0f, 0.0f, HalfHeight); + if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale) + { + HalfHeight = 0; + } - FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset); - FVector const NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset); + FVector const BaseOffset(0.0f, 0.0f, HalfHeight); + + FVector const LocalBasePos = OldLocalToWorld.InverseTransformPosition(UpdatedComponent->GetComponentLocation() - BaseOffset); + NewWorldPos = ConstrainLocationToPlane(NewLocalToWorld.TransformPosition(LocalBasePos) + BaseOffset); + } + DeltaPosition = ConstrainDirectionToPlane(NewWorldPos - UpdatedComponent->GetComponentLocation()); // move attached actor @@ -2377,16 +2411,13 @@ FVector UVRCharacterMovementComponent::GetImpartedMovementBaseVelocity() const if (bImpartBaseAngularVelocity) { // Base position should be the bottom of the actor since I offset the capsule now - float HalfHeight = CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); - if (!BaseVRCharacterOwner || BaseVRCharacterOwner->bRetainRoomscale) + if (BaseVRCharacterOwner && BaseVRCharacterOwner->bRetainRoomscale) { HalfHeight = 0.0f; } - - const FVector CharacterBasePosition = (UpdatedComponent->GetComponentLocation() - FVector(0.f, 0.f, HalfHeight)); - - + + const FVector CharacterBasePosition = (UpdatedComponent->GetComponentLocation() + HalfHeight * GetGravityDirection()); const FVector BaseTangentialVel = MovementBaseUtility::GetMovementBaseTangentialVelocity(MovementBase, CharacterOwner->GetBasedMovement().BoneName, CharacterBasePosition); BaseVelocity += BaseTangentialVel; } @@ -2562,13 +2593,13 @@ float UVRCharacterMovementComponent::ImmersionDepth() const if (VRRootCapsule) { - TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + FVector(0.f, 0.f, CollisionHalfHeight); - TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - FVector(0.f, 0.f, CollisionHalfHeight); + TraceStart = VRRootCapsule->OffsetComponentToWorld.GetLocation() + CollisionHalfHeight * -GetGravityDirection(); + TraceEnd = VRRootCapsule->OffsetComponentToWorld.GetLocation() - CollisionHalfHeight * -GetGravityDirection(); } else { - TraceStart = UpdatedComponent->GetComponentLocation() + FVector(0.f, 0.f, CollisionHalfHeight); - TraceEnd = UpdatedComponent->GetComponentLocation() -FVector(0.f, 0.f, CollisionHalfHeight); + TraceStart = UpdatedComponent->GetComponentLocation() + CollisionHalfHeight * -GetGravityDirection(); + TraceEnd = UpdatedComponent->GetComponentLocation() - CollisionHalfHeight * -GetGravityDirection(); } FCollisionQueryParams NewTraceParams(CharacterMovementComponentStatics::ImmersionDepthName, true); @@ -2669,18 +2700,19 @@ void UVRCharacterMovementComponent::PhysFlying(float deltaTime, int32 Iterations if (Hit.Time < 1.f) { - const FVector GravDir = FVector(0.f, 0.f, -1.f); const FVector VelDir = Velocity.GetSafeNormal(); - const float UpDown = GravDir | VelDir; + const float UpDown = VelDir | GetGravityDirection(); + bool bSteppedUp = false; - if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit)) + if ((FMath::Abs(GetGravitySpaceZ(Hit.ImpactNormal)) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit)) { - float stepZ = UpdatedComponent->GetComponentLocation().Z; - bSteppedUp = StepUp(GravDir, (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time) /*+ AdditionalVRInputVector.GetSafeNormal2D()*/, Hit, nullptr); + const FVector::FReal StepZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()); + bSteppedUp = StepUp(GetGravityDirection(), (Adjusted + AdditionalVRInputVector) * (1.f - Hit.Time), Hit); if (bSteppedUp) { - OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ); + const FVector::FReal LocationZ = GetGravitySpaceZ(UpdatedComponent->GetComponentLocation()) + (GetGravitySpaceZ(OldLocation) - StepZ); + SetGravitySpaceZ(OldLocation, LocationZ); } } @@ -2713,9 +2745,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration return; } - FVector FallAcceleration = GetFallingLateralAcceleration(deltaTime); - const FVector GravityRelativeFallAcceleration = RotateWorldToGravity(FallAcceleration); - FallAcceleration = RotateGravityToWorld(FVector(GravityRelativeFallAcceleration.X, GravityRelativeFallAcceleration.Y, 0)); + const FVector FallAcceleration = ProjectToGravityFloor(GetFallingLateralAcceleration(deltaTime)); const bool bHasLimitedAirControl = ShouldLimitAirControl(deltaTime, FallAcceleration); // Rewind the players position by the new capsule location @@ -2751,7 +2781,7 @@ void UVRCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iteration TGuardValue 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 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(0.33f * MaxSwimSpeed, Velocity.Z * Depth * Depth); + SetGravitySpaceZ(Velocity, FMath::Max(0.33f * MaxSwimSpeed, GetGravitySpaceZ(Velocity) * Depth * Depth)); } else if (Depth < 0.65f) { - bLimitedUpAccel = (Acceleration.Z > 0.f); - Acceleration.Z = FMath::Min(0.1f, Acceleration.Z); + bLimitedUpAccel = (OriginalAccelZ > 0.f); + SetGravitySpaceZ(Acceleration, FMath::Min(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); diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRGestureComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRGestureComponent.cpp index 00be260..b660744 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRGestureComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRGestureComponent.cpp @@ -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; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRPathFollowingComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRPathFollowingComponent.cpp index ffdc6df..6768f25 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRPathFollowingComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRPathFollowingComponent.cpp @@ -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(NavMovementInterface->GetOwnerAsObject())) + { + if (IsValid(VRMovement->VRMovementReference)) + { + OnRequestFinished.AddUObject(VRMovement->VRMovementReference, &UVRBaseCharacterMovementComponent::OnMoveCompleted); + } + } + } +} void UVRPathFollowingComponent::GetDebugStringTokens(TArray& Tokens, TArray& Flags) const { @@ -82,52 +100,8 @@ void UVRPathFollowingComponent::GetDebugStringTokens(TArray& Tokens, TA Flags.Add(bFailedHeight ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue); } -void UVRPathFollowingComponent::PauseMove(FAIRequestID RequestID, EPathFollowingVelocityMode VelocityMode) -{ - //UE_VLOG(GetOwner(), LogPathFollowing, Log, TEXT("PauseMove: RequestID(%u)"), RequestID); - if (Status == EPathFollowingStatus::Paused) - { - return; - } - if (RequestID.IsEquivalent(GetCurrentRequestId())) - { - if ((VelocityMode == EPathFollowingVelocityMode::Reset) && MovementComp && HasMovementAuthority()) - { - MovementComp->StopMovementKeepPathing(); - } - - LocationWhenPaused = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector; - PathTimeWhenPaused = Path.IsValid() ? Path->GetTimeStamp() : 0.; - Status = EPathFollowingStatus::Paused; - - UpdateMoveFocus(); - } -} - - - -bool UVRPathFollowingComponent::ShouldCheckPathOnResume() const -{ - bool bCheckPath = true; - if (MovementComp != NULL) - { - float AgentRadius = 0.0f, AgentHalfHeight = 0.0f; - MovementComp->GetOwner()->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight); - - const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocation() : MovementComp->GetActorFeetLocation()); - const FVector::FReal DeltaMove2DSq = (CurrentLocation - LocationWhenPaused).SizeSquared2D(); - const FVector::FReal DeltaZ = FMath::Abs(CurrentLocation.Z - LocationWhenPaused.Z); - if (DeltaMove2DSq < FMath::Square(AgentRadius) && DeltaZ < (AgentHalfHeight * 0.5)) - { - bCheckPath = false; - } - } - - return bCheckPath; -} - -int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const +/*int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const { int32 PickedPathPoint = INDEX_NONE; @@ -192,7 +166,7 @@ bool UVRPathFollowingComponent::UpdateBlockDetection() { LocationSamples.AddZeroed(1); } - + LocationSamples[NextSampleIdx] = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationBased() : MovementComp->GetActorFeetLocationBased()); NextSampleIdx = (NextSampleIdx + 1) % BlockDetectionSampleCount; return true; @@ -302,8 +276,8 @@ void UVRPathFollowingComponent::UpdatePathSegment() if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex)) { //LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct, - //*Path->GetPathPointLocation(MoveSegmentStartIndex), - //*Path->GetPathPointLocation(MoveSegmentEndIndex)); + //Path->GetPathPointLocation(MoveSegmentStartIndex), + //Path->GetPathPointLocation(MoveSegmentEndIndex)); } else { @@ -455,7 +429,7 @@ void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& Current const FVector ToGoal = (GoalLocation - AgentLocation); const FVector CurrentDirection = GetCurrentDirection(); - CurrentDot = FloatCastChecked(FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection), /* Precision */ 1. / 128.); + CurrentDot = FloatCastChecked(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; } +*/ \ No newline at end of file diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRStereoWidgetComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRStereoWidgetComponent.cpp index 6f03c91..2afa1d3 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRStereoWidgetComponent.cpp +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Private/VRStereoWidgetComponent.cpp @@ -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(Pivot.X); + float V = -RenderTarget->SizeY * static_cast(Pivot.Y); + float UL = RenderTarget->SizeX * (1.0f - static_cast(Pivot.X)); + float VL = RenderTarget->SizeY * (1.0f - static_cast(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(Segment) / NumSegments; const float U1 = static_cast(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; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/CharacterMovementCompTypes.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/CharacterMovementCompTypes.h index 3afc56a..faff641 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/CharacterMovementCompTypes.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/CharacterMovementCompTypes.h @@ -88,7 +88,7 @@ public: UPROPERTY() uint8 MoveActionFlags; UPROPERTY() - TArray MoveActionObjectReferences; + TArray> 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 ClientMovementBase; UPROPERTY(Transient) FName ClientBaseBoneName; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/GripMotionControllerComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/GripMotionControllerComponent.h index 3fec2f2..2cb7d90 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/GripMotionControllerComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/GripMotionControllerComponent.h @@ -809,7 +809,7 @@ public: } UPROPERTY(BlueprintReadWrite, Category = "GripMotionController") - TArray AdditionalLateUpdateComponents; + TArray> AdditionalLateUpdateComponents; // Movement Replication // Actor needs to be replicated for this to work diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Grippables/GrippablePhysicsReplication.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Grippables/GrippablePhysicsReplication.h index cfb8db2..10acf5f 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Grippables/GrippablePhysicsReplication.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Grippables/GrippablePhysicsReplication.h @@ -64,8 +64,11 @@ private: TMap ObjectToTarget; TMap ObjectToSettings; TArray ParticlesInResimIslands; + TArray 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 ReplicatedTargetsQueueVR; FPhysicsReplicationAsyncVR* PhysicsReplicationAsyncVR; FPhysicsReplicationAsyncInput* AsyncInputVR; //async data being written into before we push into callback + TWeakObjectPtr SettingsCurrent; void PrepareAsyncData_ExternalVR(const FRigidBodyErrorCorrection& ErrorCorrection); //prepare async data for writing. Call on external thread (i.e. game thread) }; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRAIPerceptionOverrides.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRAIPerceptionOverrides.h index 1006aef..388f67f 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRAIPerceptionOverrides.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRAIPerceptionOverrides.h @@ -113,7 +113,7 @@ struct FAISightTargetVR static const FTargetId InvalidTargetId; TWeakObjectPtr Target; - IAISightTargetInterface* SightTargetInterface; + TWeakInterfacePtr 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: diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VREPhysicsConstraintComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VREPhysicsConstraintComponent.h index 6bf2ef1..678597c 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VREPhysicsConstraintComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VREPhysicsConstraintComponent.h @@ -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(ConstraintInstance.ConstraintHandle.Constraint)) { Constraint->SetLinearDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration); Constraint->SetAngularDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration); } - } + }*/ //#endif } diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRFullScreenUserWidget.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRFullScreenUserWidget.h index 334198b..a4ec970 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRFullScreenUserWidget.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRFullScreenUserWidget.h @@ -388,7 +388,7 @@ public: /** */ UPROPERTY(VisibleAnywhere, Instanced, NoClear, Category = "User Interface", meta = (ShowOnlyInnerProperties)) - UVRFullScreenUserWidget* ScreenUserWidget; + TObjectPtr ScreenUserWidget; UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor") UVRFullScreenUserWidget* GetPreviewWidgetComp(); diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRLogComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRLogComponent.h index c3e04c3..970d987 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRLogComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRLogComponent.h @@ -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; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRWheeledVehicle.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRWheeledVehicle.h index d29d88c..a0ed62a 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRWheeledVehicle.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/Misc/VRWheeledVehicle.h @@ -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(this->GetMovementComponent())) + { + CurrentGearRatio = MoveComp->TransmissionSetup.FinalRatio; + } + + return CurrentGearRatio; + } + + UFUNCTION(BlueprintCallable, Category = "Pawn") + void SetFinalGearRatio(float NewFinalGearRatio) + { + if (UChaosWheeledVehicleMovementComponent* MoveComp = Cast(this->GetMovementComponent())) + { + MoveComp->TransmissionSetup.FinalRatio = NewFinalGearRatio; + } + } + + // Calls the movement components override controller function UFUNCTION(BlueprintCallable, Category = "Pawn") virtual bool SetBindToInput(AController * CController, bool bBindToInput) { diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/ReplicatedVRCameraComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/ReplicatedVRCameraComponent.h index 74e657d..c54247c 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/ReplicatedVRCameraComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/ReplicatedVRCameraComponent.h @@ -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; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRBaseCharacter.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRBaseCharacter.h index 7764226..c63ef91 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRBaseCharacter.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRBaseCharacter.h @@ -50,67 +50,7 @@ public: UPROPERTY(Transient) TObjectPtr 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 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 diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRCharacterMovementComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRCharacterMovementComponent.h index 14e4765..0078854 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRCharacterMovementComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRCharacterMovementComponent.h @@ -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; diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRPathFollowingComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRPathFollowingComponent.h index eb8a7ab..6ab4df7 100644 --- a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRPathFollowingComponent.h +++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionPlugin/Public/VRPathFollowingComponent.h @@ -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 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& Tokens, TArray& 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;*/ }; \ No newline at end of file diff --git a/VIRTUOS_ExpansionPluginTests/VRExpPluginExample.uproject b/VIRTUOS_ExpansionPluginTests/VRExpPluginExample.uproject index ec67351..5fbb902 100644 --- a/VIRTUOS_ExpansionPluginTests/VRExpPluginExample.uproject +++ b/VIRTUOS_ExpansionPluginTests/VRExpPluginExample.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "5.4", + "EngineAssociation": "5.5", "Category": "", "Description": "", "Modules": [