A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
ReplicatedVRCameraComponent.cpp
Go to the documentation of this file.
1// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
2
4#include "Net/UnrealNetwork.h"
5#include "Engine/Engine.h"
6#include "VRBaseCharacter.h"
7#include "IXRTrackingSystem.h"
8#include "IXRCamera.h"
9#include "Rendering/MotionVectorSimulation.h"
10#include "VRBaseCharacter.h"
11#include "IHeadMountedDisplay.h"
12
13
14// Ported epics head tracking allowed for world fix back to this temp patch in 4.27
15bool TMP_IsHeadTrackingAllowedForWorld(IXRTrackingSystem* XRSystem, UWorld* World)
16{
17#if WITH_EDITOR
18 // This implementation is constrained by hotfix rules. It would be better to cache this somewhere.
19 if (!XRSystem->IsHeadTrackingAllowed())
20 {
21 return false;
22 }
23
24 if (World->WorldType != EWorldType::PIE)
25 {
26 return true;
27 }
28
29 // If we are a pie instance then the first pie world that is not a dedicated server uses head tracking
30 const int32 MyPIEInstanceID = World->GetOutermost()->PIEInstanceID;
31 for (const FWorldContext& WorldContext : GEngine->GetWorldContexts())
32 {
33 if (WorldContext.WorldType == EWorldType::PIE && WorldContext.RunAsDedicated == false && WorldContext.World())
34 {
35 return WorldContext.World()->GetOutermost()->PIEInstanceID == MyPIEInstanceID;
36 }
37 }
38 return false;
39#endif
40 return XRSystem->IsHeadTrackingAllowedForWorld(*World);
41}
42
43
44UReplicatedVRCameraComponent::UReplicatedVRCameraComponent(const FObjectInitializer& ObjectInitializer)
45 : Super(ObjectInitializer)
46{
47 PrimaryComponentTick.bCanEverTick = true;
48 PrimaryComponentTick.bStartWithTickEnabled = true;
49 //PrimaryComponentTick.TickGroup = TG_PrePhysics;
50
51 SetIsReplicatedByDefault(true);
52 SetRelativeScale3D(FVector(1.0f, 1.0f, 1.0f));
53
54 // Default 100 htz update rate, same as the 100htz update rate of rep_notify, will be capped to 90/45 though because of vsync on HMD
55 //bReplicateTransform = true;
56 NetUpdateRate = 100.0f; // 100 htz is default
57 NetUpdateCount = 0.0f;
58
59 bUsePawnControlRotation = false;
60 bAutoSetLockToHmd = true;
61 bScaleTracking = false;
62 TrackingScaler = FVector(1.0f);
63 bOffsetByHMD = false;
64 bLimitMinHeight = false;
66 bLimitMaxHeight = false;
67 MaxHeightAllowed = 300.f;
68 bLimitBounds = false;
69 // Just shy of 20' distance from the center of tracked space
71
74 bLerpingPosition = false;
75 bReppedOnce = false;
76
77 OverrideSendTransform = nullptr;
78
79 LastRelativePosition = FTransform::Identity;
82
83 //bUseVRNeckOffset = true;
84 //VRNeckOffset = FTransform(FRotator::ZeroRotator, FVector(15.0f,0,0), FVector(1.0f));
85
86}
87
88
89//=============================================================================
90void UReplicatedVRCameraComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
91{
92
93 // I am skipping the Scene component replication here
94 // Generally components aren't set to replicate anyway and I need it to NOT pass the Relative position through the network
95 // There isn't much in the scene component to replicate anyway
96 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
97
98 DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeLocation);
99 DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeRotation);
100 DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D);
101
102 // Skipping the owner with this as the owner will use the location directly
103 DOREPLIFETIME_CONDITION(UReplicatedVRCameraComponent, ReplicatedCameraTransform, COND_SkipOwner);
106 //DOREPLIFETIME(UReplicatedVRCameraComponent, bReplicateTransform);
107}
108
109// Just skipping this, it generates warnings for attached meshes when using this method of denying transform replication
110/*void UReplicatedVRCameraComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
111{
112 Super::PreReplication(ChangedPropertyTracker);
113
114 // Don't ever replicate these, they are getting replaced by my custom send anyway
115 DOREPLIFETIME_ACTIVE_OVERRIDE(USceneComponent, RelativeLocation, false);
116 DOREPLIFETIME_ACTIVE_OVERRIDE(USceneComponent, RelativeRotation, false);
117 DOREPLIFETIME_ACTIVE_OVERRIDE(USceneComponent, RelativeScale3D, false);
118}*/
119
120void UReplicatedVRCameraComponent::Server_SendCameraTransform_Implementation(FBPVRComponentPosRep NewTransform)
121{
122 // Store new transform and trigger OnRep_Function
123 ReplicatedCameraTransform = NewTransform;
124
125 // Don't call on rep on the server if the server controls this controller
126 if (!bHasAuthority)
127 {
129 }
130}
131
132bool UReplicatedVRCameraComponent::Server_SendCameraTransform_Validate(FBPVRComponentPosRep NewTransform)
133{
134 return true;
135 // Optionally check to make sure that player is inside of their bounds and deny it if they aren't?
136}
137
138/*bool UReplicatedVRCameraComponent::IsServer()
139{
140 if (GEngine != nullptr && GWorld != nullptr)
141 {
142 switch (GEngine->GetNetMode(GWorld))
143 {
144 case NM_Client:
145 {return false; } break;
146 case NM_DedicatedServer:
147 case NM_ListenServer:
148 default:
149 {return true; } break;
150 }
151 }
152
153 return false;
154}*/
155
157{
158 if (AVRBaseCharacter* CharacterOwner = Cast<AVRBaseCharacter>(this->GetOwner()))
159 {
160 AttachChar = CharacterOwner;
161 }
162 else
163 {
164 AttachChar.Reset();
165 }
166
167 Super::OnAttachmentChanged();
168}
169
174
176{
177 if (bOffsetByHMD)
178 {
179 OriginalPosition.X = 0;
180 OriginalPosition.Y = 0;
181 }
182
183 if (bLimitBounds)
184 {
185 OriginalPosition.X = FMath::Clamp(OriginalPosition.X, -MaximumTrackedBounds, MaximumTrackedBounds);
186 OriginalPosition.Y = FMath::Clamp(OriginalPosition.Y, -MaximumTrackedBounds, MaximumTrackedBounds);
187 }
188
189 if (bScaleTracking)
190 {
191 OriginalPosition *= TrackingScaler;
192 }
193
194 if (bLimitMaxHeight)
195 {
196 OriginalPosition.Z = FMath::Min(MaxHeightAllowed, OriginalPosition.Z);
197 }
198
199 if (bLimitMinHeight)
200 {
201 OriginalPosition.Z = FMath::Max(MinimumHeightAllowed, OriginalPosition.Z);
202 }
203}
204
206{
208
209 // Don't do any of the below if we aren't the authority
210 if (bHasAuthority)
211 {
212 // For non view target positional updates (third party and the like)
213 if (bSetPositionDuringTick && bLockToHmd && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowedForWorld(*GetWorld()))
214 {
215 //ResetRelativeTransform();
216 FQuat Orientation;
217 FVector Position;
218 if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, Orientation, Position))
219 {
221 {
222 ApplyTrackingParameters(Position);
223 }
224
225 SetRelativeTransform(FTransform(Orientation, Position));
226 }
227 }
228 }
229 else
230 {
231 // Run any networked smoothing
232 RunNetworkedSmoothing(DeltaTime);
233 }
234
235 // Save out the component velocity from this and last frame
236 if(bHadValidFirstVelocity || !LastRelativePosition.Equals(FTransform::Identity))
237 {
239 ComponentVelocity = ((bSampleVelocityInWorldSpace ? GetComponentLocation() : GetRelativeLocation()) - LastRelativePosition.GetTranslation()) / DeltaTime;
240 }
241
242 LastRelativePosition = bSampleVelocityInWorldSpace ? this->GetComponentTransform() : this->GetRelativeTransform();
243}
244
246{
248 {
250 {
251 NetUpdateCount += DeltaTime;
252 float LerpVal = FMath::Clamp(NetUpdateCount / (1.0f / NetUpdateRate), 0.0f, 1.0f);
253
254 if (LerpVal >= 1.0f)
255 {
257
258 // Stop lerping, wait for next update if it is delayed or lost then it will hitch here
259 // Actual prediction might be something to consider in the future, but rough to do in VR
260 // considering the speed and accuracy of movements
261 // would like to consider sub stepping but since there is no server rollback...not sure how useful it would be
262 // and might be perf taxing enough to not make it worth it.
263 bLerpingPosition = false;
264 NetUpdateCount = 0.0f;
265 }
266 else
267 {
268 // Removed variables to speed this up a bit
269 SetRelativeLocationAndRotation(
270 FMath::Lerp(LastUpdatesRelativePosition, (FVector)ReplicatedCameraTransform.Position, LerpVal),
272 );
273 }
274 }
275 else // Exponential Smoothing
276 {
277 if (InterpolationSpeed <= 0.f)
278 {
279 SetRelativeLocationAndRotation((FVector)ReplicatedCameraTransform.Position, ReplicatedCameraTransform.Rotation);
280 bLerpingPosition = false;
281 return;
282 }
283
284 const float Alpha = FMath::Clamp(DeltaTime * InterpolationSpeed, 0.f, 1.f);
285
286 FTransform NA = FTransform(GetRelativeRotation(), GetRelativeLocation(), FVector(1.0f));
287 FTransform NB = FTransform(ReplicatedCameraTransform.Rotation, (FVector)ReplicatedCameraTransform.Position, FVector(1.0f));
288 NA.NormalizeRotation();
289 NB.NormalizeRotation();
290
291 NA.Blend(NA, NB, Alpha);
292
293 // If we are nearly equal then snap to final position
294 if (NA.EqualsNoScale(NB))
295 {
297 }
298 else // Else just keep going
299 {
300 SetRelativeLocationAndRotation(NA.GetTranslation(), NA.Rotator());
301 }
302 }
303 }
304}
305
306
307void UReplicatedVRCameraComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
308{
309 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
310
311
312 if (!bUpdateInCharacterMovement || !AttachChar.IsValid())
313 {
314 UpdateTracking(DeltaTime);
315 }
316 else
317 {
318 UCharacterMovementComponent* CharMove = AttachChar->GetCharacterMovement();
319 if (!CharMove || !CharMove->IsComponentTickEnabled() || !CharMove->IsActive() || (!CharMove->PrimaryComponentTick.bTickEvenWhenPaused && GetWorld()->IsPaused()))
320 {
321 // Our character movement isn't handling our updates, lets do it ourself.
322 UpdateTracking(DeltaTime);
323 }
324 }
325
326 if (bHasAuthority)
327 {
328 // Send changes
329 if (this->GetIsReplicated())
330 {
331 FRotator RelativeRot = GetRelativeRotation();
332 FVector RelativeLoc = GetRelativeLocation();
333
334 // Don't rep if no changes
335 if (!RelativeLoc.Equals(ReplicatedCameraTransform.Position) || !RelativeRot.Equals(ReplicatedCameraTransform.Rotation))
336 {
337 NetUpdateCount += DeltaTime;
338
339 if (NetUpdateCount >= (1.0f / NetUpdateRate))
340 {
341 NetUpdateCount = 0.0f;
344
345
346 if (GetNetMode() == NM_Client)
347 {
348 AVRBaseCharacter* OwningChar = Cast<AVRBaseCharacter>(GetOwner());
349 if (OverrideSendTransform != nullptr && OwningChar != nullptr)
350 {
352 }
353 else
354 {
355 // Don't bother with any of this if not replicating transform
356 //if (bHasAuthority && bReplicateTransform)
358 }
359 }
360 }
361 }
362 }
363 }
364}
365
366void UReplicatedVRCameraComponent::GetCameraView(float DeltaTime, FMinimalViewInfo& DesiredView)
367{
368 bool bIsLocallyControlled = IsLocallyControlled();
369
371 {
372 if (bIsLocallyControlled)
373 bLockToHmd = true;
374 else
375 bLockToHmd = false;
376 }
377
378 if (bIsLocallyControlled && GEngine && GEngine->XRSystem.IsValid() && GetWorld() && GetWorld()->WorldType != EWorldType::Editor)
379 {
380 IXRTrackingSystem* XRSystem = GEngine->XRSystem.Get();
381 auto XRCamera = XRSystem->GetXRCamera();
382
383 if (XRCamera.IsValid())
384 {
385 //if (XRSystem->IsHeadTrackingAllowedForWorld(*GetWorld()))
386 if (TMP_IsHeadTrackingAllowedForWorld(XRSystem, GetWorld()))
387 {
388 const FTransform ParentWorld = CalcNewComponentToWorld(FTransform());
389 XRCamera->SetupLateUpdate(ParentWorld, this, bLockToHmd == 0);
390
391 if (bLockToHmd)
392 {
393 FQuat Orientation;
394 FVector Position;
395 if (XRCamera->UpdatePlayerCamera(Orientation, Position))
396 {
398 {
399 ApplyTrackingParameters(Position);
400 }
401
402 SetRelativeTransform(FTransform(Orientation, Position));
403 }
404 else
405 {
406 SetRelativeScale3D(FVector(1.0f));
407 //ResetRelativeTransform(); stop doing this, it is problematic
408 // Let the camera freeze in the last position instead
409 // Setting scale by itself makes sure we don't get camera scaling but keeps the last location and rotation alive
410 }
411 }
412
413 // #TODO: Check back on this, was moved here in 4.20 but shouldn't it be inside of bLockToHMD?
414 XRCamera->OverrideFOV(this->FieldOfView);
415 }
416 }
417 }
418
419 if (bUsePawnControlRotation)
420 {
421 const APawn* OwningPawn = Cast<APawn>(GetOwner());
422 const AController* OwningController = OwningPawn ? OwningPawn->GetController() : nullptr;
423 if (OwningController && OwningController->IsLocalPlayerController())
424 {
425 const FRotator PawnViewRotation = OwningPawn->GetViewRotation();
426 if (!PawnViewRotation.Equals(GetComponentRotation()))
427 {
428 SetWorldRotation(PawnViewRotation);
429 }
430 }
431 }
432
433 if (bUseAdditiveOffset)
434 {
435 FTransform OffsetCamToBaseCam = AdditiveOffset;
436 FTransform BaseCamToWorld = GetComponentToWorld();
437 FTransform OffsetCamToWorld = OffsetCamToBaseCam * BaseCamToWorld;
438
439 DesiredView.Location = OffsetCamToWorld.GetLocation();
440 DesiredView.Rotation = OffsetCamToWorld.Rotator();
441 }
442 else
443 {
444 DesiredView.Location = GetComponentLocation();
445 DesiredView.Rotation = GetComponentRotation();
446 }
447
448 DesiredView.FOV = bUseAdditiveOffset ? (FieldOfView + AdditiveFOVOffset) : FieldOfView;
449 DesiredView.AspectRatio = AspectRatio;
450 DesiredView.bConstrainAspectRatio = bConstrainAspectRatio;
451 DesiredView.bUseFieldOfViewForLOD = bUseFieldOfViewForLOD;
452 DesiredView.ProjectionMode = ProjectionMode;
453 DesiredView.OrthoWidth = OrthoWidth;
454 DesiredView.OrthoNearClipPlane = OrthoNearClipPlane;
455 DesiredView.OrthoFarClipPlane = OrthoFarClipPlane;
456
457 // See if the CameraActor wants to override the PostProcess settings used.
458 DesiredView.PostProcessBlendWeight = PostProcessBlendWeight;
459 if (PostProcessBlendWeight > 0.0f)
460 {
461 DesiredView.PostProcessSettings = PostProcessSettings;
462 }
463
464 // If this camera component has a motion vector simumlation transform, use that for the current view's previous transform
465 DesiredView.PreviousViewTransform = FMotionVectorSimulation::Get().GetPreviousTransform(this);
466}
467
469{
470 if (GetNetMode() < ENetMode::NM_Client && HasTrackingParameters())
471 {
472 // Ensure that we clamp to the expected values from the client
474 }
475
477 {
478 if (bReppedOnce)
479 {
480 bLerpingPosition = true;
481 NetUpdateCount = 0.0f;
482 LastUpdatesRelativePosition = this->GetRelativeLocation();
483 LastUpdatesRelativeRotation = this->GetRelativeRotation();
484
486 {
488 float NewDistance = OldToNewVector.SizeSquared();
489
490 // Too far, snap to the new value
491 if (NewDistance >= FMath::Square(NetworkNoSmoothUpdateDistance))
492 {
494 bLerpingPosition = false;
495 }
496 // Outside of the buffer distance, snap within buffer and keep smoothing from there
497 else if (NewDistance >= FMath::Square(NetworkMaxSmoothUpdateDistance))
498 {
499 FVector Offset = (OldToNewVector.Size() - NetworkMaxSmoothUpdateDistance) * OldToNewVector.GetSafeNormal();
500 SetRelativeLocation(LastUpdatesRelativePosition + Offset);
501 }
502 }
503 }
504 else
505 {
507 bReppedOnce = true;
508 }
509 }
510 else
512}
bool TMP_IsHeadTrackingAllowedForWorld(IXRTrackingSystem *XRSystem, UWorld *World)
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = VRExpansionLibrary)
bool bSampleVelocityInWorldSpace
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|ComponentVelocity")
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override
float NetworkMaxSmoothUpdateDistance
UPROPERTY(EditAnywhere, Category = "GripMotionController|Networking|Smoothing", meta = (editcondition...
bool bScaleTracking
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking")
float NetworkNoSmoothUpdateDistance
UPROPERTY(EditAnywhere, Category = "GripMotionController|Networking|Smoothing", meta = (editcondition...
bool bOffsetByHMD
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera")
bool bLimitMaxHeight
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking")
uint32 bAutoSetLockToHmd
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking")
TWeakObjectPtr< AVRBaseCharacter > AttachChar
UPROPERTY()
FVector TrackingScaler
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking",...
float NetUpdateRate
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "ReplicatedCamera|Networking")
FBPVRComponentPosRep ReplicatedCameraTransform
UPROPERTY(EditDefaultsOnly, ReplicatedUsing = OnRep_ReplicatedCameraTransform, Category = "Replicated...
bool bSetPositionDuringTick
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera")
VRBaseCharTransformRPC_Pointer OverrideSendTransform
UReplicatedVRCameraComponent(const FObjectInitializer &ObjectInitializer)
float MaximumTrackedBounds
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking",...
float MinimumHeightAllowed
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking",...
bool bUpdateInCharacterMovement
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRExpansionLibrary")
virtual void OnRep_ReplicatedCameraTransform()
UFUNCTION()
float MaxHeightAllowed
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking",...
virtual void GetCameraView(float DeltaTime, FMinimalViewInfo &DesiredView) override
void ApplyTrackingParameters(FVector &OriginalPosition)
void Server_SendCameraTransform(FBPVRComponentPosRep NewTransform)
UFUNCTION(Unreliable, Server, WithValidation)
bool bSmoothReplicatedMotion
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "ReplicatedCamera|Networking")
bool bLimitMinHeight
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking")
float InterpolationSpeed
UPROPERTY(EditAnywhere, Category = "GripMotionController|Networking|Smoothing", meta = (editcondition...
bool bUseExponentialSmoothing
UPROPERTY(EditAnywhere, Category = "GripMotionController|Networking|Smoothing", meta = (editcondition...
bool bLimitBounds
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ReplicatedCamera|Advanced|Tracking")
FVector Position
UPROPERTY(Transient)
FRotator Rotation
UPROPERTY(Transient)