I think I got a working later update solution for it that doesn’t bastardize anything. I’ll do some testing and then give you the header assuming everything works out.
Its just slightly earlier in the stack than the FSceneView late updates (scene captures sit there), but should be pretty close.
Edit Here is a full header for a scene capture class with a basic positional update just prior to rendering.
I didn’t fully implement your example so you’ll have to insert your custom stuff and replace my debug position setting (I saw you had some offset components or something for your portals).
This is the latest I could find to update them without engine edits.
#pragma once
#include "Components/SceneCaptureComponent2D.h"
#include "Camera/PlayerCameraManager.h"
#include "Engine/Engine.h"
#include "CoreMinimal.h"
#include "IHeadMountedDisplay.h"
#include "VRSceneCaptureComponent2D.generated.h"
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class UVRSceneCaptureComponent2D : public USceneCaptureComponent2D
{
GENERATED_BODY()
public:
// Toggles applying late HMD positional / rotational updates to the capture
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool bTrackLocalHMDOrCamera;
// If is an HMD enabled capture, is this the left eye
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool bIsLeftEye;
virtual void UpdateSceneCaptureContents(FSceneInterface* Scene) override
{
// Apply eye offset
// Apply eye matrix
// Apply late update
if (bTrackLocalHMDOrCamera)
{
EStereoscopicPass StereoPass = bIsLeftEye ? EStereoscopicPass::eSSP_LEFT_EYE : eSSP_RIGHT_EYE;
FQuat Orientation = FQuat::Identity;
FVector Position = FVector::ZeroVector;
if (GEngine->HMDDevice.IsValid() && GEngine->IsStereoscopic3D() && GEngine->HMDDevice->IsHeadTrackingAllowed() && GEngine->HMDDevice->HasValidTrackingPosition())
{
GEngine->HMDDevice->GetCurrentOrientationAndPosition(Orientation, Position);
float WorldToMeters = GetWorld() ? GetWorld()->GetWorldSettings()->WorldToMeters : 100.0f;
GEngine->StereoRenderingDevice->CalculateStereoViewOffset(StereoPass, Orientation.Rotator(), WorldToMeters /*1.0f?*/, Position);
this->bUseCustomProjectionMatrix = true;
float ActualFOV = 90.0f;
if (GEngine->HMDDevice.IsValid())
{
float HMDVerticalFOV, HMDHorizontalFOV;
GEngine->HMDDevice->GetFieldOfView(HMDHorizontalFOV, HMDVerticalFOV);
if (HMDHorizontalFOV > 0)
{
ActualFOV = HMDHorizontalFOV;
}
}
this->CustomProjectionMatrix = GEngine->HMDDevice.Get()->GetStereoProjectionMatrix(StereoPass, ActualFOV);
this->CaptureStereoPass = StereoPass;
}
else
{
this->bUseCustomProjectionMatrix = false;
this->CaptureStereoPass = EStereoscopicPass::eSSP_FULL;
APlayerController* Player = GetWorld()->GetFirstPlayerController();
if (Player != nullptr && Player->IsLocalController())
{
if (Player->GetPawn())
{
for (UActorComponent* CamComponent : Player->GetPawn()->GetComponentsByClass(UCameraComponent::StaticClass()))
{
UCameraComponent * CameraComponent = Cast<UCameraComponent>(CamComponent);
if (CameraComponent != nullptr && CameraComponent->bIsActive)
{
FTransform trans = CameraComponent->GetRelativeTransform();
Orientation = trans.GetRotation();
Position = trans.GetTranslation();
break;
}
}
}
}
}
this->SetRelativeLocationAndRotation(Position, Orientation);
}
else
{
this->bUseCustomProjectionMatrix = false;
this->CaptureStereoPass = EStereoscopicPass::eSSP_FULL;
}
// This pulls from the GetComponentToWorld so setting just prior to it should have worked
Super::UpdateSceneCaptureContents(Scene);
}
};
Even with Mordentral’s modification, it isn’t really satisfactory for me. There’s still a noticeable lag and the performance cost is super heavy, essentially rendering the scene 4 times instead of just 2. This might be a case we need to wait for hardware to catch up to the concept.