A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
GrippablePhysicsReplication.cpp
Go to the documentation of this file.
1// Fill out your copyright notice in the Description page of Project Settings.
2
4#include "UObject/ObjectMacros.h"
5#include "UObject/Interface.h"
6#include "DrawDebugHelpers.h"
7#if WITH_CHAOS
8#include "Chaos/ChaosMarshallingManager.h"
9#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
10#include "PBDRigidsSolver.h"
11#include "Chaos/PBDRigidsEvolutionGBF.h"
12#endif
13
14// I cannot dynamic cast without RTTI so I am using a static var as a declarative in case the user removed our custom replicator
15// We don't want our casts to cause issues.
17{
18 static bool bHasVRPhysicsReplication = false;
19}
20
21#if WITH_CHAOS
22struct FAsyncPhysicsRepCallbackDataVR : public Chaos::FSimCallbackInput
23{
24 TArray<FAsyncPhysicsDesiredState> Buffer;
25 float LinearVelocityCoefficient;
26 float AngularVelocityCoefficient;
27 float PositionLerp;
28 float AngleLerp;
29
30 void Reset()
31 {
32 Buffer.Reset();
33 }
34};
35
36class FPhysicsReplicationAsyncCallbackVR final : public Chaos::TSimCallbackObject<FAsyncPhysicsRepCallbackDataVR>
37{
38 virtual void OnPreSimulate_Internal() override
39 {
40 FPhysicsReplicationVR::ApplyAsyncDesiredStateVR(GetDeltaTime_Internal(), GetConsumerInput_Internal());
41 }
42};
43#endif
44
46{
47#if WITH_CHAOS
48 if (AsyncCallbackServer)
49 {
50 if (auto* Solver = PhysSceneVR->GetSolver())
51 {
52 Solver->UnregisterAndFreeSimCallbackObject_External(AsyncCallbackServer);
53 }
54 }
55#endif
56}
57
58
59void ComputeDeltasVR(const FVector& CurrentPos, const FQuat& CurrentQuat, const FVector& TargetPos, const FQuat& TargetQuat, FVector& OutLinDiff, float& OutLinDiffSize,
60 FVector& OutAngDiffAxis, float& OutAngDiff, float& OutAngDiffSize)
61{
62 OutLinDiff = TargetPos - CurrentPos;
63 OutLinDiffSize = OutLinDiff.Size();
64 const FQuat InvCurrentQuat = CurrentQuat.Inverse();
65 const FQuat DeltaQuat = InvCurrentQuat * TargetQuat;
66 DeltaQuat.ToAxisAndAngle(OutAngDiffAxis, OutAngDiff);
67 OutAngDiff = FMath::RadiansToDegrees(FMath::UnwindRadians(OutAngDiff));
68 OutAngDiffSize = FMath::Abs(OutAngDiff);
69}
70
72 FPhysicsReplication(PhysScene)
73{
74 PhysSceneVR = PhysScene;
75#if WITH_CHAOS
76 CurAsyncDataVR = nullptr;
77 AsyncCallbackServer = nullptr;
78#endif
80
81#if PHYSICS_INTERFACE_PHYSX
82 const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
83 if (VRSettings.MaxCCDPasses != 1)
84 {
85 if (PxScene * PScene = PhysScene->GetPxScene())
86 {
87 PScene->setCCDMaxPasses(VRSettings.MaxCCDPasses);
88 }
89 }
90
91#endif
92}
93
94#if WITH_CHAOS
95
96void FPhysicsReplicationVR::PrepareAsyncData_ExternalVR(const FRigidBodyErrorCorrection& ErrorCorrection)
97{
98 //todo move this logic into a common function?
99 static const auto CVarLinSet = IConsoleManager::Get().FindConsoleVariable(TEXT("p.PositionLerp"));
100 const float PositionLerp = CVarLinSet->GetFloat() >= 0.0f ? CVarLinSet->GetFloat() : ErrorCorrection.PositionLerp;
101
102 static const auto CVarLinLerp = IConsoleManager::Get().FindConsoleVariable(TEXT("p.LinearVelocityCoefficient"));
103 const float LinearVelocityCoefficient = CVarLinLerp->GetFloat() >= 0.0f ? CVarLinLerp->GetFloat() : ErrorCorrection.LinearVelocityCoefficient;
104
105 static const auto CVarAngSet = IConsoleManager::Get().FindConsoleVariable(TEXT("p.AngleLerp"));
106 const float AngleLerp = CVarAngSet->GetFloat() >= 0.0f ? CVarAngSet->GetFloat() : ErrorCorrection.AngleLerp;
107
108 static const auto CVarAngLerp = IConsoleManager::Get().FindConsoleVariable(TEXT("p.AngularVelocityCoefficient"));
109 const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrection.AngularVelocityCoefficient;
110
111 CurAsyncDataVR = AsyncCallbackServer->GetProducerInputData_External();
112 CurAsyncDataVR->PositionLerp = PositionLerp;
113 CurAsyncDataVR->AngleLerp = AngleLerp;
114 CurAsyncDataVR->LinearVelocityCoefficient = LinearVelocityCoefficient;
115 CurAsyncDataVR->AngularVelocityCoefficient = AngularVelocityCoefficient;
116}
117
118void FPhysicsReplicationVR::ApplyAsyncDesiredStateVR(const float DeltaSeconds, const FAsyncPhysicsRepCallbackDataVR* AsyncData)
119{
120 using namespace Chaos;
121 if (AsyncData)
122 {
123 const float LinearVelocityCoefficient = AsyncData->LinearVelocityCoefficient;
124 const float AngularVelocityCoefficient = AsyncData->AngularVelocityCoefficient;
125 const float PositionLerp = AsyncData->PositionLerp;
126 const float AngleLerp = AsyncData->AngleLerp;
127
128 for (const FAsyncPhysicsDesiredState& State : AsyncData->Buffer)
129 {
130 //Proxy should exist because we are using latest and any pending deletes would have been enqueued after
131 FSingleParticlePhysicsProxy* Proxy = State.Proxy;
132 auto* Handle = Proxy->GetPhysicsThreadAPI();
133
134
135 if (Handle && Handle->CanTreatAsRigid())
136 {
137 const FVector TargetPos = State.WorldTM.GetLocation();
138 const FQuat TargetQuat = State.WorldTM.GetRotation();
139
140 // Get Current state
141 FRigidBodyState CurrentState;
142 CurrentState.Position = Handle->X();
143 CurrentState.Quaternion = Handle->R();
144 CurrentState.AngVel = Handle->W();
145 CurrentState.LinVel = Handle->V();
146
147 FVector LinDiff;
148 float LinDiffSize;
149 FVector AngDiffAxis;
150 float AngDiff;
151 float AngDiffSize;
152 ComputeDeltasVR(CurrentState.Position, CurrentState.Quaternion, TargetPos, TargetQuat, LinDiff, LinDiffSize, AngDiffAxis, AngDiff, AngDiffSize);
153
154 const FVector NewLinVel = FVector(State.LinearVelocity) + (LinDiff * LinearVelocityCoefficient * DeltaSeconds);
155 const FVector NewAngVel = FVector(State.AngularVelocity) + (AngDiffAxis * AngDiff * AngularVelocityCoefficient * DeltaSeconds);
156
157 const FVector NewPos = FMath::Lerp(FVector(CurrentState.Position), TargetPos, PositionLerp);
158 const FQuat NewAng = FQuat::Slerp(CurrentState.Quaternion, TargetQuat, AngleLerp);
159
160 Handle->SetX(NewPos);
161 Handle->SetR(NewAng);
162 Handle->SetV(NewLinVel);
163 Handle->SetW(FMath::DegreesToRadians(NewAngVel));
164
165 EObjectStateType ObjectStateType = State.ObjectState;
166 static const auto CVarApplyAsyncSleepState = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ApplyAsyncSleepState"));
167 if ((CVarApplyAsyncSleepState->GetInt() != 0) && State.bShouldSleep)
168 {
169 ObjectStateType = EObjectStateType::Sleeping;
170 }
171 auto* Solver = Proxy->GetSolver<FPBDRigidsSolver>();
172 Solver->GetEvolution()->SetParticleObjectState(Proxy->GetHandle_LowLevel()->CastToRigidParticle(), ObjectStateType); //todo: move object state into physics thread api
173 }
174 }
175 }
176}
177#endif
178
179
184
185bool FPhysicsReplicationVR::ApplyRigidBodyState(float DeltaSeconds, FBodyInstance* BI, FReplicatedPhysicsTarget& PhysicsTarget, const FRigidBodyErrorCorrection& ErrorCorrection, const float PingSecondsOneWay)
186{
187 // Skip all of the custom logic if we aren't the server
188 if (const UWorld* World = GetOwningWorld())
189 {
190 if (World->GetNetMode() == ENetMode::NM_Client)
191 {
192 return FPhysicsReplication::ApplyRigidBodyState(DeltaSeconds, BI, PhysicsTarget, ErrorCorrection, PingSecondsOneWay);
193 }
194 }
195
196 static const auto CVarSkipPhysicsReplication = IConsoleManager::Get().FindConsoleVariable(TEXT("p.SkipPhysicsReplication"));
197 if (CVarSkipPhysicsReplication->GetInt())
198 {
199 return false;
200 }
201
202 if (!BI->IsInstanceSimulatingPhysics())
203 {
204 return false;
205 }
206
207 //
208 // NOTES:
209 //
210 // The operation of this method has changed since 4.18.
211 //
212 // When a new remote physics state is received, this method will
213 // be called on tick until the local state is within an adequate
214 // tolerance of the new state.
215 //
216 // The received state is extrapolated based on ping, by some
217 // adjustable amount.
218 //
219 // A correction velocity is added new state's velocity, and assigned
220 // to the body. The correction velocity scales with the positional
221 // difference, so without the interference of external forces, this
222 // will result in an exponentially decaying correction.
223 //
224 // Generally it is not needed and will interrupt smoothness of
225 // the replication, but stronger corrections can be obtained by
226 // adjusting position lerping.
227 //
228 // If progress is not being made towards equilibrium, due to some
229 // divergence in physics states between the owning and local sims,
230 // an error value is accumulated, representing the amount of time
231 // spent in an unresolvable state.
232 //
233 // Once the error value has exceeded some threshold (0.5 seconds
234 // by default), a hard snap to the target physics state is applied.
235 //
236
237 bool bRestoredState = true;
238 const FRigidBodyState NewState = PhysicsTarget.TargetState;
239 const float NewQuatSizeSqr = NewState.Quaternion.SizeSquared();
240
241 // failure cases
242 if (!BI->IsInstanceSimulatingPhysics())
243 {
244 UE_LOG(LogPhysics, Warning, TEXT("Physics replicating on non-simulated body. (%s)"), *BI->GetBodyDebugName());
245 return bRestoredState;
246 }
247 else if (NewQuatSizeSqr < KINDA_SMALL_NUMBER)
248 {
249 UE_LOG(LogPhysics, Warning, TEXT("Invalid zero quaternion set for body. (%s)"), *BI->GetBodyDebugName());
250 return bRestoredState;
251 }
252 else if (FMath::Abs(NewQuatSizeSqr - 1.f) > KINDA_SMALL_NUMBER)
253 {
254 UE_LOG(LogPhysics, Warning, TEXT("Quaternion (%f %f %f %f) with non-unit magnitude detected. (%s)"),
255 NewState.Quaternion.X, NewState.Quaternion.Y, NewState.Quaternion.Z, NewState.Quaternion.W, *BI->GetBodyDebugName());
256 return bRestoredState;
257 }
258
259 // Grab configuration variables from engine config or from CVars if overriding is turned on.
260 static const auto CVarNetPingExtrapolation = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetPingExtrapolation"));
261 const float NetPingExtrapolation = CVarNetPingExtrapolation->GetFloat() >= 0.0f ? CVarNetPingExtrapolation->GetFloat() : ErrorCorrection.PingExtrapolation;
262
263 static const auto CVarNetPingLimit = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetPingLimit"));
264 const float NetPingLimit = CVarNetPingLimit->GetFloat() > 0.0f ? CVarNetPingLimit->GetFloat() : ErrorCorrection.PingLimit;
265
266 static const auto CVarErrorPerLinearDifference = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ErrorPerLinearDifference"));
267 const float ErrorPerLinearDiff = CVarErrorPerLinearDifference->GetFloat() >= 0.0f ? CVarErrorPerLinearDifference->GetFloat() : ErrorCorrection.ErrorPerLinearDifference;
268
269 static const auto CVarErrorPerAngularDifference = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ErrorPerAngularDifference"));
270 const float ErrorPerAngularDiff = CVarErrorPerAngularDifference->GetFloat() >= 0.0f ? CVarErrorPerAngularDifference->GetFloat() : ErrorCorrection.ErrorPerAngularDifference;
271
272 static const auto CVarMaxRestoredStateError = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxRestoredStateError"));
273 const float MaxRestoredStateError = CVarMaxRestoredStateError->GetFloat() >= 0.0f ? CVarMaxRestoredStateError->GetFloat() : ErrorCorrection.MaxRestoredStateError;
274
275 static const auto CVarErrorAccumulation = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ErrorAccumulationSeconds"));
276 const float ErrorAccumulationSeconds = CVarErrorAccumulation->GetFloat() >= 0.0f ? CVarErrorAccumulation->GetFloat() : ErrorCorrection.ErrorAccumulationSeconds;
277
278 static const auto CVarErrorAccumulationDistanceSq = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ErrorAccumulationDistanceSq"));
279 const float ErrorAccumulationDistanceSq = CVarErrorAccumulationDistanceSq->GetFloat() >= 0.0f ? CVarErrorAccumulationDistanceSq->GetFloat() : ErrorCorrection.ErrorAccumulationDistanceSq;
280
281 static const auto CVarErrorAccumulationSimilarity = IConsoleManager::Get().FindConsoleVariable(TEXT("p.ErrorAccumulationSimilarity"));
282 const float ErrorAccumulationSimilarity = CVarErrorAccumulationSimilarity->GetFloat() >= 0.0f ? CVarErrorAccumulationSimilarity->GetFloat() : ErrorCorrection.ErrorAccumulationSimilarity;
283
284 static const auto CVarLinSet = IConsoleManager::Get().FindConsoleVariable(TEXT("p.PositionLerp"));
285 const float PositionLerp = CVarLinSet->GetFloat() >= 0.0f ? CVarLinSet->GetFloat() : ErrorCorrection.PositionLerp;
286
287 static const auto CVarLinLerp = IConsoleManager::Get().FindConsoleVariable(TEXT("p.LinearVelocityCoefficient"));
288 const float LinearVelocityCoefficient = CVarLinLerp->GetFloat() >= 0.0f ? CVarLinLerp->GetFloat() : ErrorCorrection.LinearVelocityCoefficient;
289
290 static const auto CVarAngSet = IConsoleManager::Get().FindConsoleVariable(TEXT("p.AngleLerp"));
291 const float AngleLerp = CVarAngSet->GetFloat() >= 0.0f ? CVarAngSet->GetFloat() : ErrorCorrection.AngleLerp;
292
293 static const auto CVarAngLerp = IConsoleManager::Get().FindConsoleVariable(TEXT("p.AngularVelocityCoefficient"));
294 const float AngularVelocityCoefficient = CVarAngLerp->GetFloat() >= 0.0f ? CVarAngLerp->GetFloat() : ErrorCorrection.AngularVelocityCoefficient;
295
296 static const auto CVarMaxLinearHardSnapDistance = IConsoleManager::Get().FindConsoleVariable(TEXT("p.MaxLinearHardSnapDistance"));
297 const float MaxLinearHardSnapDistance = CVarMaxLinearHardSnapDistance->GetFloat() >= 0.f ? CVarMaxLinearHardSnapDistance->GetFloat() : ErrorCorrection.MaxLinearHardSnapDistance;
298
299 // Get Current state
300 FRigidBodyState CurrentState;
301 BI->GetRigidBodyState(CurrentState);
302
304
305 // Starting from the last known authoritative position, and
306 // extrapolate an approximation using the last known velocity
307 // and ping.
308 const float PingSeconds = FMath::Clamp(PingSecondsOneWay, 0.f, NetPingLimit);
309 const float ExtrapolationDeltaSeconds = PingSeconds * NetPingExtrapolation;
310 const FVector ExtrapolationDeltaPos = NewState.LinVel * ExtrapolationDeltaSeconds;
311 const FVector_NetQuantize100 TargetPos = NewState.Position + ExtrapolationDeltaPos;
312 float NewStateAngVel;
313 FVector NewStateAngVelAxis;
314 NewState.AngVel.FVector::ToDirectionAndLength(NewStateAngVelAxis, NewStateAngVel);
315 NewStateAngVel = FMath::DegreesToRadians(NewStateAngVel);
316 const FQuat ExtrapolationDeltaQuaternion = FQuat(NewStateAngVelAxis, NewStateAngVel * ExtrapolationDeltaSeconds);
317 FQuat TargetQuat = ExtrapolationDeltaQuaternion * NewState.Quaternion;
318
320
321 FVector LinDiff;
322 float LinDiffSize;
323 FVector AngDiffAxis;
324 float AngDiff;
325
326 float AngDiffSize;
327
328 ComputeDeltasVR(CurrentState.Position, CurrentState.Quaternion, TargetPos, TargetQuat, LinDiff, LinDiffSize, AngDiffAxis, AngDiff, AngDiffSize);
329
331
332 // Store sleeping state
333 const bool bShouldSleep = (NewState.Flags & ERigidBodyFlags::Sleeping) != 0;
334 const bool bWasAwake = BI->IsInstanceAwake();
335 const bool bAutoWake = false;
336
337 const float Error = (LinDiffSize * ErrorPerLinearDiff) + (AngDiffSize * ErrorPerAngularDiff);
338 bRestoredState = Error < MaxRestoredStateError;
339 if (bRestoredState)
340 {
341 PhysicsTarget.AccumulatedErrorSeconds = 0.0f;
342 }
343 else
344 {
345 //
346 // The heuristic for error accumulation here is:
347 // 1. Did the physics tick from the previous step fail to
348 // move the body towards a resolved position?
349 // 2. Was the linear error in the same direction as the
350 // previous frame?
351 // 3. Is the linear error large enough to accumulate error?
352 //
353 // If these conditions are met, then "error" time will accumulate.
354 // Once error has accumulated for a certain number of seconds,
355 // a hard-snap to the target will be performed.
356 //
357 // TODO: Rotation while moving linearly can still mess up this
358 // heuristic. We need to account for it.
359 //
360
361 // Project the change in position from the previous tick onto the
362 // linear error from the previous tick. This value roughly represents
363 // how much correction was performed over the previous physics tick.
364 const float PrevProgress = FVector::DotProduct(
365 FVector(CurrentState.Position) - PhysicsTarget.PrevPos,
366 (PhysicsTarget.PrevPosTarget - PhysicsTarget.PrevPos).GetSafeNormal());
367
368 // Project the current linear error onto the linear error from the
369 // previous tick. This value roughly represents how little the direction
370 // of the linear error state has changed, and how big the error is.
371 const float PrevSimilarity = FVector::DotProduct(
372 TargetPos - FVector(CurrentState.Position),
373 PhysicsTarget.PrevPosTarget - PhysicsTarget.PrevPos);
374
375 // If the conditions from the heuristic outlined above are met, accumulate
376 // error. Otherwise, reduce it.
377 if (PrevProgress < ErrorAccumulationDistanceSq &&
378 PrevSimilarity > ErrorAccumulationSimilarity)
379 {
380 PhysicsTarget.AccumulatedErrorSeconds += DeltaSeconds;
381 }
382 else
383 {
384 PhysicsTarget.AccumulatedErrorSeconds = FMath::Max(PhysicsTarget.AccumulatedErrorSeconds - DeltaSeconds, 0.0f);
385 }
386
387 // Hard snap if error accumulation or linear error is big enough, and clear the error accumulator.
388 static const auto CVarAlwaysHardSnap = IConsoleManager::Get().FindConsoleVariable(TEXT("p.AlwaysHardSnap"));
389 const bool bHardSnap =
390 LinDiffSize > MaxLinearHardSnapDistance ||
391 PhysicsTarget.AccumulatedErrorSeconds > ErrorAccumulationSeconds ||
392 CVarAlwaysHardSnap->GetInt();
393
394 const FTransform IdealWorldTM(TargetQuat, TargetPos);
395
396 if (bHardSnap)
397 {
398 // Too much error so just snap state here and be done with it
399 PhysicsTarget.AccumulatedErrorSeconds = 0.0f;
400 bRestoredState = true;
401 BI->SetBodyTransform(IdealWorldTM, ETeleportType::ResetPhysics, bAutoWake);
402
403 // Set the new velocities
404 BI->SetLinearVelocity(NewState.LinVel, false, bAutoWake);
405 BI->SetAngularVelocityInRadians(FMath::DegreesToRadians(NewState.AngVel), false, bAutoWake);
406 }
407 else
408 {
409 // Small enough error to interpolate
410#if WITH_CHAOS
411 if (AsyncCallbackServer == nullptr) //sync case
412#endif
413 {
414 const FVector NewLinVel = FVector(NewState.LinVel) + (LinDiff * LinearVelocityCoefficient * DeltaSeconds);
415 const FVector NewAngVel = FVector(NewState.AngVel) + (AngDiffAxis * AngDiff * AngularVelocityCoefficient * DeltaSeconds);
416
417 const FVector NewPos = FMath::Lerp(FVector(CurrentState.Position), FVector(TargetPos), PositionLerp);
418 const FQuat NewAng = FQuat::Slerp(CurrentState.Quaternion, TargetQuat, AngleLerp);
419
420 BI->SetBodyTransform(FTransform(NewAng, NewPos), ETeleportType::ResetPhysics);
421 BI->SetLinearVelocity(NewLinVel, false);
422 BI->SetAngularVelocityInRadians(FMath::DegreesToRadians(NewAngVel), false);
423 }
424#if WITH_CHAOS
425 else
426 {
427 //If async is used, enqueue for callback
428 FAsyncPhysicsDesiredState AsyncDesiredState;
429 AsyncDesiredState.WorldTM = IdealWorldTM;
430 AsyncDesiredState.LinearVelocity = NewState.LinVel;
431 AsyncDesiredState.AngularVelocity = NewState.AngVel;
432 AsyncDesiredState.Proxy = static_cast<FSingleParticlePhysicsProxy*>(BI->GetPhysicsActorHandle());
433 AsyncDesiredState.ObjectState = AsyncDesiredState.Proxy->GetGameThreadAPI().ObjectState();
434 AsyncDesiredState.bShouldSleep = bShouldSleep;
435 CurAsyncDataVR->Buffer.Add(AsyncDesiredState);
436 }
437#endif
438 }
439
440 // Should we show the async part?
441#if !UE_BUILD_SHIPPING
442 static const auto CVarNetShowCorrections = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetShowCorrections"));
443 if (CVarNetShowCorrections->GetInt() != 0)
444 {
445 PhysicsTarget.ErrorHistory.bAutoAdjustMinMax = false;
446 PhysicsTarget.ErrorHistory.MinValue = 0.0f;
447 PhysicsTarget.ErrorHistory.MaxValue = 1.0f;
448 PhysicsTarget.ErrorHistory.AddSample(PhysicsTarget.AccumulatedErrorSeconds / ErrorAccumulationSeconds);
449 if (UWorld* OwningWorld = GetOwningWorld())
450 {
451 FColor Color = FColor::White;
452 static const auto CVarNetCorrectionLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetCorrectionLifetime"));
453 DrawDebugDirectionalArrow(OwningWorld, CurrentState.Position, TargetPos, 5.0f, Color, true, CVarNetCorrectionLifetime->GetFloat(), 0, 1.5f);
454#if 0
455 //todo: do we show this in async mode?
456 DrawDebugFloatHistory(*OwningWorld, PhysicsTarget.ErrorHistory, NewPos + FVector(0.0f, 0.0f, 100.0f), FVector2D(100.0f, 50.0f), FColor::White);
457#endif
458 }
459 }
460#endif
461 }
462
464
465#if WITH_CHAOS
466 if (bShouldSleep)
467 {
468 // In the async case, we apply sleep state in ApplyAsyncDesiredState
469 if (AsyncCallbackServer == nullptr)
470 {
471 BI->PutInstanceToSleep();
472 }
473 }
474#endif
475
476 PhysicsTarget.PrevPosTarget = TargetPos;
477 PhysicsTarget.PrevPos = FVector(CurrentState.Position);
478
479 return bRestoredState;
480}
481
482void FPhysicsReplicationVR::OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrimitiveComponent>, FReplicatedPhysicsTarget>& ComponentsToTargets)
483{
484 // Skip all of the custom logic if we aren't the server
485 if (const UWorld* World = GetOwningWorld())
486 {
487 if (World->GetNetMode() == ENetMode::NM_Client)
488 {
489 return FPhysicsReplication::OnTick(DeltaSeconds, ComponentsToTargets);
490 }
491 }
492
493#if WITH_CHAOS
494 using namespace Chaos;
495 if (AsyncCallbackServer == nullptr)
496 {
497 if (auto* Solver = PhysSceneVR->GetSolver())
498 {
499 AsyncCallbackServer = Solver->CreateAndRegisterSimCallbackObject_External<FPhysicsReplicationAsyncCallbackVR>();
500 }
501 }
502#endif
503
504 const FRigidBodyErrorCorrection& PhysicErrorCorrection = UPhysicsSettings::Get()->PhysicErrorCorrection;
505
506#if WITH_CHAOS
507 using namespace Chaos;
508 if (AsyncCallbackServer)
509 {
510 PrepareAsyncData_ExternalVR(PhysicErrorCorrection);
511 }
512#endif
513
514 // Get the ping between this PC & the server
515 const float LocalPing = 0.0f;//GetLocalPing();
516
517 /*float CurrentTimeSeconds = 0.0f;
518
519 if (UWorld* OwningWorld = GetOwningWorld())
520 {
521 CurrentTimeSeconds = OwningWorld->GetTimeSeconds();
522 }*/
523
524 for (auto Itr = ComponentsToTargets.CreateIterator(); Itr; ++Itr)
525 {
526
527 // Its been more than half a second since the last update, lets cease using the target as a failsafe
528 // Clients will never update with that much latency, and if they somehow are, then they are dropping so many
529 // packets that it will be useless to use their data anyway
530 /*if ((CurrentTimeSeconds - Itr.Value().ArrivedTimeSeconds) > 0.5f)
531 {
532 OnTargetRestored(Itr.Key().Get(), Itr.Value());
533 Itr.RemoveCurrent();
534 }
535 else */if (UPrimitiveComponent* PrimComp = Itr.Key().Get())
536 {
537 bool bRemoveItr = false;
538
539 if (FBodyInstance* BI = PrimComp->GetBodyInstance(Itr.Value().BoneName))
540 {
541 FReplicatedPhysicsTarget& PhysicsTarget = Itr.Value();
542 FRigidBodyState& UpdatedState = PhysicsTarget.TargetState;
543 bool bUpdated = false;
544 if (AActor* OwningActor = PrimComp->GetOwner())
545 {
546
547 // Remove if there is no owner
548 if (!OwningActor->GetNetOwningPlayer())
549 {
550 bRemoveItr = true;
551 }
552 else
553 {
554 // Removed as this is server sided
555 /*const ENetRole OwnerRole = OwningActor->GetLocalRole();
556 const bool bIsSimulated = OwnerRole == ROLE_SimulatedProxy;
557 const bool bIsReplicatedAutonomous = OwnerRole == ROLE_AutonomousProxy && PrimComp->bReplicatePhysicsToAutonomousProxy;
558 if (bIsSimulated || bIsReplicatedAutonomous)*/
559
560
561 // Deleted everything here, we will always be the server, I already filtered out clients to default logic
562 {
563 /*const*/ float OwnerPing = 0.0f;// GetOwnerPing(OwningActor, PhysicsTarget);
564
565 /*if (UPlayer* OwningPlayer = OwningActor->GetNetOwningPlayer())
566 {
567 if (APlayerController* PlayerController = OwningPlayer->GetPlayerController(nullptr))
568 {
569 if (APlayerState* PlayerState = PlayerController->PlayerState)
570 {
571 OwnerPing = PlayerState->ExactPing;
572 }
573 }
574 }*/
575
576 // Get the total ping - this approximates the time since the update was
577 // actually generated on the machine that is doing the authoritative sim.
578 // NOTE: We divide by 2 to approximate 1-way ping from 2-way ping.
579 const float PingSecondsOneWay = 0.0f;// (LocalPing + OwnerPing) * 0.5f * 0.001f;
580
581
582 if (UpdatedState.Flags & ERigidBodyFlags::NeedsUpdate)
583 {
584 const bool bRestoredState = ApplyRigidBodyState(DeltaSeconds, BI, PhysicsTarget, PhysicErrorCorrection, PingSecondsOneWay);
585
586 // Need to update the component to match new position.
587 static const auto CVarSkipSkeletalRepOptimization = IConsoleManager::Get().FindConsoleVariable(TEXT("p.SkipSkeletalRepOptimization"));
588 if (/*PhysicsReplicationCVars::SkipSkeletalRepOptimization*/CVarSkipSkeletalRepOptimization->GetInt() == 0 || Cast<USkeletalMeshComponent>(PrimComp) == nullptr) //simulated skeletal mesh does its own polling of physics results so we don't need to call this as it'll happen at the end of the physics sim
589 {
590 PrimComp->SyncComponentToRBPhysics();
591 }
592
593 // Added a sleeping check from the input state as well, we always want to cease activity on sleep
594 if (bRestoredState || ((UpdatedState.Flags & ERigidBodyFlags::Sleeping) != 0))
595 {
596 bRemoveItr = true;
597 }
598 }
599 }
600 }
601 }
602 }
603
604 if (bRemoveItr)
605 {
606 OnTargetRestored(Itr.Key().Get(), Itr.Value());
607 Itr.RemoveCurrent();
608 }
609 }
610 }
611#if WITH_CHAOS
612 CurAsyncDataVR = nullptr;
613#endif
614
615 //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Phys Rep Tick!"));
616 //FPhysicsReplication::OnTick(DeltaSeconds, ComponentsToTargets);
617}
618
619#if PHYSICS_INTERFACE_PHYSX
620void FContactModifyCallbackVR::onContactModify(PxContactModifyPair* const pairs, PxU32 count)
621{
622 for (uint32 PairIdx = 0; PairIdx < count; PairIdx++)
623 {
624 const PxActor* PActor0 = pairs[PairIdx].actor[0];
625 const PxActor* PActor1 = pairs[PairIdx].actor[1];
626 check(PActor0 && PActor1);
627
628 const PxRigidBody* PRigidBody0 = PActor0->is<PxRigidBody>();
629 const PxRigidBody* PRigidBody1 = PActor1->is<PxRigidBody>();
630
631 //physx::PxRigidActor* SyncActor;
632
633 const FBodyInstance* BodyInst0 = FPhysxUserData::Get<FBodyInstance>(PActor0->userData);
634 const FBodyInstance* BodyInst1 = FPhysxUserData::Get<FBodyInstance>(PActor1->userData);
635 if (BodyInst0 == nullptr || BodyInst1 == nullptr)
636 {
637 continue;
638 }
639
640 if (BodyInst0->bContactModification && BodyInst1->bContactModification)
641 {
642 FRWScopeLock(RWAccessLock, FRWScopeLockType::SLT_ReadOnly);
643
644 const FContactModBodyInstancePair* prop = ContactsToIgnore.FindByPredicate([&](const FContactModBodyInstancePair& it) {return (it.Actor1.SyncActor == PRigidBody0 && it.Actor2.SyncActor == PRigidBody1) || (it.Actor2.SyncActor == PRigidBody0 && it.Actor1.SyncActor == PRigidBody1); });
645 if (prop)
646 {
647 for (uint32 ContactPt = 0; ContactPt < pairs[PairIdx].contacts.size(); ContactPt++)
648 {
649 pairs[PairIdx].contacts.ignore(ContactPt);
650 }
651 }
652 }
653 }
654}
655
656void FCCDContactModifyCallbackVR::onCCDContactModify(PxContactModifyPair* const pairs, PxU32 count)
657{
658 for (uint32 PairIdx = 0; PairIdx < count; PairIdx++)
659 {
660 const PxActor* PActor0 = pairs[PairIdx].actor[0];
661 const PxActor* PActor1 = pairs[PairIdx].actor[1];
662 check(PActor0 && PActor1);
663
664 const PxRigidBody* PRigidBody0 = PActor0->is<PxRigidBody>();
665 const PxRigidBody* PRigidBody1 = PActor1->is<PxRigidBody>();
666
667 //physx::PxRigidActor* SyncActor;
668
669 const FBodyInstance* BodyInst0 = FPhysxUserData::Get<FBodyInstance>(PActor0->userData);
670 const FBodyInstance* BodyInst1 = FPhysxUserData::Get<FBodyInstance>(PActor1->userData);
671
672 if (BodyInst0 == nullptr || BodyInst1 == nullptr)
673 {
674 continue;
675 }
676
677 if (BodyInst0->bContactModification && BodyInst1->bContactModification)
678 {
679 FRWScopeLock(RWAccessLock, FRWScopeLockType::SLT_ReadOnly);
680
681 const FContactModBodyInstancePair* prop = ContactsToIgnore.FindByPredicate([&](const FContactModBodyInstancePair& it) {return (it.Actor1.SyncActor == PRigidBody0 && it.Actor2.SyncActor == PRigidBody1) || (it.Actor2.SyncActor == PRigidBody0 && it.Actor1.SyncActor == PRigidBody1); });
682 if (prop)
683 {
684 for (uint32 ContactPt = 0; ContactPt < pairs[PairIdx].contacts.size(); ContactPt++)
685 {
686 pairs[PairIdx].contacts.ignore(ContactPt);
687 }
688 }
689 }
690 }
691}
692#endif
693
695{
696 LocationQuantizationLevel = EVectorQuantization::RoundTwoDecimals;
697 VelocityQuantizationLevel = EVectorQuantization::RoundTwoDecimals;
698 RotationQuantizationLevel = ERotatorQuantization::ShortComponents;
699}
700
702{
704
705 LinearVelocity = other.LinearVelocity;
706 AngularVelocity = other.AngularVelocity;
707 Location = other.Location;
708 Rotation = other.Rotation;
709 bSimulatedPhysicSleep = other.bSimulatedPhysicSleep;
710 bRepPhysics = other.bRepPhysics;
711}
712
714{
715 other.LinearVelocity = LinearVelocity;
716 other.AngularVelocity = AngularVelocity;
717 other.Location = Location;
718 other.Rotation = Rotation;
719 other.bSimulatedPhysicSleep = bSimulatedPhysicSleep;
720 other.bRepPhysics = bRepPhysics;
721}
722
723bool FRepMovementVR::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
724{
725 return FRepMovement::NetSerialize(Ar, Map, bOutSuccess);
726}
727
729{
730 //if (/*bReplicateMovement || (RootComponent && RootComponent->GetAttachParent())*/)
731 {
732 UPrimitiveComponent* RootPrimComp = Cast<UPrimitiveComponent>(OwningActor->GetRootComponent());
733 if (RootPrimComp && RootPrimComp->IsSimulatingPhysics())
734 {
735 FRigidBodyState RBState;
736 RootPrimComp->GetRigidBodyState(RBState);
737
738 FillFrom(RBState, OwningActor);
739 // Don't replicate movement if we're welded to another parent actor.
740 // Their replication will affect our position indirectly since we are attached.
741 bRepPhysics = !RootPrimComp->IsWelded();
742 }
743 else if (RootPrimComp != nullptr)
744 {
745 // If we are attached, don't replicate absolute position, use AttachmentReplication instead.
746 if (RootPrimComp->GetAttachParent() != nullptr)
747 {
748 return false; // We don't handle attachment rep
749
750 }
751 else
752 {
753 Location = FRepMovement::RebaseOntoZeroOrigin(RootPrimComp->GetComponentLocation(), OwningActor);
754 Rotation = RootPrimComp->GetComponentRotation();
755 LinearVelocity = OwningActor->GetVelocity();
756 AngularVelocity = FVector::ZeroVector;
757 }
758
759 bRepPhysics = false;
760 }
761 }
762
763 /*if (const UWorld* World = GetOwningWorld())
764 {
765 if (APlayerController* PlayerController = World->GetFirstPlayerController())
766 {
767 if (APlayerState* PlayerState = PlayerController->PlayerState)
768 {
769 CurrentPing = PlayerState->ExactPing;
770 }
771 }
772 }*/
773
774 return true;
775}
void ComputeDeltasVR(const FVector &CurrentPos, const FQuat &CurrentQuat, const FVector &TargetPos, const FQuat &TargetQuat, FVector &OutLinDiff, float &OutLinDiffSize, FVector &OutAngDiffAxis, float &OutAngDiff, float &OutAngDiffSize)
FPhysicsReplicationVR(FPhysScene *PhysScene)
virtual bool ApplyRigidBodyState(float DeltaSeconds, FBodyInstance *BI, FReplicatedPhysicsTarget &PhysicsTarget, const FRigidBodyErrorCorrection &ErrorCorrection, const float PingSecondsOneWay) override
virtual void OnTick(float DeltaSeconds, TMap< TWeakObjectPtr< UPrimitiveComponent >, FReplicatedPhysicsTarget > &ComponentsToTargets) override
UCLASS(config = Engine, defaultconfig)
int MaxCCDPasses
UPROPERTY(config, EditAnywhere, Category = "Physics")
bool NetSerialize(FArchive &Ar, class UPackageMap *Map, bool &bOutSuccess)
void CopyTo(FRepMovement &other) const
bool GatherActorsMovement(AActor *OwningActor)