diff --git a/Engine/Plugins/Runtime/OculusRift/OculusRift.uplugin b/Engine/Plugins/Runtime/OculusRift/OculusRift.uplugin index 2a92984946f5..d0ca6370f534 100644 --- a/Engine/Plugins/Runtime/OculusRift/OculusRift.uplugin +++ b/Engine/Plugins/Runtime/OculusRift/OculusRift.uplugin @@ -16,8 +16,7 @@ { "Name" : "OculusRift", "Type" : "Runtime", - "WhitelistPlatforms" : [ "Win64", "Win32", "Mac" ], - "LoadingPhase" : "PostConfigInit" + "WhitelistPlatforms" : [ "Win64", "Win32", "Mac" ] } ] } diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/OculusRift.Build.cs b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/OculusRift.Build.cs index 75852918b42f..b9fd01b9f381 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/OculusRift.Build.cs +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/OculusRift.Build.cs @@ -37,7 +37,7 @@ namespace UnrealBuildTool.Rules } // Currently, the Rift is only supported on windows and mac platforms - if ((Target.Platform == UnrealTargetPlatform.Win32 && !WindowsPlatform.IsWindowsXPSupported()) || Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.Mac) + if (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.Mac) { PrivateDependencyModuleNames.AddRange(new string[] { "LibOVR", "OpenGLDrv" }); @@ -45,6 +45,14 @@ namespace UnrealBuildTool.Rules if (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64) { PrivateDependencyModuleNames.AddRange(new string[] { "D3D11RHI" }); + PrivateIncludePaths.AddRange( + new string[] { + "OculusRift/Private", + "../../../../Source/Runtime/Windows/D3D11RHI/Private", + "../../../../Source/Runtime/Windows/D3D11RHI/Private/Windows", + // ... add other private include paths required here ... + } + ); } if (Target.Platform == UnrealTargetPlatform.Mac) { diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftHMD_05.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftHMD_05.cpp new file mode 100644 index 000000000000..d14f4d3d78fe --- /dev/null +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftHMD_05.cpp @@ -0,0 +1,1805 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#include "HMDPrivatePCH.h" +#include "OculusRiftHMD.h" +// Mac uses 0.5.0 SDK, while PC - 0.6.0. This version supports 0.5.0 (Mac/Linux). +#if PLATFORM_MAC +#include "EngineAnalytics.h" +#include "Runtime/Analytics/Analytics/Public/Interfaces/IAnalyticsProvider.h" +#include "SceneViewport.h" + +#if WITH_EDITOR +#include "Editor/UnrealEd/Classes/Editor/EditorEngine.h" +#endif + +//--------------------------------------------------- +// Oculus Rift Plugin Implementation +//--------------------------------------------------- + +class FOculusRiftPlugin : public IOculusRiftPlugin +{ + /** IHeadMountedDisplayModule implementation */ + virtual TSharedPtr< class IHeadMountedDisplay, ESPMode::ThreadSafe > CreateHeadMountedDisplay() override; + + FString GetModulePriorityKeyName() const + { + return FString(TEXT("OculusRift")); + } +}; + +IMPLEMENT_MODULE( FOculusRiftPlugin, OculusRift ) + +TSharedPtr< class IHeadMountedDisplay, ESPMode::ThreadSafe > FOculusRiftPlugin::CreateHeadMountedDisplay() +{ +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + TSharedPtr< FOculusRiftHMD, ESPMode::ThreadSafe > OculusRiftHMD( new FOculusRiftHMD() ); + if( OculusRiftHMD->IsInitialized() ) + { + return OculusRiftHMD; + } +#endif//OCULUS_RIFT_SUPPORTED_PLATFORMS + return NULL; +} + +//--------------------------------------------------- +// Oculus Rift IHeadMountedDisplay Implementation +//--------------------------------------------------- + +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + +#if !UE_BUILD_SHIPPING +////////////////////////////////////////////////////////////////////////// +static void OVR_CDECL OvrLogCallback(int level, const char* message) +{ + const TCHAR* tbuf = ANSI_TO_TCHAR(message); + const TCHAR* levelStr = TEXT(""); + switch (level) + { + case ovrLogLevel_Debug: levelStr = TEXT(" Debug:"); break; + case ovrLogLevel_Info: levelStr = TEXT(" Info:"); break; + case ovrLogLevel_Error: levelStr = TEXT(" Error:"); break; + default:; + } + GLog->Logf(TEXT("OCULUS:%s %s"), levelStr, tbuf); +} +#endif + +////////////////////////////////////////////////////////////////////////// +FSettings::FSettings() +{ +#ifndef OVR_VISION_ENABLED + Flags.bHmdPosTracking = false; +#endif +#ifndef OVR_SDK_RENDERING + Flags.bTimeWarp = false; +#else +#endif + FMemory::Memset(EyeRenderDesc, 0); + FMemory::Memset(EyeProjectionMatrices, 0); + FMemory::Memset(EyeFov, 0); + + SupportedTrackingCaps = SupportedDistortionCaps = SupportedHmdCaps = 0; + TrackingCaps = DistortionCaps = HmdCaps = 0; + +#ifndef OVR_SDK_RENDERING + FMemory::Memset(UVScaleOffset, 0); +#endif + +#ifndef OVR_SDK_RENDERING + for (unsigned i = 0; i < sizeof(pDistortionMesh) / sizeof(pDistortionMesh[0]); ++i) + { + pDistortionMesh[i] = nullptr; + } +#endif +} + +TSharedPtr FSettings::Clone() const +{ + TSharedPtr NewSettings = MakeShareable(new FSettings(*this)); + return NewSettings; +} + +void FSettings::SetEyeRenderViewport(int OneEyeVPw, int OneEyeVPh) +{ + FHMDSettings::SetEyeRenderViewport(OneEyeVPw, OneEyeVPh); + EyeRenderViewport[0].Max.X -= GetTexturePaddingPerEye(); + EyeRenderViewport[1].Min.X += GetTexturePaddingPerEye(); +} + +////////////////////////////////////////////////////////////////////////// +FGameFrame::FGameFrame() +{ + FMemory::Memset(CurEyeRenderPose, 0); + FMemory::Memset(CurTrackingState, 0); + FMemory::Memset(EyeRenderPose, 0); + FMemory::Memset(HeadPose, 0); +} + +TSharedPtr FGameFrame::Clone() const +{ + TSharedPtr NewFrame = MakeShareable(new FGameFrame(*this)); + return NewFrame; +} + +////////////////////////////////////////////////////////////////////////// + +// a hack to allow quickly check if Oculus is in Direct mode +bool FOculusRiftHMD::bDirectModeHack = false; + +TSharedPtr FOculusRiftHMD::CreateNewGameFrame() const +{ + TSharedPtr Result(MakeShareable(new FGameFrame())); + return Result; +} + +TSharedPtr FOculusRiftHMD::CreateNewSettings() const +{ + TSharedPtr Result(MakeShareable(new FSettings())); + return Result; +} + +bool FOculusRiftHMD::OnStartGameFrame() +{ + bool rv = FHeadMountedDisplay::OnStartGameFrame(); + if (!rv) + { + return false; + } + + FGameFrame* CurrentFrame = GetFrame(); + + // need to make a copy of settings here, since settings could change. + CurrentFrame->Settings = Settings->Clone(); + FSettings* CurrentSettings = CurrentFrame->GetSettings(); + + if (Hmd) + { + // Save eye and head poses + const ovrVector3f hmdToEyeViewOffset[2] = { CurrentSettings->EyeRenderDesc[0].HmdToEyeViewOffset, CurrentSettings->EyeRenderDesc[1].HmdToEyeViewOffset }; + ovrHmd_GetEyePoses(Hmd, CurrentFrame->FrameNumber, hmdToEyeViewOffset, CurrentFrame->CurEyeRenderPose, &CurrentFrame->CurTrackingState); +#ifdef OVR_VISION_ENABLED + if (CurrentSettings->Flags.bHmdPosTracking) + { + CurrentFrame->Flags.bHaveVisionTracking = (CurrentFrame->CurTrackingState.StatusFlags & ovrStatus_PositionTracked) != 0; + if (CurrentFrame->Flags.bHaveVisionTracking && !Flags.bHadVisionTracking) + { + UE_LOG(LogHMD, Warning, TEXT("Vision Tracking Acquired")); + } + if (!CurrentFrame->Flags.bHaveVisionTracking && Flags.bHadVisionTracking) + { + UE_LOG(LogHMD, Warning, TEXT("Lost Vision Tracking")); + } + Flags.bHadVisionTracking = CurrentFrame->Flags.bHaveVisionTracking; + } +#endif // OVR_VISION_ENABLED +#if !UE_BUILD_SHIPPING + { // used for debugging, do not remove + FQuat CurHmdOrientation; + FVector CurHmdPosition; + GetCurrentPose(CurHmdOrientation, CurHmdPosition, false, false); + //UE_LOG(LogHMD, Log, TEXT("BFPOSE: Pos %.3f %.3f %.3f, fr: %d"), CurHmdPosition.X, CurHmdPosition.Y, CurHmdPosition.Y,(int)CurrentFrame->FrameNumber); + //UE_LOG(LogHMD, Log, TEXT("BFPOSE: Yaw %.3f Pitch %.3f Roll %.3f, fr: %d"), CurHmdOrientation.Rotator().Yaw, CurHmdOrientation.Rotator().Pitch, CurHmdOrientation.Rotator().Roll,(int)CurrentFrame->FrameNumber); + } +#endif + } + return true; +} + +bool FOculusRiftHMD::IsHMDConnected() +{ + if (Settings->Flags.bHMDEnabled) + { + InitDevice(); + return Hmd != nullptr; + } + return false; +} + +FGameFrame* FOculusRiftHMD::GetFrame() +{ + return static_cast(GetCurrentFrame()); +} + +const FGameFrame* FOculusRiftHMD::GetFrame() const +{ + return static_cast(GetCurrentFrame()); +} + +EHMDDeviceType::Type FOculusRiftHMD::GetHMDDeviceType() const +{ + return EHMDDeviceType::DT_OculusRift; +} + +bool FOculusRiftHMD::GetHMDMonitorInfo(MonitorInfo& MonitorDesc) +{ + if (IsInitialized()) + { + InitDevice(); + } + if (Hmd) + { + MonitorDesc.MonitorName = Hmd->DisplayDeviceName; + MonitorDesc.MonitorId = Hmd->DisplayId; + MonitorDesc.DesktopX = Hmd->WindowsPos.x; + MonitorDesc.DesktopY = Hmd->WindowsPos.y; + MonitorDesc.ResolutionX = Hmd->Resolution.w; + MonitorDesc.ResolutionY = Hmd->Resolution.h; + MonitorDesc.WindowSizeX = Settings->MirrorWindowSize.X; + MonitorDesc.WindowSizeY = Settings->MirrorWindowSize.Y; + return true; + } + else + { + MonitorDesc.MonitorName = ""; + MonitorDesc.MonitorId = 0; + MonitorDesc.DesktopX = MonitorDesc.DesktopY = MonitorDesc.ResolutionX = MonitorDesc.ResolutionY = 0; + MonitorDesc.WindowSizeX = MonitorDesc.WindowSizeY = 0; + } + return false; +} + +bool FOculusRiftHMD::IsFullscreenAllowed() +{ + if (!Hmd) + { + InitDevice(); + } + return ((Hmd && (Hmd->HmdCaps & ovrHmdCap_ExtendDesktop) != 0) || !Hmd) ? true : false; +} + +bool FOculusRiftHMD::DoesSupportPositionalTracking() const +{ +#ifdef OVR_VISION_ENABLED + const FGameFrame* frame = GetFrame(); + const FSettings* OculusSettings = frame->GetSettings(); + return (frame && OculusSettings->Flags.bHmdPosTracking && (OculusSettings->SupportedTrackingCaps & ovrTrackingCap_Position) != 0); +#else + return false; +#endif //OVR_VISION_ENABLED +} + +bool FOculusRiftHMD::HasValidTrackingPosition() +{ +#ifdef OVR_VISION_ENABLED + const auto frame = GetFrame(); + return (frame && frame->Settings->Flags.bHmdPosTracking && frame->Flags.bHaveVisionTracking); +#else + return false; +#endif //OVR_VISION_ENABLED +} + +#define TRACKER_FOCAL_DISTANCE 1.00f // meters (focal point to origin for position) + +void FOculusRiftHMD::GetPositionalTrackingCameraProperties(FVector& OutOrigin, FQuat& OutOrientation, float& OutHFOV, float& OutVFOV, float& OutCameraDistance, float& OutNearPlane, float& OutFarPlane) const +{ + const auto frame = GetFrame(); + if (!frame) + { + return; + } + OutOrigin = FVector::ZeroVector; + OutOrientation = FQuat::Identity; + OutHFOV = OutVFOV = OutCameraDistance = OutNearPlane = OutFarPlane = 0; + + if (!Hmd) + { + return; + } + + check(frame->WorldToMetersScale >= 0); + OutCameraDistance = TRACKER_FOCAL_DISTANCE * frame->WorldToMetersScale; + OutHFOV = FMath::RadiansToDegrees(Hmd->CameraFrustumHFovInRadians); + OutVFOV = FMath::RadiansToDegrees(Hmd->CameraFrustumVFovInRadians); + OutNearPlane = Hmd->CameraFrustumNearZInMeters * frame->WorldToMetersScale; + OutFarPlane = Hmd->CameraFrustumFarZInMeters * frame->WorldToMetersScale; + + // Read the camera pose + if (!(frame->CurTrackingState.StatusFlags & ovrStatus_CameraPoseTracked)) + { + return; + } + const ovrPosef& cameraPose = frame->CurTrackingState.CameraPose; + + FQuat Orient; + FVector Pos; + frame->PoseToOrientationAndPosition(cameraPose, Orient, Pos); + + OutOrientation = Orient; + OutOrigin = Pos; +} + +bool FOculusRiftHMD::IsInLowPersistenceMode() const +{ + const auto frame = GetFrame(); + const auto FrameSettings = frame->GetSettings(); + return (frame && FrameSettings->Flags.bLowPersistenceMode && (FrameSettings->SupportedHmdCaps & ovrHmdCap_LowPersistence) != 0); +} + +void FOculusRiftHMD::EnableLowPersistenceMode(bool Enable) +{ + Settings->Flags.bLowPersistenceMode = Enable; + Flags.bNeedUpdateHmdCaps = true; +} + +void FGameFrame::PoseToOrientationAndPosition(const ovrPosef& InPose, FQuat& OutOrientation, FVector& OutPosition) const +{ + OutOrientation = ToFQuat(InPose.Orientation); + + check(WorldToMetersScale >= 0); + // correct position according to BaseOrientation and BaseOffset. + const FVector Pos = (ToFVector_M2U(OVR::Vector3f(InPose.Position), WorldToMetersScale) - (Settings->BaseOffset * WorldToMetersScale)) * CameraScale3D; + OutPosition = Settings->BaseOrientation.Inverse().RotateVector(Pos); + + // apply base orientation correction to OutOrientation + OutOrientation = Settings->BaseOrientation.Inverse() * OutOrientation; + OutOrientation.Normalize(); +} + +void FOculusRiftHMD::GetCurrentHMDPose(FQuat& CurrentOrientation, FVector& CurrentPosition, + bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector& PositionScale) +{ + // only supposed to be used from the game thread + check(IsInGameThread()); + auto frame = GetFrame(); + if (!frame) + { + CurrentOrientation = FQuat::Identity; + CurrentPosition = FVector::ZeroVector; + return; + } + if (PositionScale != FVector::ZeroVector) + { + frame->CameraScale3D = PositionScale; + frame->Flags.bCameraScale3DAlreadySet = true; + } + GetCurrentPose(CurrentOrientation, CurrentPosition, bUseOrienationForPlayerCamera, bUsePositionForPlayerCamera); + if (bUseOrienationForPlayerCamera) + { + frame->LastHmdOrientation = CurrentOrientation; + frame->Flags.bOrientationChanged = bUseOrienationForPlayerCamera; + } + if (bUsePositionForPlayerCamera) + { + frame->LastHmdPosition = CurrentPosition; + frame->Flags.bPositionChanged = bUsePositionForPlayerCamera; + } +} + +void FOculusRiftHMD::GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) +{ + GetCurrentHMDPose(CurrentOrientation, CurrentPosition, false, false, FVector::ZeroVector); +} + +FVector FOculusRiftHMD::GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale) +{ + const auto frame = GetFrame(); + if (!frame) + { + return FVector::ZeroVector; + } + FVector UnrotatedPos = CurrentOrientation.Inverse().RotateVector(CurrentPosition); + UnrotatedPos.X -= frame->Settings->NeckToEyeInMeters.X * frame->WorldToMetersScale; + UnrotatedPos.Z -= frame->Settings->NeckToEyeInMeters.Y * frame->WorldToMetersScale; + return UnrotatedPos; +} + +void FOculusRiftHMD::GetCurrentPose(FQuat& CurrentHmdOrientation, FVector& CurrentHmdPosition, bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera) +{ + check(IsInGameThread()); + + auto frame = GetFrame(); + check(frame); + + if (bUseOrienationForPlayerCamera || bUsePositionForPlayerCamera) + { + // if this pose is going to be used for camera update then save it. + // This matters only if bUpdateOnRT is OFF. + frame->EyeRenderPose[0] = frame->CurEyeRenderPose[0]; + frame->EyeRenderPose[1] = frame->CurEyeRenderPose[1]; + frame->HeadPose = frame->CurTrackingState.HeadPose.ThePose; + } + + frame->PoseToOrientationAndPosition(frame->CurTrackingState.HeadPose.ThePose, CurrentHmdOrientation, CurrentHmdPosition); + //UE_LOG(LogHMD, Log, TEXT("CRHDPS: Pos %.3f %.3f %.3f"), frame->CurTrackingState.HeadPose.ThePose.Position.x, frame->CurTrackingState.HeadPose.ThePose.Position.y, frame->CurTrackingState.HeadPose.ThePose.Position.z); + //UE_LOG(LogHMD, Log, TEXT("CRPOSE: Pos %.3f %.3f %.3f"), CurrentHmdPosition.X, CurrentHmdPosition.Y, CurrentHmdPosition.Z); + //UE_LOG(LogHMD, Log, TEXT("CRPOSE: Yaw %.3f Pitch %.3f Roll %.3f"), CurrentHmdOrientation.Rotator().Yaw, CurrentHmdOrientation.Rotator().Pitch, CurrentHmdOrientation.Rotator().Roll); +} + +TSharedPtr FOculusRiftHMD::GetViewExtension() +{ + TSharedPtr Result(MakeShareable(new FViewExtension(this))); + return Result; +} + +void FOculusRiftHMD::ResetStereoRenderingParams() +{ + FHeadMountedDisplay::ResetStereoRenderingParams(); + Settings->InterpupillaryDistance = ovrHmd_GetFloat(Hmd, OVR_KEY_IPD, OVR_DEFAULT_IPD); +} + +bool FOculusRiftHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) +{ + if (FHeadMountedDisplay::Exec(InWorld, Cmd, Ar)) + { + return true; + } + + auto frame = GetFrame(); + + if (FParse::Command(&Cmd, TEXT("HMD"))) + { + if (FParse::Command(&Cmd, TEXT("LP"))) // low persistence mode + { + FString CmdName = FParse::Token(Cmd, 0); + if (!CmdName.IsEmpty()) + { + if (!FCString::Stricmp(*CmdName, TEXT("ON"))) + { + Settings->Flags.bLowPersistenceMode = true; + } + else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) + { + Settings->Flags.bLowPersistenceMode = false; + } + else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) + { + Settings->Flags.bLowPersistenceMode = !Settings->Flags.bLowPersistenceMode; + } + else + { + return false; + } + } + else + { + Settings->Flags.bLowPersistenceMode = !Settings->Flags.bLowPersistenceMode; + } + Flags.bNeedUpdateHmdCaps = true; + Ar.Logf(TEXT("Low Persistence is currently %s"), (Settings->Flags.bLowPersistenceMode) ? TEXT("ON") : TEXT("OFF")); + return true; + } + else if (FParse::Command(&Cmd, TEXT("MIRROR"))) // to mirror or not to mirror?... + { + FString CmdName = FParse::Token(Cmd, 0); + if (!CmdName.IsEmpty()) + { + if (!FCString::Stricmp(*CmdName, TEXT("ON"))) + { + Settings->Flags.bMirrorToWindow = true; + } + else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) + { + Settings->Flags.bMirrorToWindow = false; + } + else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) + { + Settings->Flags.bMirrorToWindow = !Settings->Flags.bMirrorToWindow; + } + else if (!FCString::Stricmp(*CmdName, TEXT("RESET"))) + { + Settings->Flags.bMirrorToWindow = true; + Settings->MirrorWindowSize.X = Settings->MirrorWindowSize.Y = 0; + } + else + { + int32 X = FCString::Atoi(*CmdName); + const TCHAR* CmdTemp = FCString::Strchr(*CmdName, 'x') ? FCString::Strchr(*CmdName, 'x') + 1 : FCString::Strchr(*CmdName, 'X') ? FCString::Strchr(*CmdName, 'X') + 1 : TEXT(""); + int32 Y = FCString::Atoi(CmdTemp); + + Settings->MirrorWindowSize.X = X; + Settings->MirrorWindowSize.Y = Y; + } + } + else + { + Settings->Flags.bMirrorToWindow = !Settings->Flags.bMirrorToWindow; + } + Flags.bNeedUpdateHmdCaps = true; + Ar.Logf(TEXT("Mirroring is currently %s"), (Settings->Flags.bMirrorToWindow) ? TEXT("ON") : TEXT("OFF")); + if (Settings->Flags.bMirrorToWindow && (Settings->MirrorWindowSize.X != 0 || Settings->MirrorWindowSize.Y != 0)) + { + Ar.Logf(TEXT("Mirror window size is %d x %d"), Settings->MirrorWindowSize.X, Settings->MirrorWindowSize.Y); + } + return true; + } +#ifdef OVR_SDK_RENDERING + else if (FParse::Command(&Cmd, TEXT("TIMEWARP"))) + { + FString CmdName = FParse::Token(Cmd, 0); + if (!CmdName.IsEmpty()) + { + if (!FCString::Stricmp(*CmdName, TEXT("ON"))) + { + Settings->Flags.bTimeWarp = true; + } + else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) + { + Settings->Flags.bTimeWarp = false; + } + else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) + { + Settings->Flags.bTimeWarp = !Settings->Flags.bTimeWarp; + } + else + { + return false; + } + } + else + { + Settings->Flags.bTimeWarp = !Settings->Flags.bTimeWarp; + } + Flags.bNeedUpdateDistortionCaps = true; + Ar.Logf(TEXT("TimeWarp is currently %s"), (Settings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); + return true; + } +#endif // #ifdef OVR_SDK_RENDERING +#if !UE_BUILD_SHIPPING + else if (FParse::Command(&Cmd, TEXT("STATS"))) // status / statistics + { + Settings->Flags.bShowStats = !Settings->Flags.bShowStats; + return true; + } + else if (FParse::Command(&Cmd, TEXT("GRID"))) // grid + { + Settings->Flags.bDrawGrid = !Settings->Flags.bDrawGrid; + return true; + } + else if (FParse::Command(&Cmd, TEXT("PROFILE"))) // profile + { + Settings->Flags.bProfiling = !Settings->Flags.bProfiling; + Flags.bNeedUpdateDistortionCaps = true; + Ar.Logf(TEXT("Profiling mode is currently %s"), (Settings->Flags.bProfiling) ? TEXT("ON") : TEXT("OFF")); + return true; + } +#endif //UE_BUILD_SHIPPING + } + else if (FParse::Command(&Cmd, TEXT("HMDMAG"))) + { + if (FParse::Command(&Cmd, TEXT("ON"))) + { + Settings->Flags.bYawDriftCorrectionEnabled = true; + Flags.bNeedUpdateHmdCaps = true; + return true; + } + else if (FParse::Command(&Cmd, TEXT("OFF"))) + { + Settings->Flags.bYawDriftCorrectionEnabled = false; + Flags.bNeedUpdateHmdCaps = true; + return true; + } + else if (FParse::Command(&Cmd, TEXT("SHOW"))) + { + Ar.Logf(TEXT("mag %s"), Settings->Flags.bYawDriftCorrectionEnabled ? + TEXT("on") : TEXT("off")); + return true; + } + return false; + } + else if (FParse::Command(&Cmd, TEXT("HMDWARP"))) + { +#ifndef OVR_SDK_RENDERING + if (FParse::Command( &Cmd, TEXT("ON") )) + { + Settings->Flags.bHmdDistortion = true; + return true; + } + else if (FParse::Command( &Cmd, TEXT("OFF") )) + { + Settings->Flags.bHmdDistortion = false; + return true; + } +#endif //OVR_SDK_RENDERING + if (FParse::Command(&Cmd, TEXT("CHA"))) + { + Settings->Flags.bChromaAbCorrectionEnabled = true; + Flags.bNeedUpdateDistortionCaps = true; + return true; + } + else if (FParse::Command(&Cmd, TEXT("NOCHA"))) + { + Settings->Flags.bChromaAbCorrectionEnabled = false; + Flags.bNeedUpdateDistortionCaps = true; + return true; + } + else if (FParse::Command(&Cmd, TEXT("SHOW"))) + { + Ar.Logf(TEXT("hmdwarp %s sc=%f %s"), (Settings->Flags.bHmdDistortion ? TEXT("on") : TEXT("off")) + , Settings->IdealScreenPercentage / 100.f + , (Settings->Flags.bChromaAbCorrectionEnabled ? TEXT("cha") : TEXT("nocha"))); + } + return true; + } + else if (FParse::Command(&Cmd, TEXT("OVRVERSION"))) + { + // deprecated. Use 'hmdversion' instead + Ar.Logf(*GetVersionString()); + return true; + } + + return false; +} + +FString FOculusRiftHMD::GetVersionString() const +{ + const char* Results = ovr_GetVersionString(); + FString s = FString::Printf(TEXT("%s, LibOVR: %s, built %s, %s"), *GEngineVersion.ToString(), UTF8_TO_TCHAR(Results), + UTF8_TO_TCHAR(__DATE__), UTF8_TO_TCHAR(__TIME__)); + return s; +} + +void FOculusRiftHMD::RecordAnalytics() +{ + if (FEngineAnalytics::IsAvailable()) + { + // prepare and send analytics data + TArray EventAttributes; + + FHeadMountedDisplay::MonitorInfo MonitorInfo; + GetHMDMonitorInfo(MonitorInfo); + if (Hmd) + { + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("DeviceName"), FString::Printf(TEXT("%s - %s"), ANSI_TO_TCHAR(Hmd->Manufacturer), ANSI_TO_TCHAR(Hmd->ProductName)))); + } + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("DisplayDeviceName"), MonitorInfo.MonitorName)); +#if PLATFORM_MAC // On OS X MonitorId is the CGDirectDisplayID aka uint64, not a string + FString DisplayId(FString::Printf(TEXT("%llu"), MonitorInfo.MonitorId)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("DisplayId"), DisplayId)); +#else + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("DisplayId"), MonitorInfo.MonitorId)); +#endif + FString MonResolution(FString::Printf(TEXT("(%d, %d)"), MonitorInfo.ResolutionX, MonitorInfo.ResolutionY)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("Resolution"), MonResolution)); + + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("ChromaAbCorrectionEnabled"), Settings->Flags.bChromaAbCorrectionEnabled)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("MagEnabled"), Settings->Flags.bYawDriftCorrectionEnabled)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("DevSettingsEnabled"), Settings->Flags.bDevSettingsEnabled)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("OverrideInterpupillaryDistance"), Settings->Flags.bOverrideIPD)); + if (Settings->Flags.bOverrideIPD) + { + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("InterpupillaryDistance"), GetInterpupillaryDistance())); + } + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("OverrideStereo"), Settings->Flags.bOverrideStereo)); + if (Settings->Flags.bOverrideStereo) + { + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("HFOV"), Settings->HFOVInRadians)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("VFOV"), Settings->VFOVInRadians)); + } + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("OverrideVSync"), Settings->Flags.bOverrideVSync)); + if (Settings->Flags.bOverrideVSync) + { + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("VSync"), Settings->Flags.bVSync)); + } + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("OverrideScreenPercentage"), Settings->Flags.bOverrideScreenPercentage)); + if (Settings->Flags.bOverrideScreenPercentage) + { + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("ScreenPercentage"), Settings->ScreenPercentage)); + } + if (Settings->Flags.bWorldToMetersOverride) + { + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("WorldToMetersScale"), Settings->WorldToMetersScale)); + } + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("InterpupillaryDistance"), Settings->InterpupillaryDistance)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("TimeWarp"), Settings->Flags.bTimeWarp)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("AllowFinishCurrentFrame"), Settings->Flags.bAllowFinishCurrentFrame)); +#ifdef OVR_VISION_ENABLED + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("HmdPosTracking"), Settings->Flags.bHmdPosTracking)); +#endif + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("LowPersistenceMode"), Settings->Flags.bLowPersistenceMode)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("UpdateOnRT"), Settings->Flags.bUpdateOnRT)); + EventAttributes.Add(FAnalyticsEventAttribute(TEXT("MirrorToWindow"), Settings->Flags.bMirrorToWindow)); + + FString OutStr(TEXT("Editor.VR.DeviceInitialised")); + FEngineAnalytics::GetProvider().RecordEvent(OutStr, EventAttributes); + } +} + +class FSceneViewport* FOculusRiftHMD::FindSceneViewport() +{ + if (!GIsEditor) + { + UGameEngine* GameEngine = Cast(GEngine); + return GameEngine->SceneViewport.Get(); + } +#if WITH_EDITOR + else + { + UEditorEngine* EditorEngine = Cast(GEngine); + return (FSceneViewport*)(EditorEngine->GetPIEViewport()); + } +#endif + return nullptr; +} + +//--------------------------------------------------- +// Oculus Rift IStereoRendering Implementation +//--------------------------------------------------- +bool FOculusRiftHMD::DoEnableStereo(bool bStereo, bool bApplyToHmd) +{ + check(IsInGameThread()); + + FSceneViewport* SceneVP = FindSceneViewport(); + if (bStereo && (!SceneVP || !SceneVP->IsStereoRenderingAllowed())) + { + return false; + } + + bool stereoToBeEnabled = (Settings->Flags.bHMDEnabled) ? bStereo : false; + + if ((Settings->Flags.bStereoEnabled && stereoToBeEnabled) || (!Settings->Flags.bStereoEnabled && !stereoToBeEnabled)) + { + // already in the desired mode + return Settings->Flags.bStereoEnabled; + } + + TSharedPtr Window; + if (SceneVP) + { + Window = SceneVP->FindWindow(); + } + + if (stereoToBeEnabled) + { + // check if we already have a window; if not, queue enable stereo to the next frames and exit + if (!Window.IsValid()) + { + Flags.bNeedEnableStereo = true; + Flags.bEnableStereoToHmd = bApplyToHmd || !IsFullscreenAllowed(); + return Settings->Flags.bStereoEnabled; + } + } + + bool wasFullscreenAllowed = IsFullscreenAllowed(); + if (OnOculusStateChange(stereoToBeEnabled)) + { + Settings->Flags.bStereoEnabled = stereoToBeEnabled; + + if (SceneVP && SceneVP->GetViewportWidget().IsValid()) + { + if (!IsFullscreenAllowed() && stereoToBeEnabled) + { + if (Hmd) + { + // keep window size, but set viewport size to Rift resolution + SceneVP->SetViewportSize(Hmd->Resolution.w, Hmd->Resolution.h); + } + } + else if (!wasFullscreenAllowed && !stereoToBeEnabled) + { + // restoring original viewport size (to be equal to window size). + if (Window.IsValid()) + { + FVector2D size = Window->GetSizeInScreen(); + SceneVP->SetViewportSize(size.X, size.Y); + Window->SetViewportSizeDrivenByWindow(true); + } + } + + if (SceneVP) + { + if (Window.IsValid()) + { + if (bApplyToHmd && IsFullscreenAllowed()) + { + { + FVector2D size = Window->GetSizeInScreen(); + SceneVP->SetViewportSize(size.X, size.Y); + Window->SetViewportSizeDrivenByWindow(true); + } + + if (stereoToBeEnabled) + { + EWindowMode::Type wm = (!GIsEditor) ? EWindowMode::Fullscreen : EWindowMode::WindowedFullscreen; + FVector2D size = Window->GetSizeInScreen(); + SceneVP->ResizeFrame(size.X, size.Y, wm, 0, 0); + } + else + { + // In Editor we cannot use ResizeFrame trick since it is called too late and App::IsGame + // returns false. + if (GIsEditor) + { + FSlateRect PreFullScreenRect; + PopPreFullScreenRect(PreFullScreenRect); + if (PreFullScreenRect.GetSize().X > 0 && PreFullScreenRect.GetSize().Y > 0 && IsFullscreenAllowed()) + { + Window->MoveWindowTo(FVector2D(PreFullScreenRect.Left, PreFullScreenRect.Top)); + } + } + else + { + FVector2D size = Window->GetSizeInScreen(); + SceneVP->ResizeFrame(size.X, size.Y, EWindowMode::Windowed, 0, 0); + } + } + } + else if (!IsFullscreenAllowed()) + { + // a special case when 'stereo on' or 'stereo hmd' is used in Direct mode. We must set proper window mode, otherwise + // it will be lost once window loses and regains the focus. + FVector2D size = Window->GetSizeInScreen(); + FSystemResolution::RequestResolutionChange(size.X, size.Y, (stereoToBeEnabled) ? EWindowMode::WindowedMirror : EWindowMode::Windowed); + } + } + } + } + } + return Settings->Flags.bStereoEnabled; +} + +bool FOculusRiftHMD::OnOculusStateChange(bool bIsEnabledNow) +{ + Settings->Flags.bHmdDistortion = bIsEnabledNow; + if (!bIsEnabledNow) + { + // Switching from stereo + ReleaseDevice(); + + ResetControlRotation(); + RestoreSystemValues(); + return true; + } + else + { + // Switching to stereo + InitDevice(); + + if (Hmd) + { + SaveSystemValues(); + Flags.bApplySystemOverridesOnStereo = true; + + UpdateStereoRenderingParams(); + return true; + } + DeltaControlRotation = FRotator::ZeroRotator; + } + return false; +} + +void FOculusRiftHMD::ApplySystemOverridesOnStereo(bool bForce) +{ + if (Settings->Flags.bStereoEnabled || bForce) + { + // Set the current VSync state + if (Settings->Flags.bOverrideVSync) + { + static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); + CVSyncVar->Set(Settings->Flags.bVSync != 0); + } + else + { + static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); + Settings->Flags.bVSync = CVSyncVar->GetInt() != 0; + } + UpdateHmdCaps(); + +#ifndef OVR_SDK_RENDERING + static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); + CFinishFrameVar->Set(Settings->Flags.bAllowFinishCurrentFrame != 0); +#endif + } +} + +void FOculusRiftHMD::SaveSystemValues() +{ + static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); + Settings->Flags.bSavedVSync = CVSyncVar->GetInt() != 0; +} + +void FOculusRiftHMD::RestoreSystemValues() +{ + static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); + // todo: bad - cvars are a user wish, this should be changed + CVSyncVar->Set(Settings->Flags.bSavedVSync != 0); + + static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); + // todo: bad - cvars are a user wish, this should be changed + CFinishFrameVar->Set(false); +} + +void FOculusRiftHMD::UpdatePostProcessSettings(FPostProcessSettings* Settings) +{ + const auto frame = GetFrame(); + if (frame && frame->Flags.bScreenPercentageEnabled) + { + Settings->ScreenPercentage = GetActualScreenPercentage(); + } +} + +void FOculusRiftHMD::CalculateStereoViewOffset(const EStereoscopicPass StereoPassType, const FRotator& ViewRotation, const float WorldToMeters, FVector& ViewLocation) +{ + check(WorldToMeters != 0.f); + + const int idx = (StereoPassType == eSSP_LEFT_EYE) ? 0 : 1; + + if (IsInGameThread()) + { + const auto frame = GetFrame(); + if (!frame) + { + return; + } + + // This method is called from GetProjectionData on a game thread. + // The modified ViewLocation is used ONLY for ViewMatrix composition, it is not + // stored modified in the ViewInfo. ViewInfo.ViewLocation remains unmodified. + + if (StereoPassType != eSSP_FULL || frame->Settings->Flags.bHeadTrackingEnforced) + { + if (!frame->Flags.bOrientationChanged) + { + UE_LOG(LogHMD, Log, TEXT("Orientation wasn't applied to a camera in frame %d"), GFrameCounter); + } + + FVector CurEyePosition; + FQuat CurEyeOrient; + frame->PoseToOrientationAndPosition(frame->EyeRenderPose[idx], CurEyeOrient, CurEyePosition); + + FVector HeadPosition = FVector::ZeroVector; + // If we use PlayerController->bFollowHmd then we must apply full EyePosition (HeadPosition == 0). + // Otherwise, we will apply only a difference between EyePosition and HeadPosition, since + // HeadPosition is supposedly already applied. + if (!frame->Flags.bPlayerControllerFollowsHmd) + { + FQuat HeadOrient; + frame->PoseToOrientationAndPosition(frame->HeadPose, HeadOrient, HeadPosition); + } + + // apply stereo disparity to ViewLocation. Note, ViewLocation already contains HeadPose.Position, thus + // we just need to apply delta between EyeRenderPose.Position and the HeadPose.Position. + // EyeRenderPose and HeadPose are captured by the same call to GetEyePoses. + const FVector HmdToEyeOffset = CurEyePosition - HeadPosition; + + // Calculate the difference between the final ViewRotation and EyeOrientation: + // we need to rotate the HmdToEyeOffset by this differential quaternion. + // When bPlayerControllerFollowsHmd == true, the DeltaControlOrientation already contains + // the proper value (see ApplyHmdRotation) + //FRotator r = ViewRotation - CurEyeOrient.Rotator(); + const FQuat ViewOrient = ViewRotation.Quaternion(); + const FQuat DeltaControlOrientation = ViewOrient * CurEyeOrient.Inverse(); + + //UE_LOG(LogHMD, Log, TEXT("EYEROT: Yaw %.3f Pitch %.3f Roll %.3f"), CurEyeOrient.Rotator().Yaw, CurEyeOrient.Rotator().Pitch, CurEyeOrient.Rotator().Roll); + //UE_LOG(LogHMD, Log, TEXT("VIEROT: Yaw %.3f Pitch %.3f Roll %.3f"), ViewRotation.Yaw, ViewRotation.Pitch, ViewRotation.Roll); + //UE_LOG(LogHMD, Log, TEXT("DLTROT: Yaw %.3f Pitch %.3f Roll %.3f"), DeltaControlOrientation.Rotator().Yaw, DeltaControlOrientation.Rotator().Pitch, DeltaControlOrientation.Rotator().Roll); + + // The HMDPosition already has HMD orientation applied. + // Apply rotational difference between HMD orientation and ViewRotation + // to HMDPosition vector. + const FVector vEyePosition = DeltaControlOrientation.RotateVector(HmdToEyeOffset); + ViewLocation += vEyePosition; + + //UE_LOG(LogHMD, Log, TEXT("DLTPOS: %.3f %.3f %.3f"), vEyePosition.X, vEyePosition.Y, vEyePosition.Z); + } + } +} + +void FOculusRiftHMD::ResetOrientationAndPosition(float yaw) +{ + ResetOrientation(yaw); + ResetPosition(); +} + +void FOculusRiftHMD::ResetOrientation(float yaw) +{ + const auto frame = GetFrame(); + OVR::Quatf orientation; + if (frame) + { + orientation = OVR::Quatf(frame->CurTrackingState.HeadPose.ThePose.Orientation); + } + else + { + const ovrTrackingState ss = ovrHmd_GetTrackingState(Hmd, ovr_GetTimeInSeconds()); + const ovrPosef& pose = ss.HeadPose.ThePose; + orientation = OVR::Quatf(pose.Orientation); + } + + FRotator ViewRotation; + ViewRotation = FRotator(ToFQuat(orientation)); + ViewRotation.Pitch = 0; + ViewRotation.Roll = 0; + + if (yaw != 0.f) + { + // apply optional yaw offset + ViewRotation.Yaw -= yaw; + ViewRotation.Normalize(); + } + + Settings->BaseOrientation = ViewRotation.Quaternion(); +} + +void FOculusRiftHMD::ResetPosition() +{ + // Reset position + auto frame = GetFrame(); + OVR::Quatf orientation; + if (frame) + { + orientation = OVR::Quatf(frame->CurTrackingState.HeadPose.ThePose.Orientation); + Settings->BaseOffset = ToFVector(frame->CurTrackingState.HeadPose.ThePose.Position); + } + else + { + const ovrTrackingState ss = ovrHmd_GetTrackingState(Hmd, ovr_GetTimeInSeconds()); + const ovrPosef& pose = ss.HeadPose.ThePose; + orientation = OVR::Quatf(pose.Orientation); + Settings->BaseOffset = ToFVector(pose.Position); + } +} + +FMatrix FOculusRiftHMD::GetStereoProjectionMatrix(EStereoscopicPass StereoPassType, const float FOV) const +{ + auto frame = GetFrame(); + check(frame); + check(IsStereoEnabled()); + + const FSettings* FrameSettings = frame->GetSettings(); + + const int idx = (StereoPassType == eSSP_LEFT_EYE) ? 0 : 1; + + FMatrix proj = ToFMatrix(FrameSettings->EyeProjectionMatrices[idx]); + + // correct far and near planes for reversed-Z projection matrix + const float InNearZ = (FrameSettings->NearClippingPlane) ? FrameSettings->NearClippingPlane : GNearClippingPlane; + const float InFarZ = (FrameSettings->FarClippingPlane) ? FrameSettings->FarClippingPlane : GNearClippingPlane; + proj.M[3][3] = 0.0f; + proj.M[2][3] = 1.0f; + + proj.M[2][2] = (InNearZ == InFarZ) ? 0.0f : InNearZ / (InNearZ - InFarZ); + proj.M[3][2] = (InNearZ == InFarZ) ? InNearZ : -InFarZ * InNearZ / (InNearZ - InFarZ); + + return proj; +} + +void FOculusRiftHMD::GetOrthoProjection(int32 RTWidth, int32 RTHeight, float OrthoDistance, FMatrix OrthoProjection[2]) const +{ + const auto frame = GetFrame(); + check(frame); + + const FSettings* FrameSettings = frame->GetSettings(); + + OrthoDistance /= frame->WorldToMetersScale; // This is meters from the camera (viewer) that we place the ortho plane. + + const OVR::Vector2f orthoScale[2] = + { + OVR::Vector2f(1.f) / OVR::Vector2f(FrameSettings->EyeRenderDesc[0].PixelsPerTanAngleAtCenter), + OVR::Vector2f(1.f) / OVR::Vector2f(FrameSettings->EyeRenderDesc[1].PixelsPerTanAngleAtCenter) + }; + + OVR::Matrix4f SubProjection[2]; + SubProjection[0] = ovrMatrix4f_OrthoSubProjection(FrameSettings->PerspectiveProjection[0], orthoScale[0], OrthoDistance, FrameSettings->EyeRenderDesc[0].HmdToEyeViewOffset.x); + SubProjection[1] = ovrMatrix4f_OrthoSubProjection(FrameSettings->PerspectiveProjection[1], orthoScale[1], OrthoDistance, FrameSettings->EyeRenderDesc[1].HmdToEyeViewOffset.x); + + //Translate the subprojection for half of the screen . map it from [0,1] to [-1,1] . The total translation is translated * 0.25. + OrthoProjection[0] = FTranslationMatrix(FVector(SubProjection[0].M[0][3] * RTWidth * .25 , 0 , 0)); + //Right eye gets translated to right half of screen + OrthoProjection[1] = FTranslationMatrix(FVector(SubProjection[1].M[0][3] * RTWidth * .25 + RTWidth * .5 + FrameSettings->TexturePaddingPerEye*2, 0 , 0)); + + if (FrameSettings->TexturePaddingPerEye > 0) + { + // Apply scale to compensate the texture padding between two views. + FMatrix ScaleMatrix( + FPlane(1, 0, 0, 0), + FPlane(0, 0, 0, 0), + FPlane(0, 0, 0, 0), + FPlane(0, 0, 0, 0)); + + ScaleMatrix = ScaleMatrix.ApplyScale(float(RTWidth) / (RTWidth + FrameSettings->TexturePaddingPerEye*2)); + ScaleMatrix.M[1][1] = 1.0f; + ScaleMatrix.M[2][2] = 1.0f; + ScaleMatrix.M[3][3] = 1.0f; + + OrthoProjection[0] *= ScaleMatrix; + OrthoProjection[1] *= ScaleMatrix; + } +} + +void FOculusRiftHMD::InitCanvasFromView(FSceneView* InView, UCanvas* Canvas) +{ + // This is used for placing small HUDs (with names) + // over other players (for example, in Capture Flag). + // HmdOrientation should be initialized by GetCurrentOrientation (or + // user's own value). +} + +//--------------------------------------------------- +// Oculus Rift ISceneViewExtension Implementation +//--------------------------------------------------- + +void FOculusRiftHMD::SetupViewFamily(FSceneViewFamily& InViewFamily) +{ + auto frame = GetFrame(); + check(frame); + + InViewFamily.EngineShowFlags.MotionBlur = 0; +#ifndef OVR_SDK_RENDERING + InViewFamily.EngineShowFlags.HMDDistortion = frame->Settings->Flags.bHmdDistortion; +#else + InViewFamily.EngineShowFlags.HMDDistortion = false; +#endif + InViewFamily.EngineShowFlags.StereoRendering = IsStereoEnabled(); + + frame->Flags.bScreenPercentageEnabled = InViewFamily.EngineShowFlags.ScreenPercentage; +} + +void FOculusRiftHMD::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) +{ + auto frame = GetFrame(); + check(frame); + + InView.BaseHmdOrientation = frame->LastHmdOrientation; + InView.BaseHmdLocation = frame->LastHmdPosition; + +#ifndef OVR_SDK_RENDERING + InViewFamily.bUseSeparateRenderTarget = false; + + // check and save texture size. + if (InView.StereoPass == eSSP_LEFT_EYE) + { + if (Settings->EyeRenderViewport[0].Size() != InView.ViewRect.Size()) + { + Settings->SetEyeRenderViewport(InView.ViewRect.Size().X, InView.ViewRect.Size().Y); + + // patch EyeRenderViewport in the frame's settings as well + FMemory::Memcpy(frame->GetSettings()->EyeRenderViewport, Settings->EyeRenderViewport); + Flags.bNeedUpdateStereoRenderingParams = true; + } + } +#else + InViewFamily.bUseSeparateRenderTarget = ShouldUseSeparateRenderTarget(); + + if (InView.StereoPass == eSSP_RIGHT_EYE) + { + InView.ViewRect.Min.X += frame->GetSettings()->GetTexturePaddingPerEye()*2; + InView.ViewRect.Max.X += frame->GetSettings()->GetTexturePaddingPerEye()*2; + } + +#endif + + const int eyeIdx = (InView.StereoPass == eSSP_LEFT_EYE) ? 0 : 1; + frame->CachedViewRotation[eyeIdx] = InView.ViewRotation; +} + +bool FOculusRiftHMD::IsHeadTrackingAllowed() const +{ +#if WITH_EDITOR + if (GIsEditor) + { + UEditorEngine* EdEngine = Cast(GEngine); + return Hmd && (!EdEngine || EdEngine->bUseVRPreviewForPlayWorld || GetDefault()->ViewportGetsHMDControl) && (Settings->Flags.bHeadTrackingEnforced || GEngine->IsStereoscopic3D()); + } +#endif//WITH_EDITOR + return Hmd && FHeadMountedDisplay::IsHeadTrackingAllowed(); +} + +//--------------------------------------------------- +// Oculus Rift Specific +//--------------------------------------------------- + +FOculusRiftHMD::FOculusRiftHMD() + : + Hmd(nullptr) +{ + OCFlags.Raw = 0; + DeltaControlRotation = FRotator::ZeroRotator; + + Settings = MakeShareable(new FSettings); + + if (GIsEditor) + { + Settings->Flags.bOverrideScreenPercentage = true; + Settings->ScreenPercentage = 100; + } + OSWindowHandle = nullptr; + Startup(); +} + +FOculusRiftHMD::~FOculusRiftHMD() +{ + Shutdown(); +} + +void FOculusRiftHMD::Startup() +{ +#if PLATFORM_MAC + if (GIsEditor) + { + // no editor support for Mac yet + return; + } +#endif + if ((!IsRunningGame() && !GIsEditor) || (Settings->Flags.InitStatus & FSettings::eStartupExecuted) != 0) + { + // do not initialize plugin for server or if it was already initialized + return; + } + Settings->Flags.InitStatus |= FSettings::eStartupExecuted; + + // Initializes LibOVR. + ovrInitParams initParams; + FMemory::Memset(initParams, 0); + initParams.Flags = ovrInit_RequestVersion; + initParams.RequestedMinorVersion = OVR_MINOR_VERSION; +#if !UE_BUILD_SHIPPING + initParams.LogCallback = OvrLogCallback; +#endif + ovrBool bWasInitialized = ovr_Initialize(&initParams); + + if (GIsEditor) + { + Settings->Flags.bHeadTrackingEnforced = true; + } + + bool bForced = FParse::Param(FCommandLine::Get(), TEXT("forcedrift")); + bool bInitialized = false; + if (!bForced) + { + bInitialized = InitDevice(); + } + + if (!bInitialized && !bForced) + { + ovr_Shutdown(); + Settings->Flags.InitStatus = 0; + return; + } + + // Uncap fps to enable FPS higher than 62 + GEngine->bSmoothFrameRate = false; + + SaveSystemValues(); + +#ifdef OVR_SDK_RENDERING +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + if (IsPCPlatform(GMaxRHIShaderPlatform) && !IsOpenGLPlatform(GMaxRHIShaderPlatform)) + { + pD3D11Bridge = new D3D11Bridge(); + } +#endif +#if defined(OVR_GL) + if (IsOpenGLPlatform(GMaxRHIShaderPlatform)) + { + pOGLBridge = new OGLBridge(); + } +#endif +#endif // #ifdef OVR_SDK_RENDERING + + if (bForced || Hmd) + { + Settings->Flags.InitStatus |= FSettings::eInitialized; + + UE_LOG(LogHMD, Log, TEXT("Oculus plugin initialized. Version: %s"), *GetVersionString()); + } +} + +void FOculusRiftHMD::Shutdown() +{ + if (!(Settings->Flags.InitStatus & FSettings::eInitialized)) + { + return; + } + + RestoreSystemValues(); + +#ifdef OVR_SDK_RENDERING + ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ShutdownRen, + FOculusRiftHMD*, Plugin, this, + { + Plugin->ShutdownRendering(); + }); + FlushRenderingCommands(); +#endif // OVR_SDK_RENDERING + ReleaseDevice(); + + Settings = nullptr; + Frame = nullptr; + RenderFrame = nullptr; + + ovr_Shutdown(); + UE_LOG(LogHMD, Log, TEXT("Oculus shutdown.")); +} + +bool FOculusRiftHMD::InitDevice() +{ + if (Hmd) + { + const ovrTrackingState ss = ovrHmd_GetTrackingState(Hmd, ovr_GetTimeInSeconds()); + if (!(ss.StatusFlags & ovrStatus_HmdConnected)) + { + ReleaseDevice(); + } + else + { + return true; // already created and present + } + } + check(!Hmd); + + FSettings* CurrentSettings = GetSettings(); + + Hmd = ovrHmd_Create(0); + if (Hmd) + { + bDirectModeHack = (Hmd->HmdCaps & ovrHmdCap_ExtendDesktop) == 0; + + CurrentSettings->SupportedDistortionCaps = Hmd->DistortionCaps; + CurrentSettings->SupportedHmdCaps = Hmd->HmdCaps; + CurrentSettings->SupportedTrackingCaps = Hmd->TrackingCaps; + +#ifndef OVR_SDK_RENDERING + CurrentSettings->SupportedDistortionCaps &= ~ovrDistortionCap_Overdrive; +#endif +#ifndef OVR_VISION_ENABLED + CurrentSettings->SupportedTrackingCaps &= ~ovrTrackingCap_Position; +#endif + + CurrentSettings->DistortionCaps = CurrentSettings->SupportedDistortionCaps & (ovrDistortionCap_TimeWarp | ovrDistortionCap_Vignette | ovrDistortionCap_Overdrive); + CurrentSettings->TrackingCaps = CurrentSettings->SupportedTrackingCaps & (ovrTrackingCap_Orientation | ovrTrackingCap_MagYawCorrection | ovrTrackingCap_Position); + CurrentSettings->HmdCaps = CurrentSettings->SupportedHmdCaps & (ovrHmdCap_DynamicPrediction | ovrHmdCap_LowPersistence); + CurrentSettings->HmdCaps |= (CurrentSettings->Flags.bVSync ? 0 : ovrHmdCap_NoVSync); + + if (!(CurrentSettings->SupportedDistortionCaps & ovrDistortionCap_TimeWarp)) + { + CurrentSettings->Flags.bTimeWarp = false; + } + + CurrentSettings->Flags.bHmdPosTracking = (CurrentSettings->SupportedTrackingCaps & ovrTrackingCap_Position) != 0; + + LoadFromIni(); + + UpdateDistortionCaps(); + UpdateHmdRenderInfo(); + UpdateStereoRenderingParams(); + UpdateHmdCaps(); + } + + return Hmd != nullptr; +} + +void FOculusRiftHMD::ReleaseDevice() +{ + if (Hmd) + { + SaveToIni(); + + ovrHmd_AttachToWindow(Hmd, NULL, NULL, NULL); + + // Wait for all resources to be released +#ifdef OVR_SDK_RENDERING + ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ResetRen, + FOculusRiftHMD*, Plugin, this, + { + if (Plugin->GetActiveRHIBridgeImpl()) + { + Plugin->GetActiveRHIBridgeImpl()->Reset(); + } + }); +#endif + // Wait for all resources to be released + FlushRenderingCommands(); + + ovrHmd_Destroy(Hmd); + Hmd = nullptr; + } +} + +void FOculusRiftHMD::UpdateDistortionCaps() +{ + FSettings* CurrentSettings = GetSettings(); + + if (IsOpenGLPlatform(GMaxRHIShaderPlatform)) + { + CurrentSettings->DistortionCaps &= ~ovrDistortionCap_SRGB; + CurrentSettings->DistortionCaps |= ovrDistortionCap_FlipInput; + } + (CurrentSettings->Flags.bTimeWarp) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_TimeWarp : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_TimeWarp; + CurrentSettings->DistortionCaps |= ovrDistortionCap_Overdrive; + //(CurrentSettings->Flags.bHQDistortion) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_HqDistortion : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_HqDistortion; +#if !UE_BUILD_SHIPPING + (CurrentSettings->Flags.bProfiling) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_ProfileNoSpinWaits : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_ProfileNoSpinWaits; +#endif // #if !UE_BUILD_SHIPPING + +#ifdef OVR_SDK_RENDERING + if (GetActiveRHIBridgeImpl()) + { + GetActiveRHIBridgeImpl()->SetNeedReinitRendererAPI(); + } +#endif // OVR_SDK_RENDERING + Flags.bNeedUpdateDistortionCaps = false; +} + +void FOculusRiftHMD::UpdateHmdCaps() +{ + if (Hmd) + { + FSettings* CurrentSettings = GetSettings(); + + CurrentSettings->TrackingCaps = ovrTrackingCap_Orientation; + if (CurrentSettings->Flags.bYawDriftCorrectionEnabled) + { + CurrentSettings->TrackingCaps |= ovrTrackingCap_MagYawCorrection; + } + else + { + CurrentSettings->TrackingCaps &= ~ovrTrackingCap_MagYawCorrection; + } + if (CurrentSettings->Flags.bHmdPosTracking) + { + CurrentSettings->TrackingCaps |= ovrTrackingCap_Position; + } + else + { + CurrentSettings->TrackingCaps &= ~ovrTrackingCap_Position; + } + + if (CurrentSettings->Flags.bLowPersistenceMode) + { + CurrentSettings->HmdCaps |= ovrHmdCap_LowPersistence; + } + else + { + CurrentSettings->HmdCaps &= ~ovrHmdCap_LowPersistence; + } + + if (CurrentSettings->Flags.bVSync) + { + CurrentSettings->HmdCaps &= ~ovrHmdCap_NoVSync; + } + else + { + CurrentSettings->HmdCaps |= ovrHmdCap_NoVSync; + } + + if (CurrentSettings->Flags.bMirrorToWindow) + { + CurrentSettings->HmdCaps &= ~ovrHmdCap_NoMirrorToWindow; + } + else + { + CurrentSettings->HmdCaps |= ovrHmdCap_NoMirrorToWindow; + } + ovrHmd_SetEnabledCaps(Hmd, CurrentSettings->HmdCaps); + + ovrHmd_ConfigureTracking(Hmd, CurrentSettings->TrackingCaps, 0); + Flags.bNeedUpdateHmdCaps = false; + } +} + +FORCEINLINE static float GetVerticalFovRadians(const ovrFovPort& fov) +{ + return FMath::Atan(fov.UpTan) + FMath::Atan(fov.DownTan); +} + +FORCEINLINE static float GetHorizontalFovRadians(const ovrFovPort& fov) +{ + return FMath::Atan(fov.LeftTan) + FMath::Atan(fov.RightTan); +} + +void FOculusRiftHMD::UpdateHmdRenderInfo() +{ + check(Hmd); + + UE_LOG(LogHMD, Warning, TEXT("HMD %s, Monitor %s, res = %d x %d, windowPos = {%d, %d}"), ANSI_TO_TCHAR(Hmd->ProductName), + ANSI_TO_TCHAR(Hmd->DisplayDeviceName), Hmd->Resolution.w, Hmd->Resolution.h, Hmd->WindowsPos.x, Hmd->WindowsPos.y); + + FSettings* CurrentSettings = GetSettings(); + + // Calc FOV + if (!CurrentSettings->Flags.bOverrideFOV) + { + // Calc FOV, symmetrical, for each eye. + CurrentSettings->EyeFov[0] = Hmd->DefaultEyeFov[0]; + CurrentSettings->EyeFov[1] = Hmd->DefaultEyeFov[1]; + + // Calc FOV in radians + CurrentSettings->VFOVInRadians = FMath::Max(GetVerticalFovRadians(CurrentSettings->EyeFov[0]), GetVerticalFovRadians(CurrentSettings->EyeFov[1])); + CurrentSettings->HFOVInRadians = FMath::Max(GetHorizontalFovRadians(CurrentSettings->EyeFov[0]), GetHorizontalFovRadians(CurrentSettings->EyeFov[1])); + } + + const ovrSizei recommenedTex0Size = ovrHmd_GetFovTextureSize(Hmd, ovrEye_Left, CurrentSettings->EyeFov[0], 1.0f); + const ovrSizei recommenedTex1Size = ovrHmd_GetFovTextureSize(Hmd, ovrEye_Right, CurrentSettings->EyeFov[1], 1.0f); + + ovrSizei idealRenderTargetSize; + idealRenderTargetSize.w = recommenedTex0Size.w + recommenedTex1Size.w; + idealRenderTargetSize.h = FMath::Max(recommenedTex0Size.h, recommenedTex1Size.h); + + CurrentSettings->IdealScreenPercentage = FMath::Max(float(idealRenderTargetSize.w) / float(Hmd->Resolution.w) * 100.f, + float(idealRenderTargetSize.h) / float(Hmd->Resolution.h) * 100.f); + + // Override eye distance by the value from HMDInfo (stored in Profile). + if (!CurrentSettings->Flags.bOverrideIPD) + { + CurrentSettings->InterpupillaryDistance = ovrHmd_GetFloat(Hmd, OVR_KEY_IPD, OVR_DEFAULT_IPD); + } + + // cache eye to neck distance. + float neck2eye[2] = { OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL }; + ovrHmd_GetFloatArray(Hmd, OVR_KEY_NECK_TO_EYE_DISTANCE, neck2eye, 2); + CurrentSettings->NeckToEyeInMeters = FVector2D(neck2eye[0], neck2eye[1]); + + // Default texture size (per eye) is equal to half of W x H resolution. Will be overridden in SetupView. + //CurrentSettings->SetViewportSize(Hmd->Resolution.w / 2, Hmd->Resolution.h); + + Flags.bNeedUpdateStereoRenderingParams = true; +} + +void FOculusRiftHMD::UpdateStereoRenderingParams() +{ + check(IsInGameThread()); + + FSettings* CurrentSettings = GetSettings(); + + if ((!CurrentSettings->IsStereoEnabled() && !CurrentSettings->Flags.bHeadTrackingEnforced)) + { + return; + } + if (IsInitialized() && Hmd) + { + //!AB: note, for Direct Rendering EyeRenderDesc is calculated twice, once + // here and another time in BeginRendering_RenderThread. I need to have EyeRenderDesc + // on a game thread for ViewAdjust (for StereoViewOffset calculation). + CurrentSettings->EyeRenderDesc[0] = ovrHmd_GetRenderDesc(Hmd, ovrEye_Left, CurrentSettings->EyeFov[0]); + CurrentSettings->EyeRenderDesc[1] = ovrHmd_GetRenderDesc(Hmd, ovrEye_Right, CurrentSettings->EyeFov[1]); + if (CurrentSettings->Flags.bOverrideIPD) + { + CurrentSettings->EyeRenderDesc[0].HmdToEyeViewOffset.x = CurrentSettings->InterpupillaryDistance * 0.5f; + CurrentSettings->EyeRenderDesc[1].HmdToEyeViewOffset.x = -CurrentSettings->InterpupillaryDistance * 0.5f; + } + + const unsigned int ProjModifiers = ovrProjection_None; //@TODO revise to use ovrProjection_FarClipAtInfinity and/or ovrProjection_FarLessThanNear + // Far and Near clipping planes will be modified in GetStereoProjectionMatrix() + CurrentSettings->EyeProjectionMatrices[0] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[0], 0.01f, 10000.0f, ProjModifiers); + CurrentSettings->EyeProjectionMatrices[1] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[1], 0.01f, 10000.0f, ProjModifiers); + + CurrentSettings->PerspectiveProjection[0] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[0], 0.01f, 10000.f, ProjModifiers | ovrProjection_RightHanded); + CurrentSettings->PerspectiveProjection[1] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[1], 0.01f, 10000.f, ProjModifiers | ovrProjection_RightHanded); + + PrecalculateDistortionMesh(); + Flags.bNeedUpdateStereoRenderingParams = false; + } +} + +void FOculusRiftHMD::LoadFromIni() +{ + const TCHAR* OculusSettings = TEXT("Oculus.Settings"); + bool v; + float f; + if (GConfig->GetBool(OculusSettings, TEXT("bChromaAbCorrectionEnabled"), v, GEngineIni)) + { + Settings->Flags.bChromaAbCorrectionEnabled = v; + } + if (GConfig->GetBool(OculusSettings, TEXT("bYawDriftCorrectionEnabled"), v, GEngineIni)) + { + Settings->Flags.bYawDriftCorrectionEnabled = v; + } + if (GConfig->GetBool(OculusSettings, TEXT("bDevSettingsEnabled"), v, GEngineIni)) + { + Settings->Flags.bDevSettingsEnabled = v; + } + if (GConfig->GetBool(OculusSettings, TEXT("bOverrideIPD"), v, GEngineIni)) + { + Settings->Flags.bOverrideIPD = v; + if (Settings->Flags.bOverrideIPD) + { + if (GConfig->GetFloat(OculusSettings, TEXT("IPD"), f, GEngineIni)) + { + SetInterpupillaryDistance(f); + } + } + } + if (GConfig->GetBool(OculusSettings, TEXT("bOverrideStereo"), v, GEngineIni)) + { + Settings->Flags.bOverrideStereo = v; + if (Settings->Flags.bOverrideStereo) + { + if (GConfig->GetFloat(OculusSettings, TEXT("HFOV"), f, GEngineIni)) + { + Settings->HFOVInRadians = f; + } + if (GConfig->GetFloat(OculusSettings, TEXT("VFOV"), f, GEngineIni)) + { + Settings->VFOVInRadians = f; + } + } + } + if (GConfig->GetBool(OculusSettings, TEXT("bOverrideVSync"), v, GEngineIni)) + { + Settings->Flags.bOverrideVSync = v; + if (GConfig->GetBool(OculusSettings, TEXT("bVSync"), v, GEngineIni)) + { + Settings->Flags.bVSync = v; + } + } + if (!GIsEditor) + { + if (GConfig->GetBool(OculusSettings, TEXT("bOverrideScreenPercentage"), v, GEngineIni)) + { + Settings->Flags.bOverrideScreenPercentage = v; + if (GConfig->GetFloat(OculusSettings, TEXT("ScreenPercentage"), f, GEngineIni)) + { + Settings->ScreenPercentage = f; + } + } + } + if (GConfig->GetBool(OculusSettings, TEXT("bAllowFinishCurrentFrame"), v, GEngineIni)) + { + Settings->Flags.bAllowFinishCurrentFrame = v; + } + if (GConfig->GetBool(OculusSettings, TEXT("bLowPersistenceMode"), v, GEngineIni)) + { + Settings->Flags.bLowPersistenceMode = v; + } + if (GConfig->GetBool(OculusSettings, TEXT("bUpdateOnRT"), v, GEngineIni)) + { + Settings->Flags.bUpdateOnRT = v; + } + if (GConfig->GetFloat(OculusSettings, TEXT("FarClippingPlane"), f, GEngineIni)) + { + Settings->FarClippingPlane = f; + } + if (GConfig->GetFloat(OculusSettings, TEXT("NearClippingPlane"), f, GEngineIni)) + { + Settings->NearClippingPlane = f; + } +#if !UE_BUILD_SHIPPING + FString s; + if (GConfig->GetString(OculusSettings, TEXT("CubeMeshName"), s, GEngineIni)) + { + CubeMeshName = s; + } + if (GConfig->GetString(OculusSettings, TEXT("CubeMaterialName"), s, GEngineIni)) + { + CubeMaterialName = s; + } +#endif +} + +void FOculusRiftHMD::SaveToIni() +{ + const TCHAR* OculusSettings = TEXT("Oculus.Settings"); + GConfig->SetBool(OculusSettings, TEXT("bChromaAbCorrectionEnabled"), Settings->Flags.bChromaAbCorrectionEnabled, GEngineIni); + GConfig->SetBool(OculusSettings, TEXT("bYawDriftCorrectionEnabled"), Settings->Flags.bYawDriftCorrectionEnabled, GEngineIni); + GConfig->SetBool(OculusSettings, TEXT("bDevSettingsEnabled"), Settings->Flags.bDevSettingsEnabled, GEngineIni); + + GConfig->SetBool(OculusSettings, TEXT("bOverrideIPD"), Settings->Flags.bOverrideIPD, GEngineIni); + if (Settings->Flags.bOverrideIPD) + { + GConfig->SetFloat(OculusSettings, TEXT("IPD"), GetInterpupillaryDistance(), GEngineIni); + } + GConfig->SetBool(OculusSettings, TEXT("bOverrideStereo"), Settings->Flags.bOverrideStereo, GEngineIni); + if (Settings->Flags.bOverrideStereo) + { + GConfig->SetFloat(OculusSettings, TEXT("HFOV"), Settings->HFOVInRadians, GEngineIni); + GConfig->SetFloat(OculusSettings, TEXT("VFOV"), Settings->VFOVInRadians, GEngineIni); + } + + GConfig->SetBool(OculusSettings, TEXT("bOverrideVSync"), Settings->Flags.bOverrideVSync, GEngineIni); + if (Settings->Flags.bOverrideVSync) + { + GConfig->SetBool(OculusSettings, TEXT("VSync"), Settings->Flags.bVSync, GEngineIni); + } + + if (!GIsEditor) + { + GConfig->SetBool(OculusSettings, TEXT("bOverrideScreenPercentage"), Settings->Flags.bOverrideScreenPercentage, GEngineIni); + if (Settings->Flags.bOverrideScreenPercentage) + { + // Save the current ScreenPercentage state + GConfig->SetFloat(OculusSettings, TEXT("ScreenPercentage"), Settings->ScreenPercentage, GEngineIni); + } + } + GConfig->SetBool(OculusSettings, TEXT("bAllowFinishCurrentFrame"), Settings->Flags.bAllowFinishCurrentFrame, GEngineIni); + + GConfig->SetBool(OculusSettings, TEXT("bLowPersistenceMode"), Settings->Flags.bLowPersistenceMode, GEngineIni); + + GConfig->SetBool(OculusSettings, TEXT("bUpdateOnRT"), Settings->Flags.bUpdateOnRT, GEngineIni); + + if (Settings->Flags.bClippingPlanesOverride) + { + GConfig->SetFloat(OculusSettings, TEXT("FarClippingPlane"), Settings->FarClippingPlane, GEngineIni); + GConfig->SetFloat(OculusSettings, TEXT("NearClippingPlane"), Settings->NearClippingPlane, GEngineIni); + } +} + +bool FOculusRiftHMD::HandleInputKey(UPlayerInput* pPlayerInput, + const FKey& Key, EInputEvent EventType, float AmountDepressed, bool bGamepad) +{ + if (Hmd && EventType == IE_Pressed && Settings->IsStereoEnabled()) + { + if (!Key.IsMouseButton()) + { + ovrHmd_DismissHSWDisplay(Hmd); + } + } + return false; +} + +void FOculusRiftHMD::OnBeginPlay() +{ + // @TODO: add more values here. + // This call make sense when 'Play' is used from the Editor; + if (GIsEditor) + { + Settings->BaseOrientation = FQuat::Identity; + Settings->BaseOffset = FVector::ZeroVector; + Settings->WorldToMetersScale = 100.f; + Settings->Flags.bWorldToMetersOverride = false; + InitDevice(); + } +} + +void FOculusRiftHMD::OnEndPlay() +{ + if (GIsEditor) + { + EnableStereo(false); + ReleaseDevice(); + } +} + +void FOculusRiftHMD::GetRawSensorData(SensorData& OutData) +{ + FMemory::Memset(OutData, 0); + InitDevice(); + if (Hmd) + { + const ovrTrackingState ss = ovrHmd_GetTrackingState(Hmd, ovr_GetTimeInSeconds()); + OutData.Accelerometer = ToFVector(ss.RawSensorData.Accelerometer); + OutData.Gyro = ToFVector(ss.RawSensorData.Gyro); + OutData.Magnetometer = ToFVector(ss.RawSensorData.Magnetometer); + OutData.Temperature = ss.RawSensorData.Temperature; + OutData.TimeInSeconds = ss.RawSensorData.TimeInSeconds; + } +} + +bool FOculusRiftHMD::GetUserProfile(UserProfile& OutProfile) +{ + InitDevice(); + if (Hmd) + { + OutProfile.Name = ovrHmd_GetString(Hmd, OVR_KEY_USER, ""); + OutProfile.Gender = ovrHmd_GetString(Hmd, OVR_KEY_GENDER, OVR_DEFAULT_GENDER); + OutProfile.PlayerHeight = ovrHmd_GetFloat(Hmd, OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT); + OutProfile.EyeHeight = ovrHmd_GetFloat(Hmd, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT); + OutProfile.IPD = ovrHmd_GetFloat(Hmd, OVR_KEY_IPD, OVR_DEFAULT_IPD); + float neck2eye[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL}; + ovrHmd_GetFloatArray(Hmd, OVR_KEY_NECK_TO_EYE_DISTANCE, neck2eye, 2); + OutProfile.NeckToEyeDistance = FVector2D(neck2eye[0], neck2eye[1]); + OutProfile.ExtraFields.Reset(); + + int EyeRelief = ovrHmd_GetInt(Hmd, OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL); + OutProfile.ExtraFields.Add(FString(OVR_KEY_EYE_RELIEF_DIAL)) = FString::FromInt(EyeRelief); + + float eye2nose[2] = { 0.f, 0.f }; + ovrHmd_GetFloatArray(Hmd, OVR_KEY_EYE_TO_NOSE_DISTANCE, eye2nose, 2); + OutProfile.ExtraFields.Add(FString(OVR_KEY_EYE_RELIEF_DIAL"Horizontal")) = FString::SanitizeFloat(eye2nose[0]); + OutProfile.ExtraFields.Add(FString(OVR_KEY_EYE_RELIEF_DIAL"Vertical")) = FString::SanitizeFloat(eye2nose[1]); + + float maxEye2Plate[2] = { 0.f, 0.f }; + ovrHmd_GetFloatArray(Hmd, OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, maxEye2Plate, 2); + OutProfile.ExtraFields.Add(FString(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE"Horizontal")) = FString::SanitizeFloat(maxEye2Plate[0]); + OutProfile.ExtraFields.Add(FString(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE"Vertical")) = FString::SanitizeFloat(maxEye2Plate[1]); + + OutProfile.ExtraFields.Add(FString(ovrHmd_GetString(Hmd, OVR_KEY_EYE_CUP, ""))); + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +FViewExtension::FViewExtension(FHeadMountedDisplay* InDelegate) + : FHMDViewExtension(InDelegate) + , ShowFlags(ESFIM_All0) + , bFrameBegun(false) +{ + auto OculusHMD = static_cast(InDelegate); + Hmd = OculusHMD->Hmd; +#ifdef OVR_SDK_RENDERING + pPresentBridge = OculusHMD->GetActiveRHIBridgeImpl(); +#endif +} + +#endif //OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //#if PLATFORM_MAC diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftHMD_05.h b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftHMD_05.h new file mode 100644 index 000000000000..b2e1b60bdb07 --- /dev/null +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftHMD_05.h @@ -0,0 +1,575 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. + +#pragma once +// Mac uses 0.5.0 SDK, while PC - 0.6.0. This version supports 0.5.0 (Mac/Linux). +#if PLATFORM_MAC + +#include "IOculusRiftPlugin.h" +#include "IHeadMountedDisplay.h" +#include "HeadMountedDisplayCommon.h" + +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + +#include "SceneViewExtension.h" + +#if PLATFORM_SUPPORTS_PRAGMA_PACK +#pragma pack (push,8) +#endif + +#if PLATFORM_WINDOWS + #define OVR_VISION_ENABLED + #define OVR_SDK_RENDERING + #define OVR_D3D_VERSION 11 + #define OVR_GL +#elif PLATFORM_MAC + #define OVR_VISION_ENABLED + #define OVR_SDK_RENDERING + #define OVR_GL +#endif + +#ifdef OVR_VISION_ENABLED + #ifndef OVR_CAPI_VISIONSUPPORT + #define OVR_CAPI_VISIONSUPPORT + #endif +#endif + +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + #include + #include + #include + #include + #include + +#if PLATFORM_WINDOWS +#include "AllowWindowsPlatformTypes.h" +#endif +#ifdef OVR_SDK_RENDERING + #ifdef OVR_D3D_VERSION + #include "OVR_CAPI_D3D.h" + #endif // OVR_D3D_VERSION + #ifdef OVR_GL + #include "OVR_CAPI_GL.h" + #endif +#endif // OVR_SDK_RENDERING + +#endif //OCULUS_RIFT_SUPPORTED_PLATFORMS + +#if PLATFORM_SUPPORTS_PRAGMA_PACK +#pragma pack (pop) +#endif + +// #ifdef UE_BUILD_DEBUG +// #undef FORCEINLINE +// #define FORCEINLINE +// #endif + +struct FDistortionVertex; +class FOculusRiftHMD; + +namespace OculusRift +{ + /** + * Converts quat from Oculus ref frame to Unreal + */ + template + FORCEINLINE FQuat ToFQuat(const OVRQuat& InQuat) + { + return FQuat(float(-InQuat.z), float(InQuat.x), float(InQuat.y), float(-InQuat.w)); + } + /** + * Converts vector from Oculus to Unreal + */ + template + FORCEINLINE FVector ToFVector(const OVRVector3& InVec) + { + return FVector(float(-InVec.z), float(InVec.x), float(InVec.y)); + } + + /** + * Converts vector from Oculus to Unreal, also converting meters to UU (Unreal Units) + */ + template + FORCEINLINE FVector ToFVector_M2U(const OVRVector3& InVec, float WorldToMetersScale) + { + return FVector(float(-InVec.z * WorldToMetersScale), + float(InVec.x * WorldToMetersScale), + float(InVec.y * WorldToMetersScale)); + } + /** + * Converts vector from Oculus to Unreal, also converting UU (Unreal Units) to meters. + */ + template + FORCEINLINE OVRVector3 ToOVRVector_U2M(const FVector& InVec, float WorldToMetersScale) + { + return OVRVector3(float(InVec.Y * (1.f / WorldToMetersScale)), + float(InVec.Z * (1.f / WorldToMetersScale)), + float(-InVec.X * (1.f / WorldToMetersScale))); + } + /** + * Converts vector from Oculus to Unreal. + */ + template + FORCEINLINE OVRVector3 ToOVRVector(const FVector& InVec) + { + return OVRVector3(float(InVec.Y), float(InVec.Z), float(-InVec.X)); + } + + FORCEINLINE FMatrix ToFMatrix(const OVR::Matrix4f& vtm) + { + // Rows and columns are swapped between OVR::Matrix4f and FMatrix + return FMatrix( + FPlane(vtm.M[0][0], vtm.M[1][0], vtm.M[2][0], vtm.M[3][0]), + FPlane(vtm.M[0][1], vtm.M[1][1], vtm.M[2][1], vtm.M[3][1]), + FPlane(vtm.M[0][2], vtm.M[1][2], vtm.M[2][2], vtm.M[3][2]), + FPlane(vtm.M[0][3], vtm.M[1][3], vtm.M[2][3], vtm.M[3][3])); + } + + FORCEINLINE OVR::Recti ToOVRRecti(const FIntRect& rect) + { + return OVR::Recti(rect.Min.X, rect.Min.Y, rect.Size().X, rect.Size().Y); + } + +typedef uint64 bool64; + +class FSettings : public FHMDSettings +{ +public: + const int TexturePaddingPerEye = 12; // padding, in pixels, per eye (total padding will be doubled) + + ovrEyeRenderDesc EyeRenderDesc[2]; // 0 - left, 1 - right, same as Views + ovrMatrix4f EyeProjectionMatrices[2]; // 0 - left, 1 - right, same as Views + ovrMatrix4f PerspectiveProjection[2]; // used for calc ortho projection matrices + ovrFovPort EyeFov[2]; // 0 - left, 1 - right, same as Views + + unsigned SupportedTrackingCaps; + unsigned SupportedDistortionCaps; + unsigned SupportedHmdCaps; + + unsigned TrackingCaps; + unsigned DistortionCaps; + unsigned HmdCaps; + +#ifndef OVR_SDK_RENDERING + struct FDistortionMesh : public TSharedFromThis + { + FDistortionVertex* pVertices; + uint16* pIndices; + unsigned NumVertices; + unsigned NumIndices; + unsigned NumTriangles; + + FDistortionMesh() :pVertices(NULL), pIndices(NULL), NumVertices(0), NumIndices(0), NumTriangles(0) {} + ~FDistortionMesh() { Reset(); } + void Reset(); + }; + // U,V scale and offset needed for timewarp. + ovrVector2f UVScaleOffset[2][2]; // scale UVScaleOffset[x][0], offset UVScaleOffset[x][1], where x is eye idx + TSharedPtr pDistortionMesh[2]; // +#endif + + FSettings(); + virtual ~FSettings() override {} + + virtual TSharedPtr Clone() const override; + + virtual void SetEyeRenderViewport(int OneEyeVPw, int OneEyeVPh) override; + + float GetTexturePaddingPerEye() const { return TexturePaddingPerEye * GetActualScreenPercentage()/100.f; } +}; + +class FGameFrame : public FHMDGameFrame +{ +public: + ovrPosef CurEyeRenderPose[2];// eye render pose read at the beginning of the frame + ovrTrackingState CurTrackingState; // tracking state read at the beginning of the frame + + ovrPosef EyeRenderPose[2]; // eye render pose actually used + ovrPosef HeadPose; // position of head actually used + + FGameFrame(); + virtual ~FGameFrame() {} + + FSettings* GetSettings() + { + return (FSettings*)(Settings.Get()); + } + + const FSettings* GetSettings() const + { + return (FSettings*)(Settings.Get()); + } + + virtual TSharedPtr Clone() const override; + + void PoseToOrientationAndPosition(const ovrPosef& InPose, FQuat& OutOrientation, FVector& OutPosition) const; +}; + +// View extension class that contains all the rendering-specific data (also referred as 'RenderContext'). +// Each call to RT will use an unique instance of this class, attached to ViewFamily. +class FViewExtension : public FHMDViewExtension +{ +public: + FViewExtension(FHeadMountedDisplay* InDelegate); + + virtual void PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override; + virtual void PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override; + + FGameFrame* GetRenderFrame() const { return static_cast(RenderFrame.Get()); } + FSettings* GetFrameSetting() const { return static_cast(RenderFrame->GetSettings()); } +public: + class FCustomPresent* pPresentBridge; + ovrHmd Hmd; + ovrPosef CurEyeRenderPose[2];// most recent eye render poses + ovrPosef CurHeadPose; // current position of head + + FEngineShowFlags ShowFlags; // a copy of showflags + bool bFrameBegun : 1; +}; + +class FCustomPresent : public FRHICustomPresent +{ +public: + FCustomPresent() + : FRHICustomPresent(nullptr) + , bNeedReinitRendererAPI(true) + , bInitialized(false) + {} + + // Returns true if it is initialized and used. + bool IsInitialized() const { return bInitialized; } + + virtual void BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) = 0; + virtual void SetNeedReinitRendererAPI() { bNeedReinitRendererAPI = true; } + + virtual void Reset() = 0; + virtual void Shutdown() = 0; + + void UpdateViewport(const FViewport& Viewport, FRHIViewport* ViewportRHI, FGameFrame* InRenderFrame); + FGameFrame* GetRenderFrame() const { check(IsInRenderingThread()); return static_cast(RenderContext->RenderFrame.Get()); } + FViewExtension* GetRenderContext() const { return static_cast(RenderContext.Get()); } + FSettings* GetFrameSetting() const { check(IsInRenderingThread()); return static_cast(RenderContext->RenderFrame->GetSettings()); } + +protected: + void SetRenderContext(FHMDViewExtension* InRenderContext); + +protected: // data + TSharedPtr RenderContext; + bool bNeedReinitRendererAPI : 1; + bool bInitialized : 1; +}; + +} // namespace OculusRift + +using namespace OculusRift; + +/** + * Oculus Rift Head Mounted Display + */ +class FOculusRiftHMD : public FHeadMountedDisplay +{ + friend class FViewExtension; + friend class UOculusFunctionLibrary; +public: + static void PreInit(); + + /** IHeadMountedDisplay interface */ + virtual bool OnStartGameFrame() override; + + virtual bool IsHMDConnected() override; + virtual EHMDDeviceType::Type GetHMDDeviceType() const override; + virtual bool GetHMDMonitorInfo(MonitorInfo&) override; + + virtual bool DoesSupportPositionalTracking() const override; + virtual bool HasValidTrackingPosition() override; + virtual void GetPositionalTrackingCameraProperties(FVector& OutOrigin, FQuat& OutOrientation, float& OutHFOV, float& OutVFOV, float& OutCameraDistance, float& OutNearPlane, float& OutFarPlane) const override; + + virtual void GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) override; + + virtual FVector GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale) override; + + virtual TSharedPtr GetViewExtension() override; + virtual bool Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) override; + + virtual bool IsFullscreenAllowed() override; + virtual void RecordAnalytics() override; + + /** IStereoRendering interface */ + virtual void CalculateStereoViewOffset(const EStereoscopicPass StereoPassType, const FRotator& ViewRotation, + const float MetersToWorld, FVector& ViewLocation) override; + virtual FMatrix GetStereoProjectionMatrix(const EStereoscopicPass StereoPassType, const float FOV) const override; + virtual void GetOrthoProjection(int32 RTWidth, int32 RTHeight, float OrthoDistance, FMatrix OrthoProjection[2]) const override; + virtual void InitCanvasFromView(FSceneView* InView, UCanvas* Canvas) override; + virtual void GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const override; + virtual void GetTimewarpMatrices_RenderThread(const struct FRenderingCompositePassContext& Context, FMatrix& EyeRotationStart, FMatrix& EyeRotationEnd) const override; + + virtual void UpdateViewport(bool bUseSeparateRenderTarget, const FViewport& Viewport, SViewport*) override; + +#ifdef OVR_SDK_RENDERING + virtual void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) override; + virtual bool NeedReAllocateViewportRenderTarget(const FViewport& Viewport) override; +#endif//OVR_SDK_RENDERING + + virtual bool ShouldUseSeparateRenderTarget() const override + { +#ifdef OVR_SDK_RENDERING + check(IsInGameThread()); + return IsStereoEnabled(); +#else + return false; +#endif//OVR_SDK_RENDERING + } + + virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override; + virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override; + virtual uint32 GetNumberOfBufferedFrames() const override { return 1; } + + /** Positional tracking control methods */ + virtual bool IsHeadTrackingAllowed() const override; + + virtual bool IsInLowPersistenceMode() const override; + virtual void EnableLowPersistenceMode(bool Enable = true) override; + + /** Resets orientation by setting roll and pitch to 0, + assuming that current yaw is forward direction and assuming + current position as 0 point. */ + virtual void ResetOrientationAndPosition(float yaw = 0.f) override; + virtual void ResetOrientation(float Yaw = 0.f) override; + virtual void ResetPosition() override; + + virtual void DrawDistortionMesh_RenderThread(struct FRenderingCompositePassContext& Context, const FIntPoint& TextureSize) override; + virtual void UpdatePostProcessSettings(FPostProcessSettings*) override; + + virtual bool HandleInputKey(class UPlayerInput*, const FKey& Key, EInputEvent EventType, float AmountDepressed, bool bGamepad) override; + + virtual void OnBeginPlay() override; + virtual void OnEndPlay() override; + + virtual void DrawDebug(UCanvas* Canvas) override; + + /* Raw sensor data structure. */ + struct SensorData + { + FVector Accelerometer; // Acceleration reading in m/s^2. + FVector Gyro; // Rotation rate in rad/s. + FVector Magnetometer; // Magnetic field in Gauss. + float Temperature; // Temperature of the sensor in degrees Celsius. + float TimeInSeconds; // Time when the reported IMU reading took place, in seconds. + }; + + /** + * Reports raw sensor data. If HMD doesn't support any of the parameters then it should be set to zero. + * + * @param OutData (out) SensorData structure to be filled in. + */ + virtual void GetRawSensorData(SensorData& OutData); + + /** + * User profile structure. + */ + struct UserProfile + { + FString Name; + FString Gender; + float PlayerHeight; // Height of the player, in meters + float EyeHeight; // Height of the player's eyes, in meters + float IPD; // Interpupillary distance, in meters + FVector2D NeckToEyeDistance; // Neck-to-eye distance, X - horizontal, Y - vertical, in meters + TMap ExtraFields; // extra fields in name / value pairs. + }; + virtual bool GetUserProfile(UserProfile& OutProfile); + + virtual FString GetVersionString() const override; + + // An improved version of GetCurrentOrientationAndPostion, used from blueprints by OculusLibrary. + void GetCurrentHMDPose(FQuat& CurrentOrientation, FVector& CurrentPosition, + bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector& PositionScale); + +protected: + virtual TSharedPtr CreateNewGameFrame() const override; + virtual TSharedPtr CreateNewSettings() const override; + + virtual bool DoEnableStereo(bool bStereo, bool bApplyToHmd) override; + virtual void ResetStereoRenderingParams() override; + + virtual void GetCurrentPose(FQuat& CurrentHmdOrientation, FVector& CurrentHmdPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false) override; + +public: + +#ifdef OVR_SDK_RENDERING + +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + class D3D11Bridge : public FCustomPresent + { + public: + D3D11Bridge(); + + // Implementation of FRHICustomPresent + // Resets Viewport-specific pointers (BackBufferRT, SwapChain). + virtual void OnBackBufferResize() override; + // Returns true if Engine should perform its own Present. + virtual bool Present(int& SyncInterval) override; + + // Implementation of FCustomPresent, called by Plugin itself + virtual void BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) override; + void FinishRendering(); + virtual void Reset() override; + virtual void Shutdown() override; + + protected: + void Reset_RenderThread(); + protected: // data + ovrD3D11Config Cfg; + ovrD3D11Texture EyeTexture[2]; + }; + +#endif + +#ifdef OVR_GL + class OGLBridge : public FCustomPresent + { + public: + OGLBridge(); + + // Implementation of FRHICustomPresent + // Resets Viewport-specific resources. + virtual void OnBackBufferResize() override; + // Returns true if Engine should perform its own Present. + virtual bool Present(int& SyncInterval) override; + + // Implementation of FCustomPresent, called by Plugin itself + virtual void BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) override; + void FinishRendering(); + virtual void Reset() override; + virtual void Shutdown() override + { + Reset(); + } + + protected: // data + ovrGLConfig Cfg; + ovrGLTexture EyeTexture[2]; + }; +#endif // OVR_GL + FCustomPresent* GetActiveRHIBridgeImpl() const; + + void ShutdownRendering(); + +#else + + virtual void FinishRenderingFrame_RenderThread(FRHICommandListImmediate& RHICmdList) override; + +#endif // #ifdef OVR_SDK_RENDERING + + /** Constructor */ + FOculusRiftHMD(); + + /** Destructor */ + virtual ~FOculusRiftHMD(); + +private: + FOculusRiftHMD* getThis() { return this; } + + /** + * Starts up the Oculus Rift device + */ + void Startup(); + + /** + * Shuts down Oculus Rift + */ + void Shutdown(); + + bool InitDevice(); + + void ReleaseDevice(); + + /** + * Reads the device configuration, and sets up the stereoscopic rendering parameters + */ + virtual void UpdateStereoRenderingParams() override; + virtual void UpdateHmdRenderInfo() override; + virtual void UpdateDistortionCaps() override; + virtual void UpdateHmdCaps() override; + + virtual void ApplySystemOverridesOnStereo(bool force = false) override; + + /** + * Called when state changes from 'stereo' to 'non-stereo'. Suppose to distribute + * the event further to user's code (?). + * Returns true if state change confirmed, false otherwise. + */ + bool OnOculusStateChange(bool bIsEnabledNow); + + /** Load/save settings */ + void LoadFromIni(); + void SaveToIni(); + + /** Saves system values before applying overrides. */ + void SaveSystemValues(); + /** Restores system values after overrides applied. */ + void RestoreSystemValues(); + + void PrecalculateDistortionMesh(); + + class FSceneViewport* FindSceneViewport(); + + FGameFrame* GetFrame(); + const FGameFrame* GetFrame() const; + +private: // data + + ovrHmd Hmd; + + union + { + struct + { + bool64 _PlaceHolder_ : 1; + }; + uint64 Raw; + } OCFlags; + +#ifdef OVR_SDK_RENDERING + +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + TRefCountPtr pD3D11Bridge; +#endif +#if defined(OVR_GL) + TRefCountPtr pOGLBridge; +#endif +#else // !OVR_SDK_RENDERING + // this can be set and accessed only from renderthread! + TSharedPtr RenderContext; + + //TSharedPtr FindRenderContext(const FSceneViewFamily& ViewFamily) const; + FGameFrame* GetRenderFrameFromContext() const; +#endif // OVR_SDK_RENDERING + + void* OSWindowHandle; + + FGameFrame* GetGameFrame() + { + return (FGameFrame*)(Frame.Get()); + } + + const FGameFrame* GetGameFrame() const + { + return (const FGameFrame*)(Frame.Get()); + } + + FSettings* GetSettings() + { + return (FSettings*)(Settings.Get()); + } + + const FSettings* GetSettings() const + { + return (const FSettings*)(Settings.Get()); + } + +public: + static bool bDirectModeHack; // a hack to allow quickly check if Oculus is in Direct mode +}; + +#endif //OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //#if PLATFORM_MAC diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftRender_05.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftRender_05.cpp new file mode 100644 index 000000000000..71468dd85216 --- /dev/null +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/0.5/OculusRiftRender_05.cpp @@ -0,0 +1,1173 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +// +#include "HMDPrivatePCH.h" +#include "OculusRiftHMD.h" + +// Mac uses 0.5.0 SDK, while PC - 0.6.0. This version supports 0.5.0 (Mac/Linux). +#if PLATFORM_MAC +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + +#include "RendererPrivate.h" +#include "ScenePrivate.h" +#include "PostProcess/PostProcessHMD.h" + +#include "SlateBasics.h" + +#ifndef OVR_SDK_RENDERING +void FSettings::FDistortionMesh::Reset() +{ + delete[] pVertices; + delete[] pIndices; + pVertices = NULL; + pIndices = NULL; + NumVertices = NumIndices = NumTriangles = 0; +} +#endif + +void FOculusRiftHMD::PrecalculateDistortionMesh() +{ +#ifndef OVR_SDK_RENDERING + check(IsInGameThread()); + + FSettings* CurrentSettings = GetSettings(); // we need global settings here rather than frame settings + check(CurrentSettings); + + for (unsigned eyeNum = 0; eyeNum < 2; ++eyeNum) + { + // Allocate & generate distortion mesh vertices. + ovrDistortionMesh meshData; + + if (!ovrHmd_CreateDistortionMesh(Hmd, CurrentSettings->EyeRenderDesc[eyeNum].Eye, CurrentSettings->EyeRenderDesc[eyeNum].Fov, CurrentSettings->DistortionCaps, &meshData)) + { + check(false); + continue; + } + + // alloc the data + ovrDistortionVertex* ovrMeshVertData = new ovrDistortionVertex[meshData.VertexCount]; + + // Convert to final vertex data. + FDistortionVertex *pVerts = new FDistortionVertex[meshData.VertexCount]; + FDistortionVertex *pCurVert = pVerts; + ovrDistortionVertex* pCurOvrVert = meshData.pVertexData; + + for (unsigned vertNum = 0; vertNum < meshData.VertexCount; ++vertNum) + { + pCurVert->Position.X = pCurOvrVert->ScreenPosNDC.x; + pCurVert->Position.Y = pCurOvrVert->ScreenPosNDC.y; + pCurVert->TexR = FVector2D(pCurOvrVert->TanEyeAnglesR.x, pCurOvrVert->TanEyeAnglesR.y); + pCurVert->TexG = FVector2D(pCurOvrVert->TanEyeAnglesG.x, pCurOvrVert->TanEyeAnglesG.y); + pCurVert->TexB = FVector2D(pCurOvrVert->TanEyeAnglesB.x, pCurOvrVert->TanEyeAnglesB.y); + pCurVert->VignetteFactor = pCurOvrVert->VignetteFactor; + pCurVert->TimewarpFactor = pCurOvrVert->TimeWarpFactor; + pCurOvrVert++; + pCurVert++; + } + + CurrentSettings->pDistortionMesh[eyeNum] = MakeShareable(new FSettings::FDistortionMesh()); + CurrentSettings->pDistortionMesh[eyeNum]->NumTriangles = meshData.IndexCount / 3; + CurrentSettings->pDistortionMesh[eyeNum]->NumIndices = meshData.IndexCount; + CurrentSettings->pDistortionMesh[eyeNum]->NumVertices = meshData.VertexCount; + CurrentSettings->pDistortionMesh[eyeNum]->pVertices = pVerts; + + check(sizeof(*meshData.pIndexData) == sizeof(uint16)); + CurrentSettings->pDistortionMesh[eyeNum]->pIndices = new uint16[meshData.IndexCount]; + FMemory::Memcpy(CurrentSettings->pDistortionMesh[eyeNum]->pIndices, meshData.pIndexData, sizeof(uint16)*meshData.IndexCount); + + ovrHmd_DestroyDistortionMesh(&meshData); + } +#endif +} + +void FOculusRiftHMD::DrawDistortionMesh_RenderThread(FRenderingCompositePassContext& Context, const FIntPoint& InTextureSize) +{ + check(IsInRenderingThread()); + + const FSceneView& View = Context.View; + if (View.StereoPass == eSSP_FULL) + { + return; + } + +#ifndef OVR_SDK_RENDERING + float ClipSpaceQuadZ = 0.0f; + FMatrix QuadTexTransform = FMatrix::Identity; + FMatrix QuadPosTransform = FMatrix::Identity; + const FIntRect SrcRect = View.ViewRect; + + auto RenderFrame = GetRenderFrameFromContext(); + check(RenderFrame); + auto RenderFrameSettings = RenderFrame->GetSettings(); + check(RenderFrameSettings); + + FRHICommandListImmediate& RHICmdList = Context.RHICmdList; + const FSceneViewFamily& ViewFamily = *(View.Family); + int ViewportSizeX = ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeX(); + int ViewportSizeY = ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeY(); + RHICmdList.SetViewport(0, 0, 0.0f, ViewportSizeX, ViewportSizeY, 1.0f); + + TSharedPtr mesh = RenderFrameSettings->pDistortionMesh[(View.StereoPass == eSSP_LEFT_EYE) ? 0 : 1]; + + DrawIndexedPrimitiveUP(Context.RHICmdList, PT_TriangleList, 0, mesh->NumVertices, mesh->NumTriangles, mesh->pIndices, + sizeof(mesh->pIndices[0]), mesh->pVertices, sizeof(mesh->pVertices[0])); +#else + check(0); +#endif +} + +void FOculusRiftHMD::GetEyeRenderParams_RenderThread(const FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const +{ + check(IsInRenderingThread()); +#ifndef OVR_SDK_RENDERING + auto RenderFrame = GetRenderFrameFromContext(); + check(RenderFrame); + auto RenderFrameSettings = RenderFrame->GetSettings(); + check(RenderFrameSettings); + + const unsigned eyeIdx = (Context.View.StereoPass == eSSP_LEFT_EYE) ? 0 : 1; + EyeToSrcUVOffsetValue.X = RenderFrameSettings->UVScaleOffset[eyeIdx][1].x; + EyeToSrcUVOffsetValue.Y = RenderFrameSettings->UVScaleOffset[eyeIdx][1].y; + + EyeToSrcUVScaleValue.X = RenderFrameSettings->UVScaleOffset[eyeIdx][0].x; + EyeToSrcUVScaleValue.Y = RenderFrameSettings->UVScaleOffset[eyeIdx][0].y; +#else + check(0); +#endif +} + +void FOculusRiftHMD::GetTimewarpMatrices_RenderThread(const FRenderingCompositePassContext& Context, FMatrix& EyeRotationStart, FMatrix& EyeRotationEnd) const +{ + check(IsInRenderingThread()); +#ifndef OVR_SDK_RENDERING + check(RenderContext.IsValid()); + + const ovrEyeType eye = (Context.View.StereoPass == eSSP_LEFT_EYE) ? ovrEye_Left : ovrEye_Right; + ovrMatrix4f timeWarpMatrices[2]; + if (RenderContext->bFrameBegun) + { + ovrHmd_GetEyeTimewarpMatrices(Hmd, eye, RenderContext->CurEyeRenderPose[eye], timeWarpMatrices); + } + EyeRotationStart = ToFMatrix(timeWarpMatrices[0]); + EyeRotationEnd = ToFMatrix(timeWarpMatrices[1]); +#else + check(0); +#endif +} + +void FViewExtension::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) +{ + check(IsInRenderingThread()); + FViewExtension& RenderContext = *this; + FGameFrame* CurrentFrame = static_cast(RenderContext.RenderFrame.Get()); + + if (bFrameBegun || !CurrentFrame || !CurrentFrame->Settings->IsStereoEnabled()) + { + return; + } + FSettings* FrameSettings = CurrentFrame->GetSettings(); + RenderContext.ShowFlags = ViewFamily.EngineShowFlags; + + RenderContext.CurHeadPose = CurrentFrame->HeadPose; + + if (FrameSettings->TexturePaddingPerEye != 0) + { + // clear the padding between two eyes + const int32 GapMinX = ViewFamily.Views[0]->ViewRect.Max.X; + const int32 GapMaxX = ViewFamily.Views[1]->ViewRect.Min.X; + + const int ViewportSizeY = (ViewFamily.RenderTarget->GetRenderTargetTexture()) ? + ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeY() : ViewFamily.RenderTarget->GetSizeXY().Y; + RHICmdList.SetViewport(GapMinX, 0, 0, GapMaxX, ViewportSizeY, 1.0f); + RHICmdList.Clear(true, FLinearColor::Black, false, 0, false, 0, FIntRect()); + } + +#ifdef OVR_SDK_RENDERING + check(ViewFamily.RenderTarget->GetRenderTargetTexture()); + CurrentFrame->GetSettings()->SetEyeRenderViewport(ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeX()/2, + ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeY()); + pPresentBridge->BeginRendering(RenderContext, ViewFamily.RenderTarget->GetRenderTargetTexture()); + + ovrHmd_BeginFrame(Hmd, RenderContext.RenderFrame->FrameNumber); +#else + FOculusRiftHMD* OculusPlugin = static_cast(Delegate); + TSharedPtr SharedPtr(AsShared()); + OculusPlugin->RenderContext = StaticCastSharedPtr(SharedPtr); + + const FIntPoint TextureSize = FrameSettings->GetTextureSize(); + for (size_t eyeNum = 0; eyeNum < 2; ++eyeNum) + { + ovrHmd_GetRenderScaleAndOffset(FrameSettings->EyeRenderDesc[eyeNum].Fov, + OVR::Sizei(TextureSize.X, TextureSize.Y), + ToOVRRecti(FrameSettings->EyeRenderViewport[eyeNum]), + FrameSettings->UVScaleOffset[eyeNum]); + } + + ovrHmd_BeginFrameTiming(Hmd, CurrentFrame->FrameNumber); +#endif + RenderContext.bFrameBegun = true; + + if (RenderContext.ShowFlags.Rendering) + { + // get latest orientation/position and cache it + ovrTrackingState ts; + const ovrVector3f hmdToEyeViewOffset[2] = + { + FrameSettings->EyeRenderDesc[0].HmdToEyeViewOffset, + FrameSettings->EyeRenderDesc[1].HmdToEyeViewOffset + }; + ovrPosef EyeRenderPose[2]; + ovrHmd_GetEyePoses(Hmd, CurrentFrame->FrameNumber, hmdToEyeViewOffset, EyeRenderPose, &ts); + + // Take new EyeRenderPose is bUpdateOnRT. + // if !bOrientationChanged && !bPositionChanged then we still need to use new eye pose (for timewarp) + if (FrameSettings->Flags.bUpdateOnRT || + (!CurrentFrame->Flags.bOrientationChanged && !CurrentFrame->Flags.bPositionChanged)) + { + RenderContext.CurHeadPose = ts.HeadPose.ThePose; + FMemory::Memcpy(RenderContext.CurEyeRenderPose, EyeRenderPose); + } + else + { + FMemory::Memcpy(RenderContext.CurEyeRenderPose, CurrentFrame->EyeRenderPose); + } + } +} + +void FViewExtension::PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& View) +{ + check(IsInRenderingThread()); + FViewExtension& RenderContext = *this; //static_cast(InRenderContext); + const FGameFrame* CurrentFrame = static_cast(RenderContext.RenderFrame.Get()); + + if (!RenderContext.ShowFlags.Rendering || !CurrentFrame || !CurrentFrame->Settings->IsStereoEnabled()) + { + return; + } + + if (RenderContext.ShowFlags.Rendering && CurrentFrame->Settings->Flags.bUpdateOnRT) + { + const ovrEyeType eyeIdx = (View.StereoPass == eSSP_LEFT_EYE) ? ovrEye_Left : ovrEye_Right; + FQuat CurrentEyeOrientation; + FVector CurrentEyePosition; + CurrentFrame->PoseToOrientationAndPosition(RenderContext.CurEyeRenderPose[eyeIdx], CurrentEyeOrientation, CurrentEyePosition); + + FQuat ViewOrientation = View.ViewRotation.Quaternion(); + + // recalculate delta control orientation; it should match the one we used in CalculateStereoViewOffset on a game thread. + FVector GameEyePosition; + FQuat GameEyeOrient; + + CurrentFrame->PoseToOrientationAndPosition(CurrentFrame->EyeRenderPose[eyeIdx], GameEyeOrient, GameEyePosition); + const FQuat DeltaControlOrientation = ViewOrientation * GameEyeOrient.Inverse(); + // make sure we use the same viewrotation as we had on a game thread + check(View.ViewRotation == CurrentFrame->CachedViewRotation[eyeIdx]); + + if (CurrentFrame->Flags.bOrientationChanged) + { + // Apply updated orientation to corresponding View at recalc matrices. + // The updated position will be applied from inside of the UpdateViewMatrix() call. + const FQuat DeltaOrient = View.BaseHmdOrientation.Inverse() * CurrentEyeOrientation; + View.ViewRotation = FRotator(ViewOrientation * DeltaOrient); + + //UE_LOG(LogHMD, Log, TEXT("VIEWDLT: Yaw %.3f Pitch %.3f Roll %.3f"), DeltaOrient.Rotator().Yaw, DeltaOrient.Rotator().Pitch, DeltaOrient.Rotator().Roll); + } + + if (!CurrentFrame->Flags.bPositionChanged) + { + // if no positional change applied then we still need to calculate proper stereo disparity. + // use the current head pose for this calculation instead of the one that was saved on a game thread. + FQuat HeadOrientation; + CurrentFrame->PoseToOrientationAndPosition(RenderContext.CurHeadPose, HeadOrientation, View.BaseHmdLocation); + } + + // The HMDPosition already has HMD orientation applied. + // Apply rotational difference between HMD orientation and ViewRotation + // to HMDPosition vector. + // PositionOffset should be already applied to View.ViewLocation on GT in PlayerCameraUpdate. + const FVector DeltaPosition = CurrentEyePosition - View.BaseHmdLocation; + const FVector vEyePosition = DeltaControlOrientation.RotateVector(DeltaPosition); + View.ViewLocation += vEyePosition; + + //UE_LOG(LogHMD, Log, TEXT("VDLTPOS: %.3f %.3f %.3f"), vEyePosition.X, vEyePosition.Y, vEyePosition.Z); + + if (CurrentFrame->Flags.bOrientationChanged || CurrentFrame->Flags.bPositionChanged) + { + View.UpdateViewMatrix(); + } + } +} + +#ifdef OVR_SDK_RENDERING +FCustomPresent* FOculusRiftHMD::GetActiveRHIBridgeImpl() const +{ +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + if (pD3D11Bridge) + { + return pD3D11Bridge; + } +#endif +#if defined(OVR_GL) + if (pOGLBridge) + { + return pOGLBridge; + } +#endif + return nullptr; +} + +void FOculusRiftHMD::CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) +{ + check(IsInGameThread()); + + if (!Settings->IsStereoEnabled()) + { + return; + } + + if (Viewport.GetClient()->GetEngineShowFlags()->ScreenPercentage) + { + float value = GetActualScreenPercentage(); + if (value > 0.0f) + { + InOutSizeX = FMath::CeilToInt(InOutSizeX * value / 100.f) + GetSettings()->GetTexturePaddingPerEye()*2; + InOutSizeY = FMath::CeilToInt(InOutSizeY * value / 100.f); + } + } +} + +bool FOculusRiftHMD::NeedReAllocateViewportRenderTarget(const FViewport& Viewport) +{ + check(IsInGameThread()); + if (IsStereoEnabled()) + { + const uint32 InSizeX = Viewport.GetSizeXY().X; + const uint32 InSizeY = Viewport.GetSizeXY().Y; + const FIntPoint RenderTargetSize = Viewport.GetRenderTargetTextureSizeXY(); + + uint32 NewSizeX = InSizeX, NewSizeY = InSizeY; + CalculateRenderTargetSize(Viewport, NewSizeX, NewSizeY); + if (NewSizeX != RenderTargetSize.X || NewSizeY != RenderTargetSize.Y) + { + return true; + } + } + return false; +} + +#else // no direct rendering + +FGameFrame* FOculusRiftHMD::GetRenderFrameFromContext() const +{ + check(IsInRenderingThread()); + + check(RenderContext.IsValid()); + return static_cast(RenderContext->RenderFrame.Get()); +} + +void FOculusRiftHMD::FinishRenderingFrame_RenderThread(FRHICommandListImmediate& RHICmdList) +{ + check(IsInRenderingThread()); + + check(RenderContext.IsValid()); + auto RenderFrame = GetRenderFrameFromContext(); + check(RenderFrame); + auto RenderFrameSettings = RenderFrame->GetSettings(); + check(RenderFrameSettings); + if (RenderContext->bFrameBegun) + { + if (RenderFrameSettings->Flags.bTimeWarp) + { + RHICmdList.BlockUntilGPUIdle(); + } + ovrHmd_EndFrameTiming(Hmd); + RenderContext->bFrameBegun = false; + RenderContext.Reset(); + } +} + +#endif // #ifdef OVR_SDK_RENDERING + +static const char* FormatLatencyReading(char* buff, size_t size, float val) +{ + if (val < 0.000001f) + { + FCStringAnsi::Strcpy(buff, size, "N/A "); + } + else + { + FCStringAnsi::Snprintf(buff, size, "%4.2fms", val * 1000.0f); + } + return buff; +} + +#if !UE_BUILD_SHIPPING +static void RenderLines(FCanvas* Canvas, int numLines, const FColor& c, float* x, float* y) +{ + for (int i = 0; i < numLines; ++i) + { + FCanvasLineItem line(FVector2D(x[i*2], y[i*2]), FVector2D(x[i*2+1], y[i*2+1])); + line.SetColor(FLinearColor(c)); + Canvas->DrawItem(line); + } +} +#endif // #if !UE_BUILD_SHIPPING + +void FOculusRiftHMD::DrawDebug(UCanvas* Canvas) +{ +#if !UE_BUILD_SHIPPING + check(IsInGameThread()); + const auto frame = GetFrame(); + if (frame) + { + FSettings* FrameSettings = frame->GetSettings(); + + if (FrameSettings->Flags.bDrawGrid) + { + bool bStereo = Canvas->Canvas->IsStereoRendering(); + Canvas->Canvas->SetStereoRendering(false); + bool bPopTransform = false; + if (FrameSettings->EyeRenderDesc[0].DistortedViewport.Size.w != FMath::CeilToInt(Canvas->ClipX) || + FrameSettings->EyeRenderDesc[0].DistortedViewport.Size.h != Canvas->ClipY) + { + // scale if resolution of the Canvas does not match the viewport + bPopTransform = true; + Canvas->Canvas->PushAbsoluteTransform(FScaleMatrix( + FVector((Canvas->ClipX) / float(FrameSettings->EyeRenderDesc[0].DistortedViewport.Size.w), + Canvas->ClipY / float(FrameSettings->EyeRenderDesc[0].DistortedViewport.Size.h), + 1.0f))); + } + + const FColor cNormal(255, 0, 0); + const FColor cSpacer(255, 255, 0); + const FColor cMid(0, 128, 255); + for (int eye = 0; eye < 2; ++eye) + { + int lineStep = 1; + int midX = 0; + int midY = 0; + int limitX = 0; + int limitY = 0; + + int renderViewportX = FrameSettings->EyeRenderDesc[eye].DistortedViewport.Pos.x; + int renderViewportY = FrameSettings->EyeRenderDesc[eye].DistortedViewport.Pos.y; + int renderViewportW = FrameSettings->EyeRenderDesc[eye].DistortedViewport.Size.w; + int renderViewportH = FrameSettings->EyeRenderDesc[eye].DistortedViewport.Size.h; + + lineStep = 48; + OVR::Vector2f rendertargetNDC = OVR::FovPort(FrameSettings->EyeRenderDesc[eye].Fov).TanAngleToRendertargetNDC(OVR::Vector2f(0.0f)); + midX = (int)((rendertargetNDC.x * 0.5f + 0.5f) * (float)renderViewportW + 0.5f); + midY = (int)((rendertargetNDC.y * 0.5f + 0.5f) * (float)renderViewportH + 0.5f); + limitX = FMath::Max(renderViewportW - midX, midX); + limitY = FMath::Max(renderViewportH - midY, midY); + + int spacerMask = (lineStep << 1) - 1; + + for (int xp = 0; xp < limitX; xp += lineStep) + { + float x[4]; + float y[4]; + x[0] = (float)(midX + xp) + renderViewportX; + y[0] = (float)0 + renderViewportY; + x[1] = (float)(midX + xp) + renderViewportX; + y[1] = (float)renderViewportH + renderViewportY; + x[2] = (float)(midX - xp) + renderViewportX; + y[2] = (float)0 + renderViewportY; + x[3] = (float)(midX - xp) + renderViewportX; + y[3] = (float)renderViewportH + renderViewportY; + if (xp == 0) + { + RenderLines(Canvas->Canvas, 1, cMid, x, y); + } + else if ((xp & spacerMask) == 0) + { + RenderLines(Canvas->Canvas, 2, cSpacer, x, y); + } + else + { + RenderLines(Canvas->Canvas, 2, cNormal, x, y); + } + } + for (int yp = 0; yp < limitY; yp += lineStep) + { + float x[4]; + float y[4]; + x[0] = (float)0 + renderViewportX; + y[0] = (float)(midY + yp) + renderViewportY; + x[1] = (float)renderViewportW + renderViewportX; + y[1] = (float)(midY + yp) + renderViewportY; + x[2] = (float)0 + renderViewportX; + y[2] = (float)(midY - yp) + renderViewportY; + x[3] = (float)renderViewportW + renderViewportX; + y[3] = (float)(midY - yp) + renderViewportY; + if (yp == 0) + { + RenderLines(Canvas->Canvas, 1, cMid, x, y); + } + else if ((yp & spacerMask) == 0) + { + RenderLines(Canvas->Canvas, 2, cSpacer, x, y); + } + else + { + RenderLines(Canvas->Canvas, 2, cNormal, x, y); + } + } + } + if (bPopTransform) + { + Canvas->Canvas->PopTransform(); // optional scaling + } + Canvas->Canvas->SetStereoRendering(bStereo); + } + if (IsStereoEnabled() && FrameSettings->Flags.bShowStats) + { + static const FColor TextColor(0,255,0); + // Pick a larger font on console. + UFont* const Font = FPlatformProperties::SupportsWindowedMode() ? GEngine->GetSmallFont() : GEngine->GetMediumFont(); + const int32 RowHeight = FMath::TruncToInt(Font->GetMaxCharHeight() * 1.1f); + + float ClipX = Canvas->ClipX; + float ClipY = Canvas->ClipY; + float LeftPos = 0; + + ClipX -= 100; + //ClipY = ClipY * 0.60; + LeftPos = ClipX * 0.3f; + float TopPos = ClipY * 0.4f; + + int32 X = (int32)LeftPos; + int32 Y = (int32)TopPos; + + FString Str, StatusStr; + // First row + Str = FString::Printf(TEXT("TimeWarp: %s"), (FrameSettings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + Y += RowHeight; + Str = FString::Printf(TEXT("VSync: %s"), (FrameSettings->Flags.bVSync) ? TEXT("ON") : TEXT("OFF")); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + Y += RowHeight; + Str = FString::Printf(TEXT("Upd on GT/RT: %s / %s"), (!FrameSettings->Flags.bDoNotUpdateOnGT) ? TEXT("ON") : TEXT("OFF"), + (FrameSettings->Flags.bUpdateOnRT) ? TEXT("ON") : TEXT("OFF")); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + Y += RowHeight; + static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); + int finFr = CFinishFrameVar->GetInt(); + Str = FString::Printf(TEXT("FinFr: %s"), (finFr || FrameSettings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + Y += RowHeight; + int32 sp = (int32)GetActualScreenPercentage(); + Str = FString::Printf(TEXT("SP: %d"), sp); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + Y += RowHeight; + Str = FString::Printf(TEXT("FOV V/H: %.2f / %.2f deg"), + FMath::RadiansToDegrees(FrameSettings->VFOVInRadians), FMath::RadiansToDegrees(FrameSettings->HFOVInRadians)); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + Y += RowHeight; + Str = FString::Printf(TEXT("W-to-m scale: %.2f uu/m"), frame->WorldToMetersScale); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + + if ((FrameSettings->SupportedHmdCaps & ovrHmdCap_DynamicPrediction) != 0) + { + float latencies[5] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + const int numOfEntries = sizeof(latencies) / sizeof(latencies[0]); + if (ovrHmd_GetFloatArray(Hmd, "DK2Latency", latencies, numOfEntries) == numOfEntries) + { + Y += RowHeight; + + char buf[numOfEntries][20]; + char destStr[100]; + + FCStringAnsi::Snprintf(destStr, sizeof(destStr), "Latency, ren: %s tw: %s pp: %s err: %s %s", + FormatLatencyReading(buf[0], sizeof(buf[0]), latencies[0]), + FormatLatencyReading(buf[1], sizeof(buf[1]), latencies[1]), + FormatLatencyReading(buf[2], sizeof(buf[2]), latencies[2]), + FormatLatencyReading(buf[3], sizeof(buf[3]), latencies[3]), + FormatLatencyReading(buf[4], sizeof(buf[4]), latencies[4])); + + Str = ANSI_TO_TCHAR(destStr); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + } + } + + // Second row + X = (int32)LeftPos + 200; + Y = (int32)TopPos; + + StatusStr = ((FrameSettings->SupportedTrackingCaps & ovrTrackingCap_Position) != 0) ? + ((FrameSettings->Flags.bHmdPosTracking) ? TEXT("ON") : TEXT("OFF")) : TEXT("UNSUP"); + Str = FString::Printf(TEXT("PosTr: %s"), *StatusStr); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + Y += RowHeight; + + Str = FString::Printf(TEXT("Vision: %s"), (frame->Flags.bHaveVisionTracking) ? TEXT("ACQ") : TEXT("LOST")); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + Y += RowHeight; + + Str = FString::Printf(TEXT("IPD: %.2f mm"), FrameSettings->InterpupillaryDistance*1000.f); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + Y += RowHeight; + + StatusStr = ((FrameSettings->SupportedHmdCaps & ovrHmdCap_LowPersistence) != 0) ? + ((FrameSettings->Flags.bLowPersistenceMode) ? TEXT("ON") : TEXT("OFF")) : TEXT("UNSUP"); + Str = FString::Printf(TEXT("LowPers: %s"), *StatusStr); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + Y += RowHeight; + + StatusStr = ((FrameSettings->SupportedDistortionCaps & ovrDistortionCap_Overdrive) != 0) ? + ((true) ? TEXT("ON") : TEXT("OFF")) : TEXT("UNSUP"); + Str = FString::Printf(TEXT("Overdrive: %s"), *StatusStr); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + Y += RowHeight; + } + + //TODO: Where can I get context!? + UWorld* MyWorld = GWorld; + if (FrameSettings->Flags.bDrawTrackingCameraFrustum) + { + DrawDebugTrackingCameraFrustum(MyWorld, Canvas->SceneView->ViewRotation, Canvas->SceneView->ViewLocation); + } + + if (Canvas && Canvas->SceneView) + { + DrawSeaOfCubes(MyWorld, Canvas->SceneView->ViewLocation); + } + } + +#endif // #if !UE_BUILD_SHIPPING +} + +void FOculusRiftHMD::UpdateViewport(bool bUseSeparateRenderTarget, const FViewport& InViewport, SViewport* ViewportWidget) +{ + check(IsInGameThread()); + + if (GIsEditor && ViewportWidget) + { + // In editor we are going to check if the viewport widget supports stereo rendering or not. + if (!ViewportWidget->IsStereoRenderingAllowed()) + { + return; + } + } + + FRHIViewport* const ViewportRHI = InViewport.GetViewportRHI().GetReference(); + + if (!IsStereoEnabled()) + { + if ((!bUseSeparateRenderTarget || GIsEditor) && ViewportRHI) + { + ViewportRHI->SetCustomPresent(nullptr); + } +#if PLATFORM_WINDOWS + if (OSWindowHandle) + { + ovrHmd_AttachToWindow(Hmd, NULL, NULL, NULL); + OSWindowHandle = nullptr; + + // Restore AutoResizeViewport mode for the window + if (ViewportWidget && !IsFullscreenAllowed() && Settings->MirrorWindowSize.X != 0 && Settings->MirrorWindowSize.Y != 0) + { + FWidgetPath WidgetPath; + TSharedRef Widget = ViewportWidget->AsShared(); + TSharedPtr Window = FSlateApplication::Get().FindWidgetWindow(Widget, WidgetPath); + if (Window.IsValid()) + { + Window->SetViewportSizeDrivenByWindow(true); + } + } + } +#endif + return; + } + +#if PLATFORM_WINDOWS + if (ViewportRHI) + { + void *wnd = ViewportRHI->GetNativeWindow(); + if (wnd && wnd != OSWindowHandle) + { + OSWindowHandle = wnd; + if (!ovrHmd_AttachToWindow(Hmd, OSWindowHandle, NULL, NULL)) + { + UE_LOG(LogHMD, Error, TEXT("ovrHmd_AttachToWindow failed.")); + } + } + } +#endif + + if (!bUseSeparateRenderTarget) + return; + + FGameFrame* CurrentFrame = GetFrame(); + check(CurrentFrame); + + CurrentFrame->ViewportSize = InViewport.GetSizeXY(); + //CurrentFrame->ViewportRHI = ViewportRHI; + +#ifdef OVR_SDK_RENDERING + + check(GetActiveRHIBridgeImpl()); + + GetActiveRHIBridgeImpl()->UpdateViewport(InViewport, ViewportRHI, CurrentFrame); +#endif // #ifdef OVR_SDK_RENDERING +} + +#ifdef OVR_SDK_RENDERING +void FOculusRiftHMD::ShutdownRendering() +{ + check(IsInRenderingThread()); +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + if (pD3D11Bridge) + { + pD3D11Bridge->Shutdown(); + pD3D11Bridge = NULL; + } +#endif +#if defined(OVR_GL) + if (pOGLBridge) + { + pOGLBridge->Shutdown(); + pOGLBridge = NULL; + } +#endif +} + + +void FCustomPresent::SetRenderContext(FHMDViewExtension* InRenderContext) +{ + if (InRenderContext) + { + RenderContext = StaticCastSharedRef(InRenderContext->AsShared()); + } + else + { + RenderContext.Reset(); + } +} + +void FCustomPresent::UpdateViewport(const FViewport& Viewport, FRHIViewport* ViewportRHI, FGameFrame* InRenderFrame) +{ + check(IsInGameThread()); + check(ViewportRHI); + + this->ViewportRHI = ViewportRHI; + ViewportRHI->SetCustomPresent(this); +} + +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) +// FIXME: in Direct mode we must hold DXGIAdapter to avoid a crash at the exit because of shimming. +// It does cause D3D resources to leak. But it is better than a crash, right? +// Should be already fixed in the next Oculus SDK version. +static void AddRefAdapter() +{ + if (FOculusRiftHMD::bDirectModeHack) + { + ID3D11Device* D3DDevice = (ID3D11Device*)RHIGetNativeDevice(); + if (D3DDevice) + { + D3DDevice->AddRef(); + IDXGIDevice * pDXGIDevice; + HRESULT hr = D3DDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice); + if (hr == S_OK) + { + IDXGIAdapter* pAdapter; + pDXGIDevice->GetAdapter(&pAdapter); + if (pAdapter) + { + UE_LOG(LogHMD, Log, TEXT("++ Addrefing Adapter %p!"), pAdapter); + pAdapter->AddRef(); + } + } + } + } +} + +FOculusRiftHMD::D3D11Bridge::D3D11Bridge() + : FCustomPresent() +{ + FMemory::Memset(Cfg, 0); + FMemory::Memset(EyeTexture, 0); + + if (FOculusRiftHMD::bDirectModeHack) + { + // FIXME: in Direct mode we must hold DXGIAdapter to avoid a crash at the exit because of shimming. + // It does cause D3D resources to leak. But it is better than a crash, right? + // Should be already fixed in the next Oculus SDK version. + ENQUEUE_UNIQUE_RENDER_COMMAND(AdapterAddRef, + { + AddRefAdapter(); + }); + } +} + +void FOculusRiftHMD::D3D11Bridge::Shutdown() +{ + check(IsInRenderingThread()); + if (FOculusRiftHMD::bDirectModeHack) + { + AddRefAdapter(); + } + Reset(); +} + +void FOculusRiftHMD::D3D11Bridge::BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) +{ + check(IsInRenderingThread()); + + SetRenderContext(&InRenderContext); + + ID3D11Device* D3DDevice = (ID3D11Device*)RHIGetNativeDevice(); + ID3D11DeviceContext* D3DDeviceContext = nullptr; + if (D3DDevice) + { + D3DDevice->GetImmediateContext(&D3DDeviceContext); + } + if (!bInitialized || D3DDevice != Cfg.D3D11.pDevice || D3DDeviceContext != Cfg.D3D11.pDeviceContext) + { + if (D3DDevice && D3DDevice != Cfg.D3D11.pDevice) + { + if (FOculusRiftHMD::bDirectModeHack) + { + // FIXME: in Direct mode we must hold D3DDevice to avoid a crash at the exit because of shimming. + // It does cause D3D resources to leak. But it is better than a crash, right? + // Should be already fixed in the next Oculus SDK version. + D3DDevice->AddRef(); + } + } + + Cfg.D3D11.Header.API = ovrRenderAPI_D3D11; + Cfg.D3D11.Header.Multisample = 0; + // Note, neither Device nor Context are AddRef-ed here. Not sure, if we need to. + Cfg.D3D11.pDevice = D3DDevice; + Cfg.D3D11.pDeviceContext = D3DDeviceContext; + bNeedReinitRendererAPI = true; + bInitialized = true; + } + + if (bInitialized) + { + ID3D11RenderTargetView* const pD3DBBRT = (ID3D11RenderTargetView*)ViewportRHI->GetNativeBackBufferRT(); + IDXGISwapChain* const pD3DSC = (IDXGISwapChain*)ViewportRHI->GetNativeSwapChain(); + check(IsValidRef(RT)); + ID3D11Texture2D* const pD3DRT = (ID3D11Texture2D*)RT->GetNativeResource(); + ID3D11ShaderResourceView* const pD3DSRV = (ID3D11ShaderResourceView*)RT->GetNativeShaderResourceView(); + const uint32 RTSizeX = RT->GetSizeX(); + const uint32 RTSizeY = RT->GetSizeY(); + + FGameFrame* CurrentFrame = GetRenderFrame(); + check(CurrentFrame); + FSettings* FrameSettings = CurrentFrame->GetSettings(); + check(FrameSettings); + + if (Cfg.D3D11.pBackBufferRT != pD3DBBRT || + Cfg.D3D11.pSwapChain != pD3DSC || + Cfg.D3D11.Header.BackBufferSize.w != CurrentFrame->ViewportSize.X || + Cfg.D3D11.Header.BackBufferSize.h != CurrentFrame->ViewportSize.Y) + { + // Note, neither BackBufferRT nor SwapChain are AddRef-ed here. Not sure, if we need to. + // If yes, then them should be released in ReleaseBackBuffer(). + Cfg.D3D11.pBackBufferRT = pD3DBBRT; + Cfg.D3D11.pSwapChain = pD3DSC; + Cfg.D3D11.Header.BackBufferSize.w = CurrentFrame->ViewportSize.X; + Cfg.D3D11.Header.BackBufferSize.h = CurrentFrame->ViewportSize.Y; + bNeedReinitRendererAPI = true; + } + + if (EyeTexture[0].D3D11.pTexture != pD3DRT || EyeTexture[0].D3D11.pSRView != pD3DSRV || + EyeTexture[0].D3D11.Header.TextureSize.w != RTSizeX || EyeTexture[0].D3D11.Header.TextureSize.h != RTSizeY || + ((OVR::Recti)EyeTexture[0].D3D11.Header.RenderViewport) != ToOVRRecti(FrameSettings->EyeRenderViewport[0])) + { + for (int eye = 0; eye < 2; ++eye) + { + ovrD3D11TextureData oldEye = EyeTexture[eye].D3D11; + EyeTexture[eye].D3D11.Header.API = ovrRenderAPI_D3D11; + EyeTexture[eye].D3D11.Header.TextureSize = OVR::Sizei(RTSizeX, RTSizeY); + EyeTexture[eye].D3D11.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[eye]); + EyeTexture[eye].D3D11.pTexture = pD3DRT; + EyeTexture[eye].D3D11.pSRView = pD3DSRV; + if (EyeTexture[eye].D3D11.pTexture) + { + EyeTexture[eye].D3D11.pTexture->AddRef(); + } + if (EyeTexture[eye].D3D11.pSRView) + { + EyeTexture[eye].D3D11.pSRView->AddRef(); + } + + if (oldEye.pTexture) + { + oldEye.pTexture->Release(); + } + if (oldEye.pSRView) + { + oldEye.pSRView->Release(); + } + } + } + + if (bNeedReinitRendererAPI) + { + check(Cfg.D3D11.pSwapChain); // make sure Config is initialized + + if (!ovrHmd_ConfigureRendering(RenderContext->Hmd, &Cfg.Config, FrameSettings->DistortionCaps, + FrameSettings->EyeFov, FrameSettings->EyeRenderDesc)) + { + UE_LOG(LogHMD, Warning, TEXT("D3D11 ovrHmd_ConfigureRenderAPI failed.")); + return; + } + bNeedReinitRendererAPI = false; + } + } +} + +void FOculusRiftHMD::D3D11Bridge::FinishRendering() +{ + check(IsInRenderingThread()); + + check(RenderContext.IsValid()); + + if (RenderContext->bFrameBegun) + { + // Finish the frame and let OVR do buffer swap (Present) and flush/sync. + const ovrTexture eyeTextures[2] = { EyeTexture[0].Texture, EyeTexture[1].Texture }; + ovrHmd_EndFrame(RenderContext->Hmd, RenderContext->CurEyeRenderPose, eyeTextures); // This function will present + } + else + { + UE_LOG(LogHMD, Warning, TEXT("Skipping frame: FinishRendering called with no corresponding BeginRendering (was BackBuffer re-allocated?)")); + } + RenderContext->bFrameBegun = false; + SetRenderContext(nullptr); +} + +void FOculusRiftHMD::D3D11Bridge::Reset_RenderThread() +{ + Cfg.D3D11.pDevice = nullptr; + Cfg.D3D11.pDeviceContext = nullptr; + + for (int eye = 0; eye < 2; ++eye) + { + if (EyeTexture[eye].D3D11.pTexture) + { + EyeTexture[eye].D3D11.pTexture->Release(); + EyeTexture[eye].D3D11.pTexture = nullptr; + } + if (EyeTexture[eye].D3D11.pSRView) + { + EyeTexture[eye].D3D11.pSRView->Release(); + EyeTexture[eye].D3D11.pSRView = nullptr; + } + } + + Cfg.D3D11.pBackBufferRT = nullptr; + Cfg.D3D11.pSwapChain = nullptr; + + bNeedReinitRendererAPI = false; + + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + SetRenderContext(nullptr); + } +} + +void FOculusRiftHMD::D3D11Bridge::Reset() +{ + if (IsInGameThread()) + { + ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ResetD3D, + FOculusRiftHMD::D3D11Bridge*, Bridge, this, + { + Bridge->Reset_RenderThread(); + }); + // Wait for all resources to be released + FlushRenderingCommands(); + } + else + { + Reset_RenderThread(); + } + + bInitialized = false; +} + +void FOculusRiftHMD::D3D11Bridge::OnBackBufferResize() +{ + Cfg.D3D11.pBackBufferRT = nullptr; + Cfg.D3D11.pSwapChain = nullptr; + + bNeedReinitRendererAPI = true; + + // if we are in the middle of rendering: prevent from calling EndFrame + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + } +} + +bool FOculusRiftHMD::D3D11Bridge::Present(int& SyncInterval) +{ + check(IsInRenderingThread()); + + if (!RenderContext.IsValid()) + { + return true; // use regular Present; this frame is not ready yet + } + + FinishRendering(); + + return false; // indicates that we are presenting here, UE shouldn't do Present. +} +#endif // #if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + +////////////////////////////////////////////////////////////////////////// +#if defined(OVR_GL) +FOculusRiftHMD::OGLBridge::OGLBridge() : + FCustomPresent() +{ + FMemory::Memset(Cfg, 0); + FMemory::Memset(EyeTexture, 0); +} + +void FOculusRiftHMD::OGLBridge::BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) +{ + SetRenderContext(&InRenderContext); + + if (!bInitialized) + { + Cfg.OGL.Header.API = ovrRenderAPI_OpenGL; + Cfg.OGL.Header.Multisample = 1; + bNeedReinitRendererAPI = true; + bInitialized = true; + } + if (bInitialized) + { + FGameFrame* CurrentFrame = GetRenderFrame(); + check(CurrentFrame); + FSettings* FrameSettings = CurrentFrame->GetSettings(); + check(FrameSettings); + + bool bWinChanged = false; +#if PLATFORM_WINDOWS + HWND Window = *(HWND*)ViewportRHI->GetNativeWindow(); + bWinChanged = (Cfg.OGL.Window != Window); +#elif PLATFORM_MAC + //@TODO + //NSWindow +#elif PLATFORM_LINUX + //@TODO +#endif + if (bWinChanged || + Cfg.OGL.Header.BackBufferSize.w != CurrentFrame->ViewportSize.X || + Cfg.OGL.Header.BackBufferSize.h != CurrentFrame->ViewportSize.Y) + { + Cfg.OGL.Header.BackBufferSize = OVR::Sizei(CurrentFrame->ViewportSize.X, CurrentFrame->ViewportSize.Y); +#if PLATFORM_WINDOWS + Cfg.OGL.Window = Window; +#elif PLATFORM_MAC + //@TODO +#elif PLATFORM_LINUX + //@TODO + // Cfg.OGL.Disp = AddParams; //? + // Cfg.OGL.Win = hWnd; //? +#endif + bNeedReinitRendererAPI = true; + } + + check(IsValidRef(RT)); + const uint32 RTSizeX = RT->GetSizeX(); + const uint32 RTSizeY = RT->GetSizeY(); + GLuint RTTexId = *(GLuint*)RT->GetNativeResource(); + + if ((EyeTexture[0].OGL.TexId != RTTexId || + EyeTexture[0].OGL.Header.TextureSize.w != RTSizeX || EyeTexture[0].OGL.Header.TextureSize.h != RTSizeY)) + { + EyeTexture[0].OGL.Header.API = ovrRenderAPI_OpenGL; + EyeTexture[0].OGL.Header.TextureSize = OVR::Sizei(RTSizeX, RTSizeY); + EyeTexture[0].OGL.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[0]); + EyeTexture[0].OGL.TexId = RTTexId; + + // Right eye uses the same texture, but different rendering viewport. + EyeTexture[1] = EyeTexture[0]; + EyeTexture[1].OGL.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[1]); + } + if (bNeedReinitRendererAPI) + { + if (!ovrHmd_ConfigureRendering(RenderContext->Hmd, &Cfg.Config, FrameSettings->DistortionCaps, + FrameSettings->EyeFov, FrameSettings->EyeRenderDesc)) + { + UE_LOG(LogHMD, Warning, TEXT("OGL ovrHmd_ConfigureRenderAPI failed.")); + return; + } + bNeedReinitRendererAPI = false; + } + } +} + +void FOculusRiftHMD::OGLBridge::FinishRendering() +{ + check(IsInRenderingThread()); + + check(RenderContext.IsValid()); + + if (RenderContext->bFrameBegun) + { + // Finish the frame and let OVR do buffer swap (Present) and flush/sync. + const ovrTexture eyeTextures[2] = { EyeTexture[0].Texture, EyeTexture[1].Texture }; + ovrHmd_EndFrame(RenderContext->Hmd, RenderContext->CurEyeRenderPose, eyeTextures); // This function will present + RenderContext->bFrameBegun = false; + } + else + { + UE_LOG(LogHMD, Warning, TEXT("Skipping frame: FinishRendering called with no corresponding BeginRendering (was BackBuffer re-allocated?)")); + } + SetRenderContext(nullptr); +} + +void FOculusRiftHMD::OGLBridge::Reset() +{ + check(IsInRenderingThread()); + + EyeTexture[0].OGL.TexId = 0; + EyeTexture[1].OGL.TexId = 0; + + bNeedReinitRendererAPI = false; + bInitialized = false; + + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + SetRenderContext(nullptr); + } +} + +void FOculusRiftHMD::OGLBridge::OnBackBufferResize() +{ + bNeedReinitRendererAPI = true; + + // if we are in the middle of rendering: prevent from calling EndFrame + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + } +} + +bool FOculusRiftHMD::OGLBridge::Present(int& SyncInterval) +{ + check(IsInRenderingThread()); + + FinishRendering(); + + return false; // indicates that we are presenting here, UE shouldn't do Present. +} +#endif // #if defined(OVR_GL) +#endif // OVR_SDK_RENDERING + + +#endif // OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //#if PLATFORM_MAC diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.cpp index c3a311cfc427..2c5e2b15856a 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.cpp +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.cpp @@ -27,7 +27,6 @@ FHMDSettings::FHMDSettings() : Flags.bYawDriftCorrectionEnabled = true; Flags.bLowPersistenceMode = true; Flags.bUpdateOnRT = true; - Flags.bOverdrive = true; Flags.bMirrorToWindow = true; Flags.bTimeWarp = true; Flags.bHmdPosTracking = true; @@ -130,6 +129,13 @@ FHeadMountedDisplay::FHeadMountedDisplay() Flags.Raw = 0; Flags.bNeedUpdateStereoRenderingParams = true; DeltaControlRotation = FRotator::ZeroRotator; // used from ApplyHmdRotation + +#if !UE_BUILD_SHIPPING + SideOfSingleCubeInMeters = 0; + SeaOfCubesVolumeSizeInMeters = 0; + NumberOfCubesInOneSide = 0; + CenterOffsetInMeters = FVector::ZeroVector; // offset from the center of 'sea of cubes' +#endif } bool FHeadMountedDisplay::IsInitialized() const @@ -162,11 +168,6 @@ TSharedPtr FHeadMountedDisplay::CreateNewSett return Result; } -// FHMDGameFrame* FHeadMountedDisplay::GetCurrentFrame() -// { -// return const_cast(const_cast(this)->FHMDGameFrame()); -// } - FHMDGameFrame* FHeadMountedDisplay::GetCurrentFrame() const { //UE_LOG(LogHMD, Log, TEXT("Getting current frame, frame number %d"), int(GFrameCounter)); @@ -403,7 +404,10 @@ bool FHeadMountedDisplay::IsStereoEnabled() const } else { - return false; //@TODO?(!Flags.bFrameStarted && Settings->IsStereoEnabled()); + // If IsStereoEnabled is called when a game frame hasn't started, then always return false. + // In the case when you need to check if stereo is GOING TO be enabled in next frame, + // use explicit call to Settings->IsStereoEnabled() + return false; } } else @@ -413,6 +417,11 @@ bool FHeadMountedDisplay::IsStereoEnabled() const return false; } +bool FHeadMountedDisplay::IsStereoEnabledOnNextFrame() const +{ + return Settings->IsStereoEnabled(); +} + void FHeadMountedDisplay::AdjustViewRect(EStereoscopicPass StereoPass, int32& X, int32& Y, uint32& SizeX, uint32& SizeY) const { SizeX = SizeX / 2; @@ -888,6 +897,54 @@ void FHeadMountedDisplay::ResetControlRotation() const } } +void FHeadMountedDisplay::GetCurrentHMDPose(FQuat& CurrentOrientation, FVector& CurrentPosition, + bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector& PositionScale) +{ + // only supposed to be used from the game thread + check(IsInGameThread()); + auto frame = GetCurrentFrame(); + if (!frame) + { + CurrentOrientation = FQuat::Identity; + CurrentPosition = FVector::ZeroVector; + return; + } + if (PositionScale != FVector::ZeroVector) + { + frame->CameraScale3D = PositionScale; + frame->Flags.bCameraScale3DAlreadySet = true; + } + GetCurrentPose(CurrentOrientation, CurrentPosition, bUseOrienationForPlayerCamera, bUsePositionForPlayerCamera); + if (bUseOrienationForPlayerCamera) + { + frame->LastHmdOrientation = CurrentOrientation; + frame->Flags.bOrientationChanged = bUseOrienationForPlayerCamera; + } + if (bUsePositionForPlayerCamera) + { + frame->LastHmdPosition = CurrentPosition; + frame->Flags.bPositionChanged = bUsePositionForPlayerCamera; + } +} + +void FHeadMountedDisplay::GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) +{ + GetCurrentHMDPose(CurrentOrientation, CurrentPosition, false, false, FVector::ZeroVector); +} + +FVector FHeadMountedDisplay::GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale) +{ + const auto frame = GetCurrentFrame(); + if (!frame) + { + return FVector::ZeroVector; + } + FVector UnrotatedPos = CurrentOrientation.Inverse().RotateVector(CurrentPosition); + UnrotatedPos.X -= frame->Settings->NeckToEyeInMeters.X * frame->WorldToMetersScale; + UnrotatedPos.Z -= frame->Settings->NeckToEyeInMeters.Y * frame->WorldToMetersScale; + return UnrotatedPos; +} + void FHeadMountedDisplay::ApplyHmdRotation(APlayerController* PC, FRotator& ViewRotation) { auto frame = GetCurrentFrame(); @@ -951,7 +1008,7 @@ void FHeadMountedDisplay::UpdatePlayerCameraRotation(APlayerCameraManager* Camer #endif FQuat CurHmdOrientation; FVector CurHmdPosition; - GetCurrentPose(CurHmdOrientation, CurHmdPosition, true, true); // POV.bFollowHmdOrientation, POV.bFollowHmdPosition); + GetCurrentPose(CurHmdOrientation, CurHmdPosition, frame->Settings->Flags.bPlayerCameraManagerFollowsHmdOrientation, frame->Settings->Flags.bPlayerCameraManagerFollowsHmdPosition); const FQuat CurPOVOrientation = POV.Rotation.Quaternion(); @@ -1084,11 +1141,11 @@ void FHeadMountedDisplay::DrawSeaOfCubes(UWorld* World, FVector ViewLocation) AStaticMeshActor* SeaOfCubesActor; SeaOfCubesActorPtr = SeaOfCubesActor = World->SpawnActor(SpawnInfo); - const float cube_side_in_meters = 0.12f; + const float cube_side_in_meters = (SideOfSingleCubeInMeters == 0) ? 0.12f : SideOfSingleCubeInMeters; const float cube_side = cube_side_in_meters * frame->Settings->WorldToMetersScale; - const float cubes_volume_in_meters = 3.2f; + const float cubes_volume_in_meters = (SeaOfCubesVolumeSizeInMeters == 0) ? 3.2f : SeaOfCubesVolumeSizeInMeters; const float cubes_volume = cubes_volume_in_meters * frame->Settings->WorldToMetersScale; - const int num_of_cubes = 11; + const int num_of_cubes = (NumberOfCubesInOneSide == 0) ? 11 : NumberOfCubesInOneSide; const TCHAR* const cube_resource_name = (CubeMeshName.IsEmpty()) ? TEXT("/Engine/BasicShapes/Cube.Cube") : *CubeMeshName; const TCHAR* const cube_material_name = (CubeMaterialName.IsEmpty()) ? TEXT("/Engine/EngineMaterials/DefaultMaterial.DefaultMaterial") : *CubeMaterialName; @@ -1117,7 +1174,7 @@ void FHeadMountedDisplay::DrawSeaOfCubes(UWorld* World, FVector ViewLocation) const FVector box_scale(cube_side / (bounds.BoxExtent.X * 2), cube_side / (bounds.BoxExtent.Y * 2), cube_side / (bounds.BoxExtent.Z * 2)); - const FVector SeaOfCubesOrigin = ViewLocation; + const FVector SeaOfCubesOrigin = ViewLocation + (CenterOffsetInMeters * frame->Settings->WorldToMetersScale); UInstancedStaticMeshComponent* ISMComponent = NewObject(); ISMComponent->AttachTo(SeaOfCubesActor->GetStaticMeshComponent()); diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.h b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.h index 54a3dd851c27..500e383c7a46 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.h +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/HeadMountedDisplayCommon.h @@ -92,12 +92,6 @@ public: */ uint64 bUpdateOnRT : 1; - /** Overdrive brightness transitions to reduce artifacts on DK2+ displays */ - uint64 bOverdrive : 1; - - /** High-quality sampling of distortion buffer for anti-aliasing */ - uint64 bHQDistortion : 1; - /** Enforces headtracking to work even in non-stereo mode (for debugging or screenshots). See 'MOTION ENFORCE' console command. */ uint64 bHeadTrackingEnforced : 1; @@ -311,6 +305,8 @@ public: virtual bool IsHMDEnabled() const override; virtual void EnableHMD(bool allow = true) override; + virtual void GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) override; + virtual bool DoesSupportPositionalTracking() const override; virtual bool HasValidTrackingPosition() override; virtual void GetPositionalTrackingCameraProperties(FVector& OutOrigin, FQuat& OutOrientation, float& OutHFOV, float& OutVFOV, float& OutCameraDistance, float& OutNearPlane, float& OutFarPlane) const override; @@ -331,6 +327,7 @@ public: virtual bool EnableStereo(bool stereo = true) override; virtual bool IsStereoEnabled() const override; + virtual bool IsStereoEnabledOnNextFrame() const override; virtual void AdjustViewRect(EStereoscopicPass StereoPass, int32& X, int32& Y, uint32& SizeX, uint32& SizeY) const override; virtual void SetClippingPlanes(float NCP, float FCP) override; @@ -350,7 +347,7 @@ public: * @param PositionScale (in) The 3D scale that will be applied to position. * @return The estimated neck position, calculated using NeckToEye vector from User Profile. Same coordinate space as CurrentPosition. */ - virtual FVector GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale) { return FVector::ZeroVector; } + virtual FVector GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale); /** * Sets base position offset (in meters). The base position offset is the distance from the physical (0, 0, 0) position @@ -380,6 +377,10 @@ public: virtual void ApplyHmdRotation(APlayerController* PC, FRotator& ViewRotation) override; virtual void UpdatePlayerCameraRotation(APlayerCameraManager*, struct FMinimalViewInfo& POV) override; + // An improved version of GetCurrentOrientationAndPostion, used from blueprints by OculusLibrary. + virtual void GetCurrentHMDPose(FQuat& CurrentOrientation, FVector& CurrentPosition, + bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector& PositionScale = FVector::ZeroVector); + protected: virtual TSharedPtr CreateNewGameFrame() const = 0; virtual TSharedPtr CreateNewSettings() const = 0; @@ -463,9 +464,13 @@ protected: } #if !UE_BUILD_SHIPPING - TWeakObjectPtr SeaOfCubesActorPtr; + TWeakObjectPtr SeaOfCubesActorPtr; FVector CachedViewLocation; FString CubeMeshName, CubeMaterialName; + float SideOfSingleCubeInMeters; + float SeaOfCubesVolumeSizeInMeters; + int NumberOfCubesInOneSide; + FVector CenterOffsetInMeters; // offset from the center of 'sea of cubes' #endif }; diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftCAPI.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftCAPI.cpp index 0173f1c8b75c..0ab049a6a4fc 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftCAPI.cpp +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftCAPI.cpp @@ -1,8 +1,17 @@ #include "HMDPrivatePCH.h" #include "OculusRiftHMD.h" -#if OCULUS_RIFT_SUPPORTED_PLATFORMS +#if PLATFORM_WINDOWS + // Required for OVR_CAPIShim.c + #include "AllowWindowsPlatformTypes.h" +#endif + #include + +#if PLATFORM_WINDOWS + // It is required to undef WINDOWS_PLATFORM_TYPES_GUARD for any further D3D11 private includes + #undef WINDOWS_PLATFORM_TYPES_GUARD +#endif + #include #include -#endif // OCULUS_RIFT_SUPPORTED_PLATFORMS diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.cpp index 65f6c20daa80..f77826fd0402 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.cpp +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.cpp @@ -2,9 +2,13 @@ #include "HMDPrivatePCH.h" #include "OculusRiftHMD.h" + +#if !PLATFORM_MAC // Mac uses 0.5/OculusRiftHMD_05.cpp + #include "EngineAnalytics.h" #include "Runtime/Analytics/Analytics/Public/Interfaces/IAnalyticsProvider.h" #include "SceneViewport.h" +#include "PostProcess/SceneRenderTargets.h" #if WITH_EDITOR #include "Editor/UnrealEd/Classes/Editor/EditorEngine.h" @@ -19,9 +23,6 @@ class FOculusRiftPlugin : public IOculusRiftPlugin /** IHeadMountedDisplayModule implementation */ virtual TSharedPtr< class IHeadMountedDisplay, ESPMode::ThreadSafe > CreateHeadMountedDisplay() override; - // Pre-init the HMD module (optional). - virtual void PreInit() override; - FString GetModulePriorityKeyName() const { return FString(TEXT("OculusRift")); @@ -42,13 +43,6 @@ TSharedPtr< class IHeadMountedDisplay, ESPMode::ThreadSafe > FOculusRiftPlugin:: return NULL; } -void FOculusRiftPlugin::PreInit() -{ -#if OCULUS_RIFT_SUPPORTED_PLATFORMS - FOculusRiftHMD::PreInit(); -#endif//OCULUS_RIFT_SUPPORTED_PLATFORMS -} - //--------------------------------------------------- // Oculus Rift IHeadMountedDisplay Implementation //--------------------------------------------------- @@ -75,30 +69,23 @@ static void OVR_CDECL OvrLogCallback(int level, const char* message) ////////////////////////////////////////////////////////////////////////// FSettings::FSettings() { -#ifndef OVR_VISION_ENABLED - Flags.bHmdPosTracking = false; -#endif -#ifndef OVR_SDK_RENDERING - Flags.bTimeWarp = false; -#else -#endif FMemory::Memset(EyeRenderDesc, 0); FMemory::Memset(EyeProjectionMatrices, 0); FMemory::Memset(EyeFov, 0); - SupportedTrackingCaps = SupportedDistortionCaps = SupportedHmdCaps = 0; - TrackingCaps = DistortionCaps = HmdCaps = 0; + SupportedTrackingCaps = SupportedHmdCaps = 0; + TrackingCaps = HmdCaps = 0; -#ifndef OVR_SDK_RENDERING - FMemory::Memset(UVScaleOffset, 0); -#endif + MirrorWindowMode = eMirrorWindow_Distorted; -#ifndef OVR_SDK_RENDERING - for (unsigned i = 0; i < sizeof(pDistortionMesh) / sizeof(pDistortionMesh[0]); ++i) - { - pDistortionMesh[i] = nullptr; - } -#endif + PixelDensity = 1.0f; + + FMemory::Memset(EyeLayer, 0); + EyeLayer.Header.Type = ovrLayerType_EyeFov; + EyeLayer.Header.Flags = ovrLayerFlag_HighQuality; + + RenderTargetSize = FIntPoint(0, 0); + bQueueAheadEnabled = false; } TSharedPtr FSettings::Clone() const @@ -107,13 +94,6 @@ TSharedPtr FSettings::Clone() const return NewSettings; } -void FSettings::SetEyeRenderViewport(int OneEyeVPw, int OneEyeVPh) -{ - FHMDSettings::SetEyeRenderViewport(OneEyeVPw, OneEyeVPh); - EyeRenderViewport[0].Max.X -= GetTexturePaddingPerEye(); - EyeRenderViewport[1].Min.X += GetTexturePaddingPerEye(); -} - ////////////////////////////////////////////////////////////////////////// FGameFrame::FGameFrame() { @@ -130,22 +110,6 @@ TSharedPtr FGameFrame::Clone() const } ////////////////////////////////////////////////////////////////////////// - -// a hack to allow quickly check if Oculus is in Direct mode -bool FOculusRiftHMD::bDirectModeHack = false; - -void FOculusRiftHMD::PreInit() -{ - ovrInitParams initParams; - FMemory::Memset(initParams, 0); - initParams.Flags = ovrInit_RequestVersion; - initParams.RequestedMinorVersion = OVR_MINOR_VERSION; -#if !UE_BUILD_SHIPPING - initParams.LogCallback = OvrLogCallback; -#endif - ovr_Initialize(&initParams); -} - TSharedPtr FOculusRiftHMD::CreateNewGameFrame() const { TSharedPtr Result(MakeShareable(new FGameFrame())); @@ -175,9 +139,7 @@ bool FOculusRiftHMD::OnStartGameFrame() if (Hmd) { // Save eye and head poses - const ovrVector3f hmdToEyeViewOffset[2] = { CurrentSettings->EyeRenderDesc[0].HmdToEyeViewOffset, CurrentSettings->EyeRenderDesc[1].HmdToEyeViewOffset }; - ovrHmd_GetEyePoses(Hmd, CurrentFrame->FrameNumber, hmdToEyeViewOffset, CurrentFrame->CurEyeRenderPose, &CurrentFrame->CurTrackingState); -#ifdef OVR_VISION_ENABLED + CurrentFrame->GetEyePoses(Hmd, CurrentFrame->CurEyeRenderPose, CurrentFrame->CurTrackingState); if (CurrentSettings->Flags.bHmdPosTracking) { CurrentFrame->Flags.bHaveVisionTracking = (CurrentFrame->CurTrackingState.StatusFlags & ovrStatus_PositionTracked) != 0; @@ -191,7 +153,6 @@ bool FOculusRiftHMD::OnStartGameFrame() } Flags.bHadVisionTracking = CurrentFrame->Flags.bHaveVisionTracking; } -#endif // OVR_VISION_ENABLED #if !UE_BUILD_SHIPPING { // used for debugging, do not remove FQuat CurHmdOrientation; @@ -209,8 +170,8 @@ bool FOculusRiftHMD::IsHMDConnected() { if (Settings->Flags.bHMDEnabled) { - InitDevice(); - return Hmd != nullptr; + ovrResult result = ovrHmd_Detect(); + return OVR_SUCCESS(result); } return false; } @@ -236,56 +197,37 @@ bool FOculusRiftHMD::GetHMDMonitorInfo(MonitorInfo& MonitorDesc) { InitDevice(); } + MonitorDesc.MonitorName = ""; + MonitorDesc.MonitorId = 0; + MonitorDesc.DesktopX = MonitorDesc.DesktopY = MonitorDesc.ResolutionX = MonitorDesc.ResolutionY = 0; + MonitorDesc.WindowSizeX = MonitorDesc.WindowSizeY = 0; if (Hmd) { - MonitorDesc.MonitorName = Hmd->DisplayDeviceName; - MonitorDesc.MonitorId = Hmd->DisplayId; - MonitorDesc.DesktopX = Hmd->WindowsPos.x; - MonitorDesc.DesktopY = Hmd->WindowsPos.y; MonitorDesc.ResolutionX = Hmd->Resolution.w; MonitorDesc.ResolutionY = Hmd->Resolution.h; MonitorDesc.WindowSizeX = Settings->MirrorWindowSize.X; MonitorDesc.WindowSizeY = Settings->MirrorWindowSize.Y; return true; } - else - { - MonitorDesc.MonitorName = ""; - MonitorDesc.MonitorId = 0; - MonitorDesc.DesktopX = MonitorDesc.DesktopY = MonitorDesc.ResolutionX = MonitorDesc.ResolutionY = 0; - MonitorDesc.WindowSizeX = MonitorDesc.WindowSizeY = 0; - } return false; } bool FOculusRiftHMD::IsFullscreenAllowed() { - if (!Hmd) - { - InitDevice(); - } - return ((Hmd && (Hmd->HmdCaps & ovrHmdCap_ExtendDesktop) != 0) || !Hmd) ? true : false; + return false; } bool FOculusRiftHMD::DoesSupportPositionalTracking() const { -#ifdef OVR_VISION_ENABLED const FGameFrame* frame = GetFrame(); const FSettings* OculusSettings = frame->GetSettings(); return (frame && OculusSettings->Flags.bHmdPosTracking && (OculusSettings->SupportedTrackingCaps & ovrTrackingCap_Position) != 0); -#else - return false; -#endif //OVR_VISION_ENABLED } bool FOculusRiftHMD::HasValidTrackingPosition() { -#ifdef OVR_VISION_ENABLED const auto frame = GetFrame(); return (frame && frame->Settings->Flags.bHmdPosTracking && frame->Flags.bHaveVisionTracking); -#else - return false; -#endif //OVR_VISION_ENABLED } #define TRACKER_FOCAL_DISTANCE 1.00f // meters (focal point to origin for position) @@ -341,6 +283,17 @@ void FOculusRiftHMD::EnableLowPersistenceMode(bool Enable) Flags.bNeedUpdateHmdCaps = true; } +// Returns eye poses instead of head pose. +void FGameFrame::GetEyePoses(ovrHmd Hmd, ovrPosef outEyePoses[2], ovrTrackingState& outTrackingState) const +{ + const FSettings* CurrentSettings = GetSettings(); + const ovrVector3f hmdToEyeViewOffset[2] = { CurrentSettings->EyeRenderDesc[0].HmdToEyeViewOffset, CurrentSettings->EyeRenderDesc[1].HmdToEyeViewOffset }; + + ovrFrameTiming ftiming = ovrHmd_GetFrameTiming(Hmd, FrameNumber); + outTrackingState = ovrHmd_GetTrackingState(Hmd, ftiming.DisplayMidpointSeconds); + ovr_CalcEyePoses(outTrackingState.HeadPose.ThePose, hmdToEyeViewOffset, outEyePoses); +} + void FGameFrame::PoseToOrientationAndPosition(const ovrPosef& InPose, FQuat& OutOrientation, FVector& OutPosition) const { OutOrientation = ToFQuat(InPose.Orientation); @@ -355,54 +308,6 @@ void FGameFrame::PoseToOrientationAndPosition(const ovrPosef& InPose, FQuat& Out OutOrientation.Normalize(); } -void FOculusRiftHMD::GetCurrentHMDPose(FQuat& CurrentOrientation, FVector& CurrentPosition, - bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector& PositionScale) -{ - // only supposed to be used from the game thread - check(IsInGameThread()); - auto frame = GetFrame(); - if (!frame) - { - CurrentOrientation = FQuat::Identity; - CurrentPosition = FVector::ZeroVector; - return; - } - if (PositionScale != FVector::ZeroVector) - { - frame->CameraScale3D = PositionScale; - frame->Flags.bCameraScale3DAlreadySet = true; - } - GetCurrentPose(CurrentOrientation, CurrentPosition, bUseOrienationForPlayerCamera, bUsePositionForPlayerCamera); - if (bUseOrienationForPlayerCamera) - { - frame->LastHmdOrientation = CurrentOrientation; - frame->Flags.bOrientationChanged = bUseOrienationForPlayerCamera; - } - if (bUsePositionForPlayerCamera) - { - frame->LastHmdPosition = CurrentPosition; - frame->Flags.bPositionChanged = bUsePositionForPlayerCamera; - } -} - -void FOculusRiftHMD::GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) -{ - GetCurrentHMDPose(CurrentOrientation, CurrentPosition, false, false, FVector::ZeroVector); -} - -FVector FOculusRiftHMD::GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale) -{ - const auto frame = GetFrame(); - if (!frame) - { - return FVector::ZeroVector; - } - FVector UnrotatedPos = CurrentOrientation.Inverse().RotateVector(CurrentPosition); - UnrotatedPos.X -= frame->Settings->NeckToEyeInMeters.X * frame->WorldToMetersScale; - UnrotatedPos.Z -= frame->Settings->NeckToEyeInMeters.Y * frame->WorldToMetersScale; - return UnrotatedPos; -} - void FOculusRiftHMD::GetCurrentPose(FQuat& CurrentHmdOrientation, FVector& CurrentHmdPosition, bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera) { check(IsInGameThread()); @@ -441,6 +346,17 @@ bool FOculusRiftHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar { if (FHeadMountedDisplay::Exec(InWorld, Cmd, Ar)) { + if (FParse::Command(&Cmd, TEXT("HMD"))) + { + if (FParse::Command(&Cmd, TEXT("SP")) || // screen percentage is deprecated + FParse::Command(&Cmd, TEXT("SCREENPERCENTAGE"))) // use pd - pixel density + { + // convert screenpercentage to pixel density + float pd = Settings->ScreenPercentage / Settings->IdealScreenPercentage; + GetSettings()->PixelDensity = pd; + Flags.bNeedUpdateStereoRenderingParams = true; + } + } return true; } @@ -448,34 +364,46 @@ bool FOculusRiftHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar if (FParse::Command(&Cmd, TEXT("HMD"))) { - if (FParse::Command(&Cmd, TEXT("LP"))) // low persistence mode + if (FParse::Command(&Cmd, TEXT("PD"))) // pixel density { FString CmdName = FParse::Token(Cmd, 0); - if (!CmdName.IsEmpty()) + if (CmdName.IsEmpty()) + return false; + + float pd = FCString::Atof(*CmdName); + if (pd > 0 && pd <= 3) { - if (!FCString::Stricmp(*CmdName, TEXT("ON"))) - { - Settings->Flags.bLowPersistenceMode = true; - } - else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) - { - Settings->Flags.bLowPersistenceMode = false; - } - else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) - { - Settings->Flags.bLowPersistenceMode = !Settings->Flags.bLowPersistenceMode; - } - else - { - return false; - } + GetSettings()->PixelDensity = pd; + Flags.bNeedUpdateStereoRenderingParams = true; } else { - Settings->Flags.bLowPersistenceMode = !Settings->Flags.bLowPersistenceMode; + Ar.Logf(TEXT("Value is out of range (0.0..3.0f]")); + } + return true; + } + if (FParse::Command(&Cmd, TEXT("QAHEAD"))) // pixel density + { + FString CmdName = FParse::Token(Cmd, 0); + + bool qaPrev = GetSettings()->bQueueAheadEnabled; + if (!FCString::Stricmp(*CmdName, TEXT("ON"))) + { + GetSettings()->bQueueAheadEnabled = true; + } + else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) + { + GetSettings()->bQueueAheadEnabled = false; + } + else + { + GetSettings()->bQueueAheadEnabled = !GetSettings()->bQueueAheadEnabled; + } + + if (GetSettings()->bQueueAheadEnabled != qaPrev) + { + ovrHmd_SetBool(Hmd, "QueueAheadEnabled", (GetSettings()->bQueueAheadEnabled) ? ovrTrue : ovrFalse); } - Flags.bNeedUpdateHmdCaps = true; - Ar.Logf(TEXT("Low Persistence is currently %s"), (Settings->Flags.bLowPersistenceMode) ? TEXT("ON") : TEXT("OFF")); return true; } else if (FParse::Command(&Cmd, TEXT("MIRROR"))) // to mirror or not to mirror?... @@ -491,9 +419,11 @@ bool FOculusRiftHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar { Settings->Flags.bMirrorToWindow = false; } - else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) + else if (!FCString::Stricmp(*CmdName, TEXT("MODE"))) { - Settings->Flags.bMirrorToWindow = !Settings->Flags.bMirrorToWindow; + FString ModeName = FParse::Token(Cmd, 0); + int32 i = FCString::Atoi(*ModeName); + GetSettings()->MirrorWindowMode = FSettings::MirrorWindowModeType(FMath::Clamp(i, 0, (int32)FSettings::eMirrorWindow_Total_)); } else if (!FCString::Stricmp(*CmdName, TEXT("RESET"))) { @@ -522,68 +452,6 @@ bool FOculusRiftHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar } return true; } - else if (FParse::Command(&Cmd, TEXT("OVERDRIVE"))) // 2 frame raise overdrive - { - FString CmdName = FParse::Token(Cmd, 0); - if (!CmdName.IsEmpty()) - { - if (!FCString::Stricmp(*CmdName, TEXT("ON"))) - { - Settings->Flags.bOverdrive = true; - } - else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) - { - Settings->Flags.bOverdrive = false; - } - else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) - { - Settings->Flags.bOverdrive = !Settings->Flags.bOverdrive; - } - else - { - return false; - } - } - else - { - Settings->Flags.bOverdrive = !Settings->Flags.bOverdrive; - } - Flags.bNeedUpdateDistortionCaps = true; - Ar.Logf(TEXT("Overdrive is currently %s"), (Settings->Flags.bOverdrive) ? TEXT("ON") : TEXT("OFF")); - return true; - } -#ifdef OVR_SDK_RENDERING - else if (FParse::Command(&Cmd, TEXT("TIMEWARP"))) - { - FString CmdName = FParse::Token(Cmd, 0); - if (!CmdName.IsEmpty()) - { - if (!FCString::Stricmp(*CmdName, TEXT("ON"))) - { - Settings->Flags.bTimeWarp = true; - } - else if (!FCString::Stricmp(*CmdName, TEXT("OFF"))) - { - Settings->Flags.bTimeWarp = false; - } - else if (!FCString::Stricmp(*CmdName, TEXT("TOGGLE"))) - { - Settings->Flags.bTimeWarp = !Settings->Flags.bTimeWarp; - } - else - { - return false; - } - } - else - { - Settings->Flags.bTimeWarp = !Settings->Flags.bTimeWarp; - } - Flags.bNeedUpdateDistortionCaps = true; - Ar.Logf(TEXT("TimeWarp is currently %s"), (Settings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); - return true; - } -#endif // #ifdef OVR_SDK_RENDERING #if !UE_BUILD_SHIPPING else if (FParse::Command(&Cmd, TEXT("STATS"))) // status / statistics { @@ -626,60 +494,6 @@ bool FOculusRiftHMD::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar } return false; } - else if (FParse::Command(&Cmd, TEXT("HMDWARP"))) - { -#ifndef OVR_SDK_RENDERING - if (FParse::Command( &Cmd, TEXT("ON") )) - { - Settings->Flags.bHmdDistortion = true; - return true; - } - else if (FParse::Command( &Cmd, TEXT("OFF") )) - { - Settings->Flags.bHmdDistortion = false; - return true; - } -#endif //OVR_SDK_RENDERING - if (FParse::Command(&Cmd, TEXT("CHA"))) - { - Settings->Flags.bChromaAbCorrectionEnabled = true; - Flags.bNeedUpdateDistortionCaps = true; - return true; - } - else if (FParse::Command(&Cmd, TEXT("NOCHA"))) - { - Settings->Flags.bChromaAbCorrectionEnabled = false; - Flags.bNeedUpdateDistortionCaps = true; - return true; - } - else if (FParse::Command( &Cmd, TEXT("HQ") )) - { - // High quality distortion - if (FParse::Command( &Cmd, TEXT("ON") )) - { - Settings->Flags.bHQDistortion = true; - } - else if (FParse::Command(&Cmd, TEXT("OFF"))) - { - Settings->Flags.bHQDistortion = false; - } - else - { - Settings->Flags.bHQDistortion = !Settings->Flags.bHQDistortion; - } - Ar.Logf(TEXT("High quality distortion is currently %s"), (Settings->Flags.bHQDistortion) ? TEXT("ON") : TEXT("OFF")); - Flags.bNeedUpdateDistortionCaps = true; - return true; - } - - if (FParse::Command(&Cmd, TEXT("SHOW"))) - { - Ar.Logf(TEXT("hmdwarp %s sc=%f %s"), (Settings->Flags.bHmdDistortion ? TEXT("on") : TEXT("off")) - , Settings->IdealScreenPercentage / 100.f - , (Settings->Flags.bChromaAbCorrectionEnabled ? TEXT("cha") : TEXT("nocha"))); - } - return true; - } else if (FParse::Command(&Cmd, TEXT("OVRVERSION"))) { // deprecated. Use 'hmdversion' instead @@ -751,13 +565,9 @@ void FOculusRiftHMD::RecordAnalytics() } EventAttributes.Add(FAnalyticsEventAttribute(TEXT("InterpupillaryDistance"), Settings->InterpupillaryDistance)); EventAttributes.Add(FAnalyticsEventAttribute(TEXT("TimeWarp"), Settings->Flags.bTimeWarp)); - EventAttributes.Add(FAnalyticsEventAttribute(TEXT("AllowFinishCurrentFrame"), Settings->Flags.bAllowFinishCurrentFrame)); -#ifdef OVR_VISION_ENABLED EventAttributes.Add(FAnalyticsEventAttribute(TEXT("HmdPosTracking"), Settings->Flags.bHmdPosTracking)); -#endif EventAttributes.Add(FAnalyticsEventAttribute(TEXT("LowPersistenceMode"), Settings->Flags.bLowPersistenceMode)); EventAttributes.Add(FAnalyticsEventAttribute(TEXT("UpdateOnRT"), Settings->Flags.bUpdateOnRT)); - EventAttributes.Add(FAnalyticsEventAttribute(TEXT("Overdrive"), Settings->Flags.bOverdrive)); EventAttributes.Add(FAnalyticsEventAttribute(TEXT("MirrorToWindow"), Settings->Flags.bMirrorToWindow)); FString OutStr(TEXT("Editor.VR.DeviceInitialised")); @@ -907,7 +717,6 @@ bool FOculusRiftHMD::OnOculusStateChange(bool bIsEnabledNow) ReleaseDevice(); ResetControlRotation(); - RestoreSystemValues(); return true; } else @@ -917,9 +726,7 @@ bool FOculusRiftHMD::OnOculusStateChange(bool bIsEnabledNow) if (Hmd) { - SaveSystemValues(); Flags.bApplySystemOverridesOnStereo = true; - UpdateStereoRenderingParams(); return true; } @@ -928,56 +735,6 @@ bool FOculusRiftHMD::OnOculusStateChange(bool bIsEnabledNow) return false; } -void FOculusRiftHMD::ApplySystemOverridesOnStereo(bool bForce) -{ - if (Settings->Flags.bStereoEnabled || bForce) - { - // Set the current VSync state - if (Settings->Flags.bOverrideVSync) - { - static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); - CVSyncVar->Set(Settings->Flags.bVSync != 0); - } - else - { - static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); - Settings->Flags.bVSync = CVSyncVar->GetInt() != 0; - } - UpdateHmdCaps(); - -#ifndef OVR_SDK_RENDERING - static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); - CFinishFrameVar->Set(Settings->Flags.bAllowFinishCurrentFrame != 0); -#endif - } -} - -void FOculusRiftHMD::SaveSystemValues() -{ - static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); - Settings->Flags.bSavedVSync = CVSyncVar->GetInt() != 0; -} - -void FOculusRiftHMD::RestoreSystemValues() -{ - static IConsoleVariable* CVSyncVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync")); - // todo: bad - cvars are a user wish, this should be changed - CVSyncVar->Set(Settings->Flags.bSavedVSync != 0); - - static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); - // todo: bad - cvars are a user wish, this should be changed - CFinishFrameVar->Set(false); -} - -void FOculusRiftHMD::UpdatePostProcessSettings(FPostProcessSettings* Settings) -{ - const auto frame = GetFrame(); - if (frame && frame->Flags.bScreenPercentageEnabled) - { - Settings->ScreenPercentage = GetActualScreenPercentage(); - } -} - void FOculusRiftHMD::CalculateStereoViewOffset(const EStereoscopicPass StereoPassType, const FRotator& ViewRotation, const float WorldToMeters, FVector& ViewLocation) { check(WorldToMeters != 0.f); @@ -1185,14 +942,8 @@ void FOculusRiftHMD::SetupViewFamily(FSceneViewFamily& InViewFamily) check(frame); InViewFamily.EngineShowFlags.MotionBlur = 0; -#ifndef OVR_SDK_RENDERING - InViewFamily.EngineShowFlags.HMDDistortion = frame->Settings->Flags.bHmdDistortion; -#else InViewFamily.EngineShowFlags.HMDDistortion = false; -#endif InViewFamily.EngineShowFlags.StereoRendering = IsStereoEnabled(); - - frame->Flags.bScreenPercentageEnabled = InViewFamily.EngineShowFlags.ScreenPercentage; } void FOculusRiftHMD::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) @@ -1203,33 +954,12 @@ void FOculusRiftHMD::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InVie InView.BaseHmdOrientation = frame->LastHmdOrientation; InView.BaseHmdLocation = frame->LastHmdPosition; -#ifndef OVR_SDK_RENDERING - InViewFamily.bUseSeparateRenderTarget = false; - - // check and save texture size. - if (InView.StereoPass == eSSP_LEFT_EYE) - { - if (Settings->EyeRenderViewport[0].Size() != InView.ViewRect.Size()) - { - Settings->SetEyeRenderViewport(InView.ViewRect.Size().X, InView.ViewRect.Size().Y); - - // patch EyeRenderViewport in the frame's settings as well - FMemory::Memcpy(frame->GetSettings()->EyeRenderViewport, Settings->EyeRenderViewport); - Flags.bNeedUpdateStereoRenderingParams = true; - } - } -#else InViewFamily.bUseSeparateRenderTarget = ShouldUseSeparateRenderTarget(); - if (InView.StereoPass == eSSP_RIGHT_EYE) - { - InView.ViewRect.Min.X += frame->GetSettings()->GetTexturePaddingPerEye()*2; - InView.ViewRect.Max.X += frame->GetSettings()->GetTexturePaddingPerEye()*2; - } - -#endif - const int eyeIdx = (InView.StereoPass == eSSP_LEFT_EYE) ? 0 : 1; + + InView.ViewRect = frame->GetSettings()->EyeRenderViewport[eyeIdx]; + frame->CachedViewRotation[eyeIdx] = InView.ViewRotation; } @@ -1263,7 +993,7 @@ FOculusRiftHMD::FOculusRiftHMD() Settings->Flags.bOverrideScreenPercentage = true; Settings->ScreenPercentage = 100; } - OSWindowHandle = nullptr; + RendererModule = nullptr; Startup(); } @@ -1274,6 +1004,13 @@ FOculusRiftHMD::~FOculusRiftHMD() void FOculusRiftHMD::Startup() { +#if PLATFORM_MAC + if (GIsEditor) + { + // no editor support for Mac yet + return; + } +#endif if ((!IsRunningGame() && !GIsEditor) || (Settings->Flags.InitStatus & FSettings::eStartupExecuted) != 0) { // do not initialize plugin for server or if it was already initialized @@ -1281,10 +1018,24 @@ void FOculusRiftHMD::Startup() } Settings->Flags.InitStatus |= FSettings::eStartupExecuted; - // Initializes LibOVR. This LogMask_All enables maximum logging. - // Custom allocator can also be specified here. - // Actually, most likely, the ovr_Initialize is already called from PreInit. - int8 bWasInitialized = ovr_Initialize(); + // Initializes LibOVR. + ovrInitParams initParams; + FMemory::Memset(initParams, 0); + initParams.Flags = ovrInit_RequestVersion; + initParams.RequestedMinorVersion = OVR_MINOR_VERSION; +#if !UE_BUILD_SHIPPING + initParams.LogCallback = OvrLogCallback; +#endif + + ovrResult initStatus = ovr_Initialize(&initParams); + if (!OVR_SUCCESS(initStatus) && initStatus == ovrError_LibLoad) + { + // fatal errors: can't load library + UE_LOG(LogHMD, Error, TEXT("Can't find Oculus library %s: is proper Runtime installed? Version: %s"), + TEXT(OVR_FILE_DESCRIPTION_STRING), TEXT(OVR_VERSION_STRING)); + Settings->Flags.InitStatus = 0; + return; + } if (GIsEditor) { @@ -1308,22 +1059,28 @@ void FOculusRiftHMD::Startup() // Uncap fps to enable FPS higher than 62 GEngine->bSmoothFrameRate = false; - SaveSystemValues(); - -#ifdef OVR_SDK_RENDERING #if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) if (IsPCPlatform(GMaxRHIShaderPlatform) && !IsOpenGLPlatform(GMaxRHIShaderPlatform)) { - pD3D11Bridge = new D3D11Bridge(); + check(!pCustomPresent.GetReference()) + pCustomPresent = new D3D11Bridge(Hmd); } #endif #if defined(OVR_GL) if (IsOpenGLPlatform(GMaxRHIShaderPlatform)) { - pOGLBridge = new OGLBridge(); + check(!pCustomPresent.GetReference()) + pCustomPresent = new OGLBridge(Hmd); + } +#else + if (IsOpenGLPlatform(GMaxRHIShaderPlatform)) + { + UE_LOG(LogHMD, Warning, TEXT("OpenGL is not currently supported by OculusRiftHMD plugin")); + ovr_Shutdown(); + Settings->Flags.InitStatus = 0; + return; } #endif -#endif // #ifdef OVR_SDK_RENDERING if (bForced || Hmd) { @@ -1331,6 +1088,10 @@ void FOculusRiftHMD::Startup() UE_LOG(LogHMD, Log, TEXT("Oculus plugin initialized. Version: %s"), *GetVersionString()); } + + // grab a pointer to the renderer module for displaying our mirror window + static const FName RendererModuleName("Renderer"); + RendererModule = FModuleManager::GetModulePtr(RendererModuleName); } void FOculusRiftHMD::Shutdown() @@ -1340,16 +1101,13 @@ void FOculusRiftHMD::Shutdown() return; } - RestoreSystemValues(); - -#ifdef OVR_SDK_RENDERING ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ShutdownRen, FOculusRiftHMD*, Plugin, this, { Plugin->ShutdownRendering(); }); FlushRenderingCommands(); -#endif // OVR_SDK_RENDERING + ReleaseDevice(); Settings = nullptr; @@ -1378,32 +1136,20 @@ bool FOculusRiftHMD::InitDevice() FSettings* CurrentSettings = GetSettings(); - Hmd = ovrHmd_Create(0); - if (Hmd) + ovrResult result = ovrHmd_Create(0, &Hmd); + if (OVR_SUCCESS(result) && Hmd) { - bDirectModeHack = (Hmd->HmdCaps & ovrHmdCap_ExtendDesktop) == 0; - - CurrentSettings->SupportedDistortionCaps = Hmd->DistortionCaps; + if (pCustomPresent) + { + pCustomPresent->SetHmd(Hmd); + } CurrentSettings->SupportedHmdCaps = Hmd->HmdCaps; CurrentSettings->SupportedTrackingCaps = Hmd->TrackingCaps; -#ifndef OVR_SDK_RENDERING - CurrentSettings->SupportedDistortionCaps &= ~ovrDistortionCap_Overdrive; -#endif -#ifndef OVR_VISION_ENABLED - CurrentSettings->SupportedTrackingCaps &= ~ovrTrackingCap_Position; -#endif - - CurrentSettings->DistortionCaps = CurrentSettings->SupportedDistortionCaps & (ovrDistortionCap_TimeWarp | ovrDistortionCap_Vignette | ovrDistortionCap_Overdrive); CurrentSettings->TrackingCaps = CurrentSettings->SupportedTrackingCaps & (ovrTrackingCap_Orientation | ovrTrackingCap_MagYawCorrection | ovrTrackingCap_Position); CurrentSettings->HmdCaps = CurrentSettings->SupportedHmdCaps & (ovrHmdCap_DynamicPrediction | ovrHmdCap_LowPersistence); CurrentSettings->HmdCaps |= (CurrentSettings->Flags.bVSync ? 0 : ovrHmdCap_NoVSync); - if (!(CurrentSettings->SupportedDistortionCaps & ovrDistortionCap_TimeWarp)) - { - CurrentSettings->Flags.bTimeWarp = false; - } - CurrentSettings->Flags.bHmdPosTracking = (CurrentSettings->SupportedTrackingCaps & ovrTrackingCap_Position) != 0; LoadFromIni(); @@ -1412,6 +1158,8 @@ bool FOculusRiftHMD::InitDevice() UpdateHmdRenderInfo(); UpdateStereoRenderingParams(); UpdateHmdCaps(); + + ovrHmd_SetBool(Hmd, "QueueAheadEnabled", (CurrentSettings->bQueueAheadEnabled) ? ovrTrue : ovrFalse); } return Hmd != nullptr; @@ -1423,19 +1171,18 @@ void FOculusRiftHMD::ReleaseDevice() { SaveToIni(); - ovrHmd_AttachToWindow(Hmd, NULL, NULL, NULL); // Wait for all resources to be released -#ifdef OVR_SDK_RENDERING + ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ResetRen, FOculusRiftHMD*, Plugin, this, { - if (Plugin->GetActiveRHIBridgeImpl()) + if (Plugin->pCustomPresent) { - Plugin->GetActiveRHIBridgeImpl()->Reset(); + Plugin->pCustomPresent->Reset(); } }); -#endif + // Wait for all resources to be released FlushRenderingCommands(); @@ -1444,31 +1191,6 @@ void FOculusRiftHMD::ReleaseDevice() } } -void FOculusRiftHMD::UpdateDistortionCaps() -{ - FSettings* CurrentSettings = GetSettings(); - - if (IsOpenGLPlatform(GMaxRHIShaderPlatform)) - { - CurrentSettings->DistortionCaps &= ~ovrDistortionCap_SRGB; - CurrentSettings->DistortionCaps |= ovrDistortionCap_FlipInput; - } - (CurrentSettings->Flags.bTimeWarp) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_TimeWarp : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_TimeWarp; - (CurrentSettings->Flags.bOverdrive) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_Overdrive : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_Overdrive; - (CurrentSettings->Flags.bHQDistortion) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_HqDistortion : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_HqDistortion; -#if !UE_BUILD_SHIPPING - (CurrentSettings->Flags.bProfiling) ? CurrentSettings->DistortionCaps |= ovrDistortionCap_ProfileNoSpinWaits : CurrentSettings->DistortionCaps &= ~ovrDistortionCap_ProfileNoSpinWaits; -#endif // #if !UE_BUILD_SHIPPING - -#ifdef OVR_SDK_RENDERING - if (GetActiveRHIBridgeImpl()) - { - GetActiveRHIBridgeImpl()->SetNeedReinitRendererAPI(); - } -#endif // OVR_SDK_RENDERING - Flags.bNeedUpdateDistortionCaps = false; -} - void FOculusRiftHMD::UpdateHmdCaps() { if (Hmd) @@ -1511,14 +1233,6 @@ void FOculusRiftHMD::UpdateHmdCaps() CurrentSettings->HmdCaps |= ovrHmdCap_NoVSync; } - if (CurrentSettings->Flags.bMirrorToWindow) - { - CurrentSettings->HmdCaps &= ~ovrHmdCap_NoMirrorToWindow; - } - else - { - CurrentSettings->HmdCaps |= ovrHmdCap_NoMirrorToWindow; - } ovrHmd_SetEnabledCaps(Hmd, CurrentSettings->HmdCaps); ovrHmd_ConfigureTracking(Hmd, CurrentSettings->TrackingCaps, 0); @@ -1540,8 +1254,8 @@ void FOculusRiftHMD::UpdateHmdRenderInfo() { check(Hmd); - UE_LOG(LogHMD, Warning, TEXT("HMD %s, Monitor %s, res = %d x %d, windowPos = {%d, %d}"), ANSI_TO_TCHAR(Hmd->ProductName), - ANSI_TO_TCHAR(Hmd->DisplayDeviceName), Hmd->Resolution.w, Hmd->Resolution.h, Hmd->WindowsPos.x, Hmd->WindowsPos.y); + UE_LOG(LogHMD, Warning, TEXT("HMD %s, res = %d x %d"), ANSI_TO_TCHAR(Hmd->ProductName), + Hmd->Resolution.w, Hmd->Resolution.h); FSettings* CurrentSettings = GetSettings(); @@ -1612,10 +1326,28 @@ void FOculusRiftHMD::UpdateStereoRenderingParams() CurrentSettings->EyeProjectionMatrices[0] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[0], 0.01f, 10000.0f, ProjModifiers); CurrentSettings->EyeProjectionMatrices[1] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[1], 0.01f, 10000.0f, ProjModifiers); + CurrentSettings->EyeLayer.EyeFov.Fov[0] = CurrentSettings->EyeRenderDesc[0].Fov; + CurrentSettings->EyeLayer.EyeFov.Fov[1] = CurrentSettings->EyeRenderDesc[1].Fov; + + // This section is for positional TW + //CurrentSettings->PosTimewarpProjectionDesc = ovrTimewarpProjectionDesc_FromProjection(CurrentSettings->EyeProjectionMatrices[0]); + //CurrentSettings->EyeLayer.EyeFovDepth.ProjectionDesc = ovrTimewarpProjectionDesc_FromProjection(CurrentSettings->EyeProjectionMatrices[0]); + CurrentSettings->PerspectiveProjection[0] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[0], 0.01f, 10000.f, ProjModifiers | ovrProjection_RightHanded); CurrentSettings->PerspectiveProjection[1] = ovrMatrix4f_Projection(CurrentSettings->EyeFov[1], 0.01f, 10000.f, ProjModifiers | ovrProjection_RightHanded); - PrecalculateDistortionMesh(); + const ovrSizei recommenedTex0Size = ovrHmd_GetFovTextureSize(Hmd, ovrEye_Left, CurrentSettings->EyeFov[0], CurrentSettings->PixelDensity); + const ovrSizei recommenedTex1Size = ovrHmd_GetFovTextureSize(Hmd, ovrEye_Right, CurrentSettings->EyeFov[1], CurrentSettings->PixelDensity); + const float texturePadding = CurrentSettings->GetTexturePaddingPerEye(); + CurrentSettings->RenderTargetSize.X = recommenedTex0Size.w + recommenedTex1Size.w + texturePadding*2; + CurrentSettings->RenderTargetSize.Y = FMath::Max(recommenedTex0Size.h, recommenedTex1Size.h); + FSceneRenderTargets::QuantizeBufferSize(CurrentSettings->RenderTargetSize.X, CurrentSettings->RenderTargetSize.Y); + + const int32 RTSizeX = CurrentSettings->RenderTargetSize.X; + const int32 RTSizeY = CurrentSettings->RenderTargetSize.Y; + CurrentSettings->EyeRenderViewport[0] = FIntRect(0, 0, RTSizeX/2 - texturePadding, RTSizeY); + CurrentSettings->EyeRenderViewport[1] = FIntRect(RTSizeX/2 + texturePadding, 0, RTSizeX, RTSizeY); + Flags.bNeedUpdateStereoRenderingParams = false; } } @@ -1625,6 +1357,8 @@ void FOculusRiftHMD::LoadFromIni() const TCHAR* OculusSettings = TEXT("Oculus.Settings"); bool v; float f; + int i; + FVector vec; if (GConfig->GetBool(OculusSettings, TEXT("bChromaAbCorrectionEnabled"), v, GEngineIni)) { Settings->Flags.bChromaAbCorrectionEnabled = v; @@ -1671,20 +1405,13 @@ void FOculusRiftHMD::LoadFromIni() Settings->Flags.bVSync = v; } } - if (!GIsEditor) + if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensity"), f, GEngineIni)) { - if (GConfig->GetBool(OculusSettings, TEXT("bOverrideScreenPercentage"), v, GEngineIni)) - { - Settings->Flags.bOverrideScreenPercentage = v; - if (GConfig->GetFloat(OculusSettings, TEXT("ScreenPercentage"), f, GEngineIni)) - { - Settings->ScreenPercentage = f; - } - } + GetSettings()->PixelDensity = f; } - if (GConfig->GetBool(OculusSettings, TEXT("bAllowFinishCurrentFrame"), v, GEngineIni)) + if (GConfig->GetBool(OculusSettings, TEXT("QueueAheadEnabled"), v, GEngineIni)) { - Settings->Flags.bAllowFinishCurrentFrame = v; + GetSettings()->bQueueAheadEnabled = v; } if (GConfig->GetBool(OculusSettings, TEXT("bLowPersistenceMode"), v, GEngineIni)) { @@ -1702,6 +1429,23 @@ void FOculusRiftHMD::LoadFromIni() { Settings->NearClippingPlane = f; } + if (GConfig->GetVector(OculusSettings, TEXT("MirrorWindowSize"), vec, GEngineIni)) + { + Settings->MirrorWindowSize = FIntPoint(vec.X, vec.Y); + } + if (GConfig->GetInt(OculusSettings, TEXT("MirrorWindowMode"), i, GEngineIni)) + { + if (i < 0) + { + GetSettings()->MirrorWindowMode = FSettings::MirrorWindowModeType(FMath::Clamp(-i, 0, (int)FSettings::eMirrorWindow_Total_)); + GetSettings()->Flags.bMirrorToWindow = false; + } + else + { + GetSettings()->MirrorWindowMode = FSettings::MirrorWindowModeType(FMath::Clamp(i, 0, (int)FSettings::eMirrorWindow_Total_)); + GetSettings()->Flags.bMirrorToWindow = true; + } + } #if !UE_BUILD_SHIPPING FString s; if (GConfig->GetString(OculusSettings, TEXT("CubeMeshName"), s, GEngineIni)) @@ -1712,6 +1456,22 @@ void FOculusRiftHMD::LoadFromIni() { CubeMaterialName = s; } + if (GConfig->GetFloat(OculusSettings, TEXT("SideOfSingleCubeInMeters"), f, GEngineIni)) + { + SideOfSingleCubeInMeters = f; + } + if (GConfig->GetFloat(OculusSettings, TEXT("SeaOfCubesVolumeSizeInMeters"), f, GEngineIni)) + { + SeaOfCubesVolumeSizeInMeters = f; + } + if (GConfig->GetInt(OculusSettings, TEXT("NumberOfCubesInOneSide"), i, GEngineIni)) + { + NumberOfCubesInOneSide = i; + } + if (GConfig->GetVector(OculusSettings, TEXT("CenterOffsetInMeters"), vec, GEngineIni)) + { + CenterOffsetInMeters = vec; + } #endif } @@ -1740,16 +1500,9 @@ void FOculusRiftHMD::SaveToIni() GConfig->SetBool(OculusSettings, TEXT("VSync"), Settings->Flags.bVSync, GEngineIni); } - if (!GIsEditor) - { - GConfig->SetBool(OculusSettings, TEXT("bOverrideScreenPercentage"), Settings->Flags.bOverrideScreenPercentage, GEngineIni); - if (Settings->Flags.bOverrideScreenPercentage) - { - // Save the current ScreenPercentage state - GConfig->SetFloat(OculusSettings, TEXT("ScreenPercentage"), Settings->ScreenPercentage, GEngineIni); - } - } - GConfig->SetBool(OculusSettings, TEXT("bAllowFinishCurrentFrame"), Settings->Flags.bAllowFinishCurrentFrame, GEngineIni); + GConfig->SetFloat(OculusSettings, TEXT("PixelDensity"), GetSettings()->PixelDensity, GEngineIni); + + GConfig->SetBool(OculusSettings, TEXT("QueueAheadEnabled"), GetSettings()->bQueueAheadEnabled, GEngineIni); GConfig->SetBool(OculusSettings, TEXT("bLowPersistenceMode"), Settings->Flags.bLowPersistenceMode, GEngineIni); @@ -1760,6 +1513,16 @@ void FOculusRiftHMD::SaveToIni() GConfig->SetFloat(OculusSettings, TEXT("FarClippingPlane"), Settings->FarClippingPlane, GEngineIni); GConfig->SetFloat(OculusSettings, TEXT("NearClippingPlane"), Settings->NearClippingPlane, GEngineIni); } + + if (Settings->Flags.bMirrorToWindow) + { + GConfig->SetInt(OculusSettings, TEXT("MirrorWindowMode"), GetSettings()->MirrorWindowMode, GEngineIni); + } + else + { + GConfig->SetInt(OculusSettings, TEXT("MirrorWindowMode"), -GetSettings()->MirrorWindowMode, GEngineIni); + } + GConfig->SetVector(OculusSettings, TEXT("MirrorWindowSize"), FVector(Settings->MirrorWindowSize.X, Settings->MirrorWindowSize.Y, 0), GEngineIni); } bool FOculusRiftHMD::HandleInputKey(UPlayerInput* pPlayerInput, @@ -1769,7 +1532,7 @@ bool FOculusRiftHMD::HandleInputKey(UPlayerInput* pPlayerInput, { if (!Key.IsMouseButton()) { - ovrHmd_DismissHSWDisplay(Hmd); +// ovrHmd_DismissHSWDisplay(Hmd); } } return false; @@ -1855,10 +1618,11 @@ FViewExtension::FViewExtension(FHeadMountedDisplay* InDelegate) { auto OculusHMD = static_cast(InDelegate); Hmd = OculusHMD->Hmd; -#ifdef OVR_SDK_RENDERING - pPresentBridge = OculusHMD->GetActiveRHIBridgeImpl(); -#endif + RendererModule = OculusHMD->RendererModule; + + pPresentBridge = OculusHMD->pCustomPresent; } #endif //OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //#if !PLATFORM_MAC diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.h b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.h index a969658362b2..6be5c2a5971a 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.h +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftHMD.h @@ -1,6 +1,9 @@ // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #pragma once +#if PLATFORM_MAC +#include "0.5/OculusRiftHMD_05.h" +#else #include "IOculusRiftPlugin.h" #include "IHeadMountedDisplay.h" #include "HeadMountedDisplayCommon.h" @@ -14,52 +17,25 @@ #endif #if PLATFORM_WINDOWS - #define OVR_VISION_ENABLED - #define OVR_SDK_RENDERING #define OVR_D3D_VERSION 11 - #define OVR_GL +// #define OVR_GL #elif PLATFORM_MAC #define OVR_VISION_ENABLED - #define OVR_SDK_RENDERING #define OVR_GL #endif -#ifdef OVR_VISION_ENABLED - #ifndef OVR_CAPI_VISIONSUPPORT - #define OVR_CAPI_VISIONSUPPORT - #endif -#endif - #if OCULUS_RIFT_SUPPORTED_PLATFORMS #include - #include + #include #include #include #include - -#if PLATFORM_WINDOWS -#include "AllowWindowsPlatformTypes.h" -#endif -#ifdef OVR_SDK_RENDERING - #ifdef OVR_D3D_VERSION - #include "OVR_CAPI_D3D.h" - #endif // OVR_D3D_VERSION - #ifdef OVR_GL - #include "OVR_CAPI_GL.h" - #endif -#endif // OVR_SDK_RENDERING - #endif //OCULUS_RIFT_SUPPORTED_PLATFORMS #if PLATFORM_SUPPORTS_PRAGMA_PACK #pragma pack (pop) #endif -// #ifdef UE_BUILD_DEBUG -// #undef FORCEINLINE -// #define FORCEINLINE -// #endif - struct FDistortionVertex; class FOculusRiftHMD; @@ -126,8 +102,6 @@ namespace OculusRift return OVR::Recti(rect.Min.X, rect.Min.Y, rect.Size().X, rect.Size().Y); } -typedef uint64 bool64; - class FSettings : public FHMDSettings { public: @@ -137,40 +111,34 @@ public: ovrMatrix4f EyeProjectionMatrices[2]; // 0 - left, 1 - right, same as Views ovrMatrix4f PerspectiveProjection[2]; // used for calc ortho projection matrices ovrFovPort EyeFov[2]; // 0 - left, 1 - right, same as Views + ovrLayer_Union EyeLayer; + + FIntPoint RenderTargetSize; + float PixelDensity; + + bool bQueueAheadEnabled; unsigned SupportedTrackingCaps; - unsigned SupportedDistortionCaps; unsigned SupportedHmdCaps; unsigned TrackingCaps; - unsigned DistortionCaps; unsigned HmdCaps; -#ifndef OVR_SDK_RENDERING - struct FDistortionMesh : public TSharedFromThis + enum MirrorWindowModeType { - FDistortionVertex* pVertices; - uint16* pIndices; - unsigned NumVertices; - unsigned NumIndices; - unsigned NumTriangles; + eMirrorWindow_Distorted, + eMirrorWindow_Undistorted, + eMirrorWindow_SingleEye, - FDistortionMesh() :pVertices(NULL), pIndices(NULL), NumVertices(0), NumIndices(0), NumTriangles(0) {} - ~FDistortionMesh() { Reset(); } - void Reset(); + eMirrorWindow_Total_ }; - // U,V scale and offset needed for timewarp. - ovrVector2f UVScaleOffset[2][2]; // scale UVScaleOffset[x][0], offset UVScaleOffset[x][1], where x is eye idx - TSharedPtr pDistortionMesh[2]; // -#endif + MirrorWindowModeType MirrorWindowMode; FSettings(); virtual ~FSettings() override {} virtual TSharedPtr Clone() const override; - virtual void SetEyeRenderViewport(int OneEyeVPw, int OneEyeVPh) override; - float GetTexturePaddingPerEye() const { return TexturePaddingPerEye * GetActualScreenPercentage()/100.f; } }; @@ -199,6 +167,9 @@ public: virtual TSharedPtr Clone() const override; void PoseToOrientationAndPosition(const ovrPosef& InPose, FQuat& OutOrientation, FVector& OutPosition) const; + + // Returns eye poses calculated from head pose. Head pose is available via ovrTrackingState. + void GetEyePoses(ovrHmd Hmd, ovrPosef outEyePoses[2], ovrTrackingState& outTrackingState) const; }; // View extension class that contains all the rendering-specific data (also referred as 'RenderContext'). @@ -212,9 +183,11 @@ public: virtual void PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override; FGameFrame* GetRenderFrame() const { return static_cast(RenderFrame.Get()); } - FSettings* GetFrameSetting() const { return static_cast(RenderFrame->GetSettings()); } + FSettings* GetFrameSettings() const { return static_cast(RenderFrame->GetSettings()); } + public: class FCustomPresent* pPresentBridge; + IRendererModule* RendererModule; ovrHmd Hmd; ovrPosef CurEyeRenderPose[2];// most recent eye render poses ovrPosef CurHeadPose; // current position of head @@ -228,31 +201,47 @@ class FCustomPresent : public FRHICustomPresent public: FCustomPresent() : FRHICustomPresent(nullptr) - , bNeedReinitRendererAPI(true) + , Hmd(nullptr) , bInitialized(false) + , bNeedReAllocateTextureSet(true) + , bNeedReAllocateMirrorTexture(true) {} // Returns true if it is initialized and used. bool IsInitialized() const { return bInitialized; } virtual void BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) = 0; - virtual void SetNeedReinitRendererAPI() { bNeedReinitRendererAPI = true; } virtual void Reset() = 0; virtual void Shutdown() = 0; - void UpdateViewport(const FViewport& Viewport, FRHIViewport* ViewportRHI, FGameFrame* InRenderFrame); + virtual FTexture2DRHIRef GetMirrorTexture() = 0; + + virtual void UpdateViewport(const FViewport& Viewport, FRHIViewport* ViewportRHI, FGameFrame* InRenderFrame); FGameFrame* GetRenderFrame() const { check(IsInRenderingThread()); return static_cast(RenderContext->RenderFrame.Get()); } FViewExtension* GetRenderContext() const { return static_cast(RenderContext.Get()); } FSettings* GetFrameSetting() const { check(IsInRenderingThread()); return static_cast(RenderContext->RenderFrame->GetSettings()); } + virtual void SetHmd(ovrHmd InHmd) { Hmd = InHmd; } + + // marking textures invalid, that will force re-allocation of ones + void MarkTexturesInvalid(); + + bool AreTexturesMarkedAsInvalid() const { return bNeedReAllocateTextureSet; } + + // Allocates render target texture + // If returns false then a default RT texture will be used. + virtual bool AllocateRenderTargetTexture(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, uint32 TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples) = 0; + protected: void SetRenderContext(FHMDViewExtension* InRenderContext); protected: // data + ovrHmd Hmd; TSharedPtr RenderContext; - bool bNeedReinitRendererAPI : 1; bool bInitialized : 1; + bool bNeedReAllocateTextureSet : 1; + bool bNeedReAllocateMirrorTexture : 1; }; } // namespace OculusRift @@ -267,8 +256,6 @@ class FOculusRiftHMD : public FHeadMountedDisplay friend class FViewExtension; friend class UOculusFunctionLibrary; public: - static void PreInit(); - /** IHeadMountedDisplay interface */ virtual bool OnStartGameFrame() override; @@ -280,10 +267,6 @@ public: virtual bool HasValidTrackingPosition() override; virtual void GetPositionalTrackingCameraProperties(FVector& OutOrigin, FQuat& OutOrientation, float& OutHFOV, float& OutVFOV, float& OutCameraDistance, float& OutNearPlane, float& OutFarPlane) const override; - virtual void GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) override; - - virtual FVector GetNeckPosition(const FQuat& CurrentOrientation, const FVector& CurrentPosition, const FVector& PositionScale) override; - virtual TSharedPtr GetViewExtension() override; virtual bool Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) override; @@ -291,33 +274,29 @@ public: virtual void RecordAnalytics() override; /** IStereoRendering interface */ + virtual void RenderTexture_RenderThread(class FRHICommandListImmediate& RHICmdList, class FRHITexture2D* BackBuffer, class FRHITexture2D* SrcTexture) const override; virtual void CalculateStereoViewOffset(const EStereoscopicPass StereoPassType, const FRotator& ViewRotation, const float MetersToWorld, FVector& ViewLocation) override; virtual FMatrix GetStereoProjectionMatrix(const EStereoscopicPass StereoPassType, const float FOV) const override; virtual void GetOrthoProjection(int32 RTWidth, int32 RTHeight, float OrthoDistance, FMatrix OrthoProjection[2]) const override; virtual void InitCanvasFromView(FSceneView* InView, UCanvas* Canvas) override; - virtual void GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const override; - virtual void GetTimewarpMatrices_RenderThread(const struct FRenderingCompositePassContext& Context, FMatrix& EyeRotationStart, FMatrix& EyeRotationEnd) const override; virtual void UpdateViewport(bool bUseSeparateRenderTarget, const FViewport& Viewport, SViewport*) override; -#ifdef OVR_SDK_RENDERING - virtual void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) const override; - virtual bool NeedReAllocateViewportRenderTarget(const FViewport& Viewport) const override; -#endif//OVR_SDK_RENDERING + virtual void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) override; + virtual bool NeedReAllocateViewportRenderTarget(const FViewport& Viewport) override; virtual bool ShouldUseSeparateRenderTarget() const override { -#ifdef OVR_SDK_RENDERING check(IsInGameThread()); return IsStereoEnabled(); -#else - return false; -#endif//OVR_SDK_RENDERING } virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override; virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override; + virtual FRHICustomPresent* GetCustomPresent() override { return pCustomPresent; } + virtual uint32 GetNumberOfBufferedFrames() const override { return 1; } + virtual bool AllocateRenderTargetTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, uint32 TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override; /** Positional tracking control methods */ virtual bool IsHeadTrackingAllowed() const override; @@ -332,9 +311,6 @@ public: virtual void ResetOrientation(float Yaw = 0.f) override; virtual void ResetPosition() override; - virtual void DrawDistortionMesh_RenderThread(struct FRenderingCompositePassContext& Context, const FIntPoint& TextureSize) override; - virtual void UpdatePostProcessSettings(FPostProcessSettings*) override; - virtual bool HandleInputKey(class UPlayerInput*, const FKey& Key, EInputEvent EventType, float AmountDepressed, bool bGamepad) override; virtual void OnBeginPlay() override; @@ -376,10 +352,6 @@ public: virtual FString GetVersionString() const override; - // An improved version of GetCurrentOrientationAndPostion, used from blueprints by OculusLibrary. - void GetCurrentHMDPose(FQuat& CurrentOrientation, FVector& CurrentPosition, - bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector& PositionScale); - protected: virtual TSharedPtr CreateNewGameFrame() const override; virtual TSharedPtr CreateNewSettings() const override; @@ -391,31 +363,42 @@ protected: public: -#ifdef OVR_SDK_RENDERING - #if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) class D3D11Bridge : public FCustomPresent { public: - D3D11Bridge(); + D3D11Bridge(ovrHmd Hmd); // Implementation of FRHICustomPresent // Resets Viewport-specific pointers (BackBufferRT, SwapChain). virtual void OnBackBufferResize() override; // Returns true if Engine should perform its own Present. - virtual bool Present(int SyncInterval) override; + virtual bool Present(int32& SyncInterval) override; // Implementation of FCustomPresent, called by Plugin itself virtual void BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) override; void FinishRendering(); virtual void Reset() override; - virtual void Shutdown() override; + virtual void Shutdown() override + { + Reset(); + } + virtual FTexture2DRHIRef GetMirrorTexture() override { return MirrorTextureRHI; } + + virtual void SetHmd(ovrHmd InHmd) override; + + virtual bool AllocateRenderTargetTexture(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, uint32 TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples); protected: + void Init(ovrHmd InHmd); void Reset_RenderThread(); protected: // data - ovrD3D11Config Cfg; - ovrD3D11Texture EyeTexture[2]; + TRefCountPtr ColorTextureSet; + TRefCountPtr DepthTextureSet; + + // Mirror texture + ovrTexture* MirrorTexture; + FTexture2DRHIRef MirrorTextureRHI; }; #endif @@ -430,7 +413,7 @@ public: // Resets Viewport-specific resources. virtual void OnBackBufferResize() override; // Returns true if Engine should perform its own Present. - virtual bool Present(int SyncInterval) override; + virtual bool Present(int& SyncInterval) override; // Implementation of FCustomPresent, called by Plugin itself virtual void BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) override; @@ -446,16 +429,9 @@ public: ovrGLTexture EyeTexture[2]; }; #endif // OVR_GL - FCustomPresent* GetActiveRHIBridgeImpl() const; void ShutdownRendering(); -#else - - virtual void FinishRenderingFrame_RenderThread(FRHICommandListImmediate& RHICmdList) override; - -#endif // #ifdef OVR_SDK_RENDERING - /** Constructor */ FOculusRiftHMD(); @@ -484,11 +460,8 @@ private: */ virtual void UpdateStereoRenderingParams() override; virtual void UpdateHmdRenderInfo() override; - virtual void UpdateDistortionCaps() override; virtual void UpdateHmdCaps() override; - virtual void ApplySystemOverridesOnStereo(bool force = false) override; - /** * Called when state changes from 'stereo' to 'non-stereo'. Suppose to distribute * the event further to user's code (?). @@ -500,49 +473,29 @@ private: void LoadFromIni(); void SaveToIni(); - /** Saves system values before applying overrides. */ - void SaveSystemValues(); - /** Restores system values after overrides applied. */ - void RestoreSystemValues(); - - void PrecalculateDistortionMesh(); - class FSceneViewport* FindSceneViewport(); FGameFrame* GetFrame(); const FGameFrame* GetFrame() const; + // Copies one texture to another + void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FTexture2DRHIParamRef DstTexture, FTexture2DRHIParamRef SrcTexture, FIntRect DstRect = FIntRect(), FIntRect SrcRect = FIntRect()) const; + private: // data - ovrHmd Hmd; + TRefCountPtrpCustomPresent; + IRendererModule* RendererModule; + ovrHmd Hmd; union { struct { - bool64 _PlaceHolder_ : 1; + uint64 _PlaceHolder_ : 1; }; uint64 Raw; } OCFlags; -#ifdef OVR_SDK_RENDERING - -#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) - TRefCountPtr pD3D11Bridge; -#endif -#if defined(OVR_GL) - TRefCountPtr pOGLBridge; -#endif -#else // !OVR_SDK_RENDERING - // this can be set and accessed only from renderthread! - TSharedPtr RenderContext; - - //TSharedPtr FindRenderContext(const FSceneViewFamily& ViewFamily) const; - FGameFrame* GetRenderFrameFromContext() const; -#endif // OVR_SDK_RENDERING - - void* OSWindowHandle; - FGameFrame* GetGameFrame() { return (FGameFrame*)(Frame.Get()); @@ -562,9 +515,7 @@ private: // data { return (const FSettings*)(Settings.Get()); } - -public: - static bool bDirectModeHack; // a hack to allow quickly check if Oculus is in Direct mode }; #endif //OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //#if PLATFORM_MAC diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRender.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRender.cpp index 51df300c2f4a..f54c6c06679c 100644 --- a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRender.cpp +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRender.cpp @@ -3,153 +3,112 @@ #include "HMDPrivatePCH.h" #include "OculusRiftHMD.h" +#if !PLATFORM_MAC // Mac uses 0.5/OculusRiftRender_05.cpp + #if OCULUS_RIFT_SUPPORTED_PLATFORMS #include "RendererPrivate.h" #include "ScenePrivate.h" #include "PostProcess/PostProcessHMD.h" +#include "ScreenRendering.h" #include "SlateBasics.h" -#ifndef OVR_SDK_RENDERING -void FSettings::FDistortionMesh::Reset() +bool FOculusRiftHMD::AllocateRenderTargetTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, uint32 TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples) { - delete[] pVertices; - delete[] pIndices; - pVertices = NULL; - pIndices = NULL; - NumVertices = NumIndices = NumTriangles = 0; + check(Index == 0); + pCustomPresent->AllocateRenderTargetTexture(SizeX, SizeY, Format, NumMips, Flags, TargetableTextureFlags, OutTargetableTexture, OutShaderResourceTexture, NumSamples); + return true; } -#endif -void FOculusRiftHMD::PrecalculateDistortionMesh() +void FOculusRiftHMD::CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FTexture2DRHIParamRef DstTexture, FTexture2DRHIParamRef SrcTexture, + FIntRect DstRect, FIntRect SrcRect) const { -#ifndef OVR_SDK_RENDERING - check(IsInGameThread()); + check(IsInRenderingThread()); - FSettings* CurrentSettings = GetSettings(); // we need global settings here rather than frame settings - check(CurrentSettings); - - for (unsigned eyeNum = 0; eyeNum < 2; ++eyeNum) + //const FIntPoint TargetSize(DstTexture->GetSizeX(), DstTexture->GetSizeY()); + if (DstRect.IsEmpty()) { - // Allocate & generate distortion mesh vertices. - ovrDistortionMesh meshData; + DstRect = FIntRect(0, 0, DstTexture->GetSizeX(), DstTexture->GetSizeY()); + } + const uint32 ViewportWidth = DstRect.Width(); + const uint32 ViewportHeight = DstRect.Height(); + const FIntPoint TargetSize(ViewportWidth, ViewportHeight); - if (!ovrHmd_CreateDistortionMesh(Hmd, CurrentSettings->EyeRenderDesc[eyeNum].Eye, CurrentSettings->EyeRenderDesc[eyeNum].Fov, CurrentSettings->DistortionCaps, &meshData)) + const float SrcTextureWidth = SrcTexture->GetSizeX(); + const float SrcTextureHeight = SrcTexture->GetSizeY(); + float U = 0.f, V = 0.f, USize = 1.f, VSize = 1.f; + if (!SrcRect.IsEmpty()) + { + U = SrcRect.Min.X / SrcTextureWidth; + V = SrcRect.Min.Y / SrcTextureHeight; + USize = SrcRect.Width() / SrcTextureWidth; + VSize = SrcRect.Height() / SrcTextureHeight; + } + + SetRenderTarget(RHICmdList, DstTexture, FTextureRHIRef()); + RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0, DstRect.Max.X, DstRect.Max.Y, 1.0f); + + RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); + RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); + RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); + + const auto FeatureLevel = GMaxRHIFeatureLevel; + auto ShaderMap = GetGlobalShaderMap(FeatureLevel); + + TShaderMapRef VertexShader(ShaderMap); + TShaderMapRef PixelShader(ShaderMap); + + static FGlobalBoundShaderState BoundShaderState; + SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, RendererModule->GetFilterVertexDeclaration().VertexDeclarationRHI, *VertexShader, *PixelShader); + + PixelShader->SetParameters(RHICmdList, TStaticSamplerState::GetRHI(), SrcTexture); + + RendererModule->DrawRectangle( + RHICmdList, + 0, 0, + ViewportWidth, ViewportHeight, + U, V, + USize, VSize, + TargetSize, + FIntPoint(1, 1), + *VertexShader, + EDRF_Default); +} + +void FOculusRiftHMD::RenderTexture_RenderThread(class FRHICommandListImmediate& RHICmdList, class FRHITexture2D* BackBuffer, class FRHITexture2D* SrcTexture) const +{ + check(IsInRenderingThread()); + + check(pCustomPresent); + auto RenderContext = pCustomPresent->GetRenderContext(); + if (RenderContext && RenderContext->GetFrameSettings()->Flags.bMirrorToWindow) + { + if (RenderContext->GetFrameSettings()->MirrorWindowMode == FSettings::eMirrorWindow_Distorted) { - check(false); - continue; + FTexture2DRHIRef MirrorTexture = pCustomPresent->GetMirrorTexture(); + if (MirrorTexture) + { + CopyTexture_RenderThread(RHICmdList, BackBuffer, MirrorTexture); + } } - - // alloc the data - ovrDistortionVertex* ovrMeshVertData = new ovrDistortionVertex[meshData.VertexCount]; - - // Convert to final vertex data. - FDistortionVertex *pVerts = new FDistortionVertex[meshData.VertexCount]; - FDistortionVertex *pCurVert = pVerts; - ovrDistortionVertex* pCurOvrVert = meshData.pVertexData; - - for (unsigned vertNum = 0; vertNum < meshData.VertexCount; ++vertNum) + else if (RenderContext->GetFrameSettings()->MirrorWindowMode == FSettings::eMirrorWindow_Undistorted) { - pCurVert->Position.X = pCurOvrVert->ScreenPosNDC.x; - pCurVert->Position.Y = pCurOvrVert->ScreenPosNDC.y; - pCurVert->TexR = FVector2D(pCurOvrVert->TanEyeAnglesR.x, pCurOvrVert->TanEyeAnglesR.y); - pCurVert->TexG = FVector2D(pCurOvrVert->TanEyeAnglesG.x, pCurOvrVert->TanEyeAnglesG.y); - pCurVert->TexB = FVector2D(pCurOvrVert->TanEyeAnglesB.x, pCurOvrVert->TanEyeAnglesB.y); - pCurVert->VignetteFactor = pCurOvrVert->VignetteFactor; - pCurVert->TimewarpFactor = pCurOvrVert->TimeWarpFactor; - pCurOvrVert++; - pCurVert++; + auto FrameSettings = RenderContext->GetFrameSettings(); + FIntRect destRect(0, 0, BackBuffer->GetSizeX() / 2, BackBuffer->GetSizeY()); + for (int i = 0; i < 2; ++i) + { + CopyTexture_RenderThread(RHICmdList, BackBuffer, SrcTexture, destRect, FrameSettings->EyeRenderViewport[i]); + destRect.Min.X += BackBuffer->GetSizeX() / 2; + destRect.Max.X += BackBuffer->GetSizeX() / 2; + } + } + else if (RenderContext->GetFrameSettings()->MirrorWindowMode == FSettings::eMirrorWindow_SingleEye) + { + auto FrameSettings = RenderContext->GetFrameSettings(); + CopyTexture_RenderThread(RHICmdList, BackBuffer, SrcTexture, FIntRect(), FrameSettings->EyeRenderViewport[0]); } - - CurrentSettings->pDistortionMesh[eyeNum] = MakeShareable(new FSettings::FDistortionMesh()); - CurrentSettings->pDistortionMesh[eyeNum]->NumTriangles = meshData.IndexCount / 3; - CurrentSettings->pDistortionMesh[eyeNum]->NumIndices = meshData.IndexCount; - CurrentSettings->pDistortionMesh[eyeNum]->NumVertices = meshData.VertexCount; - CurrentSettings->pDistortionMesh[eyeNum]->pVertices = pVerts; - - check(sizeof(*meshData.pIndexData) == sizeof(uint16)); - CurrentSettings->pDistortionMesh[eyeNum]->pIndices = new uint16[meshData.IndexCount]; - FMemory::Memcpy(CurrentSettings->pDistortionMesh[eyeNum]->pIndices, meshData.pIndexData, sizeof(uint16)*meshData.IndexCount); - - ovrHmd_DestroyDistortionMesh(&meshData); } -#endif -} - -void FOculusRiftHMD::DrawDistortionMesh_RenderThread(FRenderingCompositePassContext& Context, const FIntPoint& InTextureSize) -{ - check(IsInRenderingThread()); - - const FSceneView& View = Context.View; - if (View.StereoPass == eSSP_FULL) - { - return; - } - -#ifndef OVR_SDK_RENDERING - float ClipSpaceQuadZ = 0.0f; - FMatrix QuadTexTransform = FMatrix::Identity; - FMatrix QuadPosTransform = FMatrix::Identity; - const FIntRect SrcRect = View.ViewRect; - - auto RenderFrame = GetRenderFrameFromContext(); - check(RenderFrame); - auto RenderFrameSettings = RenderFrame->GetSettings(); - check(RenderFrameSettings); - - FRHICommandListImmediate& RHICmdList = Context.RHICmdList; - const FSceneViewFamily& ViewFamily = *(View.Family); - int ViewportSizeX = ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeX(); - int ViewportSizeY = ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeY(); - RHICmdList.SetViewport(0, 0, 0.0f, ViewportSizeX, ViewportSizeY, 1.0f); - - TSharedPtr mesh = RenderFrameSettings->pDistortionMesh[(View.StereoPass == eSSP_LEFT_EYE) ? 0 : 1]; - - DrawIndexedPrimitiveUP(Context.RHICmdList, PT_TriangleList, 0, mesh->NumVertices, mesh->NumTriangles, mesh->pIndices, - sizeof(mesh->pIndices[0]), mesh->pVertices, sizeof(mesh->pVertices[0])); -#else - check(0); -#endif -} - -void FOculusRiftHMD::GetEyeRenderParams_RenderThread(const FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const -{ - check(IsInRenderingThread()); -#ifndef OVR_SDK_RENDERING - auto RenderFrame = GetRenderFrameFromContext(); - check(RenderFrame); - auto RenderFrameSettings = RenderFrame->GetSettings(); - check(RenderFrameSettings); - - const unsigned eyeIdx = (Context.View.StereoPass == eSSP_LEFT_EYE) ? 0 : 1; - EyeToSrcUVOffsetValue.X = RenderFrameSettings->UVScaleOffset[eyeIdx][1].x; - EyeToSrcUVOffsetValue.Y = RenderFrameSettings->UVScaleOffset[eyeIdx][1].y; - - EyeToSrcUVScaleValue.X = RenderFrameSettings->UVScaleOffset[eyeIdx][0].x; - EyeToSrcUVScaleValue.Y = RenderFrameSettings->UVScaleOffset[eyeIdx][0].y; -#else - check(0); -#endif -} - -void FOculusRiftHMD::GetTimewarpMatrices_RenderThread(const FRenderingCompositePassContext& Context, FMatrix& EyeRotationStart, FMatrix& EyeRotationEnd) const -{ - check(IsInRenderingThread()); -#ifndef OVR_SDK_RENDERING - check(RenderContext.IsValid()); - - const ovrEyeType eye = (Context.View.StereoPass == eSSP_LEFT_EYE) ? ovrEye_Left : ovrEye_Right; - ovrMatrix4f timeWarpMatrices[2]; - if (RenderContext->bFrameBegun) - { - ovrHmd_GetEyeTimewarpMatrices(Hmd, eye, RenderContext->CurEyeRenderPose[eye], timeWarpMatrices); - } - EyeRotationStart = ToFMatrix(timeWarpMatrices[0]); - EyeRotationEnd = ToFMatrix(timeWarpMatrices[1]); -#else - check(0); -#endif } void FViewExtension::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) @@ -179,42 +138,23 @@ void FViewExtension::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList.Clear(true, FLinearColor::Black, false, 0, false, 0, FIntRect()); } -#ifdef OVR_SDK_RENDERING check(ViewFamily.RenderTarget->GetRenderTargetTexture()); - CurrentFrame->GetSettings()->SetEyeRenderViewport(ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeX()/2, - ViewFamily.RenderTarget->GetRenderTargetTexture()->GetSizeY()); + + FrameSettings->EyeLayer.EyeFov.Viewport[0] = ToOVRRecti(FrameSettings->EyeRenderViewport[0]); + FrameSettings->EyeLayer.EyeFov.Viewport[1] = ToOVRRecti(FrameSettings->EyeRenderViewport[1]); + pPresentBridge->BeginRendering(RenderContext, ViewFamily.RenderTarget->GetRenderTargetTexture()); - ovrHmd_BeginFrame(Hmd, RenderContext.RenderFrame->FrameNumber); -#else - FOculusRiftHMD* OculusPlugin = static_cast(Delegate); - TSharedPtr SharedPtr(AsShared()); - OculusPlugin->RenderContext = StaticCastSharedPtr(SharedPtr); + ovrHmd_GetFrameTiming(Hmd, RenderContext.RenderFrame->FrameNumber); - const FIntPoint TextureSize = FrameSettings->GetTextureSize(); - for (size_t eyeNum = 0; eyeNum < 2; ++eyeNum) - { - ovrHmd_GetRenderScaleAndOffset(FrameSettings->EyeRenderDesc[eyeNum].Fov, - OVR::Sizei(TextureSize.X, TextureSize.Y), - ToOVRRecti(FrameSettings->EyeRenderViewport[eyeNum]), - FrameSettings->UVScaleOffset[eyeNum]); - } - - ovrHmd_BeginFrameTiming(Hmd, CurrentFrame->FrameNumber); -#endif RenderContext.bFrameBegun = true; if (RenderContext.ShowFlags.Rendering) { // get latest orientation/position and cache it ovrTrackingState ts; - const ovrVector3f hmdToEyeViewOffset[2] = - { - FrameSettings->EyeRenderDesc[0].HmdToEyeViewOffset, - FrameSettings->EyeRenderDesc[1].HmdToEyeViewOffset - }; ovrPosef EyeRenderPose[2]; - ovrHmd_GetEyePoses(Hmd, CurrentFrame->FrameNumber, hmdToEyeViewOffset, EyeRenderPose, &ts); + CurrentFrame->GetEyePoses(Hmd, EyeRenderPose, ts); // Take new EyeRenderPose is bUpdateOnRT. // if !bOrientationChanged && !bPositionChanged then we still need to use new eye pose (for timewarp) @@ -234,17 +174,17 @@ void FViewExtension::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& void FViewExtension::PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& View) { check(IsInRenderingThread()); - FViewExtension& RenderContext = *this; //static_cast(InRenderContext); - const FGameFrame* CurrentFrame = static_cast(RenderContext.RenderFrame.Get()); + FViewExtension& RenderContext = *this; + FGameFrame* CurrentFrame = static_cast(RenderContext.RenderFrame.Get()); if (!RenderContext.ShowFlags.Rendering || !CurrentFrame || !CurrentFrame->Settings->IsStereoEnabled()) { return; } + const ovrEyeType eyeIdx = (View.StereoPass == eSSP_LEFT_EYE) ? ovrEye_Left : ovrEye_Right; if (RenderContext.ShowFlags.Rendering && CurrentFrame->Settings->Flags.bUpdateOnRT) { - const ovrEyeType eyeIdx = (View.StereoPass == eSSP_LEFT_EYE) ? ovrEye_Left : ovrEye_Right; FQuat CurrentEyeOrientation; FVector CurrentEyePosition; CurrentFrame->PoseToOrientationAndPosition(RenderContext.CurEyeRenderPose[eyeIdx], CurrentEyeOrientation, CurrentEyePosition); @@ -293,27 +233,13 @@ void FViewExtension::PreRenderView_RenderThread(FRHICommandListImmediate& RHICmd View.UpdateViewMatrix(); } } + + FSettings* FrameSettings = CurrentFrame->GetSettings(); + check(FrameSettings); + FrameSettings->EyeLayer.EyeFov.RenderPose[eyeIdx] = RenderContext.CurEyeRenderPose[eyeIdx]; } -#ifdef OVR_SDK_RENDERING -FCustomPresent* FOculusRiftHMD::GetActiveRHIBridgeImpl() const -{ -#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) - if (pD3D11Bridge) - { - return pD3D11Bridge; - } -#endif -#if defined(OVR_GL) - if (pOGLBridge) - { - return pOGLBridge; - } -#endif - return nullptr; -} - -void FOculusRiftHMD::CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) const +void FOculusRiftHMD::CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) { check(IsInGameThread()); @@ -322,21 +248,22 @@ void FOculusRiftHMD::CalculateRenderTargetSize(const FViewport& Viewport, uint32 return; } - if (Viewport.GetClient()->GetEngineShowFlags()->ScreenPercentage) + // We must be sure the rendertargetsize is calculated already + if (Flags.bNeedUpdateStereoRenderingParams) { - float value = GetActualScreenPercentage(); - if (value > 0.0f) - { - InOutSizeX = FMath::CeilToInt(InOutSizeX * value / 100.f) + GetSettings()->GetTexturePaddingPerEye()*2; - InOutSizeY = FMath::CeilToInt(InOutSizeY * value / 100.f); - } + UpdateStereoRenderingParams(); } + + InOutSizeX = GetSettings()->RenderTargetSize.X; + InOutSizeY = GetSettings()->RenderTargetSize.Y; + + check(InOutSizeX != 0 && InOutSizeY != 0); } -bool FOculusRiftHMD::NeedReAllocateViewportRenderTarget(const FViewport& Viewport) const +bool FOculusRiftHMD::NeedReAllocateViewportRenderTarget(const FViewport& Viewport) { check(IsInGameThread()); - if (IsStereoEnabled()) + if (Settings->IsStereoEnabled()) { const uint32 InSizeX = Viewport.GetSizeXY().X; const uint32 InSizeY = Viewport.GetSizeXY().Y; @@ -344,7 +271,7 @@ bool FOculusRiftHMD::NeedReAllocateViewportRenderTarget(const FViewport& Viewpor uint32 NewSizeX = InSizeX, NewSizeY = InSizeY; CalculateRenderTargetSize(Viewport, NewSizeX, NewSizeY); - if (NewSizeX != RenderTargetSize.X || NewSizeY != RenderTargetSize.Y) + if (NewSizeX != RenderTargetSize.X || NewSizeY != RenderTargetSize.Y || (pCustomPresent && pCustomPresent->AreTexturesMarkedAsInvalid())) { return true; } @@ -352,39 +279,6 @@ bool FOculusRiftHMD::NeedReAllocateViewportRenderTarget(const FViewport& Viewpor return false; } -#else // no direct rendering - -FGameFrame* FOculusRiftHMD::GetRenderFrameFromContext() const -{ - check(IsInRenderingThread()); - - check(RenderContext.IsValid()); - return static_cast(RenderContext->RenderFrame.Get()); -} - -void FOculusRiftHMD::FinishRenderingFrame_RenderThread(FRHICommandListImmediate& RHICmdList) -{ - check(IsInRenderingThread()); - - check(RenderContext.IsValid()); - auto RenderFrame = GetRenderFrameFromContext(); - check(RenderFrame); - auto RenderFrameSettings = RenderFrame->GetSettings(); - check(RenderFrameSettings); - if (RenderContext->bFrameBegun) - { - if (RenderFrameSettings->Flags.bTimeWarp) - { - RHICmdList.BlockUntilGPUIdle(); - } - ovrHmd_EndFrameTiming(Hmd); - RenderContext->bFrameBegun = false; - RenderContext.Reset(); - } -} - -#endif // #ifdef OVR_SDK_RENDERING - static const char* FormatLatencyReading(char* buff, size_t size, float val) { if (val < 0.000001f) @@ -538,30 +432,36 @@ void FOculusRiftHMD::DrawDebug(UCanvas* Canvas) FString Str, StatusStr; // First row - Str = FString::Printf(TEXT("TimeWarp: %s"), (FrameSettings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); - Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); +// Str = FString::Printf(TEXT("TimeWarp: %s"), (FrameSettings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); +// Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); +// +// Y += RowHeight; + + //Str = FString::Printf(TEXT("VSync: %s"), (FrameSettings->Flags.bVSync) ? TEXT("ON") : TEXT("OFF")); + //Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + //Y += RowHeight; - Y += RowHeight; - Str = FString::Printf(TEXT("VSync: %s"), (FrameSettings->Flags.bVSync) ? TEXT("ON") : TEXT("OFF")); - Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); - - Y += RowHeight; Str = FString::Printf(TEXT("Upd on GT/RT: %s / %s"), (!FrameSettings->Flags.bDoNotUpdateOnGT) ? TEXT("ON") : TEXT("OFF"), (FrameSettings->Flags.bUpdateOnRT) ? TEXT("ON") : TEXT("OFF")); Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); Y += RowHeight; - static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); - int finFr = CFinishFrameVar->GetInt(); - Str = FString::Printf(TEXT("FinFr: %s"), (finFr || FrameSettings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); - Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); - Y += RowHeight; - int32 sp = (int32)GetActualScreenPercentage(); - Str = FString::Printf(TEXT("SP: %d"), sp); - Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); +// static IConsoleVariable* CFinishFrameVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.FinishCurrentFrame")); +// int finFr = CFinishFrameVar->GetInt(); +// Str = FString::Printf(TEXT("FinFr: %s"), (finFr || FrameSettings->Flags.bTimeWarp) ? TEXT("ON") : TEXT("OFF")); +// Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); +// +// Y += RowHeight; + Str = FString::Printf(TEXT("PD: %.2f"), FrameSettings->PixelDensity); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); Y += RowHeight; + + Str = FString::Printf(TEXT("QueueAhead: %s"), (FrameSettings->bQueueAheadEnabled) ? TEXT("ON") : TEXT("OFF")); + Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); + Y += RowHeight; + Str = FString::Printf(TEXT("FOV V/H: %.2f / %.2f deg"), FMath::RadiansToDegrees(FrameSettings->VFOVInRadians), FMath::RadiansToDegrees(FrameSettings->HFOVInRadians)); Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); @@ -617,11 +517,11 @@ void FOculusRiftHMD::DrawDebug(UCanvas* Canvas) Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); Y += RowHeight; - StatusStr = ((FrameSettings->SupportedDistortionCaps & ovrDistortionCap_Overdrive) != 0) ? - ((FrameSettings->Flags.bOverdrive) ? TEXT("ON") : TEXT("OFF")) : TEXT("UNSUP"); - Str = FString::Printf(TEXT("Overdrive: %s"), *StatusStr); - Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); - Y += RowHeight; +// StatusStr = ((FrameSettings->SupportedDistortionCaps & ovrDistortionCap_Overdrive) != 0) ? +// ((FrameSettings->Flags.bOverdrive) ? TEXT("ON") : TEXT("OFF")) : TEXT("UNSUP"); +// Str = FString::Printf(TEXT("Overdrive: %s"), *StatusStr); +// Canvas->Canvas->DrawShadowedString(X, Y, *Str, Font, TextColor); +// Y += RowHeight; } //TODO: Where can I get context!? @@ -655,87 +555,49 @@ void FOculusRiftHMD::UpdateViewport(bool bUseSeparateRenderTarget, const FViewpo FRHIViewport* const ViewportRHI = InViewport.GetViewportRHI().GetReference(); - if (!IsStereoEnabled()) + if (!Settings->IsStereoEnabled()) { if ((!bUseSeparateRenderTarget || GIsEditor) && ViewportRHI) { ViewportRHI->SetCustomPresent(nullptr); } -#if PLATFORM_WINDOWS - if (OSWindowHandle) + // Restore AutoResizeViewport mode for the window + if (ViewportWidget && !IsFullscreenAllowed() && Settings->MirrorWindowSize.X != 0 && Settings->MirrorWindowSize.Y != 0) { - ovrHmd_AttachToWindow(Hmd, NULL, NULL, NULL); - OSWindowHandle = nullptr; - - // Restore AutoResizeViewport mode for the window - if (ViewportWidget && !IsFullscreenAllowed() && Settings->MirrorWindowSize.X != 0 && Settings->MirrorWindowSize.Y != 0) + FWidgetPath WidgetPath; + TSharedRef Widget = ViewportWidget->AsShared(); + TSharedPtr Window = FSlateApplication::Get().FindWidgetWindow(Widget, WidgetPath); + if (Window.IsValid()) { - FWidgetPath WidgetPath; - TSharedRef Widget = ViewportWidget->AsShared(); - TSharedPtr Window = FSlateApplication::Get().FindWidgetWindow(Widget, WidgetPath); - if (Window.IsValid()) - { - Window->SetViewportSizeDrivenByWindow(true); - } + Window->SetViewportSizeDrivenByWindow(true); } } -#endif return; } -#if PLATFORM_WINDOWS - if (ViewportRHI) - { - void *wnd = ViewportRHI->GetNativeWindow(); - if (wnd && wnd != OSWindowHandle) - { - OSWindowHandle = wnd; - if (!ovrHmd_AttachToWindow(Hmd, OSWindowHandle, NULL, NULL)) - { - UE_LOG(LogHMD, Error, TEXT("ovrHmd_AttachToWindow failed.")); - } - } - } -#endif - - if (!bUseSeparateRenderTarget) - return; - FGameFrame* CurrentFrame = GetFrame(); + if (!bUseSeparateRenderTarget || !CurrentFrame) + return; + check(CurrentFrame); CurrentFrame->ViewportSize = InViewport.GetSizeXY(); - //CurrentFrame->ViewportRHI = ViewportRHI; -#ifdef OVR_SDK_RENDERING + check(pCustomPresent); - check(GetActiveRHIBridgeImpl()); - - GetActiveRHIBridgeImpl()->UpdateViewport(InViewport, ViewportRHI, CurrentFrame); -#endif // #ifdef OVR_SDK_RENDERING + pCustomPresent->UpdateViewport(InViewport, ViewportRHI, CurrentFrame); } -#ifdef OVR_SDK_RENDERING void FOculusRiftHMD::ShutdownRendering() { check(IsInRenderingThread()); -#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) - if (pD3D11Bridge) + if (pCustomPresent.GetReference()) { - pD3D11Bridge->Shutdown(); - pD3D11Bridge = NULL; + pCustomPresent->Shutdown(); + pCustomPresent = NULL; } -#endif -#if defined(OVR_GL) - if (pOGLBridge) - { - pOGLBridge->Shutdown(); - pOGLBridge = NULL; - } -#endif } - void FCustomPresent::SetRenderContext(FHMDViewExtension* InRenderContext) { if (InRenderContext) @@ -757,414 +619,23 @@ void FCustomPresent::UpdateViewport(const FViewport& Viewport, FRHIViewport* Vie ViewportRHI->SetCustomPresent(this); } -#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) -// FIXME: in Direct mode we must hold DXGIAdapter to avoid a crash at the exit because of shimming. -// It does cause D3D resources to leak. But it is better than a crash, right? -// Should be already fixed in the next Oculus SDK version. -static void AddRefAdapter() +void FCustomPresent::MarkTexturesInvalid() { - if (FOculusRiftHMD::bDirectModeHack) + if (IsInRenderingThread()) { - ID3D11Device* D3DDevice = (ID3D11Device*)RHIGetNativeDevice(); - if (D3DDevice) - { - D3DDevice->AddRef(); - IDXGIDevice * pDXGIDevice; - HRESULT hr = D3DDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice); - if (hr == S_OK) + bNeedReAllocateTextureSet = bNeedReAllocateMirrorTexture = true; + } + else if (IsInGameThread()) + { + ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(FlushRendering, + FCustomPresent*, Bridge, this, { - IDXGIAdapter* pAdapter; - pDXGIDevice->GetAdapter(&pAdapter); - if (pAdapter) - { - UE_LOG(LogHMD, Log, TEXT("++ Addrefing Adapter %p!"), pAdapter); - pAdapter->AddRef(); - } - } - } - } -} - -FOculusRiftHMD::D3D11Bridge::D3D11Bridge() - : FCustomPresent() -{ - FMemory::Memset(Cfg, 0); - FMemory::Memset(EyeTexture, 0); - - if (FOculusRiftHMD::bDirectModeHack) - { - // FIXME: in Direct mode we must hold DXGIAdapter to avoid a crash at the exit because of shimming. - // It does cause D3D resources to leak. But it is better than a crash, right? - // Should be already fixed in the next Oculus SDK version. - ENQUEUE_UNIQUE_RENDER_COMMAND(AdapterAddRef, - { - AddRefAdapter(); - }); - } -} - -void FOculusRiftHMD::D3D11Bridge::Shutdown() -{ - check(IsInRenderingThread()); - if (FOculusRiftHMD::bDirectModeHack) - { - AddRefAdapter(); - } - Reset(); -} - -void FOculusRiftHMD::D3D11Bridge::BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) -{ - check(IsInRenderingThread()); - - SetRenderContext(&InRenderContext); - - ID3D11Device* D3DDevice = (ID3D11Device*)RHIGetNativeDevice(); - ID3D11DeviceContext* D3DDeviceContext = nullptr; - if (D3DDevice) - { - D3DDevice->GetImmediateContext(&D3DDeviceContext); - } - if (!bInitialized || D3DDevice != Cfg.D3D11.pDevice || D3DDeviceContext != Cfg.D3D11.pDeviceContext) - { - if (D3DDevice && D3DDevice != Cfg.D3D11.pDevice) - { - if (FOculusRiftHMD::bDirectModeHack) - { - // FIXME: in Direct mode we must hold D3DDevice to avoid a crash at the exit because of shimming. - // It does cause D3D resources to leak. But it is better than a crash, right? - // Should be already fixed in the next Oculus SDK version. - D3DDevice->AddRef(); - } - } - - Cfg.D3D11.Header.API = ovrRenderAPI_D3D11; - Cfg.D3D11.Header.Multisample = 0; - // Note, neither Device nor Context are AddRef-ed here. Not sure, if we need to. - Cfg.D3D11.pDevice = D3DDevice; - Cfg.D3D11.pDeviceContext = D3DDeviceContext; - bNeedReinitRendererAPI = true; - bInitialized = true; - } - - if (bInitialized) - { - ID3D11RenderTargetView* const pD3DBBRT = (ID3D11RenderTargetView*)ViewportRHI->GetNativeBackBufferRT(); - IDXGISwapChain* const pD3DSC = (IDXGISwapChain*)ViewportRHI->GetNativeSwapChain(); - check(IsValidRef(RT)); - ID3D11Texture2D* const pD3DRT = (ID3D11Texture2D*)RT->GetNativeResource(); - ID3D11ShaderResourceView* const pD3DSRV = (ID3D11ShaderResourceView*)RT->GetNativeShaderResourceView(); - const uint32 RTSizeX = RT->GetSizeX(); - const uint32 RTSizeY = RT->GetSizeY(); - - FGameFrame* CurrentFrame = GetRenderFrame(); - check(CurrentFrame); - FSettings* FrameSettings = CurrentFrame->GetSettings(); - check(FrameSettings); - - if (Cfg.D3D11.pBackBufferRT != pD3DBBRT || - Cfg.D3D11.pSwapChain != pD3DSC || - Cfg.D3D11.Header.BackBufferSize.w != CurrentFrame->ViewportSize.X || - Cfg.D3D11.Header.BackBufferSize.h != CurrentFrame->ViewportSize.Y) - { - // Note, neither BackBufferRT nor SwapChain are AddRef-ed here. Not sure, if we need to. - // If yes, then them should be released in ReleaseBackBuffer(). - Cfg.D3D11.pBackBufferRT = pD3DBBRT; - Cfg.D3D11.pSwapChain = pD3DSC; - Cfg.D3D11.Header.BackBufferSize.w = CurrentFrame->ViewportSize.X; - Cfg.D3D11.Header.BackBufferSize.h = CurrentFrame->ViewportSize.Y; - bNeedReinitRendererAPI = true; - } - - if (EyeTexture[0].D3D11.pTexture != pD3DRT || EyeTexture[0].D3D11.pSRView != pD3DSRV || - EyeTexture[0].D3D11.Header.TextureSize.w != RTSizeX || EyeTexture[0].D3D11.Header.TextureSize.h != RTSizeY || - ((OVR::Recti)EyeTexture[0].D3D11.Header.RenderViewport) != ToOVRRecti(FrameSettings->EyeRenderViewport[0])) - { - for (int eye = 0; eye < 2; ++eye) - { - ovrD3D11TextureData oldEye = EyeTexture[eye].D3D11; - EyeTexture[eye].D3D11.Header.API = ovrRenderAPI_D3D11; - EyeTexture[eye].D3D11.Header.TextureSize = OVR::Sizei(RTSizeX, RTSizeY); - EyeTexture[eye].D3D11.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[eye]); - EyeTexture[eye].D3D11.pTexture = pD3DRT; - EyeTexture[eye].D3D11.pSRView = pD3DSRV; - if (EyeTexture[eye].D3D11.pTexture) - { - EyeTexture[eye].D3D11.pTexture->AddRef(); - } - if (EyeTexture[eye].D3D11.pSRView) - { - EyeTexture[eye].D3D11.pSRView->AddRef(); - } - - if (oldEye.pTexture) - { - oldEye.pTexture->Release(); - } - if (oldEye.pSRView) - { - oldEye.pSRView->Release(); - } - } - } - - if (bNeedReinitRendererAPI) - { - check(Cfg.D3D11.pSwapChain); // make sure Config is initialized - - if (!ovrHmd_ConfigureRendering(RenderContext->Hmd, &Cfg.Config, FrameSettings->DistortionCaps, - FrameSettings->EyeFov, FrameSettings->EyeRenderDesc)) - { - UE_LOG(LogHMD, Warning, TEXT("D3D11 ovrHmd_ConfigureRenderAPI failed.")); - return; - } - bNeedReinitRendererAPI = false; - } - } -} - -void FOculusRiftHMD::D3D11Bridge::FinishRendering() -{ - check(IsInRenderingThread()); - - check(RenderContext.IsValid()); - - if (RenderContext->bFrameBegun) - { - // Finish the frame and let OVR do buffer swap (Present) and flush/sync. - const ovrTexture eyeTextures[2] = { EyeTexture[0].Texture, EyeTexture[1].Texture }; - ovrHmd_EndFrame(RenderContext->Hmd, RenderContext->CurEyeRenderPose, eyeTextures); // This function will present - } - else - { - UE_LOG(LogHMD, Warning, TEXT("Skipping frame: FinishRendering called with no corresponding BeginRendering (was BackBuffer re-allocated?)")); - } - RenderContext->bFrameBegun = false; - SetRenderContext(nullptr); -} - -void FOculusRiftHMD::D3D11Bridge::Reset_RenderThread() -{ - Cfg.D3D11.pDevice = nullptr; - Cfg.D3D11.pDeviceContext = nullptr; - - for (int eye = 0; eye < 2; ++eye) - { - if (EyeTexture[eye].D3D11.pTexture) - { - EyeTexture[eye].D3D11.pTexture->Release(); - EyeTexture[eye].D3D11.pTexture = nullptr; - } - if (EyeTexture[eye].D3D11.pSRView) - { - EyeTexture[eye].D3D11.pSRView->Release(); - EyeTexture[eye].D3D11.pSRView = nullptr; - } - } - - Cfg.D3D11.pBackBufferRT = nullptr; - Cfg.D3D11.pSwapChain = nullptr; - - bNeedReinitRendererAPI = false; - - if (RenderContext.IsValid()) - { - RenderContext->bFrameBegun = false; - SetRenderContext(nullptr); - } -} - -void FOculusRiftHMD::D3D11Bridge::Reset() -{ - if (IsInGameThread()) - { - ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ResetD3D, - FOculusRiftHMD::D3D11Bridge*, Bridge, this, - { - Bridge->Reset_RenderThread(); - }); + Bridge->MarkTexturesInvalid(); + }); // Wait for all resources to be released FlushRenderingCommands(); } - else - { - Reset_RenderThread(); - } - - bInitialized = false; } -void FOculusRiftHMD::D3D11Bridge::OnBackBufferResize() -{ - Cfg.D3D11.pBackBufferRT = nullptr; - Cfg.D3D11.pSwapChain = nullptr; - - bNeedReinitRendererAPI = true; - - // if we are in the middle of rendering: prevent from calling EndFrame - if (RenderContext.IsValid()) - { - RenderContext->bFrameBegun = false; - } -} - -bool FOculusRiftHMD::D3D11Bridge::Present(int SyncInterval) -{ - check(IsInRenderingThread()); - - if (!RenderContext.IsValid()) - { - return true; // use regular Present; this frame is not ready yet - } - - FinishRendering(); - - return false; // indicates that we are presenting here, UE shouldn't do Present. -} -#endif // #if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) - -////////////////////////////////////////////////////////////////////////// -#if defined(OVR_GL) -FOculusRiftHMD::OGLBridge::OGLBridge() : - FCustomPresent() -{ - FMemory::Memset(Cfg, 0); - FMemory::Memset(EyeTexture, 0); -} - -void FOculusRiftHMD::OGLBridge::BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) -{ - SetRenderContext(&InRenderContext); - - if (!bInitialized) - { - Cfg.OGL.Header.API = ovrRenderAPI_OpenGL; - Cfg.OGL.Header.Multisample = 1; - bNeedReinitRendererAPI = true; - bInitialized = true; - } - if (bInitialized) - { - FGameFrame* CurrentFrame = GetRenderFrame(); - check(CurrentFrame); - FSettings* FrameSettings = CurrentFrame->GetSettings(); - check(FrameSettings); - - bool bWinChanged = false; -#if PLATFORM_WINDOWS - HWND Window = *(HWND*)ViewportRHI->GetNativeWindow(); - bWinChanged = (Cfg.OGL.Window != Window); -#elif PLATFORM_MAC - //@TODO - //NSWindow -#elif PLATFORM_LINUX - //@TODO -#endif - if (bWinChanged || - Cfg.OGL.Header.BackBufferSize.w != CurrentFrame->ViewportSize.X || - Cfg.OGL.Header.BackBufferSize.h != CurrentFrame->ViewportSize.Y) - { - Cfg.OGL.Header.BackBufferSize = OVR::Sizei(CurrentFrame->ViewportSize.X, CurrentFrame->ViewportSize.Y); -#if PLATFORM_WINDOWS - Cfg.OGL.Window = Window; -#elif PLATFORM_MAC - //@TODO -#elif PLATFORM_LINUX - //@TODO - // Cfg.OGL.Disp = AddParams; //? - // Cfg.OGL.Win = hWnd; //? -#endif - bNeedReinitRendererAPI = true; - } - - check(IsValidRef(RT)); - const uint32 RTSizeX = RT->GetSizeX(); - const uint32 RTSizeY = RT->GetSizeY(); - GLuint RTTexId = *(GLuint*)RT->GetNativeResource(); - - if ((EyeTexture[0].OGL.TexId != RTTexId || - EyeTexture[0].OGL.Header.TextureSize.w != RTSizeX || EyeTexture[0].OGL.Header.TextureSize.h != RTSizeY)) - { - EyeTexture[0].OGL.Header.API = ovrRenderAPI_OpenGL; - EyeTexture[0].OGL.Header.TextureSize = OVR::Sizei(RTSizeX, RTSizeY); - EyeTexture[0].OGL.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[0]); - EyeTexture[0].OGL.TexId = RTTexId; - - // Right eye uses the same texture, but different rendering viewport. - EyeTexture[1] = EyeTexture[0]; - EyeTexture[1].OGL.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[1]); - } - if (bNeedReinitRendererAPI) - { - if (!ovrHmd_ConfigureRendering(RenderContext->Hmd, &Cfg.Config, FrameSettings->DistortionCaps, - FrameSettings->EyeFov, FrameSettings->EyeRenderDesc)) - { - UE_LOG(LogHMD, Warning, TEXT("OGL ovrHmd_ConfigureRenderAPI failed.")); - return; - } - bNeedReinitRendererAPI = false; - } - } -} - -void FOculusRiftHMD::OGLBridge::FinishRendering() -{ - check(IsInRenderingThread()); - - check(RenderContext.IsValid()); - - if (RenderContext->bFrameBegun) - { - // Finish the frame and let OVR do buffer swap (Present) and flush/sync. - const ovrTexture eyeTextures[2] = { EyeTexture[0].Texture, EyeTexture[1].Texture }; - ovrHmd_EndFrame(RenderContext->Hmd, RenderContext->CurEyeRenderPose, eyeTextures); // This function will present - RenderContext->bFrameBegun = false; - } - else - { - UE_LOG(LogHMD, Warning, TEXT("Skipping frame: FinishRendering called with no corresponding BeginRendering (was BackBuffer re-allocated?)")); - } - SetRenderContext(nullptr); -} - -void FOculusRiftHMD::OGLBridge::Reset() -{ - check(IsInRenderingThread()); - - EyeTexture[0].OGL.TexId = 0; - EyeTexture[1].OGL.TexId = 0; - - bNeedReinitRendererAPI = false; - bInitialized = false; - - if (RenderContext.IsValid()) - { - RenderContext->bFrameBegun = false; - SetRenderContext(nullptr); - } -} - -void FOculusRiftHMD::OGLBridge::OnBackBufferResize() -{ - bNeedReinitRendererAPI = true; - - // if we are in the middle of rendering: prevent from calling EndFrame - if (RenderContext.IsValid()) - { - RenderContext->bFrameBegun = false; - } -} - -bool FOculusRiftHMD::OGLBridge::Present(int SyncInterval) -{ - check(IsInRenderingThread()); - - FinishRendering(); - - return false; // indicates that we are presenting here, UE shouldn't do Present. -} -#endif // #if defined(OVR_GL) -#endif // OVR_SDK_RENDERING - - -#endif // OCULUS_RIFT_SUPPORTED_PLATFORMS \ No newline at end of file +#endif // OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //!PLATFORM_MAC diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRenderD3D.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRenderD3D.cpp new file mode 100644 index 000000000000..1face906aac8 --- /dev/null +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRenderD3D.cpp @@ -0,0 +1,597 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +// +#include "HMDPrivatePCH.h" +#include "OculusRiftHMD.h" + +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + +#if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + +#include "D3D11RHIPrivate.h" +#include "D3D11Util.h" + +#ifndef WINDOWS_PLATFORM_TYPES_GUARD +#include "AllowWindowsPlatformTypes.h" +#endif +#include "OVR_CAPI_D3D.h" + +#include "RendererPrivate.h" +#include "ScenePrivate.h" +#include "PostProcess/PostProcessHMD.h" +#include "ScreenRendering.h" + +#include "SlateBasics.h" + +class FD3D11Texture2DSet : public FD3D11Texture2D +{ +public: + FD3D11Texture2DSet( + class FD3D11DynamicRHI* InD3DRHI, + ID3D11Texture2D* InResource, + ID3D11ShaderResourceView* InShaderResourceView, + bool bInCreatedRTVsPerSlice, + int32 InRTVArraySize, + const TArray >& InRenderTargetViews, + TRefCountPtr* InDepthStencilViews, + uint32 InSizeX, + uint32 InSizeY, + uint32 InSizeZ, + uint32 InNumMips, + uint32 InNumSamples, + EPixelFormat InFormat, + bool bInCubemap, + uint32 InFlags, + bool bInPooled + ) + : FD3D11Texture2D( + InD3DRHI, + InResource, + InShaderResourceView, + bInCreatedRTVsPerSlice, + InRTVArraySize, + InRenderTargetViews, + InDepthStencilViews, + InSizeX, + InSizeY, + InSizeZ, + InNumMips, + InNumSamples, + InFormat, + bInCubemap, + InFlags, + bInPooled + ) + { + TextureSet = nullptr; + } + + void ReleaseResources(ovrHmd Hmd); + void SwitchToNextElement(); + void AddTexture(ID3D11Texture2D*, ID3D11ShaderResourceView*, TArray >* = nullptr); + + ovrSwapTextureSet* GetTextureSet() const { return TextureSet; } + + static FD3D11Texture2DSet* D3D11CreateTexture2DSet( + FD3D11DynamicRHI* InD3D11RHI, + ovrSwapTextureSet* InTextureSet, + const D3D11_TEXTURE2D_DESC& InDsDesc, + EPixelFormat InFormat, + uint32 InFlags + ); +protected: + void InitWithCurrentElement(); + + struct TextureElement + { + TRefCountPtr Texture; + TRefCountPtr SRV; + TArray > RTVs; + }; + TArray Textures; + + ovrSwapTextureSet* TextureSet; +}; + +void FD3D11Texture2DSet::AddTexture(ID3D11Texture2D* InTexture, ID3D11ShaderResourceView* InSRV, TArray >* InRTVs) +{ + TextureElement element; + element.Texture = InTexture; + element.SRV = InSRV; + if (InRTVs) + { + element.RTVs.Empty(InRTVs->Num()); + for (int32 i = 0; i < InRTVs->Num(); ++i) + { + element.RTVs.Add((*InRTVs)[i]); + } + } + Textures.Push(element); +} + +void FD3D11Texture2DSet::SwitchToNextElement() +{ + check(TextureSet); + check(TextureSet->TextureCount == Textures.Num()); + + TextureSet->CurrentIndex = (TextureSet->CurrentIndex + 1) % TextureSet->TextureCount; + InitWithCurrentElement(); +} + +void FD3D11Texture2DSet::InitWithCurrentElement() +{ + check(TextureSet); + check(TextureSet->TextureCount == Textures.Num()); + + Resource = Textures[TextureSet->CurrentIndex].Texture; + ShaderResourceView = Textures[TextureSet->CurrentIndex].SRV; + + RenderTargetViews.Empty(Textures[TextureSet->CurrentIndex].RTVs.Num()); + for (int32 i = 0; i < Textures[TextureSet->CurrentIndex].RTVs.Num(); ++i) + { + RenderTargetViews.Add(Textures[TextureSet->CurrentIndex].RTVs[i]); + } +} + +void FD3D11Texture2DSet::ReleaseResources(ovrHmd Hmd) +{ + if (TextureSet) + { + ovrHmd_DestroySwapTextureSet(Hmd, TextureSet); + TextureSet = nullptr; + } + Textures.Empty(0); +} + +FD3D11Texture2DSet* FD3D11Texture2DSet::D3D11CreateTexture2DSet( + FD3D11DynamicRHI* InD3D11RHI, + ovrSwapTextureSet* InTextureSet, + const D3D11_TEXTURE2D_DESC& InDsDesc, + EPixelFormat InFormat, + uint32 InFlags + ) +{ + check(InTextureSet); + + TArray > RenderTargetViews; + FD3D11Texture2DSet* NewTextureSet = new FD3D11Texture2DSet( + InD3D11RHI, + nullptr, + nullptr, + false, + 1, + RenderTargetViews, + /*DepthStencilViews=*/ NULL, + InDsDesc.Width, + InDsDesc.Height, + 0, + InDsDesc.MipLevels, + InDsDesc.SampleDesc.Count, + InFormat, + /*bInCubemap=*/ false, + InFlags, + /*bPooledTexture=*/ false + ); + + const uint32 TexCount = InTextureSet->TextureCount; + const bool bSRGB = (InFlags & TexCreate_SRGB) != 0; + + const DXGI_FORMAT PlatformResourceFormat = (DXGI_FORMAT)GPixelFormats[InFormat].PlatformFormat; + const DXGI_FORMAT PlatformShaderResourceFormat = FindShaderResourceDXGIFormat(PlatformResourceFormat, bSRGB); + const DXGI_FORMAT PlatformRenderTargetFormat = FindShaderResourceDXGIFormat(PlatformResourceFormat, bSRGB); + D3D11_RTV_DIMENSION RenderTargetViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + if (InDsDesc.SampleDesc.Count > 1) + { + RenderTargetViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } + for (uint32 i = 0; i < TexCount; ++i) + { + ovrD3D11Texture D3DTex; + D3DTex.Texture = InTextureSet->Textures[i]; + + TArray > RenderTargetViews; + if (InFlags & TexCreate_RenderTargetable) + { + // Create a render target view for each mip + for (uint32 MipIndex = 0; MipIndex < InDsDesc.MipLevels; MipIndex++) + { + check(!(InFlags & TexCreate_TargetArraySlicesIndependently)); // not supported + D3D11_RENDER_TARGET_VIEW_DESC RTVDesc; + FMemory::Memzero(&RTVDesc, sizeof(RTVDesc)); + RTVDesc.Format = PlatformRenderTargetFormat; + RTVDesc.ViewDimension = RenderTargetViewDimension; + RTVDesc.Texture2D.MipSlice = MipIndex; + + TRefCountPtr RenderTargetView; + VERIFYD3D11RESULT(InD3D11RHI->GetDevice()->CreateRenderTargetView(D3DTex.D3D11.pTexture, &RTVDesc, RenderTargetView.GetInitReference())); + RenderTargetViews.Add(RenderTargetView); + } + } + + TRefCountPtr ShaderResourceView = D3DTex.D3D11.pSRView; + + // Create a shader resource view for the texture. + if (!ShaderResourceView && (InFlags & TexCreate_ShaderResource)) + { + D3D11_SRV_DIMENSION ShaderResourceViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + SRVDesc.Format = PlatformShaderResourceFormat; + + SRVDesc.ViewDimension = ShaderResourceViewDimension; + SRVDesc.Texture2D.MostDetailedMip = 0; + SRVDesc.Texture2D.MipLevels = InDsDesc.MipLevels; + + VERIFYD3D11RESULT(InD3D11RHI->GetDevice()->CreateShaderResourceView(D3DTex.D3D11.pTexture, &SRVDesc, ShaderResourceView.GetInitReference())); + + check(IsValidRef(ShaderResourceView)); + } + + NewTextureSet->AddTexture(D3DTex.D3D11.pTexture, ShaderResourceView, &RenderTargetViews); + } + + NewTextureSet->TextureSet = InTextureSet; + NewTextureSet->InitWithCurrentElement(); + return NewTextureSet; +} + +static FD3D11Texture2D* D3D11CreateTexture2DAlias( + FD3D11DynamicRHI* InD3D11RHI, + ID3D11Texture2D* InResource, + ID3D11ShaderResourceView* InShaderResourceView, + uint32 InSizeX, + uint32 InSizeY, + uint32 InSizeZ, + uint32 InNumMips, + uint32 InNumSamples, + EPixelFormat InFormat, + uint32 InFlags) +{ + const bool bSRGB = (InFlags & TexCreate_SRGB) != 0; + + const DXGI_FORMAT PlatformResourceFormat = (DXGI_FORMAT)GPixelFormats[InFormat].PlatformFormat; + const DXGI_FORMAT PlatformShaderResourceFormat = FindShaderResourceDXGIFormat(PlatformResourceFormat, bSRGB); + const DXGI_FORMAT PlatformRenderTargetFormat = FindShaderResourceDXGIFormat(PlatformResourceFormat, bSRGB); + D3D11_RTV_DIMENSION RenderTargetViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + if (InNumSamples > 1) + { + RenderTargetViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } + + TArray > RenderTargetViews; + + if (InFlags & TexCreate_RenderTargetable) + { + // Create a render target view for each mip + for (uint32 MipIndex = 0; MipIndex < InNumMips; MipIndex++) + { + check(!(InFlags & TexCreate_TargetArraySlicesIndependently)); // not supported + D3D11_RENDER_TARGET_VIEW_DESC RTVDesc; + FMemory::Memzero(&RTVDesc, sizeof(RTVDesc)); + RTVDesc.Format = PlatformRenderTargetFormat; + RTVDesc.ViewDimension = RenderTargetViewDimension; + RTVDesc.Texture2D.MipSlice = MipIndex; + + TRefCountPtr RenderTargetView; + VERIFYD3D11RESULT(InD3D11RHI->GetDevice()->CreateRenderTargetView(InResource, &RTVDesc, RenderTargetView.GetInitReference())); + RenderTargetViews.Add(RenderTargetView); + } + } + + TRefCountPtr ShaderResourceView; + + // Create a shader resource view for the texture. + if (!InShaderResourceView && (InFlags & TexCreate_ShaderResource)) + { + D3D11_SRV_DIMENSION ShaderResourceViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + SRVDesc.Format = PlatformShaderResourceFormat; + + SRVDesc.ViewDimension = ShaderResourceViewDimension; + SRVDesc.Texture2D.MostDetailedMip = 0; + SRVDesc.Texture2D.MipLevels = InNumMips; + + VERIFYD3D11RESULT(InD3D11RHI->GetDevice()->CreateShaderResourceView(InResource, &SRVDesc, ShaderResourceView.GetInitReference())); + + check(IsValidRef(ShaderResourceView)); + } + else + { + ShaderResourceView = InShaderResourceView; + } + + FD3D11Texture2D* NewTexture = new FD3D11Texture2D( + InD3D11RHI, + InResource, + ShaderResourceView, + false, + 1, + RenderTargetViews, + /*DepthStencilViews=*/ NULL, + InSizeX, + InSizeY, + InSizeZ, + InNumMips, + InNumSamples, + InFormat, + /*bInCubemap=*/ false, + InFlags, + /*bPooledTexture=*/ false + ); + + return NewTexture; +} + +////////////////////////////////////////////////////////////////////////// +FOculusRiftHMD::D3D11Bridge::D3D11Bridge(ovrHmd Hmd) + : FCustomPresent() +{ + MirrorTexture = nullptr; + Init(Hmd); +} + +void FOculusRiftHMD::D3D11Bridge::SetHmd(ovrHmd InHmd) +{ + if (InHmd != Hmd) + { + Reset(); + Init(InHmd); + bNeedReAllocateTextureSet = true; + bNeedReAllocateMirrorTexture = true; + } +} + +void FOculusRiftHMD::D3D11Bridge::Init(ovrHmd InHmd) +{ + Hmd = InHmd; + bInitialized = true; +} + +//FTexture2DRHIRef FOculusRiftHMD::D3D11Bridge::AllocateRenderTargetTexture(int32 TexWidth, int32 TexHeight, EPixelFormat Format) + +bool FOculusRiftHMD::D3D11Bridge::AllocateRenderTargetTexture(uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, uint32 TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples) +{ + check(SizeX != 0 && SizeY != 0); + + if (!ColorTextureSet || (ColorTextureSet->GetSizeX() != SizeX || ColorTextureSet->GetSizeY() != SizeY || ColorTextureSet->GetFormat() != Format)) + { + bNeedReAllocateTextureSet = true; + } + + if (Hmd && bNeedReAllocateTextureSet) + { + auto D3D11RHI = static_cast(GDynamicRHI); + if (ColorTextureSet) + { + ColorTextureSet->ReleaseResources(Hmd); + ColorTextureSet = nullptr; + } + ID3D11Device* D3DDevice = D3D11RHI->GetDevice(); + + const DXGI_FORMAT PlatformResourceFormat = (DXGI_FORMAT)GPixelFormats[Format].PlatformFormat; + + D3D11_TEXTURE2D_DESC dsDesc; + dsDesc.Width = SizeX; + dsDesc.Height = SizeY; + dsDesc.MipLevels = 1; + dsDesc.ArraySize = 1; + dsDesc.Format = PlatformResourceFormat; //DXGI_FORMAT_B8G8R8A8_UNORM; + dsDesc.SampleDesc.Count = 1; + dsDesc.SampleDesc.Quality = 0; + dsDesc.Usage = D3D11_USAGE_DEFAULT; + dsDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + dsDesc.CPUAccessFlags = 0; + dsDesc.MiscFlags = 0; + + ovrSwapTextureSet* textureSet; + ovrHmd_CreateSwapTextureSetD3D11(Hmd, D3DDevice, &dsDesc, &textureSet); + if (!textureSet) + { + UE_LOG(LogHMD, Error, TEXT("Can't create swap texture set (size %d x %d)"), SizeX, SizeY); + return false; + } + bNeedReAllocateTextureSet = false; + bNeedReAllocateMirrorTexture = true; + UE_LOG(LogHMD, Log, TEXT("Allocated a new swap texture set (size %d x %d)"), SizeX, SizeY); + + ColorTextureSet = FD3D11Texture2DSet::D3D11CreateTexture2DSet( + D3D11RHI, + textureSet, + dsDesc, + EPixelFormat(Format), + TexCreate_RenderTargetable | TexCreate_ShaderResource + ); + } + OutTargetableTexture = ColorTextureSet->GetTexture2D(); + OutShaderResourceTexture = ColorTextureSet->GetTexture2D(); + return true; +} + +void FOculusRiftHMD::D3D11Bridge::BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) +{ + check(IsInRenderingThread()); + + SetRenderContext(&InRenderContext); + + FGameFrame* CurrentFrame = GetRenderFrame(); + check(CurrentFrame); + FSettings* FrameSettings = CurrentFrame->GetSettings(); + check(FrameSettings); + + const uint32 RTSizeX = RT->GetSizeX(); + const uint32 RTSizeY = RT->GetSizeY(); + + FIntPoint ActualMirrorWindowSize(0, 0); + if (RenderContext->Hmd) + { + ActualMirrorWindowSize.X = (FrameSettings->MirrorWindowSize.X == 0) ? RenderContext->Hmd->Resolution.w : FrameSettings->MirrorWindowSize.X; + ActualMirrorWindowSize.Y = (FrameSettings->MirrorWindowSize.Y == 0) ? RenderContext->Hmd->Resolution.h : FrameSettings->MirrorWindowSize.Y; + } + // detect if mirror texture needs to be re-allocated or freed + if (Hmd && MirrorTextureRHI && (bNeedReAllocateMirrorTexture || Hmd != RenderContext->Hmd || + (FrameSettings->Flags.bMirrorToWindow && ( + FrameSettings->MirrorWindowMode != FSettings::eMirrorWindow_Distorted || + ActualMirrorWindowSize != FIntPoint(MirrorTextureRHI->GetSizeX(), MirrorTextureRHI->GetSizeY()))) || + !FrameSettings->Flags.bMirrorToWindow )) + { + check(MirrorTexture); + ovrHmd_DestroyMirrorTexture(Hmd, MirrorTexture); + MirrorTexture = nullptr; + MirrorTextureRHI = nullptr; + bNeedReAllocateMirrorTexture = false; + } + + // need to allocate a mirror texture? + if (FrameSettings->Flags.bMirrorToWindow && FrameSettings->MirrorWindowMode == FSettings::eMirrorWindow_Distorted && !MirrorTextureRHI && + ActualMirrorWindowSize.X != 0 && ActualMirrorWindowSize.Y != 0) + { + D3D11_TEXTURE2D_DESC dsDesc; + dsDesc.Width = ActualMirrorWindowSize.X; + dsDesc.Height = ActualMirrorWindowSize.Y; + dsDesc.MipLevels = 1; + dsDesc.ArraySize = 1; + dsDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + dsDesc.SampleDesc.Count = 1; + dsDesc.SampleDesc.Quality = 0; + dsDesc.Usage = D3D11_USAGE_DEFAULT; + dsDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + dsDesc.CPUAccessFlags = 0; + dsDesc.MiscFlags = 0; + + ID3D11Device* D3DDevice = (ID3D11Device*)RHIGetNativeDevice(); + + ovrHmd_CreateMirrorTextureD3D11(Hmd, D3DDevice, &dsDesc, &MirrorTexture); + if (!MirrorTexture) + { + UE_LOG(LogHMD, Error, TEXT("Can't create a mirror texture")); + return; + } + UE_LOG(LogHMD, Log, TEXT("Allocated a new mirror texture (size %d x %d)"), ActualMirrorWindowSize.X, ActualMirrorWindowSize.Y); + ovrD3D11Texture D3DMirrorTexture; + D3DMirrorTexture.Texture = *MirrorTexture; + MirrorTextureRHI = D3D11CreateTexture2DAlias( + static_cast(GDynamicRHI), + D3DMirrorTexture.D3D11.pTexture, + D3DMirrorTexture.D3D11.pSRView, + dsDesc.Width, + dsDesc.Height, + 0, + dsDesc.MipLevels, + /*ActualMSAACount=*/ 1, + (EPixelFormat)PF_B8G8R8A8, + TexCreate_RenderTargetable); + bNeedReAllocateMirrorTexture = false; + } +} + +void FOculusRiftHMD::D3D11Bridge::FinishRendering() +{ + check(IsInRenderingThread()); + + check(RenderContext.IsValid()); + + if (RenderContext->bFrameBegun && ColorTextureSet) + { + if (!ColorTextureSet) + { + UE_LOG(LogHMD, Warning, TEXT("Skipping frame: TextureSet is null ?")); + } + else + { + // Finish the frame and let OVR do buffer swap (Present) and flush/sync. + FSettings* FrameSettings = RenderContext->GetFrameSettings(); + + check(ColorTextureSet->GetTextureSet()); + FrameSettings->EyeLayer.EyeFov.ColorTexture[0] = ColorTextureSet->GetTextureSet(); + FrameSettings->EyeLayer.EyeFov.ColorTexture[1] = ColorTextureSet->GetTextureSet(); + + ovrLayerHeader* LayerList[1]; + LayerList[0] = &FrameSettings->EyeLayer.EyeFov.Header; + + // Set up positional data. + ovrViewScaleDesc viewScaleDesc; + viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; + viewScaleDesc.HmdToEyeViewOffset[0] = FrameSettings->EyeRenderDesc[0].HmdToEyeViewOffset; + viewScaleDesc.HmdToEyeViewOffset[1] = FrameSettings->EyeRenderDesc[1].HmdToEyeViewOffset; + + ovrHmd_SubmitFrame(RenderContext->Hmd, RenderContext->RenderFrame->FrameNumber, &viewScaleDesc, LayerList, 1); + + ColorTextureSet->SwitchToNextElement(); + } + } + else + { + UE_LOG(LogHMD, Warning, TEXT("Skipping frame: FinishRendering called with no corresponding BeginRendering (was BackBuffer re-allocated?)")); + } + RenderContext->bFrameBegun = false; + SetRenderContext(nullptr); +} + +void FOculusRiftHMD::D3D11Bridge::Reset_RenderThread() +{ + if (MirrorTexture) + { + ovrHmd_DestroyMirrorTexture(Hmd, MirrorTexture); + MirrorTextureRHI = nullptr; + MirrorTexture = nullptr; + } + if (ColorTextureSet) + { + ColorTextureSet->ReleaseResources(Hmd); + ColorTextureSet = nullptr; + } + Hmd = nullptr; + + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + SetRenderContext(nullptr); + } +} + +void FOculusRiftHMD::D3D11Bridge::Reset() +{ + if (IsInGameThread()) + { + ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(ResetD3D, + FOculusRiftHMD::D3D11Bridge*, Bridge, this, + { + Bridge->Reset_RenderThread(); + }); + // Wait for all resources to be released + FlushRenderingCommands(); + } + else + { + Reset_RenderThread(); + } + + bInitialized = false; +} + +void FOculusRiftHMD::D3D11Bridge::OnBackBufferResize() +{ + // if we are in the middle of rendering: prevent from calling EndFrame + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + } +} + +bool FOculusRiftHMD::D3D11Bridge::Present(int32& SyncInterval) +{ + check(IsInRenderingThread()); + + if (!RenderContext.IsValid()) + { + return true; // use regular Present; this frame is not ready yet + } + + SyncInterval = 0; // turn off VSync for the 'normal Present'. + bool bHostPresent = RenderContext->GetFrameSettings()->Flags.bMirrorToWindow; + + FinishRendering(); + return bHostPresent; +} + +#endif // #if defined(OVR_D3D_VERSION) && (OVR_D3D_VERSION == 11) + +#endif // OCULUS_RIFT_SUPPORTED_PLATFORMS \ No newline at end of file diff --git a/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRenderGL.cpp b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRenderGL.cpp new file mode 100644 index 000000000000..0a95774aa8d0 --- /dev/null +++ b/Engine/Plugins/Runtime/OculusRift/Source/OculusRift/Private/OculusRiftRenderGL.cpp @@ -0,0 +1,161 @@ +// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. +// +#include "HMDPrivatePCH.h" +#include "OculusRiftHMD.h" + +#if !PLATFORM_MAC +#if OCULUS_RIFT_SUPPORTED_PLATFORMS + +#if defined(OVR_GL) +#include "OVR_CAPI_GL.h" + +#include "RendererPrivate.h" +#include "ScenePrivate.h" +#include "PostProcess/PostProcessHMD.h" +#include "ScreenRendering.h" + +#include "SlateBasics.h" + +////////////////////////////////////////////////////////////////////////// +FOculusRiftHMD::OGLBridge::OGLBridge() : + FCustomPresent() +{ + FMemory::Memset(Cfg, 0); + FMemory::Memset(EyeTexture, 0); +} + +void FOculusRiftHMD::OGLBridge::BeginRendering(FHMDViewExtension& InRenderContext, const FTexture2DRHIRef& RT) +{ + SetRenderContext(&InRenderContext); + + if (!bInitialized) + { + Cfg.OGL.Header.API = ovrRenderAPI_OpenGL; + bNeedReinitRendererAPI = true; + bInitialized = true; + } + if (bInitialized) + { + FGameFrame* CurrentFrame = GetRenderFrame(); + check(CurrentFrame); + FSettings* FrameSettings = CurrentFrame->GetSettings(); + check(FrameSettings); + + bool bWinChanged = false; +#if PLATFORM_WINDOWS + HWND Window = *(HWND*)ViewportRHI->GetNativeWindow(); + bWinChanged = (Cfg.OGL.Window != Window); +#elif PLATFORM_MAC + //@TODO + //NSWindow +#elif PLATFORM_LINUX + //@TODO +#endif + if (bWinChanged || + Cfg.OGL.Header.BackBufferSize.w != CurrentFrame->ViewportSize.X || + Cfg.OGL.Header.BackBufferSize.h != CurrentFrame->ViewportSize.Y) + { + Cfg.OGL.Header.BackBufferSize = OVR::Sizei(CurrentFrame->ViewportSize.X, CurrentFrame->ViewportSize.Y); +#if PLATFORM_WINDOWS + Cfg.OGL.Window = Window; +#elif PLATFORM_MAC + //@TODO +#elif PLATFORM_LINUX + //@TODO + // Cfg.OGL.Disp = AddParams; //? + // Cfg.OGL.Win = hWnd; //? +#endif + bNeedReinitRendererAPI = true; + } + + check(IsValidRef(RT)); + const uint32 RTSizeX = RT->GetSizeX(); + const uint32 RTSizeY = RT->GetSizeY(); + GLuint RTTexId = *(GLuint*)RT->GetNativeResource(); + + if ((EyeTexture[0].OGL.TexId != RTTexId || + EyeTexture[0].OGL.Header.TextureSize.w != RTSizeX || EyeTexture[0].OGL.Header.TextureSize.h != RTSizeY)) + { + EyeTexture[0].OGL.Header.API = ovrRenderAPI_OpenGL; + EyeTexture[0].OGL.Header.TextureSize = OVR::Sizei(RTSizeX, RTSizeY); + EyeTexture[0].OGL.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[0]); + EyeTexture[0].OGL.TexId = RTTexId; + + // Right eye uses the same texture, but different rendering viewport. + EyeTexture[1] = EyeTexture[0]; + EyeTexture[1].OGL.Header.RenderViewport = ToOVRRecti(FrameSettings->EyeRenderViewport[1]); + } + if (bNeedReinitRendererAPI) + { + if (!ovrHmd_ConfigureRendering(RenderContext->Hmd, &Cfg.Config, FrameSettings->DistortionCaps, + FrameSettings->EyeFov, FrameSettings->EyeRenderDesc)) + { + UE_LOG(LogHMD, Warning, TEXT("OGL ovrHmd_ConfigureRenderAPI failed.")); + return; + } + bNeedReinitRendererAPI = false; + } + } +} + +void FOculusRiftHMD::OGLBridge::FinishRendering() +{ + check(IsInRenderingThread()); + + check(RenderContext.IsValid()); + FViewExtension* const RenderContext = GetRenderContext(); + + if (RenderContext->bFrameBegun) + { + // Finish the frame and let OVR do buffer swap (Present) and flush/sync. + const ovrTexture eyeTextures[2] = { EyeTexture[0].Texture, EyeTexture[1].Texture }; + ovrHmd_SubmitFrameLegacy(RenderContext->Hmd, RenderContext->RenderFrame->FrameNumber, RenderContext->CurEyeRenderPose, eyeTextures); // This function will present + RenderContext->bFrameBegun = false; + } + else + { + UE_LOG(LogHMD, Warning, TEXT("Skipping frame: FinishRendering called with no corresponding BeginRendering (was BackBuffer re-allocated?)")); + } + SetRenderContext(nullptr); +} + +void FOculusRiftHMD::OGLBridge::Reset() +{ + check(IsInRenderingThread()); + + EyeTexture[0].OGL.TexId = 0; + EyeTexture[1].OGL.TexId = 0; + + bNeedReinitRendererAPI = false; + bInitialized = false; + + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + SetRenderContext(nullptr); + } +} + +void FOculusRiftHMD::OGLBridge::OnBackBufferResize() +{ + bNeedReinitRendererAPI = true; + + // if we are in the middle of rendering: prevent from calling EndFrame + if (RenderContext.IsValid()) + { + RenderContext->bFrameBegun = false; + } +} + +bool FOculusRiftHMD::OGLBridge::Present(int SyncInterval) +{ + check(IsInRenderingThread()); + + FinishRendering(); + + return false; // indicates that we are presenting here, UE shouldn't do Present. +} +#endif // #if defined(OVR_GL) + +#endif // OCULUS_RIFT_SUPPORTED_PLATFORMS +#endif //#if !PLATFORM_MAC diff --git a/Engine/Source/Runtime/Engine/Private/Slate/SceneViewport.cpp b/Engine/Source/Runtime/Engine/Private/Slate/SceneViewport.cpp index d5b9a56f5db5..8fe4da98f66e 100644 --- a/Engine/Source/Runtime/Engine/Private/Slate/SceneViewport.cpp +++ b/Engine/Source/Runtime/Engine/Private/Slate/SceneViewport.cpp @@ -34,6 +34,7 @@ FSceneViewport::FSceneViewport( FViewportClient* InViewportClient, TSharedPtrStereoRenderingDevice.IsValid() && IsStereoRenderingAllowed()) @@ -1361,28 +1366,31 @@ void FSceneViewport::InitDynamicRHI() { NumBufferedFrames = 1; - if (GEngine->IsStereoscopic3D(this) && GEngine->StereoRenderingDevice.IsValid()) + const bool bStereo = (IsStereoRenderingAllowed() && GEngine->StereoRenderingDevice.IsValid() && GEngine->StereoRenderingDevice->IsStereoEnabledOnNextFrame()); + bool bUseCustomPresentTexture = false; + + if (bStereo) { GEngine->StereoRenderingDevice->CalculateRenderTargetSize(*this, TexSizeX, TexSizeY); - //todo, get from HMD - NumBufferedFrames = 3; + + NumBufferedFrames = GEngine->StereoRenderingDevice->GetNumberOfBufferedFrames(); } - + check(BufferedSlateHandles.Num() == BufferedRenderTargetsRHI.Num() && BufferedSlateHandles.Num() == BufferedShaderResourceTexturesRHI.Num()); - if (BufferedSlateHandles.Num() != NumBufferedFrames) + //clear existing entries + for (int32 i = 0; i < BufferedSlateHandles.Num(); ++i) { - //clear existing entries - for (int32 i = 0; i < BufferedSlateHandles.Num(); ++i) + if (!BufferedSlateHandles[i]) { - if (!BufferedSlateHandles[i]) - { - BufferedSlateHandles[i] = new FSlateRenderTargetRHI(nullptr, 0, 0); - } - BufferedRenderTargetsRHI[i] = nullptr; - BufferedShaderResourceTexturesRHI[i] = nullptr; + BufferedSlateHandles[i] = new FSlateRenderTargetRHI(nullptr, 0, 0); } + BufferedRenderTargetsRHI[i] = nullptr; + BufferedShaderResourceTexturesRHI[i] = nullptr; + } + if (BufferedSlateHandles.Num() < NumBufferedFrames) + { //add sufficient entires for buffering. for (int32 i = BufferedSlateHandles.Num(); i < NumBufferedFrames; i++) { @@ -1391,33 +1399,48 @@ void FSceneViewport::InitDynamicRHI() BufferedShaderResourceTexturesRHI.Add(nullptr); } } + else if (BufferedSlateHandles.Num() > NumBufferedFrames) + { + BufferedSlateHandles.SetNum(NumBufferedFrames); + BufferedRenderTargetsRHI.SetNum(NumBufferedFrames); + BufferedShaderResourceTexturesRHI.SetNum(NumBufferedFrames); + } + check(BufferedSlateHandles.Num() == BufferedRenderTargetsRHI.Num() && BufferedSlateHandles.Num() == BufferedShaderResourceTexturesRHI.Num()); FRHIResourceCreateInfo CreateInfo; FTexture2DRHIRef BufferedRTRHI; FTexture2DRHIRef BufferedSRVRHI; - for (int32 i = 0; i < NumBufferedFrames; ++i) { - RHICreateTargetableShaderResource2D(TexSizeX, TexSizeY, PF_B8G8R8A8, 1, TexCreate_None, TexCreate_RenderTargetable, false, CreateInfo, BufferedRTRHI, BufferedSRVRHI); + // try to allocate texture via StereoRenderingDevice; if not successful, use the default way + if (!bStereo || !GEngine->StereoRenderingDevice->AllocateRenderTargetTexture(i, TexSizeX, TexSizeY, PF_B8G8R8A8, 1, TexCreate_None, TexCreate_RenderTargetable, BufferedRTRHI, BufferedSRVRHI)) + { + RHICreateTargetableShaderResource2D(TexSizeX, TexSizeY, PF_B8G8R8A8, 1, TexCreate_None, TexCreate_RenderTargetable, false, CreateInfo, BufferedRTRHI, BufferedSRVRHI); + } BufferedRenderTargetsRHI[i] = BufferedRTRHI; BufferedShaderResourceTexturesRHI[i] = BufferedSRVRHI; - BufferedSlateHandles[i]->SetRHIRef(BufferedShaderResourceTexturesRHI[0], TexSizeX, TexSizeY); + if (BufferedSlateHandles[i]) + { + BufferedSlateHandles[i]->SetRHIRef(BufferedShaderResourceTexturesRHI[0], TexSizeX, TexSizeY); + } } // clear out any extra entries we have hanging around for (int32 i = NumBufferedFrames; i < BufferedSlateHandles.Num(); ++i) { - BufferedSlateHandles[i]->SetRHIRef(nullptr, 0, 0); + if (BufferedSlateHandles[i]) + { + BufferedSlateHandles[i]->SetRHIRef(nullptr, 0, 0); + } BufferedRenderTargetsRHI[i] = nullptr; BufferedShaderResourceTexturesRHI[i] = nullptr; } CurrentBufferedTargetIndex = 0; + NextBufferedTargetIndex = (CurrentBufferedTargetIndex + 1) % BufferedSlateHandles.Num(); RenderTargetTextureRHI = BufferedShaderResourceTexturesRHI[CurrentBufferedTargetIndex]; - - } else { @@ -1431,6 +1454,7 @@ void FSceneViewport::InitDynamicRHI() NumBufferedFrames = 1; RenderTargetTextureRHI = nullptr; + CurrentBufferedTargetIndex = NextBufferedTargetIndex = 0; } //how is this useful at all? Pinning a weakptr to get a non-threadsafe shared ptr? Pinning a weakptr is supposed to be protecting me from my weakptr dying underneath me... diff --git a/Engine/Source/Runtime/Engine/Public/Slate/SceneViewport.h b/Engine/Source/Runtime/Engine/Public/Slate/SceneViewport.h index b3673b0bf4a1..fc1fe9399df9 100644 --- a/Engine/Source/Runtime/Engine/Public/Slate/SceneViewport.h +++ b/Engine/Source/Runtime/Engine/Public/Slate/SceneViewport.h @@ -349,6 +349,7 @@ private: int32 NumBufferedFrames; int32 CurrentBufferedTargetIndex; + int32 NextBufferedTargetIndex; }; diff --git a/Engine/Source/Runtime/Engine/Public/StereoRendering.h b/Engine/Source/Runtime/Engine/Public/StereoRendering.h index 295fef0bd74b..c83bf19be32a 100644 --- a/Engine/Source/Runtime/Engine/Public/StereoRendering.h +++ b/Engine/Source/Runtime/Engine/Public/StereoRendering.h @@ -10,10 +10,16 @@ class IStereoRendering { public: /** - * Whether or not stereo rendering is on. + * Whether or not stereo rendering is on this frame. */ virtual bool IsStereoEnabled() const = 0; + /** + * Whether or not stereo rendering is on on next frame. Useful to determine if some preparation work + * should be done before stereo got enabled in next frame. + */ + virtual bool IsStereoEnabledOnNextFrame() const { return IsStereoEnabled(); } + /** * Switches stereo rendering on / off. Returns current state of stereo. * @return Returns current state of stereo (true / false). @@ -66,12 +72,12 @@ public: /** * Calculates dimensions of the render target texture for direct rendering of distortion. */ - virtual void CalculateRenderTargetSize(const class FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) const {} + virtual void CalculateRenderTargetSize(const class FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) {} /** * Returns true, if render target texture must be re-calculated. */ - virtual bool NeedReAllocateViewportRenderTarget(const class FViewport& Viewport) const { return false; } + virtual bool NeedReAllocateViewportRenderTarget(const class FViewport& Viewport) { return false; } // Whether separate render target should be used or not. virtual bool ShouldUseSeparateRenderTarget() const { return false; } @@ -117,4 +123,24 @@ public: */ virtual void SetClippingPlanes(float NCP, float FCP) {} + /** + * Returns currently active custom present. + */ + virtual FRHICustomPresent* GetCustomPresent() { return nullptr; } + + /** + * Returns number of required buffered frames. + */ + virtual uint32 GetNumberOfBufferedFrames() const { return 3; } + + /** + * Allocates a render target texture. + * + * @param Index (in) index of the buffer, changing from 0 to GetNumberOfBufferedFrames() + * @return true, if texture was allocated; false, if the default texture allocation should be used. + */ + virtual bool AllocateRenderTargetTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, uint32 Flags, uint32 TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) + { + return false; + } }; diff --git a/Engine/Source/Runtime/RHI/Public/RHIResources.h b/Engine/Source/Runtime/RHI/Public/RHIResources.h index f8b58db5d681..46de974eed7b 100644 --- a/Engine/Source/Runtime/RHI/Public/RHIResources.h +++ b/Engine/Source/Runtime/RHI/Public/RHIResources.h @@ -577,6 +577,11 @@ public: * Sets custom Present handler on the viewport */ virtual void SetCustomPresent(class FRHICustomPresent*) {} + + /** + * Returns currently set custom present handler. + */ + virtual class FRHICustomPresent* GetCustomPresent() const { return nullptr; } }; // @@ -1044,8 +1049,10 @@ public: // Called when viewport is resized. virtual void OnBackBufferResize() = 0; - // @return true if normal Present should be performed; false otherwise. - virtual bool Present(int32 SyncInterval) = 0; + // @param InOutSyncInterval - in out param, indicates if vsync is on (>0) or off (==0). + // @return true if normal Present should be performed; false otherwise. If it returns + // true, then InOutSyncInterval could be modified to switch between VSync/NoVSync for the normal Present. + virtual bool Present(int32& InOutSyncInterval) = 0; protected: // Weak reference, don't create a circular dependency that would prevent the viewport from being destroyed. diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp b/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp index 89ae40d2120f..f0145abcc5b5 100644 --- a/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp +++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp @@ -1247,7 +1247,7 @@ void FSceneRenderTargets::InitEditorPrimitivesDepth() GRenderTargetPool.FindFreeElement(Desc, EditorPrimitivesDepth, TEXT("EditorPrimitivesDepth")); } -void FSceneRenderTargets::QuantizeBufferSize(int32& InOutBufferSizeX, int32& InOutBufferSizeY) const +void FSceneRenderTargets::QuantizeBufferSize(int32& InOutBufferSizeX, int32& InOutBufferSizeY) { // ensure sizes are dividable by DividableBy to get post processing effects with lower resolution working well const uint32 DividableBy = 8; diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.h b/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.h index c66c55d4a465..465dc1145eea 100644 --- a/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.h +++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.h @@ -106,7 +106,7 @@ END_UNIFORM_BUFFER_STRUCT( FGBufferResourceStruct ) /** * Encapsulates the render targets used for scene rendering. */ -class FSceneRenderTargets : public FRenderResource +class RENDERER_API FSceneRenderTargets : public FRenderResource { public: /** Destructor. */ @@ -453,6 +453,13 @@ public: TRefCountPtr& GetReflectionBrightnessTarget(); + /** + * Takes the requested buffer size and quantizes it to an appropriate size for the rest of the + * rendering pipeline. Currently ensures that sizes are multiples of 8 so that they can safely + * be halved in size several times. + */ + static void QuantizeBufferSize(int32& InOutBufferSizeX, int32& InOutBufferSizeY); + private: // Get...() methods instead of direct access // 0 before BeginRenderingSceneColor and after tone mapping in deferred shading @@ -552,13 +559,6 @@ private: /** to detect when LargestDesiredSizeThisFrame is outdated */ uint32 ThisFrameNumber; - /** - * Takes the requested buffer size and quantizes it to an appropriate size for the rest of the - * rendering pipeline. Currently ensures that sizes are multiples of 8 so that they can safely - * be halved in size several times. - */ - void QuantizeBufferSize(int32& InOutBufferSizeX, int32& InOutBufferSizeY) const; - /** * Initializes the editor primitive color render target */ @@ -654,4 +654,4 @@ private: }; /** The global render targets used for scene rendering. */ -extern TGlobalResource GSceneRenderTargets; +extern RENDERER_API TGlobalResource GSceneRenderTargets; diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RHIPrivate.h b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RHIPrivate.h index 7198b9b905c8..2e41b92ef10b 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RHIPrivate.h +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RHIPrivate.h @@ -37,8 +37,6 @@ DECLARE_LOG_CATEGORY_EXTERN(LogD3D11RHI, Log, All); // DX11 doesn't support higher MSAA count #define DX_MAX_MSAA_COUNT 8 -// Definitions. -#define D3D11 1 /** * The D3D RHI stats. @@ -265,7 +263,7 @@ struct FD3DGPUProfiler : public FGPUProfiler }; /** The interface which is implemented by the dynamically bound RHI. */ -class FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContext +class D3D11RHI_API FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContext { public: diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Resources.h b/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Resources.h index fcc9a77da6e9..f3f6994f49dd 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Resources.h +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Resources.h @@ -166,7 +166,7 @@ class FD3D11BaseShaderResource : public IRefCountedObject }; /** Texture base class. */ -class FD3D11TextureBase : public FD3D11BaseShaderResource +class D3D11RHI_API FD3D11TextureBase : public FD3D11BaseShaderResource { public: @@ -291,7 +291,7 @@ protected: /** 2D texture (vanilla, cubemap or 2D array) */ template -class TD3D11Texture2D : public BaseResourceType, public FD3D11TextureBase +class D3D11RHI_API TD3D11Texture2D : public BaseResourceType, public FD3D11TextureBase { public: diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Util.h b/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Util.h index 4b78c3e1bb28..55dca2391696 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Util.h +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Util.h @@ -13,7 +13,7 @@ * @param Filename - The filename of the source file containing Code. * @param Line - The line number of Code within Filename. */ -extern void VerifyD3D11Result(HRESULT Result,const ANSICHAR* Code,const ANSICHAR* Filename,uint32 Line, ID3D11Device* Device); +extern D3D11RHI_API void VerifyD3D11Result(HRESULT Result,const ANSICHAR* Code,const ANSICHAR* Filename,uint32 Line, ID3D11Device* Device); /** * Checks that the given result isn't a failure. If it is, the application exits with an appropriate error message. @@ -22,7 +22,7 @@ extern void VerifyD3D11Result(HRESULT Result,const ANSICHAR* Code,const ANSICHAR * @param Filename - The filename of the source file containing Code. * @param Line - The line number of Code within Filename. */ -extern void VerifyD3D11CreateTextureResult(HRESULT D3DResult,const ANSICHAR* Code,const ANSICHAR* Filename,uint32 Line, +extern D3D11RHI_API void VerifyD3D11CreateTextureResult(HRESULT D3DResult,const ANSICHAR* Code,const ANSICHAR* Filename,uint32 Line, uint32 SizeX,uint32 SizeY,uint32 SizeZ,uint8 D3DFormat,uint32 NumMips,uint32 Flags); /** @@ -35,7 +35,7 @@ extern void VerifyD3D11CreateTextureResult(HRESULT D3DResult,const ANSICHAR* Cod /** * Checks that a COM object has the expected number of references. */ -extern void VerifyComRefCount(IUnknown* Object,int32 ExpectedRefs,const TCHAR* Code,const TCHAR* Filename,int32 Line); +extern D3D11RHI_API void VerifyComRefCount(IUnknown* Object,int32 ExpectedRefs,const TCHAR* Code,const TCHAR* Filename,int32 Line); #define checkComRefCount(Obj,ExpectedRefs) VerifyComRefCount(Obj,ExpectedRefs,TEXT(#Obj),TEXT(__FILE__),__LINE__) /** Returns a string for the provided DXGI format. */ diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Viewport.h b/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Viewport.h index af4ddd28690a..ac738ce480b6 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Viewport.h +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Public/D3D11Viewport.h @@ -92,6 +92,7 @@ public: { CustomPresent = InCustomPresent; } + virtual FRHICustomPresent* GetCustomPresent() const { return CustomPresent; } virtual void* GetNativeWindow(void** AddParam = nullptr) const override { return (void*)WindowHandle; } diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR.build.cs b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR.build.cs index b6e7a5752dcd..8a7d03b894c0 100644 --- a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR.build.cs +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR.build.cs @@ -6,15 +6,29 @@ public class LibOVR : ModuleRules { // Version of LibOVR public const int LibOVR_Major = 0; - public const int LibOVR_Minor = 5; + public int LibOVR_Minor = 6; public const int LibOVR_Patch = 0; public LibOVR(TargetInfo Target) { Type = ModuleType.External; - PublicIncludePaths.Add(UEBuildConfiguration.UEThirdPartySourceDirectory + - "Oculus/LibOVR/LibOVR_" + LibOVR_Major + "_" + - LibOVR_Minor + "_" + LibOVR_Patch); + // PC uses SDK 0.6.0, while Mac uses 0.5.0 + if (Target.Platform == UnrealTargetPlatform.Mac) + { + LibOVR_Minor = 5; + PublicIncludePaths.Add(UEBuildConfiguration.UEThirdPartySourceDirectory + "Oculus/LibOVR/" + + "/LibOVR_" + LibOVR_Major + "_" + + LibOVR_Minor + "_" + LibOVR_Patch); + } + else + { + PublicIncludePaths.Add(UEBuildConfiguration.UEThirdPartySourceDirectory + "Oculus/LibOVR/" + + "/LibOVR_" + LibOVR_Major + "_" + + LibOVR_Minor + "_" + LibOVR_Patch + "/Include"); + PublicIncludePaths.Add(UEBuildConfiguration.UEThirdPartySourceDirectory + "Oculus/LibOVR/" + + "/LibOVR_" + LibOVR_Major + "_" + + LibOVR_Minor + "_" + LibOVR_Patch + "/Src"); + } } } diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPIShim.c b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPIShim.c index 683e14224b0a..0520e1861b76 100644 --- a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPIShim.c +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPIShim.c @@ -423,9 +423,28 @@ static ModuleHandleType OVR_OpenLibrary(const FilePathCharType* libraryPath) #if defined(_WIN32) return LoadLibraryW(libraryPath); #else + // Don't bother trying to dlopen() a file that is not even there. + if (access(libraryPath, X_OK | R_OK ) != 0) + { + return NULL; + } + + dlerror(); // Clear any previous dlopen() errors + // Use RTLD_NOW because we don't want unexpected stalls at runtime, and the library isn't very large. // Use RTLD_LOCAL to avoid unilaterally exporting resolved symbols to the rest of this process. - return dlopen(libraryPath, RTLD_NOW | RTLD_LOCAL); + void *lib = dlopen(libraryPath, RTLD_NOW | RTLD_LOCAL); + + if (!lib) + { + #if defined(__APPLE__) + // TODO: Output the error in whatever logging system OSX uses (jhughes) + #else // __APPLE__ + fprintf(stderr, "ERROR: Can't load '%s':\n%s\n", libraryPath, dlerror()); + #endif // __APPLE__ + } + + return lib; #endif } diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPI_0_5_0.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPI_0_5_0.h index 99b3b05cde20..01f0dd80ab9a 100644 --- a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPI_0_5_0.h +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_5_0/OVR_CAPI_0_5_0.h @@ -464,19 +464,6 @@ typedef struct ovrSensorData_ } ovrSensorData; -///@cond DoxygenIgnoreCode -// Deprecated struct. Will be remove in a future version. -typedef struct ovrTrackingStateInternal_ -{ - #if !defined(_MSC_VER) && defined(__i386__) // 32 bit Unix ABIs - uint32_t InternalData[60]; - #else - uint64_t InternalData[30]; - #endif -} ovrTrackingStateInternal; -///@endcond - - /// Tracking state at a given absolute time (describes predicted HMD pose etc). /// Returned by ovrHmd_GetTrackingState. typedef struct ovrTrackingState_ @@ -504,9 +491,8 @@ typedef struct ovrTrackingState_ /// Tag the vision processing results to a certain frame counter number. uint32_t LastCameraFrameCounter; - /// Will be removed in a future version. - ovrTrackingStateInternal InternalData; - + /// Unused struct padding. + uint32_t Pad; } ovrTrackingState; @@ -621,7 +607,6 @@ typedef struct ovrTextureHeader_ ovrRenderAPIType API; ///< The graphics API in use. ovrSizei TextureSize; ///< The size of the texture. ovrRecti RenderViewport; ///< Pixel viewport in texture that holds eye image. - uint32_t Pad; ///< Unused struct padding } ovrTextureHeader; /// Contains platform-specific information about a texture. diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/Extras/OVR_Math.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/Extras/OVR_Math.h new file mode 100644 index 000000000000..7c90cc01611f --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/Extras/OVR_Math.h @@ -0,0 +1,3668 @@ +/************************************************************************************ + +PublicHeader: OVR_Kernel.h +Filename : OVR_Math.h +Content : Implementation of 3D primitives such as vectors, matrices. +Created : September 4, 2012 +Authors : Andrew Reisse, Michael Antonov, Steve LaValle, + Anna Yershova, Max Katsev, Dov Katz + +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Math_h +#define OVR_Math_h + + +// This file is intended to be independent of the rest of LibOVR and LibOVRKernel and thus +// has no #include dependencies on either. + +#include +#include +#include +#include +#include +#include +#include "../OVR_CAPI.h" // Currently required due to a dependence on the ovrFovPort_ declaration. + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4127) // conditional expression is constant +#endif + + +#if defined(_MSC_VER) + #define OVRMath_sprintf sprintf_s +#else + #define OVRMath_sprintf snprintf +#endif + + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_ASSERT +// +// Independent debug break implementation for OVR_Math.h. + +#if !defined(OVR_MATH_DEBUG_BREAK) + #if defined(_DEBUG) + #if defined(_MSC_VER) + #define OVR_MATH_DEBUG_BREAK __debugbreak() + #else + #define OVR_MATH_DEBUG_BREAK __builtin_trap() + #endif + #else + #define OVR_MATH_DEBUG_BREAK ((void)0) + #endif +#endif + + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_ASSERT +// +// Independent OVR_MATH_ASSERT implementation for OVR_Math.h. + +#if !defined(OVR_MATH_ASSERT) + #if defined(_DEBUG) + #define OVR_MATH_ASSERT(p) if (!(p)) { OVR_MATH_DEBUG_BREAK; } + #else + #define OVR_MATH_ASSERT(p) ((void)0) + #endif +#endif + + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_STATIC_ASSERT +// +// Independent OVR_MATH_ASSERT implementation for OVR_Math.h. + +#if !defined(OVR_MATH_STATIC_ASSERT) + #if defined(__cplusplus) && ((defined(_MSC_VER) && (defined(_MSC_VER) >= 1600)) || defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)) + #define OVR_MATH_STATIC_ASSERT static_assert + #else + #if !defined(OVR_SA_UNUSED) + #if defined(__GNUC__) || defined(__clang__) + #define OVR_SA_UNUSED __attribute__((unused)) + #else + #define OVR_SA_UNUSED + #endif + #define OVR_SA_PASTE(a,b) a##b + #define OVR_SA_HELP(a,b) OVR_SA_PASTE(a,b) + #endif + + #define OVR_MATH_STATIC_ASSERT(expression, msg) typedef char OVR_SA_HELP(compileTimeAssert, __LINE__) [((expression) != 0) ? 1 : -1] OVR_SA_UNUSED + #endif +#endif + + + +namespace OVR { + +template +const T OVRMath_Min(const T a, const T b) +{ return (a < b) ? a : b; } + +template +const T OVRMath_Max(const T a, const T b) +{ return (b < a) ? a : b; } + +template +void OVRMath_Swap(T& a, T& b) +{ T temp(a); a = b; b = temp; } + + +//------------------------------------------------------------------------------------- +// ***** Constants for 3D world/axis definitions. + +// Definitions of axes for coordinate and rotation conversions. +enum Axis +{ + Axis_X = 0, Axis_Y = 1, Axis_Z = 2 +}; + +// RotateDirection describes the rotation direction around an axis, interpreted as follows: +// CW - Clockwise while looking "down" from positive axis towards the origin. +// CCW - Counter-clockwise while looking from the positive axis towards the origin, +// which is in the negative axis direction. +// CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate +// system defines Y up, X right, and Z back (pointing out from the screen). In this +// system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane. +enum RotateDirection +{ + Rotate_CCW = 1, + Rotate_CW = -1 +}; + +// Constants for right handed and left handed coordinate systems +enum HandedSystem +{ + Handed_R = 1, Handed_L = -1 +}; + +// AxisDirection describes which way the coordinate axis points. Used by WorldAxes. +enum AxisDirection +{ + Axis_Up = 2, + Axis_Down = -2, + Axis_Right = 1, + Axis_Left = -1, + Axis_In = 3, + Axis_Out = -3 +}; + +struct WorldAxes +{ + AxisDirection XAxis, YAxis, ZAxis; + + WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z) + : XAxis(x), YAxis(y), ZAxis(z) + { OVR_MATH_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));} +}; + +} // namespace OVR + + +//------------------------------------------------------------------------------------// +// ***** C Compatibility Types + +// These declarations are used to support conversion between C types used in +// LibOVR C interfaces and their C++ versions. As an example, they allow passing +// Vector3f into a function that expects ovrVector3f. + +typedef struct ovrQuatf_ ovrQuatf; +typedef struct ovrQuatd_ ovrQuatd; +typedef struct ovrSizei_ ovrSizei; +typedef struct ovrSizef_ ovrSizef; +typedef struct ovrSized_ ovrSized; +typedef struct ovrRecti_ ovrRecti; +typedef struct ovrVector2i_ ovrVector2i; +typedef struct ovrVector2f_ ovrVector2f; +typedef struct ovrVector2d_ ovrVector2d; +typedef struct ovrVector3f_ ovrVector3f; +typedef struct ovrVector3d_ ovrVector3d; +typedef struct ovrVector4f_ ovrVector4f; +typedef struct ovrVector4d_ ovrVector4d; +typedef struct ovrMatrix2f_ ovrMatrix2f; +typedef struct ovrMatrix2d_ ovrMatrix2d; +typedef struct ovrMatrix3f_ ovrMatrix3f; +typedef struct ovrMatrix3d_ ovrMatrix3d; +typedef struct ovrMatrix4f_ ovrMatrix4f; +typedef struct ovrMatrix4d_ ovrMatrix4d; +typedef struct ovrPosef_ ovrPosef; +typedef struct ovrPosed_ ovrPosed; +typedef struct ovrPoseStatef_ ovrPoseStatef; +typedef struct ovrPoseStated_ ovrPoseStated; + +namespace OVR { + +// Forward-declare our templates. +template class Quat; +template class Size; +template class Rect; +template class Vector2; +template class Vector3; +template class Vector4; +template class Matrix3; +template class Matrix4; +template class Pose; +template class PoseState; + +// CompatibleTypes::Type is used to lookup a compatible C-version of a C++ class. +template +struct CompatibleTypes +{ + // Declaration here seems necessary for MSVC; specializations are + // used instead. + typedef struct {} Type; +}; + +// Specializations providing CompatibleTypes::Type value. +template<> struct CompatibleTypes > { typedef ovrQuatf Type; }; +template<> struct CompatibleTypes > { typedef ovrQuatd Type; }; +template<> struct CompatibleTypes > { typedef ovrMatrix3f Type; }; +template<> struct CompatibleTypes > { typedef ovrMatrix3d Type; }; +template<> struct CompatibleTypes > { typedef ovrMatrix4f Type; }; +template<> struct CompatibleTypes > { typedef ovrMatrix4d Type; }; +template<> struct CompatibleTypes > { typedef ovrSizei Type; }; +template<> struct CompatibleTypes > { typedef ovrSizef Type; }; +template<> struct CompatibleTypes > { typedef ovrSized Type; }; +template<> struct CompatibleTypes > { typedef ovrRecti Type; }; +template<> struct CompatibleTypes > { typedef ovrVector2i Type; }; +template<> struct CompatibleTypes > { typedef ovrVector2f Type; }; +template<> struct CompatibleTypes > { typedef ovrVector2d Type; }; +template<> struct CompatibleTypes > { typedef ovrVector3f Type; }; +template<> struct CompatibleTypes > { typedef ovrVector3d Type; }; +template<> struct CompatibleTypes > { typedef ovrVector4f Type; }; +template<> struct CompatibleTypes > { typedef ovrVector4d Type; }; +template<> struct CompatibleTypes > { typedef ovrPosef Type; }; +template<> struct CompatibleTypes > { typedef ovrPosed Type; }; + +//------------------------------------------------------------------------------------// +// ***** Math +// +// Math class contains constants and functions. This class is a template specialized +// per type, with Math and Math being distinct. +template +class Math +{ +public: + // By default, support explicit conversion to float. This allows Vector2 to + // compile, for example. + typedef float OtherFloatType; + + static int Tolerance() { return 0; } // Default value so integer types compile +}; + + +//------------------------------------------------------------------------------------// +// ***** double constants +#define MATH_DOUBLE_PI 3.14159265358979323846 +#define MATH_DOUBLE_TWOPI (2*MATH_DOUBLE_PI) +#define MATH_DOUBLE_PIOVER2 (0.5*MATH_DOUBLE_PI) +#define MATH_DOUBLE_PIOVER4 (0.25*MATH_DOUBLE_PI) +#define MATH_FLOAT_MAXVALUE (FLT_MAX) + +#define MATH_DOUBLE_RADTODEGREEFACTOR (360.0 / MATH_DOUBLE_TWOPI) +#define MATH_DOUBLE_DEGREETORADFACTOR (MATH_DOUBLE_TWOPI / 360.0) + +#define MATH_DOUBLE_E 2.71828182845904523536 +#define MATH_DOUBLE_LOG2E 1.44269504088896340736 +#define MATH_DOUBLE_LOG10E 0.434294481903251827651 +#define MATH_DOUBLE_LN2 0.693147180559945309417 +#define MATH_DOUBLE_LN10 2.30258509299404568402 + +#define MATH_DOUBLE_SQRT2 1.41421356237309504880 +#define MATH_DOUBLE_SQRT1_2 0.707106781186547524401 + +#define MATH_DOUBLE_TOLERANCE 1e-12 // a default number for value equality tolerance: about 4096*Epsilon; +#define MATH_DOUBLE_SINGULARITYRADIUS 1e-12 // about 1-cos(.0001 degree), for gimbal lock numerical problems + +//------------------------------------------------------------------------------------// +// ***** float constants +#define MATH_FLOAT_PI float(MATH_DOUBLE_PI) +#define MATH_FLOAT_TWOPI float(MATH_DOUBLE_TWOPI) +#define MATH_FLOAT_PIOVER2 float(MATH_DOUBLE_PIOVER2) +#define MATH_FLOAT_PIOVER4 float(MATH_DOUBLE_PIOVER4) + +#define MATH_FLOAT_RADTODEGREEFACTOR float(MATH_DOUBLE_RADTODEGREEFACTOR) +#define MATH_FLOAT_DEGREETORADFACTOR float(MATH_DOUBLE_DEGREETORADFACTOR) + +#define MATH_FLOAT_E float(MATH_DOUBLE_E) +#define MATH_FLOAT_LOG2E float(MATH_DOUBLE_LOG2E) +#define MATH_FLOAT_LOG10E float(MATH_DOUBLE_LOG10E) +#define MATH_FLOAT_LN2 float(MATH_DOUBLE_LN2) +#define MATH_FLOAT_LN10 float(MATH_DOUBLE_LN10) + +#define MATH_FLOAT_SQRT2 float(MATH_DOUBLE_SQRT2) +#define MATH_FLOAT_SQRT1_2 float(MATH_DOUBLE_SQRT1_2) + +#define MATH_FLOAT_TOLERANCE 1e-5f // a default number for value equality tolerance: 1e-5, about 64*EPSILON; +#define MATH_FLOAT_SINGULARITYRADIUS 1e-7f // about 1-cos(.025 degree), for gimbal lock numerical problems + + + +// Single-precision Math constants class. +template<> +class Math +{ +public: + typedef double OtherFloatType; + + static inline float Tolerance() { return MATH_FLOAT_TOLERANCE; }; // a default number for value equality tolerance + static inline float SingularityRadius() { return MATH_FLOAT_SINGULARITYRADIUS; }; // for gimbal lock numerical problems +}; + +// Double-precision Math constants class +template<> +class Math +{ +public: + typedef float OtherFloatType; + + static inline double Tolerance() { return MATH_DOUBLE_TOLERANCE; }; // a default number for value equality tolerance + static inline double SingularityRadius() { return MATH_DOUBLE_SINGULARITYRADIUS; }; // for gimbal lock numerical problems +}; + +typedef Math Mathf; +typedef Math Mathd; + +// Conversion functions between degrees and radians +// (non-templated to ensure passing int arguments causes warning) +inline float RadToDegree(float rad) { return rad * MATH_FLOAT_RADTODEGREEFACTOR; } +inline double RadToDegree(double rad) { return rad * MATH_DOUBLE_RADTODEGREEFACTOR; } + +inline float DegreeToRad(float deg) { return deg * MATH_FLOAT_DEGREETORADFACTOR; } +inline double DegreeToRad(double deg) { return deg * MATH_DOUBLE_DEGREETORADFACTOR; } + +// Square function +template +inline T Sqr(T x) { return x*x; } + +// Sign: returns 0 if x == 0, -1 if x < 0, and 1 if x > 0 +template +inline T Sign(T x) { return (x != T(0)) ? (x < T(0) ? T(-1) : T(1)) : T(0); } + +// Numerically stable acos function +inline float Acos(float x) { return (x > 1.0f) ? 0.0f : (x < -1.0f) ? MATH_FLOAT_PI : acos(x); } +inline double Acos(double x) { return (x > 1.0) ? 0.0 : (x < -1.0) ? MATH_DOUBLE_PI : acos(x); } + +// Numerically stable asin function +inline float Asin(float x) { return (x > 1.0f) ? MATH_FLOAT_PIOVER2 : (x < -1.0f) ? -MATH_FLOAT_PIOVER2 : asin(x); } +inline double Asin(double x) { return (x > 1.0) ? MATH_DOUBLE_PIOVER2 : (x < -1.0) ? -MATH_DOUBLE_PIOVER2 : asin(x); } + +#if defined(_MSC_VER) + inline int isnan(double x) { return ::_isnan(x); } +#elif !defined(isnan) // Some libraries #define isnan. + inline int isnan(double x) { return ::isnan(x); } +#endif + +template +class Quat; + + +//------------------------------------------------------------------------------------- +// ***** Vector2<> + +// Vector2f (Vector2d) represents a 2-dimensional vector or point in space, +// consisting of coordinates x and y + +template +class Vector2 +{ +public: + typedef T ElementType; + static const size_t ElementCount = 2; + + T x, y; + + Vector2() : x(0), y(0) { } + Vector2(T x_, T y_) : x(x_), y(y_) { } + explicit Vector2(T s) : x(s), y(s) { } + explicit Vector2(const Vector2::OtherFloatType> &src) + : x((T)src.x), y((T)src.y) { } + + static Vector2 Zero() { return Vector2(0, 0); } + + // C-interop support. + typedef typename CompatibleTypes >::Type CompatibleType; + + Vector2(const CompatibleType& s) : x(s.x), y(s.y) { } + + operator const CompatibleType& () const + { + OVR_MATH_STATIC_ASSERT(sizeof(Vector2) == sizeof(CompatibleType), "sizeof(Vector2) failure"); + return reinterpret_cast(*this); + } + + + bool operator== (const Vector2& b) const { return x == b.x && y == b.y; } + bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; } + + Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); } + Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; } + Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); } + Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; } + Vector2 operator- () const { return Vector2(-x, -y); } + + // Scalar multiplication/division scales vector. + Vector2 operator* (T s) const { return Vector2(x*s, y*s); } + Vector2& operator*= (T s) { x *= s; y *= s; return *this; } + + Vector2 operator/ (T s) const { T rcp = T(1)/s; + return Vector2(x*rcp, y*rcp); } + Vector2& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; + return *this; } + + static Vector2 Min(const Vector2& a, const Vector2& b) { return Vector2((a.x < b.x) ? a.x : b.x, + (a.y < b.y) ? a.y : b.y); } + static Vector2 Max(const Vector2& a, const Vector2& b) { return Vector2((a.x > b.x) ? a.x : b.x, + (a.y > b.y) ? a.y : b.y); } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool Compare(const Vector2&b, T tolerance =Math::Tolerance()) + { + return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance); + } + + // Access element by index + T& operator[] (int idx) + { + OVR_MATH_ASSERT(0 <= idx && idx < 2); + return *(&x + idx); + } + const T& operator[] (int idx) const + { + OVR_MATH_ASSERT(0 <= idx && idx < 2); + return *(&x + idx); + } + + // Entry-wise product of two vectors + Vector2 EntrywiseMultiply(const Vector2& b) const { return Vector2(x * b.x, y * b.y);} + + + // Multiply and divide operators do entry-wise math. Used Dot() for dot product. + Vector2 operator* (const Vector2& b) const { return Vector2(x * b.x, y * b.y); } + Vector2 operator/ (const Vector2& b) const { return Vector2(x / b.x, y / b.y); } + + // Dot product + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T Dot(const Vector2& b) const { return x*b.x + y*b.y; } + + // Returns the angle from this vector to b, in radians. + T Angle(const Vector2& b) const + { + T div = LengthSq()*b.LengthSq(); + OVR_MATH_ASSERT(div != T(0)); + T result = Acos((this->Dot(b))/sqrt(div)); + return result; + } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y); } + + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + // Returns squared distance between two points represented by vectors. + T DistanceSq(const Vector2& b) const { return (*this - b).LengthSq(); } + + // Returns distance between two points represented by vectors. + T Distance(const Vector2& b) const { return (*this - b).Length(); } + + // Determine if this a unit vector. + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance(); } + + // Normalize, convention vector length to 1. + void Normalize() + { + T s = Length(); + if (s != 0) + s = T(1) / s; + *this *= s; + } + + // Returns normalized (unit) version of the vector without modifying itself. + Vector2 Normalized() const + { + T s = Length(); + if (s != 0) + s = T(1) / s; + return *this * s; + } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; } + + // Projects this vector onto the argument; in other words, + // A.Project(B) returns projection of vector A onto B. + Vector2 ProjectTo(const Vector2& b) const + { + T l2 = b.LengthSq(); + OVR_MATH_ASSERT(l2 != T(0)); + return b * ( Dot(b) / l2 ); + } + + // returns true if vector b is clockwise from this vector + bool IsClockwise(const Vector2& b) const + { + return (x * b.y - y * b.x) < 0; + } +}; + + +typedef Vector2 Vector2f; +typedef Vector2 Vector2d; +typedef Vector2 Vector2i; + +typedef Vector2 Point2f; +typedef Vector2 Point2d; +typedef Vector2 Point2i; + +//------------------------------------------------------------------------------------- +// ***** Vector3<> - 3D vector of {x, y, z} + +// +// Vector3f (Vector3d) represents a 3-dimensional vector or point in space, +// consisting of coordinates x, y and z. + +template +class Vector3 +{ +public: + typedef T ElementType; + static const size_t ElementCount = 3; + + T x, y, z; + + // FIXME: default initialization of a vector class can be very expensive in a full-blown + // application. A few hundred thousand vector constructions is not unlikely and can add + // up to milliseconds of time on processors like the PS3 PPU. + Vector3() : x(0), y(0), z(0) { } + Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { } + explicit Vector3(T s) : x(s), y(s), z(s) { } + explicit Vector3(const Vector3::OtherFloatType> &src) + : x((T)src.x), y((T)src.y), z((T)src.z) { } + + static Vector3 Zero() { return Vector3(0, 0, 0); } + + // C-interop support. + typedef typename CompatibleTypes >::Type CompatibleType; + + Vector3(const CompatibleType& s) : x(s.x), y(s.y), z(s.z) { } + + operator const CompatibleType& () const + { + OVR_MATH_STATIC_ASSERT(sizeof(Vector3) == sizeof(CompatibleType), "sizeof(Vector3) failure"); + return reinterpret_cast(*this); + } + + bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; } + bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; } + + Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); } + Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; } + Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); } + Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; } + Vector3 operator- () const { return Vector3(-x, -y, -z); } + + // Scalar multiplication/division scales vector. + Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); } + Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; } + + Vector3 operator/ (T s) const { T rcp = T(1)/s; + return Vector3(x*rcp, y*rcp, z*rcp); } + Vector3& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; z *= rcp; + return *this; } + + static Vector3 Min(const Vector3& a, const Vector3& b) + { + return Vector3((a.x < b.x) ? a.x : b.x, + (a.y < b.y) ? a.y : b.y, + (a.z < b.z) ? a.z : b.z); + } + static Vector3 Max(const Vector3& a, const Vector3& b) + { + return Vector3((a.x > b.x) ? a.x : b.x, + (a.y > b.y) ? a.y : b.y, + (a.z > b.z) ? a.z : b.z); + } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool IsEqual(const Vector3&b, T tolerance = Math::Tolerance()) + { + return (fabs(b.x-x) < tolerance) && + (fabs(b.y-y) < tolerance) && + (fabs(b.z-z) < tolerance); + } + + T& operator[] (int idx) + { + OVR_MATH_ASSERT(0 <= idx && idx < 3); + return *(&x + idx); + } + + const T& operator[] (int idx) const + { + OVR_MATH_ASSERT(0 <= idx && idx < 3); + return *(&x + idx); + } + + // Entrywise product of two vectors + Vector3 EntrywiseMultiply(const Vector3& b) const { return Vector3(x * b.x, + y * b.y, + z * b.z);} + + // Multiply and divide operators do entry-wise math + Vector3 operator* (const Vector3& b) const { return Vector3(x * b.x, + y * b.y, + z * b.z); } + + Vector3 operator/ (const Vector3& b) const { return Vector3(x / b.x, + y / b.y, + z / b.z); } + + + // Dot product + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T Dot(const Vector3& b) const { return x*b.x + y*b.y + z*b.z; } + + // Compute cross product, which generates a normal vector. + // Direction vector can be determined by right-hand rule: Pointing index finder in + // direction a and middle finger in direction b, thumb will point in a.Cross(b). + Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y, + z*b.x - x*b.z, + x*b.y - y*b.x); } + + // Returns the angle from this vector to b, in radians. + T Angle(const Vector3& b) const + { + T div = LengthSq()*b.LengthSq(); + OVR_MATH_ASSERT(div != T(0)); + T result = Acos((this->Dot(b))/sqrt(div)); + return result; + } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y + z * z); } + + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + // Returns squared distance between two points represented by vectors. + T DistanceSq(Vector3 const& b) const { return (*this - b).LengthSq(); } + + // Returns distance between two points represented by vectors. + T Distance(Vector3 const& b) const { return (*this - b).Length(); } + + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance(); } + + // Normalize, convention vector length to 1. + void Normalize() + { + T s = Length(); + if (s != 0) + s = T(1) / s; + *this *= s; + } + + // Returns normalized (unit) version of the vector without modifying itself. + Vector3 Normalized() const + { + T s = Length(); + if (s != 0) + s = T(1) / s; + return *this * s; + } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; } + + // Projects this vector onto the argument; in other words, + // A.Project(B) returns projection of vector A onto B. + Vector3 ProjectTo(const Vector3& b) const + { + T l2 = b.LengthSq(); + OVR_MATH_ASSERT(l2 != T(0)); + return b * ( Dot(b) / l2 ); + } + + // Projects this vector onto a plane defined by a normal vector + Vector3 ProjectToPlane(const Vector3& normal) const { return *this - this->ProjectTo(normal); } +}; + +typedef Vector3 Vector3f; +typedef Vector3 Vector3d; +typedef Vector3 Vector3i; + +OVR_MATH_STATIC_ASSERT((sizeof(Vector3f) == 3*sizeof(float)), "sizeof(Vector3f) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Vector3d) == 3*sizeof(double)), "sizeof(Vector3d) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Vector3i) == 3*sizeof(int32_t)), "sizeof(Vector3i) failure"); + +typedef Vector3 Point3f; +typedef Vector3 Point3d; +typedef Vector3 Point3i; + + +//------------------------------------------------------------------------------------- +// ***** Vector4<> - 4D vector of {x, y, z, w} + +// +// Vector4f (Vector4d) represents a 3-dimensional vector or point in space, +// consisting of coordinates x, y, z and w. + +template +class Vector4 +{ +public: + typedef T ElementType; + static const size_t ElementCount = 4; + + T x, y, z, w; + + // FIXME: default initialization of a vector class can be very expensive in a full-blown + // application. A few hundred thousand vector constructions is not unlikely and can add + // up to milliseconds of time on processors like the PS3 PPU. + Vector4() : x(0), y(0), z(0), w(0) { } + Vector4(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) { } + explicit Vector4(T s) : x(s), y(s), z(s), w(s) { } + explicit Vector4(const Vector3& v, const float w_=1) : x(v.x), y(v.y), z(v.z), w(w_) { } + explicit Vector4(const Vector4::OtherFloatType> &src) + : x((T)src.x), y((T)src.y), z((T)src.z), w((T)src.w) { } + + static Vector4 Zero() { return Vector4(0, 0, 0, 0); } + + // C-interop support. + typedef typename CompatibleTypes< Vector4 >::Type CompatibleType; + + Vector4(const CompatibleType& s) : x(s.x), y(s.y), z(s.z), w(s.w) { } + + operator const CompatibleType& () const + { + OVR_MATH_STATIC_ASSERT(sizeof(Vector4) == sizeof(CompatibleType), "sizeof(Vector4) failure"); + return reinterpret_cast(*this); + } + + Vector4& operator= (const Vector3& other) { x=other.x; y=other.y; z=other.z; w=1; return *this; } + bool operator== (const Vector4& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } + bool operator!= (const Vector4& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } + + Vector4 operator+ (const Vector4& b) const { return Vector4(x + b.x, y + b.y, z + b.z, w + b.w); } + Vector4& operator+= (const Vector4& b) { x += b.x; y += b.y; z += b.z; w += b.w; return *this; } + Vector4 operator- (const Vector4& b) const { return Vector4(x - b.x, y - b.y, z - b.z, w - b.w); } + Vector4& operator-= (const Vector4& b) { x -= b.x; y -= b.y; z -= b.z; w -= b.w; return *this; } + Vector4 operator- () const { return Vector4(-x, -y, -z, -w); } + + // Scalar multiplication/division scales vector. + Vector4 operator* (T s) const { return Vector4(x*s, y*s, z*s, w*s); } + Vector4& operator*= (T s) { x *= s; y *= s; z *= s; w *= s;return *this; } + + Vector4 operator/ (T s) const { T rcp = T(1)/s; + return Vector4(x*rcp, y*rcp, z*rcp, w*rcp); } + Vector4& operator/= (T s) { T rcp = T(1)/s; + x *= rcp; y *= rcp; z *= rcp; w *= rcp; + return *this; } + + static Vector4 Min(const Vector4& a, const Vector4& b) + { + return Vector4((a.x < b.x) ? a.x : b.x, + (a.y < b.y) ? a.y : b.y, + (a.z < b.z) ? a.z : b.z, + (a.w < b.w) ? a.w : b.w); + } + static Vector4 Max(const Vector4& a, const Vector4& b) + { + return Vector4((a.x > b.x) ? a.x : b.x, + (a.y > b.y) ? a.y : b.y, + (a.z > b.z) ? a.z : b.z, + (a.w > b.w) ? a.w : b.w); + } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool Compare(const Vector4&b, T tolerance = Math::Tolerance()) + { + return (fabs(b.x-x) < tolerance) && + (fabs(b.y-y) < tolerance) && + (fabs(b.z-z) < tolerance) && + (fabs(b.w-w) < tolerance); + } + + T& operator[] (int idx) + { + OVR_MATH_ASSERT(0 <= idx && idx < 4); + return *(&x + idx); + } + + const T& operator[] (int idx) const + { + OVR_MATH_ASSERT(0 <= idx && idx < 4); + return *(&x + idx); + } + + // Entry wise product of two vectors + Vector4 EntrywiseMultiply(const Vector4& b) const { return Vector4(x * b.x, + y * b.y, + z * b.z);} + + // Multiply and divide operators do entry-wise math + Vector4 operator* (const Vector4& b) const { return Vector4(x * b.x, + y * b.y, + z * b.z, + w * b.w); } + + Vector4 operator/ (const Vector4& b) const { return Vector4(x / b.x, + y / b.y, + z / b.z, + w / b.w); } + + + // Dot product + T Dot(const Vector4& b) const { return x*b.x + y*b.y + z*b.z + w*b.w; } + + // Return Length of the vector squared. + T LengthSq() const { return (x * x + y * y + z * z + w * w); } + + // Return vector length. + T Length() const { return sqrt(LengthSq()); } + + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance(); } + + // Normalize, convention vector length to 1. + void Normalize() + { + T s = Length(); + if (s != 0) + s = T(1) / s; + *this *= s; + } + + // Returns normalized (unit) version of the vector without modifying itself. + Vector4 Normalized() const + { + T s = Length(); + if (s != 0) + s = T(1) / s; + return *this * s; + } +}; + +typedef Vector4 Vector4f; +typedef Vector4 Vector4d; +typedef Vector4 Vector4i; + + +//------------------------------------------------------------------------------------- +// ***** Bounds3 + +// Bounds class used to describe a 3D axis aligned bounding box. + +template +class Bounds3 +{ +public: + Vector3 b[2]; + + Bounds3() + { + } + + Bounds3( const Vector3 & mins, const Vector3 & maxs ) +{ + b[0] = mins; + b[1] = maxs; + } + + void Clear() + { + b[0].x = b[0].y = b[0].z = Math::MaxValue; + b[1].x = b[1].y = b[1].z = -Math::MaxValue; + } + + void AddPoint( const Vector3 & v ) + { + b[0].x = (b[0].x < v.x ? b[0].x : v.x); + b[0].y = (b[0].y < v.y ? b[0].y : v.y); + b[0].z = (b[0].z < v.z ? b[0].z : v.z); + b[1].x = (v.x < b[1].x ? b[1].x : v.x); + b[1].y = (v.y < b[1].y ? b[1].y : v.y); + b[1].z = (v.z < b[1].z ? b[1].z : v.z); + } + + const Vector3 & GetMins() const { return b[0]; } + const Vector3 & GetMaxs() const { return b[1]; } + + Vector3 & GetMins() { return b[0]; } + Vector3 & GetMaxs() { return b[1]; } +}; + +typedef Bounds3 Bounds3f; +typedef Bounds3 Bounds3d; + + +//------------------------------------------------------------------------------------- +// ***** Size + +// Size class represents 2D size with Width, Height components. +// Used to describe distentions of render targets, etc. + +template +class Size +{ +public: + T w, h; + + Size() : w(0), h(0) { } + Size(T w_, T h_) : w(w_), h(h_) { } + explicit Size(T s) : w(s), h(s) { } + explicit Size(const Size::OtherFloatType> &src) + : w((T)src.w), h((T)src.h) { } + + // C-interop support. + typedef typename CompatibleTypes >::Type CompatibleType; + + Size(const CompatibleType& s) : w(s.w), h(s.h) { } + + operator const CompatibleType& () const + { + OVR_MATH_STATIC_ASSERT(sizeof(Size) == sizeof(CompatibleType), "sizeof(Size) failure"); + return reinterpret_cast(*this); + } + + bool operator== (const Size& b) const { return w == b.w && h == b.h; } + bool operator!= (const Size& b) const { return w != b.w || h != b.h; } + + Size operator+ (const Size& b) const { return Size(w + b.w, h + b.h); } + Size& operator+= (const Size& b) { w += b.w; h += b.h; return *this; } + Size operator- (const Size& b) const { return Size(w - b.w, h - b.h); } + Size& operator-= (const Size& b) { w -= b.w; h -= b.h; return *this; } + Size operator- () const { return Size(-w, -h); } + Size operator* (const Size& b) const { return Size(w * b.w, h * b.h); } + Size& operator*= (const Size& b) { w *= b.w; h *= b.h; return *this; } + Size operator/ (const Size& b) const { return Size(w / b.w, h / b.h); } + Size& operator/= (const Size& b) { w /= b.w; h /= b.h; return *this; } + + // Scalar multiplication/division scales both components. + Size operator* (T s) const { return Size(w*s, h*s); } + Size& operator*= (T s) { w *= s; h *= s; return *this; } + Size operator/ (T s) const { return Size(w/s, h/s); } + Size& operator/= (T s) { w /= s; h /= s; return *this; } + + static Size Min(const Size& a, const Size& b) { return Size((a.w < b.w) ? a.w : b.w, + (a.h < b.h) ? a.h : b.h); } + static Size Max(const Size& a, const Size& b) { return Size((a.w > b.w) ? a.w : b.w, + (a.h > b.h) ? a.h : b.h); } + + T Area() const { return w * h; } + + inline Vector2 ToVector() const { return Vector2(w, h); } +}; + + +typedef Size Sizei; +typedef Size Sizeu; +typedef Size Sizef; +typedef Size Sized; + + + +//----------------------------------------------------------------------------------- +// ***** Rect + +// Rect describes a rectangular area for rendering, that includes position and size. +template +class Rect +{ +public: + T x, y; + T w, h; + + Rect() { } + Rect(T x1, T y1, T w1, T h1) : x(x1), y(y1), w(w1), h(h1) { } + Rect(const Vector2& pos, const Size& sz) : x(pos.x), y(pos.y), w(sz.w), h(sz.h) { } + Rect(const Size& sz) : x(0), y(0), w(sz.w), h(sz.h) { } + + // C-interop support. + typedef typename CompatibleTypes >::Type CompatibleType; + + Rect(const CompatibleType& s) : x(s.Pos.x), y(s.Pos.y), w(s.Size.w), h(s.Size.h) { } + + operator const CompatibleType& () const + { + OVR_MATH_STATIC_ASSERT(sizeof(Rect) == sizeof(CompatibleType), "sizeof(Rect) failure"); + return reinterpret_cast(*this); + } + + Vector2 GetPos() const { return Vector2(x, y); } + Size GetSize() const { return Size(w, h); } + void SetPos(const Vector2& pos) { x = pos.x; y = pos.y; } + void SetSize(const Size& sz) { w = sz.w; h = sz.h; } + + bool operator == (const Rect& vp) const + { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } + bool operator != (const Rect& vp) const + { return !operator == (vp); } +}; + +typedef Rect Recti; + + +//-------------------------------------------------------------------------------------// +// ***** Quat +// +// Quatf represents a quaternion class used for rotations. +// +// Quaternion multiplications are done in right-to-left order, to match the +// behavior of matrices. + + +template +class Quat +{ +public: + typedef T ElementType; + static const size_t ElementCount = 4; + + // x,y,z = axis*sin(angle), w = cos(angle) + T x, y, z, w; + + Quat() : x(0), y(0), z(0), w(1) { } + Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) { } + explicit Quat(const Quat::OtherFloatType> &src) + : x((T)src.x), y((T)src.y), z((T)src.z), w((T)src.w) { } + + typedef typename CompatibleTypes >::Type CompatibleType; + + // C-interop support. + Quat(const CompatibleType& s) : x(s.x), y(s.y), z(s.z), w(s.w) { } + + operator CompatibleType () const + { + CompatibleType result; + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; + } + + // Constructs quaternion for rotation around the axis by an angle. + Quat(const Vector3& axis, T angle) + { + // Make sure we don't divide by zero. + if (axis.LengthSq() == 0) + { + // Assert if the axis is zero, but the angle isn't + OVR_MATH_ASSERT(angle == 0); + x = 0; y = 0; z = 0; w = 1; + return; + } + + Vector3 unitAxis = axis.Normalized(); + T sinHalfAngle = sin(angle * T(0.5)); + + w = cos(angle * T(0.5)); + x = unitAxis.x * sinHalfAngle; + y = unitAxis.y * sinHalfAngle; + z = unitAxis.z * sinHalfAngle; + } + + // Constructs quaternion for rotation around one of the coordinate axis by an angle. + Quat(Axis A, T angle, RotateDirection d = Rotate_CCW, HandedSystem s = Handed_R) + { + T sinHalfAngle = s * d *sin(angle * T(0.5)); + T v[3]; + v[0] = v[1] = v[2] = T(0); + v[A] = sinHalfAngle; + + w = cos(angle * T(0.5)); + x = v[0]; + y = v[1]; + z = v[2]; + } + + // Explicit identity for clairity, saves ambiguity at callsite about Quat() + // being initialized or not. + static Quat Identity() { return Quat(0, 0, 0, 1); } + + // Compute axis and angle from quaternion + void GetAxisAngle(Vector3* axis, T* angle) const + { + if ( x*x + y*y + z*z > Math::Tolerance() * Math::Tolerance() ) { + *axis = Vector3(x, y, z).Normalized(); + *angle = 2 * Acos(w); + if (*angle > ((T)MATH_DOUBLE_PI)) // Reduce the magnitude of the angle, if necessary + { + *angle = ((T)MATH_DOUBLE_TWOPI) - *angle; + *axis = *axis * (-1); + } + } + else + { + *axis = Vector3(1, 0, 0); + *angle= 0; + } + } + + // Convert a quaternion to a rotation vector, also known as + // Rodrigues vector, AxisAngle vector, SORA vector, exponential map. + // A rotation vector describes a rotation about an axis: + // the axis of rotation is the vector normalized, + // the angle of rotation is the magnitude of the vector. + Vector3 ToRotationVector() const + { + T s = T(0); + T sinHalfAngle = sqrt(x*x + y*y + z*z); + if (sinHalfAngle > T(0)) + { + T cosHalfAngle = w; + T halfAngle = atan2(sinHalfAngle, cosHalfAngle); + + // Ensure minimum rotation magnitude + if (cosHalfAngle < 0) + halfAngle -= T(MATH_DOUBLE_PI); + + s = T(2) * (halfAngle / sinHalfAngle); + } + return Vector3(x*s, y*s, z*s); + } + + // Faster version of the above, optimized for use with small rotations, where rotation angle ~= sin(angle) + inline OVR::Vector3 FastToRotationVector() const + { + T s; + T sinHalfSquared = x*x + y*y + z*z; + if (sinHalfSquared < T(.0037)) // =~ sin(7/2 degrees)^2 + { + // Max rotation magnitude error is (1 - 2*halfAngle/sin(halfAngle)) = -.0044 radians (.06%) at 7 degrees rotation + s = T(2); + } + else + { + T sinHalfAngle = sqrt(sinHalfSquared); + T cosHalfAngle = w; + T halfAngle = atan2(sinHalfAngle, cosHalfAngle); + + // Ensure minimum rotation magnitude + if (cosHalfAngle < 0) + halfAngle -= T(MATH_DOUBLE_PI); + + s = T(2) * halfAngle / sinHalfAngle; + } + return Vector3(x*s, y*s, z*s); + } + + // Given a rotation vector of form unitRotationAxis * angle, + // returns the equivalent quaternion (unitRotationAxis * sin(angle), cos(Angle)). + static Quat FromRotationVector(const Vector3& v) + { + T angleSquared = v.LengthSq(); + T s = T(0); + T c = T(1); + if (angleSquared > T(0)) + { + T angle = sqrt(angleSquared); + s = sin(angle * T(0.5)) / angle; // normalize + c = cos(angle * T(0.5)); + } + return Quat(s*v.x, s*v.y, s*v.z, c); + } + + // Faster version of above, optimized for use with small rotation magnitudes, where rotation angle =~ sin(angle). + // If normalize is false, small-angle quaternions are returned un-normalized. + inline static Quat FastFromRotationVector(const OVR::Vector3& v, bool normalize = true) + { + T s, c; + T angleSquared = v.LengthSq(); + if (angleSquared < T(0.0076)) // =~ (5 degrees*pi/180)^2 + { + s = T(0.5); + c = T(1.0); + // Max rotation magnitude error (after normalization) is about .0031 degrees at 5 degrees, or .06% + if (normalize && angleSquared > 0) + { + T invLen = T(1) / sqrt(s * angleSquared + c); // normalize + s = s * invLen; + c = c * invLen; + } + } + else + { + T angle = sqrt(angleSquared); + s = sin(angle * T(0.5)) / angle; + c = cos(angle * T(0.5)); + } + return Quat(s*v.x, s*v.y, s*v.z, c); + } + + // Constructs the quaternion from a rotation matrix + explicit Quat(const Matrix4& m) + { + T trace = m.M[0][0] + m.M[1][1] + m.M[2][2]; + + // In almost all cases, the first part is executed. + // However, if the trace is not positive, the other + // cases arise. + if (trace > T(0)) + { + T s = sqrt(trace + T(1)) * T(2); // s=4*qw + w = T(0.25) * s; + x = (m.M[2][1] - m.M[1][2]) / s; + y = (m.M[0][2] - m.M[2][0]) / s; + z = (m.M[1][0] - m.M[0][1]) / s; + } + else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2])) + { + T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2); + w = (m.M[2][1] - m.M[1][2]) / s; + x = T(0.25) * s; + y = (m.M[0][1] + m.M[1][0]) / s; + z = (m.M[2][0] + m.M[0][2]) / s; + } + else if (m.M[1][1] > m.M[2][2]) + { + T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy + w = (m.M[0][2] - m.M[2][0]) / s; + x = (m.M[0][1] + m.M[1][0]) / s; + y = T(0.25) * s; + z = (m.M[1][2] + m.M[2][1]) / s; + } + else + { + T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz + w = (m.M[1][0] - m.M[0][1]) / s; + x = (m.M[0][2] + m.M[2][0]) / s; + y = (m.M[1][2] + m.M[2][1]) / s; + z = T(0.25) * s; + } + } + + // Constructs the quaternion from a rotation matrix + explicit Quat(const Matrix3& m) + { + T trace = m.M[0][0] + m.M[1][1] + m.M[2][2]; + + // In almost all cases, the first part is executed. + // However, if the trace is not positive, the other + // cases arise. + if (trace > T(0)) + { + T s = sqrt(trace + T(1)) * T(2); // s=4*qw + w = T(0.25) * s; + x = (m.M[2][1] - m.M[1][2]) / s; + y = (m.M[0][2] - m.M[2][0]) / s; + z = (m.M[1][0] - m.M[0][1]) / s; + } + else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2])) + { + T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2); + w = (m.M[2][1] - m.M[1][2]) / s; + x = T(0.25) * s; + y = (m.M[0][1] + m.M[1][0]) / s; + z = (m.M[2][0] + m.M[0][2]) / s; + } + else if (m.M[1][1] > m.M[2][2]) + { + T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy + w = (m.M[0][2] - m.M[2][0]) / s; + x = (m.M[0][1] + m.M[1][0]) / s; + y = T(0.25) * s; + z = (m.M[1][2] + m.M[2][1]) / s; + } + else + { + T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz + w = (m.M[1][0] - m.M[0][1]) / s; + x = (m.M[0][2] + m.M[2][0]) / s; + y = (m.M[1][2] + m.M[2][1]) / s; + z = T(0.25) * s; + } + } + + bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } + bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } + + Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); } + Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; } + Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); } + Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; } + + Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); } + Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; } + Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); } + Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; } + + // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. + bool IsEqual(const Quat&b, T tolerance = Math::Tolerance()) + { + return Abs(Dot(b)) >= 1 - tolerance; + } + + static T Abs(const T v) { return (v >= 0) ? v : -v; } + + // Get Imaginary part vector + Vector3 Imag() const { return Vector3(x,y,z); } + + // Get quaternion length. + T Length() const { return sqrt(LengthSq()); } + + // Get quaternion length squared. + T LengthSq() const { return (x * x + y * y + z * z + w * w); } + + // Simple Euclidean distance in R^4 (not SLERP distance, but at least respects Haar measure) + T Distance(const Quat& q) const + { + T d1 = (*this - q).Length(); + T d2 = (*this + q).Length(); // Antipodal point check + return (d1 < d2) ? d1 : d2; + } + + T DistanceSq(const Quat& q) const + { + T d1 = (*this - q).LengthSq(); + T d2 = (*this + q).LengthSq(); // Antipodal point check + return (d1 < d2) ? d1 : d2; + } + + T Dot(const Quat& q) const + { + return x * q.x + y * q.y + z * q.z + w * q.w; + } + + // Angle between two quaternions in radians + T Angle(const Quat& q) const + { + return 2 * Acos(Abs(Dot(q))); + } + + // Normalize + bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance(); } + + void Normalize() + { + T s = Length(); + if (s != 0) + s = T(1) / s; + *this *= s; + } + + Quat Normalized() const + { + T s = Length(); + if (s != 0) + s = T(1) / s; + return *this * s; + } + + inline void EnsureSameHemisphere(const Quat& o) + { + if (Dot(o) < T(0)) + { + x = -x; + y = -y; + z = -z; + w = -w; + } + } + + // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. + Quat Conj() const { return Quat(-x, -y, -z, w); } + + // Quaternion multiplication. Combines quaternion rotations, performing the one on the + // right hand side first. + Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y, + w * b.y - x * b.z + y * b.w + z * b.x, + w * b.z + x * b.y - y * b.x + z * b.w, + w * b.w - x * b.x - y * b.y - z * b.z); } + const Quat& operator*= (const Quat& b) { *this = *this * b; return *this; } + + // + // this^p normalized; same as rotating by this p times. + Quat PowNormalized(T p) const + { + Vector3 v; + T a; + GetAxisAngle(&v, &a); + return Quat(v, a * p); + } + + // Compute quaternion that rotates v into alignTo: alignTo = Quat::Align(alignTo, v).Rotate(v). + // NOTE: alignTo and v must be normalized. + static Quat Align(const Vector3& alignTo, const Vector3& v) + { + Vector3 bisector = (v + alignTo); + bisector.Normalize(); + T cosHalfAngle = v.Dot(bisector); // 0..1 + if (cosHalfAngle > T(0)) + { + Vector3 imag = v.Cross(bisector); + return Quat(imag.x, imag.y, imag.z, cosHalfAngle); + } + else + { + // cosHalfAngle == 0: a 180 degree rotation. + // sinHalfAngle == 1, rotation axis is any axis perpendicular + // to alignTo. Choose axis to include largest magnitude components + if (fabs(v.x) > fabs(v.y)) + { + // x or z is max magnitude component + // = Cross(v, (0,1,0)).Normalized(); + T invLen = sqrt(v.x*v.x + v.z*v.z); + if (invLen > T(0)) + invLen = T(1) / invLen; + return Quat(-v.z*invLen, 0, v.x*invLen, 0); + } + else + { + // y or z is max magnitude component + // = Cross(v, (1,0,0)).Normalized(); + T invLen = sqrt(v.y*v.y + v.z*v.z); + if (invLen > T(0)) + invLen = T(1) / invLen; + return Quat(0, v.z*invLen, -v.y*invLen, 0); + } + } + } + + // Normalized linear interpolation of quaternions + Quat Lerp(const Quat& b, T s) const + { + return (*this * (T(1) - s) + b * (Dot(b) < 0 ? -s : s)).Normalized(); + } + + // Normalized linear interpolation of quaternions + // WARNING, POSSIBLE BUG: This function doesn't work like Lerp() for other classes! + // If f is 1, then full value is given to *this, not "other" like other Lerp() functions + Quat Nlerp(const Quat& other, T a) + { + T sign = (Dot(other) >= 0) ? 1 : -1; + return (*this * sign * a + other * (1-a)).Normalized(); + } + + // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise, + // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1. + Vector3 Rotate(const Vector3& v) const + { + // rv = q * (v,0) * q' + // Same as rv = v + real * cross(imag,v)*2 + cross(imag, cross(imag,v)*2); + + // uv = 2 * Imag().Cross(v); + T uvx = T(2) * (y*v.z - z*v.y); + T uvy = T(2) * (z*v.x - x*v.z); + T uvz = T(2) * (x*v.y - y*v.x); + + // return v + Real()*uv + Imag().Cross(uv); + return Vector3(v.x + w*uvx + y*uvz - z*uvy, + v.y + w*uvy + z*uvx - x*uvz, + v.z + w*uvz + x*uvy - y*uvx); + } + + // Rotation by inverse of *this + Vector3 InverseRotate(const Vector3& v) const + { + // rv = q' * (v,0) * q + // Same as rv = v + real * cross(-imag,v)*2 + cross(-imag, cross(-imag,v)*2); + // or rv = v - real * cross(imag,v)*2 + cross(imag, cross(imag,v)*2); + + // uv = 2 * Imag().Cross(v); + T uvx = T(2) * (y*v.z - z*v.y); + T uvy = T(2) * (z*v.x - x*v.z); + T uvz = T(2) * (x*v.y - y*v.x); + + // return v - Real()*uv + Imag().Cross(uv); + return Vector3(v.x - w*uvx + y*uvz - z*uvy, + v.y - w*uvy + z*uvx - x*uvz, + v.z - w*uvz + x*uvy - y*uvx); + } + + // Inversed quaternion rotates in the opposite direction. + Quat Inverted() const + { + return Quat(-x, -y, -z, w); + } + + Quat Inverse() const + { + return Quat(-x, -y, -z, w); + } + + // Sets this quaternion to the one rotates in the opposite direction. + void Invert() + { + *this = Quat(-x, -y, -z, w); + } + + // Time integration of constant angular velocity over dt + Quat TimeIntegrate(Vector3 angularVelocity, T dt) const + { + // solution is: this * exp( omega*dt/2 ); FromRotationVector(v) gives exp(v*.5). + return (*this * FastFromRotationVector(angularVelocity * dt, false)).Normalized(); + } + + // Time integration of constant angular acceleration and velocity over dt + // These are the first two terms of the "Magnus expansion" of the solution + // + // o = o * exp( W=(W1 + W2 + W3+...) * 0.5 ); + // + // omega1 = (omega + omegaDot*dt) + // W1 = (omega + omega1)*dt/2 + // W2 = cross(omega, omega1)/12*dt^2 % (= -cross(omega_dot, omega)/12*dt^3) + // Terms 3 and beyond are vanishingly small: + // W3 = cross(omega_dot, cross(omega_dot, omega))/240*dt^5 + Quat TimeIntegrate(Vector3 angularVelocity, Vector3 angularAcceleration, T dt, T dtAcceleration) const + { + (void)dtAcceleration; + + const Vector3& omega = angularVelocity; + const Vector3& omegaDot = angularAcceleration; + + Vector3 omega1 = (omega + omegaDot * dtAcceleration); + Vector3 W = T(0.5)*dt*((omega + omega1) / T(2) + omega.Cross(omega1)*(dt * T(1)/T(12))); + + // FromRotationVector(v) is exp(v*.5) + return (*this * FastFromRotationVector(W, false)).Normalized(); + } + + // Decompose rotation into three rotations: + // roll radians about Z axis, then pitch radians about X axis, then yaw radians about Y axis. + // Call with nullptr if a return value is not needed. + void GetYawPitchRoll(T* yaw, T* pitch, T* roll) const + { + return GetEulerAngles(yaw, pitch, roll); + } + + // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of + // axis rotations and the specified coordinate system. Right-handed coordinate system + // is the default, with CCW rotations while looking in the negative axis direction. + // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. + // Rotation order is c, b, a: + // rotation c around axis A3 + // is followed by rotation b around axis A2 + // is followed by rotation a around axis A1 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + // + template + void GetEulerAngles(T *a, T *b, T *c) const + { + OVR_MATH_STATIC_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3), "(A1 != A2) && (A2 != A3) && (A1 != A3)"); + + T Q[3] = { x, y, z }; //Quaternion components x,y,z + + T ww = w*w; + T Q11 = Q[A1]*Q[A1]; + T Q22 = Q[A2]*Q[A2]; + T Q33 = Q[A3]*Q[A3]; + + T psign = T(-1); + // Determine whether even permutation + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) + psign = T(1); + + T s2 = psign * T(2) * (psign*w*Q[A2] + Q[A1]*Q[A3]); + + T singularityRadius = Math::SingularityRadius(); + if (s2 < T(-1) + singularityRadius) + { // South pole singularity + if (a) *a = T(0); + if (b) *b = -S*D*((T)MATH_DOUBLE_PIOVER2); + if (c) *c = S*D*atan2(T(2)*(psign*Q[A1] * Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33 ); + } + else if (s2 > T(1) - singularityRadius) + { // North pole singularity + if (a) *a = T(0); + if (b) *b = S*D*((T)MATH_DOUBLE_PIOVER2); + if (c) *c = S*D*atan2(T(2)*(psign*Q[A1] * Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33); + } + else + { + if (a) *a = -S*D*atan2(T(-2)*(w*Q[A1] - psign*Q[A2] * Q[A3]), ww + Q33 - Q11 - Q22); + if (b) *b = S*D*asin(s2); + if (c) *c = S*D*atan2(T(2)*(w*Q[A3] - psign*Q[A1] * Q[A2]), ww + Q11 - Q22 - Q33); + } + } + + template + void GetEulerAngles(T *a, T *b, T *c) const + { GetEulerAngles(a, b, c); } + + template + void GetEulerAngles(T *a, T *b, T *c) const + { GetEulerAngles(a, b, c); } + + // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of + // axis rotations and the specified coordinate system. Right-handed coordinate system + // is the default, with CCW rotations while looking in the negative axis direction. + // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A1 + // Rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void GetEulerAnglesABA(T *a, T *b, T *c) const + { + OVR_MATH_STATIC_ASSERT(A1 != A2, "A1 != A2"); + + T Q[3] = {x, y, z}; // Quaternion components + + // Determine the missing axis that was not supplied + int m = 3 - A1 - A2; + + T ww = w*w; + T Q11 = Q[A1]*Q[A1]; + T Q22 = Q[A2]*Q[A2]; + T Qmm = Q[m]*Q[m]; + + T psign = T(-1); + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + { + psign = T(1); + } + + T c2 = ww + Q11 - Q22 - Qmm; + if (c2 < T(-1) + Math::SingularityRadius) + { // South pole singularity + if (a) *a = T(0); + if (b) *b = S*D*((T)MATH_DOUBLE_PI); + if (c) *c = S*D*atan2(T(2)*(w*Q[A1] - psign*Q[A2] * Q[m]), + ww + Q22 - Q11 - Qmm); + } + else if (c2 > T(1) - Math::SingularityRadius) + { // North pole singularity + if (a) *a = T(0); + if (b) *b = T(0); + if (c) *c = S*D*atan2(T(2)*(w*Q[A1] - psign*Q[A2] * Q[m]), + ww + Q22 - Q11 - Qmm); + } + else + { + if (a) *a = S*D*atan2(psign*w*Q[m] + Q[A1] * Q[A2], + w*Q[A2] -psign*Q[A1]*Q[m]); + if (b) *b = S*D*acos(c2); + if (c) *c = S*D*atan2(-psign*w*Q[m] + Q[A1] * Q[A2], + w*Q[A2] + psign*Q[A1]*Q[m]); + } + } +}; + +typedef Quat Quatf; +typedef Quat Quatd; + +OVR_MATH_STATIC_ASSERT((sizeof(Quatf) == 4*sizeof(float)), "sizeof(Quatf) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Quatd) == 4*sizeof(double)), "sizeof(Quatd) failure"); + +//------------------------------------------------------------------------------------- +// ***** Pose +// +// Position and orientation combined. +// +// This structure needs to be the same size and layout on 32-bit and 64-bit arch. +// Update OVR_PadCheck.cpp when updating this object. +template +class Pose +{ +public: + typedef typename CompatibleTypes >::Type CompatibleType; + + Pose() { } + Pose(const Quat& orientation, const Vector3& pos) + : Rotation(orientation), Translation(pos) { } + Pose(const Pose& s) + : Rotation(s.Rotation), Translation(s.Translation) { } + Pose(const Matrix3& R, const Vector3& t) + : Rotation((Quat)R), Translation(t) { } + Pose(const CompatibleType& s) + : Rotation(s.Orientation), Translation(s.Position) { } + explicit Pose(const Pose::OtherFloatType> &s) + : Rotation(s.Rotation), Translation(s.Translation) { } + + static Pose Identity() { return Pose(Quat(0, 0, 0, 1), Vector3(0, 0, 0)); } + + void SetIdentity() { Rotation = Quat(0, 0, 0, 1); Translation = Vector3(0, 0, 0); } + + // used to make things obviously broken if someone tries to use the value + void SetInvalid() { Rotation = Quat(NAN, NAN, NAN, NAN); Translation = Vector3(NAN, NAN, NAN); } + + bool IsEqual(const Pose&b, T tolerance = Math::Tolerance()) + { + return Translation.IsEqual(b.Translation, tolerance) && Rotation.IsEqual(b.Rotation, tolerance); + } + + operator typename CompatibleTypes >::Type () const + { + typename CompatibleTypes >::Type result; + result.Orientation = Rotation; + result.Position = Translation; + return result; + } + + Quat Rotation; + Vector3 Translation; + + OVR_MATH_STATIC_ASSERT((sizeof(T) == sizeof(double) || sizeof(T) == sizeof(float)), "(sizeof(T) == sizeof(double) || sizeof(T) == sizeof(float))"); + + void ToArray(T* arr) const + { + T temp[7] = { Rotation.x, Rotation.y, Rotation.z, Rotation.w, Translation.x, Translation.y, Translation.z }; + for (int i = 0; i < 7; i++) arr[i] = temp[i]; + } + + static Pose FromArray(const T* v) + { + Quat rotation(v[0], v[1], v[2], v[3]); + Vector3 translation(v[4], v[5], v[6]); + return Pose(rotation, translation); + } + + Vector3 Rotate(const Vector3& v) const + { + return Rotation.Rotate(v); + } + + Vector3 InverseRotate(const Vector3& v) const + { + return Rotation.InverseRotate(v); + } + + Vector3 Translate(const Vector3& v) const + { + return v + Translation; + } + + Vector3 Transform(const Vector3& v) const + { + return Rotate(v) + Translation; + } + + Vector3 InverseTransform(const Vector3& v) const + { + return InverseRotate(v - Translation); + } + + + Vector3 Apply(const Vector3& v) const + { + return Transform(v); + } + + Pose operator*(const Pose& other) const + { + return Pose(Rotation * other.Rotation, Apply(other.Translation)); + } + + Pose Inverted() const + { + Quat inv = Rotation.Inverted(); + return Pose(inv, inv.Rotate(-Translation)); + } + + Pose TimeIntegrate(const Vector3& linearVelocity, const Vector3& angularVelocity, T dt) const + { + return Pose( + (Rotation * Quat::FastFromRotationVector(angularVelocity * dt, false)).Normalized(), + Translation + linearVelocity * dt); + } + + Pose TimeIntegrate(const Vector3& linearVelocity, const Vector3& linearAcceleration, + const Vector3& angularVelocity, const Vector3& angularAcceleration, + T dt, T dtAcceleration) const + { + return Pose(Rotation.TimeIntegrate(angularVelocity, angularAcceleration, dt, dtAcceleration), + Translation + linearVelocity*dt + linearAcceleration*dt*dt / T(2)); + } +}; + +typedef Pose Posef; +typedef Pose Posed; + +OVR_MATH_STATIC_ASSERT((sizeof(Posed) == sizeof(Quatd) + sizeof(Vector3d)), "sizeof(Posed) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Posef) == sizeof(Quatf) + sizeof(Vector3f)), "sizeof(Posef) failure"); + + +//------------------------------------------------------------------------------------- +// ***** Matrix4 +// +// Matrix4 is a 4x4 matrix used for 3d transformations and projections. +// Translation stored in the last column. +// The matrix is stored in row-major order in memory, meaning that values +// of the first row are stored before the next one. +// +// The arrangement of the matrix is chosen to be in Right-Handed +// coordinate system and counterclockwise rotations when looking down +// the axis +// +// Transformation Order: +// - Transformations are applied from right to left, so the expression +// M1 * M2 * M3 * V means that the vector V is transformed by M3 first, +// followed by M2 and M1. +// +// Coordinate system: Right Handed +// +// Rotations: Counterclockwise when looking down the axis. All angles are in radians. +// +// | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector. +// | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector. +// | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector. +// | 30 31 32 33 | +// +// The basis vectors are first three columns. + +template +class Matrix4 +{ +public: + typedef T ElementType; + static const size_t Dimension = 4; + + T M[4][4]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix4(NoInitType) { } + + // By default, we construct identity matrix. + Matrix4() + { + M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; + M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; + M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; + M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; + } + + Matrix4(T m11, T m12, T m13, T m14, + T m21, T m22, T m23, T m24, + T m31, T m32, T m33, T m34, + T m41, T m42, T m43, T m44) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34; + M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44; + } + + Matrix4(T m11, T m12, T m13, + T m21, T m22, T m23, + T m31, T m32, T m33) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0; + M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; + } + + explicit Matrix4(const Matrix3& m) + { + M[0][0] = m.M[0][0]; M[0][1] = m.M[0][1]; M[0][2] = m.M[0][2]; M[0][3] = 0; + M[1][0] = m.M[1][0]; M[1][1] = m.M[1][1]; M[1][2] = m.M[1][2]; M[1][3] = 0; + M[2][0] = m.M[2][0]; M[2][1] = m.M[2][1]; M[2][2] = m.M[2][2]; M[2][3] = 0; + M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; + } + + explicit Matrix4(const Quat& q) + { + T ww = q.w*q.w; + T xx = q.x*q.x; + T yy = q.y*q.y; + T zz = q.z*q.z; + + M[0][0] = ww + xx - yy - zz; M[0][1] = 2 * (q.x*q.y - q.w*q.z); M[0][2] = 2 * (q.x*q.z + q.w*q.y); M[0][3] = 0; + M[1][0] = 2 * (q.x*q.y + q.w*q.z); M[1][1] = ww - xx + yy - zz; M[1][2] = 2 * (q.y*q.z - q.w*q.x); M[1][3] = 0; + M[2][0] = 2 * (q.x*q.z - q.w*q.y); M[2][1] = 2 * (q.y*q.z + q.w*q.x); M[2][2] = ww - xx - yy + zz; M[2][3] = 0; + M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; + } + + explicit Matrix4(const Pose& p) + { + Matrix4 result(p.Rotation); + result.SetTranslation(p.Translation); + *this = result; + } + + + // C-interop support + explicit Matrix4(const Matrix4::OtherFloatType> &src) + { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] = (T)src.M[i][j]; + } + + // C-interop support. + Matrix4(const typename CompatibleTypes >::Type& s) + { + OVR_MATH_STATIC_ASSERT(sizeof(s) == sizeof(Matrix4), "sizeof(s) == sizeof(Matrix4)"); + memcpy(M, s.M, sizeof(M)); + } + + operator typename CompatibleTypes >::Type () const + { + typename CompatibleTypes >::Type result; + OVR_MATH_STATIC_ASSERT(sizeof(result) == sizeof(Matrix4), "sizeof(result) == sizeof(Matrix4)"); + memcpy(result.M, M, sizeof(M)); + return result; + } + + void ToString(char* dest, size_t destsize) const + { + size_t pos = 0; + for (int r=0; r<4; r++) + { + for (int c=0; c<4; c++) + { + pos += OVRMath_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]); + } + } + } + + static Matrix4 FromString(const char* src) + { + Matrix4 result; + if (src) + { + for (int r = 0; r < 4; r++) + { + for (int c = 0; c < 4; c++) + { + result.M[r][c] = (T)atof(src); + while (*src && *src != ' ') + { + src++; + } + while (*src && *src == ' ') + { + src++; + } + } + } + } + return result; + } + + static Matrix4 Identity() { return Matrix4(); } + + void SetIdentity() + { + M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; + M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; + M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; + M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; + } + + void SetXBasis(const Vector3f & v) + { + M[0][0] = v.x; + M[1][0] = v.y; + M[2][0] = v.z; + } + Vector3f GetXBasis() const + { + return Vector3f(M[0][0], M[1][0], M[2][0]); + } + + void SetYBasis(const Vector3f & v) + { + M[0][1] = v.x; + M[1][1] = v.y; + M[2][1] = v.z; + } + Vector3f GetYBasis() const + { + return Vector3f(M[0][1], M[1][1], M[2][1]); + } + + void SetZBasis(const Vector3f & v) + { + M[0][2] = v.x; + M[1][2] = v.y; + M[2][2] = v.z; + } + Vector3f GetZBasis() const + { + return Vector3f(M[0][2], M[1][2], M[2][2]); + } + + bool operator== (const Matrix4& b) const + { + bool isEqual = true; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + isEqual &= (M[i][j] == b.M[i][j]); + + return isEqual; + } + + Matrix4 operator+ (const Matrix4& b) const + { + Matrix4 result(*this); + result += b; + return result; + } + + Matrix4& operator+= (const Matrix4& b) + { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] += b.M[i][j]; + return *this; + } + + Matrix4 operator- (const Matrix4& b) const + { + Matrix4 result(*this); + result -= b; + return result; + } + + Matrix4& operator-= (const Matrix4& b) + { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] -= b.M[i][j]; + return *this; + } + + // Multiplies two matrices into destination with minimum copying. + static Matrix4& Multiply(Matrix4* d, const Matrix4& a, const Matrix4& b) + { + OVR_MATH_ASSERT((d != &a) && (d != &b)); + int i = 0; + do { + d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0]; + d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1]; + d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2]; + d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3]; + } while((++i) < 4); + + return *d; + } + + Matrix4 operator* (const Matrix4& b) const + { + Matrix4 result(Matrix4::NoInit); + Multiply(&result, *this, b); + return result; + } + + Matrix4& operator*= (const Matrix4& b) + { + return Multiply(this, Matrix4(*this), b); + } + + Matrix4 operator* (T s) const + { + Matrix4 result(*this); + result *= s; + return result; + } + + Matrix4& operator*= (T s) + { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] *= s; + return *this; + } + + + Matrix4 operator/ (T s) const + { + Matrix4 result(*this); + result /= s; + return result; + } + + Matrix4& operator/= (T s) + { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] /= s; + return *this; + } + + Vector3 Transform(const Vector3& v) const + { + const T rcpW = 1.0f / (M[3][0] * v.x + M[3][1] * v.y + M[3][2] * v.z + M[3][3]); + return Vector3((M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3]) * rcpW, + (M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3]) * rcpW, + (M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]) * rcpW); + } + + Vector4 Transform(const Vector4& v) const + { + return Vector4(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3] * v.w, + M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3] * v.w, + M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3] * v.w, + M[3][0] * v.x + M[3][1] * v.y + M[3][2] * v.z + M[3][3] * v.w); + } + + Matrix4 Transposed() const + { + return Matrix4(M[0][0], M[1][0], M[2][0], M[3][0], + M[0][1], M[1][1], M[2][1], M[3][1], + M[0][2], M[1][2], M[2][2], M[3][2], + M[0][3], M[1][3], M[2][3], M[3][3]); + } + + void Transpose() + { + *this = Transposed(); + } + + + T SubDet (const size_t* rows, const size_t* cols) const + { + return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) + - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); + } + + T Cofactor(size_t I, size_t J) const + { + const size_t indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}}; + return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]); + } + + T Determinant() const + { + return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3); + } + + Matrix4 Adjugated() const + { + return Matrix4(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0), + Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1), + Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2), + Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3)); + } + + Matrix4 Inverted() const + { + T det = Determinant(); + OVR_MATH_ASSERT(det != 0); + return Adjugated() * (1.0f/det); + } + + void Invert() + { + *this = Inverted(); + } + + // This is more efficient than general inverse, but ONLY works + // correctly if it is a homogeneous transform matrix (rot + trans) + Matrix4 InvertedHomogeneousTransform() const + { + // Make the inverse rotation matrix + Matrix4 rinv = this->Transposed(); + rinv.M[3][0] = rinv.M[3][1] = rinv.M[3][2] = 0.0f; + // Make the inverse translation matrix + Vector3 tvinv(-M[0][3],-M[1][3],-M[2][3]); + Matrix4 tinv = Matrix4::Translation(tvinv); + return rinv * tinv; // "untranslate", then "unrotate" + } + + // This is more efficient than general inverse, but ONLY works + // correctly if it is a homogeneous transform matrix (rot + trans) + void InvertHomogeneousTransform() + { + *this = InvertedHomogeneousTransform(); + } + + // Matrix to Euler Angles conversion + // a,b,c, are the YawPitchRoll angles to be returned + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A3 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void ToEulerAngles(T *a, T *b, T *c) const + { + OVR_MATH_STATIC_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3), "(A1 != A2) && (A2 != A3) && (A1 != A3)"); + + T psign = -1; + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation + psign = 1; + + T pm = psign*M[A1][A3]; + if (pm < -1.0f + Math::SingularityRadius) + { // South pole singularity + *a = 0; + *b = -S*D*((T)MATH_DOUBLE_PIOVER2); + *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); + } + else if (pm > 1.0f - Math::SingularityRadius) + { // North pole singularity + *a = 0; + *b = S*D*((T)MATH_DOUBLE_PIOVER2); + *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); + } + else + { // Normal case (nonsingular) + *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] ); + *b = S*D*asin(pm); + *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] ); + } + + return; + } + + // Matrix to Euler Angles conversion + // a,b,c, are the YawPitchRoll angles to be returned + // rotation a around axis A1 + // is followed by rotation b around axis A2 + // is followed by rotation c around axis A1 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + template + void ToEulerAnglesABA(T *a, T *b, T *c) const + { + OVR_MATH_STATIC_ASSERT(A1 != A2, "A1 != A2"); + + // Determine the axis that was not supplied + int m = 3 - A1 - A2; + + T psign = -1; + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + psign = 1.0f; + + T c2 = M[A1][A1]; + if (c2 < -1 + Math::SingularityRadius) + { // South pole singularity + *a = 0; + *b = S*D*((T)MATH_DOUBLE_PI); + *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); + } + else if (c2 > 1.0f - Math::SingularityRadius) + { // North pole singularity + *a = 0; + *b = 0; + *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); + } + else + { // Normal case (nonsingular) + *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]); + *b = S*D*acos(c2); + *c = S*D*atan2( M[A1][A2],psign*M[A1][m]); + } + return; + } + + // Creates a matrix that converts the vertices from one coordinate system + // to another. + static Matrix4 AxisConversion(const WorldAxes& to, const WorldAxes& from) + { + // Holds axis values from the 'to' structure + int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis }; + + // The inverse of the toArray + int inv[4]; + inv[0] = inv[abs(to.XAxis)] = 0; + inv[abs(to.YAxis)] = 1; + inv[abs(to.ZAxis)] = 2; + + Matrix4 m(0, 0, 0, + 0, 0, 0, + 0, 0, 0); + + // Only three values in the matrix need to be changed to 1 or -1. + m.M[inv[abs(from.XAxis)]][0] = T(from.XAxis/toArray[inv[abs(from.XAxis)]]); + m.M[inv[abs(from.YAxis)]][1] = T(from.YAxis/toArray[inv[abs(from.YAxis)]]); + m.M[inv[abs(from.ZAxis)]][2] = T(from.ZAxis/toArray[inv[abs(from.ZAxis)]]); + return m; + } + + + // Creates a matrix for translation by vector + static Matrix4 Translation(const Vector3& v) + { + Matrix4 t; + t.M[0][3] = v.x; + t.M[1][3] = v.y; + t.M[2][3] = v.z; + return t; + } + + // Creates a matrix for translation by vector + static Matrix4 Translation(T x, T y, T z = 0.0f) + { + Matrix4 t; + t.M[0][3] = x; + t.M[1][3] = y; + t.M[2][3] = z; + return t; + } + + // Sets the translation part + void SetTranslation(const Vector3& v) + { + M[0][3] = v.x; + M[1][3] = v.y; + M[2][3] = v.z; + } + + Vector3 GetTranslation() const + { + return Vector3( M[0][3], M[1][3], M[2][3] ); + } + + // Creates a matrix for scaling by vector + static Matrix4 Scaling(const Vector3& v) + { + Matrix4 t; + t.M[0][0] = v.x; + t.M[1][1] = v.y; + t.M[2][2] = v.z; + return t; + } + + // Creates a matrix for scaling by vector + static Matrix4 Scaling(T x, T y, T z) + { + Matrix4 t; + t.M[0][0] = x; + t.M[1][1] = y; + t.M[2][2] = z; + return t; + } + + // Creates a matrix for scaling by constant + static Matrix4 Scaling(T s) + { + Matrix4 t; + t.M[0][0] = s; + t.M[1][1] = s; + t.M[2][2] = s; + return t; + } + + // Simple L1 distance in R^12 + T Distance(const Matrix4& m2) const + { + T d = fabs(M[0][0] - m2.M[0][0]) + fabs(M[0][1] - m2.M[0][1]); + d += fabs(M[0][2] - m2.M[0][2]) + fabs(M[0][3] - m2.M[0][3]); + d += fabs(M[1][0] - m2.M[1][0]) + fabs(M[1][1] - m2.M[1][1]); + d += fabs(M[1][2] - m2.M[1][2]) + fabs(M[1][3] - m2.M[1][3]); + d += fabs(M[2][0] - m2.M[2][0]) + fabs(M[2][1] - m2.M[2][1]); + d += fabs(M[2][2] - m2.M[2][2]) + fabs(M[2][3] - m2.M[2][3]); + d += fabs(M[3][0] - m2.M[3][0]) + fabs(M[3][1] - m2.M[3][1]); + d += fabs(M[3][2] - m2.M[3][2]) + fabs(M[3][3] - m2.M[3][3]); + return d; + } + + // Creates a rotation matrix rotating around the X axis by 'angle' radians. + // Just for quick testing. Not for final API. Need to remove case. + static Matrix4 RotationAxis(Axis A, T angle, RotateDirection d, HandedSystem s) + { + T sina = s * d *sin(angle); + T cosa = cos(angle); + + switch(A) + { + case Axis_X: + return Matrix4(1, 0, 0, + 0, cosa, -sina, + 0, sina, cosa); + case Axis_Y: + return Matrix4(cosa, 0, sina, + 0, 1, 0, + -sina, 0, cosa); + case Axis_Z: + return Matrix4(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, 1); + } + } + + + // Creates a rotation matrix rotating around the X axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4 RotationX(T angle) + { + T sina = sin(angle); + T cosa = cos(angle); + return Matrix4(1, 0, 0, + 0, cosa, -sina, + 0, sina, cosa); + } + + // Creates a rotation matrix rotating around the Y axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4 RotationY(T angle) + { + T sina = sin(angle); + T cosa = cos(angle); + return Matrix4(cosa, 0, sina, + 0, 1, 0, + -sina, 0, cosa); + } + + // Creates a rotation matrix rotating around the Z axis by 'angle' radians. + // Rotation direction is depends on the coordinate system: + // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), + // while looking in the negative axis direction. This is the + // same as looking down from positive axis values towards origin. + // LHS: Positive angle values rotate clock-wise (CW), while looking in the + // negative axis direction. + static Matrix4 RotationZ(T angle) + { + T sina = sin(angle); + T cosa = cos(angle); + return Matrix4(cosa, -sina, 0, + sina, cosa, 0, + 0, 0, 1); + } + + // LookAtRH creates a View transformation matrix for right-handed coordinate system. + // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' + // specifying the up vector. The resulting matrix should be used with PerspectiveRH + // projection. + static Matrix4 LookAtRH(const Vector3& eye, const Vector3& at, const Vector3& up) + { + Vector3 z = (eye - at).Normalized(); // Forward + Vector3 x = up.Cross(z).Normalized(); // Right + Vector3 y = z.Cross(x); + + Matrix4 m(x.x, x.y, x.z, -(x.Dot(eye)), + y.x, y.y, y.z, -(y.Dot(eye)), + z.x, z.y, z.z, -(z.Dot(eye)), + 0, 0, 0, 1 ); + return m; + } + + // LookAtLH creates a View transformation matrix for left-handed coordinate system. + // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' + // specifying the up vector. + static Matrix4 LookAtLH(const Vector3& eye, const Vector3& at, const Vector3& up) + { + Vector3 z = (at - eye).Normalized(); // Forward + Vector3 x = up.Cross(z).Normalized(); // Right + Vector3 y = z.Cross(x); + + Matrix4 m(x.x, x.y, x.z, -(x.Dot(eye)), + y.x, y.y, y.z, -(y.Dot(eye)), + z.x, z.y, z.z, -(z.Dot(eye)), + 0, 0, 0, 1 ); + return m; + } + + // PerspectiveRH creates a right-handed perspective projection matrix that can be + // used with the Oculus sample renderer. + // yfov - Specifies vertical field of view in radians. + // aspect - Screen aspect ration, which is usually width/height for square pixels. + // Note that xfov = yfov * aspect. + // znear - Absolute value of near Z clipping clipping range. + // zfar - Absolute value of far Z clipping clipping range (larger then near). + // Even though RHS usually looks in the direction of negative Z, positive values + // are expected for znear and zfar. + static Matrix4 PerspectiveRH(T yfov, T aspect, T znear, T zfar) + { + Matrix4 m; + T tanHalfFov = tan(yfov * 0.5f); + + m.M[0][0] = 1. / (aspect * tanHalfFov); + m.M[1][1] = 1. / tanHalfFov; + m.M[2][2] = zfar / (znear - zfar); + m.M[3][2] = -1.; + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = 0.; + + // Note: Post-projection matrix result assumes Left-Handed coordinate system, + // with Y up, X right and Z forward. This supports positive z-buffer values. + // This is the case even for RHS coordinate input. + return m; + } + + // PerspectiveLH creates a left-handed perspective projection matrix that can be + // used with the Oculus sample renderer. + // yfov - Specifies vertical field of view in radians. + // aspect - Screen aspect ration, which is usually width/height for square pixels. + // Note that xfov = yfov * aspect. + // znear - Absolute value of near Z clipping clipping range. + // zfar - Absolute value of far Z clipping clipping range (larger then near). + static Matrix4 PerspectiveLH(T yfov, T aspect, T znear, T zfar) + { + Matrix4 m; + T tanHalfFov = tan(yfov * 0.5f); + + m.M[0][0] = 1. / (aspect * tanHalfFov); + m.M[1][1] = 1. / tanHalfFov; + //m.M[2][2] = zfar / (znear - zfar); + m.M[2][2] = zfar / (zfar - znear); + m.M[3][2] = -1.; + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = 0.; + + // Note: Post-projection matrix result assumes Left-Handed coordinate system, + // with Y up, X right and Z forward. This supports positive z-buffer values. + // This is the case even for RHS coordinate input. + return m; + } + + static Matrix4 Ortho2D(T w, T h) + { + Matrix4 m; + m.M[0][0] = 2.0/w; + m.M[1][1] = -2.0/h; + m.M[0][3] = -1.0; + m.M[1][3] = 1.0; + m.M[2][2] = 0; + return m; + } +}; + +typedef Matrix4 Matrix4f; +typedef Matrix4 Matrix4d; + +//------------------------------------------------------------------------------------- +// ***** Matrix3 +// +// Matrix3 is a 3x3 matrix used for representing a rotation matrix. +// The matrix is stored in row-major order in memory, meaning that values +// of the first row are stored before the next one. +// +// The arrangement of the matrix is chosen to be in Right-Handed +// coordinate system and counterclockwise rotations when looking down +// the axis +// +// Transformation Order: +// - Transformations are applied from right to left, so the expression +// M1 * M2 * M3 * V means that the vector V is transformed by M3 first, +// followed by M2 and M1. +// +// Coordinate system: Right Handed +// +// Rotations: Counterclockwise when looking down the axis. All angles are in radians. + +template +class Matrix3 +{ +public: + typedef T ElementType; + static const size_t Dimension = 3; + + T M[3][3]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix3(NoInitType) { } + + // By default, we construct identity matrix. + Matrix3() + { + M[0][0] = M[1][1] = M[2][2] = 1; + M[0][1] = M[1][0] = M[2][0] = 0; + M[0][2] = M[1][2] = M[2][1] = 0; + } + + Matrix3(T m11, T m12, T m13, + T m21, T m22, T m23, + T m31, T m32, T m33) + { + M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; + M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; + M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; + } + + // Construction from X, Y, Z basis vectors + Matrix3(const Vector3& xBasis, const Vector3& yBasis, const Vector3& zBasis) + { + M[0][0] = xBasis.x; M[0][1] = yBasis.x; M[0][2] = zBasis.x; + M[1][0] = xBasis.y; M[1][1] = yBasis.y; M[1][2] = zBasis.y; + M[2][0] = xBasis.z; M[2][1] = yBasis.z; M[2][2] = zBasis.z; + } + + explicit Matrix3(const Quat& q) + { + const T tx = q.x+q.x, ty = q.y+q.y, tz = q.z+q.z; + const T twx = q.w*tx, twy = q.w*ty, twz = q.w*tz; + const T txx = q.x*tx, txy = q.x*ty, txz = q.x*tz; + const T tyy = q.y*ty, tyz = q.y*tz, tzz = q.z*tz; + M[0][0] = T(1) - (tyy + tzz); M[0][1] = txy - twz; M[0][2] = txz + twy; + M[1][0] = txy + twz; M[1][1] = T(1) - (txx + tzz); M[1][2] = tyz - twx; + M[2][0] = txz - twy; M[2][1] = tyz + twx; M[2][2] = T(1) - (txx + tyy); + } + + inline explicit Matrix3(T s) + { + M[0][0] = M[1][1] = M[2][2] = s; + M[0][1] = M[0][2] = M[1][0] = M[1][2] = M[2][0] = M[2][1] = 0; + } + + Matrix3(T m11, T m22, T m33) + { + M[0][0] = m11; M[0][1] = 0; M[0][2] = 0; + M[1][0] = 0; M[1][1] = m22; M[1][2] = 0; + M[2][0] = 0; M[2][1] = 0; M[2][2] = m33; + } + + explicit Matrix3(const Matrix3::OtherFloatType> &src) + { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + M[i][j] = (T)src.M[i][j]; + } + + // C-interop support. + Matrix3(const typename CompatibleTypes >::Type& s) + { + OVR_MATH_STATIC_ASSERT(sizeof(s) == sizeof(Matrix3), "sizeof(s) == sizeof(Matrix3)"); + memcpy(M, s.M, sizeof(M)); + } + + operator const typename CompatibleTypes >::Type () const + { + typename CompatibleTypes >::Type result; + OVR_MATH_STATIC_ASSERT(sizeof(result) == sizeof(Matrix3), "sizeof(result) == sizeof(Matrix3)"); + memcpy(result.M, M, sizeof(M)); + return result; + } + + T operator()(int i, int j) const { return M[i][j]; } + T& operator()(int i, int j) { return M[i][j]; } + + void ToString(char* dest, size_t destsize) const + { + size_t pos = 0; + for (int r=0; r<3; r++) + { + for (int c=0; c<3; c++) + pos += OVRMath_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]); + } + } + + static Matrix3 FromString(const char* src) + { + Matrix3 result; + if (src) + { + for (int r=0; r<3; r++) + { + for (int c=0; c<3; c++) + { + result.M[r][c] = (T)atof(src); + while (*src && *src != ' ') + src++; + while (*src && *src == ' ') + src++; + } + } + } + return result; + } + + static Matrix3 Identity() { return Matrix3(); } + + void SetIdentity() + { + M[0][0] = M[1][1] = M[2][2] = 1; + M[0][1] = M[1][0] = M[2][0] = 0; + M[0][2] = M[1][2] = M[2][1] = 0; + } + + static Matrix3 Diagonal(T m00, T m11, T m22) + { + return Matrix3(m00, 0, 0, + 0, m11, 0, + 0, 0, m22); + } + static Matrix3 Diagonal(const Vector3& v) { return Diagonal(v.x, v.y, v.z); } + + T Trace() const { return M[0][0] + M[1][1] + M[2][2]; } + + bool operator== (const Matrix3& b) const + { + bool isEqual = true; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + isEqual &= (M[i][j] == b.M[i][j]); + } + + return isEqual; + } + + Matrix3 operator+ (const Matrix3& b) const + { + Matrix3 result(*this); + result += b; + return result; + } + + Matrix3& operator+= (const Matrix3& b) + { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + M[i][j] += b.M[i][j]; + return *this; + } + + void operator= (const Matrix3& b) + { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + M[i][j] = b.M[i][j]; + } + + Matrix3 operator- (const Matrix3& b) const + { + Matrix3 result(*this); + result -= b; + return result; + } + + Matrix3& operator-= (const Matrix3& b) + { + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + M[i][j] -= b.M[i][j]; + } + + return *this; + } + + // Multiplies two matrices into destination with minimum copying. + static Matrix3& Multiply(Matrix3* d, const Matrix3& a, const Matrix3& b) + { + OVR_MATH_ASSERT((d != &a) && (d != &b)); + int i = 0; + do { + d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0]; + d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1]; + d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2]; + } while((++i) < 3); + + return *d; + } + + Matrix3 operator* (const Matrix3& b) const + { + Matrix3 result(Matrix3::NoInit); + Multiply(&result, *this, b); + return result; + } + + Matrix3& operator*= (const Matrix3& b) + { + return Multiply(this, Matrix3(*this), b); + } + + Matrix3 operator* (T s) const + { + Matrix3 result(*this); + result *= s; + return result; + } + + Matrix3& operator*= (T s) + { + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + M[i][j] *= s; + } + + return *this; + } + + Vector3 operator* (const Vector3 &b) const + { + Vector3 result; + result.x = M[0][0]*b.x + M[0][1]*b.y + M[0][2]*b.z; + result.y = M[1][0]*b.x + M[1][1]*b.y + M[1][2]*b.z; + result.z = M[2][0]*b.x + M[2][1]*b.y + M[2][2]*b.z; + + return result; + } + + Matrix3 operator/ (T s) const + { + Matrix3 result(*this); + result /= s; + return result; + } + + Matrix3& operator/= (T s) + { + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + M[i][j] /= s; + } + + return *this; + } + + Vector2 Transform(const Vector2& v) const + { + const float rcpZ = 1.0f / (M[2][0] * v.x + M[2][1] * v.y + M[2][2]); + return Vector2((M[0][0] * v.x + M[0][1] * v.y + M[0][2]) * rcpZ, + (M[1][0] * v.x + M[1][1] * v.y + M[1][2]) * rcpZ); + } + + Vector3 Transform(const Vector3& v) const + { + return Vector3(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z, + M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z, + M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z); + } + + Matrix3 Transposed() const + { + return Matrix3(M[0][0], M[1][0], M[2][0], + M[0][1], M[1][1], M[2][1], + M[0][2], M[1][2], M[2][2]); + } + + void Transpose() + { + *this = Transposed(); + } + + + T SubDet (const size_t* rows, const size_t* cols) const + { + return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) + - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); + } + + + // M += a*b.t() + inline void Rank1Add(const Vector3 &a, const Vector3 &b) + { + M[0][0] += a.x*b.x; M[0][1] += a.x*b.y; M[0][2] += a.x*b.z; + M[1][0] += a.y*b.x; M[1][1] += a.y*b.y; M[1][2] += a.y*b.z; + M[2][0] += a.z*b.x; M[2][1] += a.z*b.y; M[2][2] += a.z*b.z; + } + + // M -= a*b.t() + inline void Rank1Sub(const Vector3 &a, const Vector3 &b) + { + M[0][0] -= a.x*b.x; M[0][1] -= a.x*b.y; M[0][2] -= a.x*b.z; + M[1][0] -= a.y*b.x; M[1][1] -= a.y*b.y; M[1][2] -= a.y*b.z; + M[2][0] -= a.z*b.x; M[2][1] -= a.z*b.y; M[2][2] -= a.z*b.z; + } + + inline Vector3 Col(int c) const + { + return Vector3(M[0][c], M[1][c], M[2][c]); + } + + inline Vector3 Row(int r) const + { + return Vector3(M[r][0], M[r][1], M[r][2]); + } + + inline Vector3 GetColumn(int c) const + { + return Vector3(M[0][c], M[1][c], M[2][c]); + } + + inline Vector3 GetRow(int r) const + { + return Vector3(M[r][0], M[r][1], M[r][2]); + } + + inline void SetColumn(int c, const Vector3& v) + { + M[0][c] = v.x; + M[1][c] = v.y; + M[2][c] = v.z; + } + + inline void SetRow(int r, const Vector3& v) + { + M[r][0] = v.x; + M[r][1] = v.y; + M[r][2] = v.z; + } + + inline T Determinant() const + { + const Matrix3& m = *this; + T d; + + d = m.M[0][0] * (m.M[1][1]*m.M[2][2] - m.M[1][2] * m.M[2][1]); + d -= m.M[0][1] * (m.M[1][0]*m.M[2][2] - m.M[1][2] * m.M[2][0]); + d += m.M[0][2] * (m.M[1][0]*m.M[2][1] - m.M[1][1] * m.M[2][0]); + + return d; + } + + inline Matrix3 Inverse() const + { + Matrix3 a; + const Matrix3& m = *this; + T d = Determinant(); + + OVR_MATH_ASSERT(d != 0); + T s = T(1)/d; + + a.M[0][0] = s * (m.M[1][1] * m.M[2][2] - m.M[1][2] * m.M[2][1]); + a.M[1][0] = s * (m.M[1][2] * m.M[2][0] - m.M[1][0] * m.M[2][2]); + a.M[2][0] = s * (m.M[1][0] * m.M[2][1] - m.M[1][1] * m.M[2][0]); + + a.M[0][1] = s * (m.M[0][2] * m.M[2][1] - m.M[0][1] * m.M[2][2]); + a.M[1][1] = s * (m.M[0][0] * m.M[2][2] - m.M[0][2] * m.M[2][0]); + a.M[2][1] = s * (m.M[0][1] * m.M[2][0] - m.M[0][0] * m.M[2][1]); + + a.M[0][2] = s * (m.M[0][1] * m.M[1][2] - m.M[0][2] * m.M[1][1]); + a.M[1][2] = s * (m.M[0][2] * m.M[1][0] - m.M[0][0] * m.M[1][2]); + a.M[2][2] = s * (m.M[0][0] * m.M[1][1] - m.M[0][1] * m.M[1][0]); + + return a; + } + + // Outer Product of two column vectors: a * b.Transpose() + static Matrix3 OuterProduct(const Vector3& a, const Vector3& b) + { + return Matrix3(a.x*b.x, a.x*b.y, a.x*b.z, + a.y*b.x, a.y*b.y, a.y*b.z, + a.z*b.x, a.z*b.y, a.z*b.z); + } + + // Angle in radians between two rotation matrices + // Uses identity trace(a*b') = 2*cos(theta) + 1 + T Angle(const Matrix3& b) const + { + const Matrix3& a = *this; + return Acos(((a * b.Transposed()).Trace() - T(1)) * T(0.5)); + } +}; + +typedef Matrix3 Matrix3f; +typedef Matrix3 Matrix3d; + +//------------------------------------------------------------------------------------- +// ***** Matrix2 + +template +class Matrix2 +{ +public: + typedef T ElementType; + static const size_t Dimension = 2; + + T M[2][2]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix2(NoInitType) { } + + // By default, we construct identity matrix. + Matrix2() + { + M[0][0] = M[1][1] = 1; + M[0][1] = M[1][0] = 0; + } + + Matrix2(T m11, T m12, + T m21, T m22) + { + M[0][0] = m11; M[0][1] = m12; + M[1][0] = m21; M[1][1] = m22; + } + + // Construction from X, Y basis vectors + Matrix2(const Vector2& xBasis, const Vector2& yBasis) + { + M[0][0] = xBasis.x; M[0][1] = yBasis.x; + M[1][0] = xBasis.y; M[1][1] = yBasis.y; + } + + explicit Matrix2(T s) + { + M[0][0] = M[1][1] = s; + M[0][1] = M[1][0] = 0; + } + + Matrix2(T m11, T m22) + { + M[0][0] = m11; M[0][1] = 0; + M[1][0] = 0; M[1][1] = m22; + } + + explicit Matrix2(const Matrix2::OtherFloatType> &src) + { + M[0][0] = T(src.M[0][0]); M[0][1] = T(src.M[0][1]); + M[1][0] = T(src.M[1][0]); M[1][1] = T(src.M[1][1]); + } + + // C-interop support + Matrix2(const typename CompatibleTypes >::Type& s) + { + OVR_MATH_STATIC_ASSERT(sizeof(s) == sizeof(Matrix2), "sizeof(s) == sizeof(Matrix2)"); + memcpy(M, s.M, sizeof(M)); + } + + operator const typename CompatibleTypes >::Type() const + { + typename CompatibleTypes >::Type result; + OVR_MATH_STATIC_ASSERT(sizeof(result) == sizeof(Matrix2), "sizeof(result) == sizeof(Matrix2)"); + memcpy(result.M, M, sizeof(M)); + return result; + } + + T operator()(int i, int j) const { return M[i][j]; } + T& operator()(int i, int j) { return M[i][j]; } + const T* operator[](int i) const { return M[i]; } + T* operator[](int i) { return M[i]; } + + static Matrix2 Identity() { return Matrix2(); } + + void SetIdentity() + { + M[0][0] = M[1][1] = 1; + M[0][1] = M[1][0] = 0; + } + + static Matrix2 Diagonal(T m00, T m11) + { + return Matrix2(m00, m11); + } + static Matrix2 Diagonal(const Vector2& v) { return Matrix2(v.x, v.y); } + + T Trace() const { return M[0][0] + M[1][1]; } + + bool operator== (const Matrix2& b) const + { + return M[0][0] == b.M[0][0] && M[0][1] == b.M[0][1] && + M[1][0] == b.M[1][0] && M[1][1] == b.M[1][1]; + } + + Matrix2 operator+ (const Matrix2& b) const + { + return Matrix2(M[0][0] + b.M[0][0], M[0][1] + b.M[0][1], + M[1][0] + b.M[1][0], M[1][1] + b.M[1][1]); + } + + Matrix2& operator+= (const Matrix2& b) + { + M[0][0] += b.M[0][0]; M[0][1] += b.M[0][1]; + M[1][0] += b.M[1][0]; M[1][1] += b.M[1][1]; + return *this; + } + + void operator= (const Matrix2& b) + { + M[0][0] = b.M[0][0]; M[0][1] = b.M[0][1]; + M[1][0] = b.M[1][0]; M[1][1] = b.M[1][1]; + } + + Matrix2 operator- (const Matrix2& b) const + { + return Matrix2(M[0][0] - b.M[0][0], M[0][1] - b.M[0][1], + M[1][0] - b.M[1][0], M[1][1] - b.M[1][1]); + } + + Matrix2& operator-= (const Matrix2& b) + { + M[0][0] -= b.M[0][0]; M[0][1] -= b.M[0][1]; + M[1][0] -= b.M[1][0]; M[1][1] -= b.M[1][1]; + return *this; + } + + Matrix2 operator* (const Matrix2& b) const + { + return Matrix2(M[0][0] * b.M[0][0] + M[0][1] * b.M[1][0], M[0][0] * b.M[0][1] + M[0][1] * b.M[1][1], + M[1][0] * b.M[0][0] + M[1][1] * b.M[1][0], M[1][0] * b.M[0][1] + M[1][1] * b.M[1][1]); + } + + Matrix2& operator*= (const Matrix2& b) + { + *this = *this * b; + } + + Matrix2 operator* (T s) const + { + return Matrix2(M[0][0] * s, M[0][1] * s, + M[1][0] * s, M[1][1] * s); + } + + Matrix2& operator*= (T s) + { + M[0][0] *= s; M[0][1] *= s; + M[1][0] *= s; M[1][1] *= s; + return *this; + } + + Matrix2 operator/ (T s) const + { + return *this * (T(1) / s); + } + + Matrix2& operator/= (T s) + { + return *this *= (T(1) / s); + } + + Vector2 operator* (const Vector2 &b) const + { + return Vector2(M[0][0] * b.x + M[0][1] * b.y, + M[1][0] * b.x + M[1][1] * b.y); + } + + Vector2 Transform(const Vector2& v) const + { + return Vector2(M[0][0] * v.x + M[0][1] * v.y, + M[1][0] * v.x + M[1][1] * v.y); + } + + Matrix2 Transposed() const + { + return Matrix2(M[0][0], M[1][0], + M[0][1], M[1][1]); + } + + void Transpose() + { + OVRMath_Swap(M[1][0], M[0][1]); + } + + Vector2 GetColumn(int c) const + { + return Vector2(M[0][c], M[1][c]); + } + + Vector2 GetRow(int r) const + { + return Vector2(M[r][0], M[r][1]); + } + + void SetColumn(int c, const Vector2& v) + { + M[0][c] = v.x; + M[1][c] = v.y; + } + + void SetRow(int r, const Vector2& v) + { + M[r][0] = v.x; + M[r][1] = v.y; + } + + T Determinant() const + { + return M[0][0] * M[1][1] - M[0][1] * M[1][0]; + } + + Matrix2 Inverse() const + { + T rcpDet = T(1) / Determinant(); + return Matrix2( M[1][1] * rcpDet, -M[0][1] * rcpDet, + -M[1][0] * rcpDet, M[0][0] * rcpDet); + } + + // Outer Product of two column vectors: a * b.Transpose() + static Matrix2 OuterProduct(const Vector2& a, const Vector2& b) + { + return Matrix2(a.x*b.x, a.x*b.y, + a.y*b.x, a.y*b.y); + } + + // Angle in radians between two rotation matrices + T Angle(const Matrix2& b) const + { + const Matrix2& a = *this; + return Acos(a(0, 0)*b(0, 0) + a(1, 0)*b(1, 0)); + } +}; + +typedef Matrix2 Matrix2f; +typedef Matrix2 Matrix2d; + +//------------------------------------------------------------------------------------- + +template +class SymMat3 +{ +private: + typedef SymMat3 this_type; + +public: + typedef T Value_t; + // Upper symmetric + T v[6]; // _00 _01 _02 _11 _12 _22 + + inline SymMat3() {} + + inline explicit SymMat3(T s) + { + v[0] = v[3] = v[5] = s; + v[1] = v[2] = v[4] = 0; + } + + inline explicit SymMat3(T a00, T a01, T a02, T a11, T a12, T a22) + { + v[0] = a00; v[1] = a01; v[2] = a02; + v[3] = a11; v[4] = a12; + v[5] = a22; + } + + // Cast to symmetric Matrix3 + operator Matrix3() const + { + return Matrix3(v[0], v[1], v[2], + v[1], v[3], v[4], + v[2], v[4], v[5]); + } + + static inline int Index(unsigned int i, unsigned int j) + { + return (i <= j) ? (3*i - i*(i+1)/2 + j) : (3*j - j*(j+1)/2 + i); + } + + inline T operator()(int i, int j) const { return v[Index(i,j)]; } + + inline T &operator()(int i, int j) { return v[Index(i,j)]; } + + inline this_type& operator+=(const this_type& b) + { + v[0]+=b.v[0]; + v[1]+=b.v[1]; + v[2]+=b.v[2]; + v[3]+=b.v[3]; + v[4]+=b.v[4]; + v[5]+=b.v[5]; + return *this; + } + + inline this_type& operator-=(const this_type& b) + { + v[0]-=b.v[0]; + v[1]-=b.v[1]; + v[2]-=b.v[2]; + v[3]-=b.v[3]; + v[4]-=b.v[4]; + v[5]-=b.v[5]; + + return *this; + } + + inline this_type& operator*=(T s) + { + v[0]*=s; + v[1]*=s; + v[2]*=s; + v[3]*=s; + v[4]*=s; + v[5]*=s; + + return *this; + } + + inline SymMat3 operator*(T s) const + { + SymMat3 d; + d.v[0] = v[0]*s; + d.v[1] = v[1]*s; + d.v[2] = v[2]*s; + d.v[3] = v[3]*s; + d.v[4] = v[4]*s; + d.v[5] = v[5]*s; + + return d; + } + + // Multiplies two matrices into destination with minimum copying. + static SymMat3& Multiply(SymMat3* d, const SymMat3& a, const SymMat3& b) + { + // _00 _01 _02 _11 _12 _22 + + d->v[0] = a.v[0] * b.v[0]; + d->v[1] = a.v[0] * b.v[1] + a.v[1] * b.v[3]; + d->v[2] = a.v[0] * b.v[2] + a.v[1] * b.v[4]; + + d->v[3] = a.v[3] * b.v[3]; + d->v[4] = a.v[3] * b.v[4] + a.v[4] * b.v[5]; + + d->v[5] = a.v[5] * b.v[5]; + + return *d; + } + + inline T Determinant() const + { + const this_type& m = *this; + T d; + + d = m(0,0) * (m(1,1)*m(2,2) - m(1,2) * m(2,1)); + d -= m(0,1) * (m(1,0)*m(2,2) - m(1,2) * m(2,0)); + d += m(0,2) * (m(1,0)*m(2,1) - m(1,1) * m(2,0)); + + return d; + } + + inline this_type Inverse() const + { + this_type a; + const this_type& m = *this; + T d = Determinant(); + + OVR_MATH_ASSERT(d != 0); + T s = T(1)/d; + + a(0,0) = s * (m(1,1) * m(2,2) - m(1,2) * m(2,1)); + + a(0,1) = s * (m(0,2) * m(2,1) - m(0,1) * m(2,2)); + a(1,1) = s * (m(0,0) * m(2,2) - m(0,2) * m(2,0)); + + a(0,2) = s * (m(0,1) * m(1,2) - m(0,2) * m(1,1)); + a(1,2) = s * (m(0,2) * m(1,0) - m(0,0) * m(1,2)); + a(2,2) = s * (m(0,0) * m(1,1) - m(0,1) * m(1,0)); + + return a; + } + + inline T Trace() const { return v[0] + v[3] + v[5]; } + + // M = a*a.t() + inline void Rank1(const Vector3 &a) + { + v[0] = a.x*a.x; v[1] = a.x*a.y; v[2] = a.x*a.z; + v[3] = a.y*a.y; v[4] = a.y*a.z; + v[5] = a.z*a.z; + } + + // M += a*a.t() + inline void Rank1Add(const Vector3 &a) + { + v[0] += a.x*a.x; v[1] += a.x*a.y; v[2] += a.x*a.z; + v[3] += a.y*a.y; v[4] += a.y*a.z; + v[5] += a.z*a.z; + } + + // M -= a*a.t() + inline void Rank1Sub(const Vector3 &a) + { + v[0] -= a.x*a.x; v[1] -= a.x*a.y; v[2] -= a.x*a.z; + v[3] -= a.y*a.y; v[4] -= a.y*a.z; + v[5] -= a.z*a.z; + } +}; + +typedef SymMat3 SymMat3f; +typedef SymMat3 SymMat3d; + +template +inline Matrix3 operator*(const SymMat3& a, const SymMat3& b) +{ + #define AJB_ARBC(r,c) (a(r,0)*b(0,c)+a(r,1)*b(1,c)+a(r,2)*b(2,c)) + return Matrix3( + AJB_ARBC(0,0), AJB_ARBC(0,1), AJB_ARBC(0,2), + AJB_ARBC(1,0), AJB_ARBC(1,1), AJB_ARBC(1,2), + AJB_ARBC(2,0), AJB_ARBC(2,1), AJB_ARBC(2,2)); + #undef AJB_ARBC +} + +template +inline Matrix3 operator*(const Matrix3& a, const SymMat3& b) +{ + #define AJB_ARBC(r,c) (a(r,0)*b(0,c)+a(r,1)*b(1,c)+a(r,2)*b(2,c)) + return Matrix3( + AJB_ARBC(0,0), AJB_ARBC(0,1), AJB_ARBC(0,2), + AJB_ARBC(1,0), AJB_ARBC(1,1), AJB_ARBC(1,2), + AJB_ARBC(2,0), AJB_ARBC(2,1), AJB_ARBC(2,2)); + #undef AJB_ARBC +} + +//------------------------------------------------------------------------------------- +// ***** Angle + +// Cleanly representing the algebra of 2D rotations. +// The operations maintain the angle between -Pi and Pi, the same range as atan2. + +template +class Angle +{ +public: + enum AngularUnits + { + Radians = 0, + Degrees = 1 + }; + + Angle() : a(0) {} + + // Fix the range to be between -Pi and Pi + Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*((T)MATH_DOUBLE_DEGREETORADFACTOR)) { FixRange(); } + + T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*((T)MATH_DOUBLE_RADTODEGREEFACTOR); } + void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*((T)MATH_DOUBLE_DEGREETORADFACTOR); FixRange(); } + int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; } + T Abs() const { return (a >= 0) ? a : -a; } + + bool operator== (const Angle& b) const { return a == b.a; } + bool operator!= (const Angle& b) const { return a != b.a; } +// bool operator< (const Angle& b) const { return a < a.b; } +// bool operator> (const Angle& b) const { return a > a.b; } +// bool operator<= (const Angle& b) const { return a <= a.b; } +// bool operator>= (const Angle& b) const { return a >= a.b; } +// bool operator= (const T& x) { a = x; FixRange(); } + + // These operations assume a is already between -Pi and Pi. + Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; } + Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; } + Angle operator+ (const Angle& b) const { Angle res = *this; res += b; return res; } + Angle operator+ (const T& x) const { Angle res = *this; res += x; return res; } + Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; } + Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; } + Angle operator- (const Angle& b) const { Angle res = *this; res -= b; return res; } + Angle operator- (const T& x) const { Angle res = *this; res -= x; return res; } + + T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= ((T)MATH_DOUBLE_PI)) ? c : ((T)MATH_DOUBLE_TWOPI) - c; } + +private: + + // The stored angle, which should be maintained between -Pi and Pi + T a; + + // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side + inline void FastFixRange() + { + if (a < -((T)MATH_DOUBLE_PI)) + a += ((T)MATH_DOUBLE_TWOPI); + else if (a > ((T)MATH_DOUBLE_PI)) + a -= ((T)MATH_DOUBLE_TWOPI); + } + + // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method + inline void FixRange() + { + // do nothing if the value is already in the correct range, since fmod call is expensive + if (a >= -((T)MATH_DOUBLE_PI) && a <= ((T)MATH_DOUBLE_PI)) + return; + a = fmod(a,((T)MATH_DOUBLE_TWOPI)); + if (a < -((T)MATH_DOUBLE_PI)) + a += ((T)MATH_DOUBLE_TWOPI); + else if (a > ((T)MATH_DOUBLE_PI)) + a -= ((T)MATH_DOUBLE_TWOPI); + } +}; + + +typedef Angle Anglef; +typedef Angle Angled; + + +//------------------------------------------------------------------------------------- +// ***** Plane + +// Consists of a normal vector and distance from the origin where the plane is located. + +template +class Plane +{ +public: + Vector3 N; + T D; + + Plane() : D(0) {} + + // Normals must already be normalized + Plane(const Vector3& n, T d) : N(n), D(d) {} + Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {} + + // construct from a point on the plane and the normal + Plane(const Vector3& p, const Vector3& n) : N(n), D(-(p * n)) {} + + // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane). + T TestSide(const Vector3& p) const + { + return (N.Dot(p)) + D; + } + + Plane Flipped() const + { + return Plane(-N, -D); + } + + void Flip() + { + N = -N; + D = -D; + } + + bool operator==(const Plane& rhs) const + { + return (this->D == rhs.D && this->N == rhs.N); + } +}; + +typedef Plane Planef; +typedef Plane Planed; + + + + +//----------------------------------------------------------------------------------- +// ***** ScaleAndOffset2D + +struct ScaleAndOffset2D +{ + Vector2f Scale; + Vector2f Offset; + + ScaleAndOffset2D(float sx = 0.0f, float sy = 0.0f, float ox = 0.0f, float oy = 0.0f) + : Scale(sx, sy), Offset(ox, oy) + { } +}; + + +//----------------------------------------------------------------------------------- +// ***** FovPort + +// FovPort describes Field Of View (FOV) of a viewport. +// This class has values for up, down, left and right, stored in +// tangent of the angle units to simplify calculations. +// +// As an example, for a standard 90 degree vertical FOV, we would +// have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }. +// +// CreateFromRadians/Degrees helper functions can be used to +// access FOV in different units. + + +// ***** FovPort + +struct FovPort +{ + float UpTan; + float DownTan; + float LeftTan; + float RightTan; + + FovPort ( float sideTan = 0.0f ) : + UpTan(sideTan), DownTan(sideTan), LeftTan(sideTan), RightTan(sideTan) { } + FovPort ( float u, float d, float l, float r ) : + UpTan(u), DownTan(d), LeftTan(l), RightTan(r) { } + + // C-interop support: FovPort <-> ovrFovPort (implementation in OVR_CAPI.cpp). + FovPort(const ovrFovPort &src) + : UpTan(src.UpTan), DownTan(src.DownTan), LeftTan(src.LeftTan), RightTan(src.RightTan) + { } + + operator ovrFovPort () const + { + ovrFovPort result; + result.LeftTan = LeftTan; + result.RightTan = RightTan; + result.UpTan = UpTan; + result.DownTan = DownTan; + return result; + } + + static FovPort CreateFromRadians(float horizontalFov, float verticalFov) + { + FovPort result; + result.UpTan = tanf ( verticalFov * 0.5f ); + result.DownTan = tanf ( verticalFov * 0.5f ); + result.LeftTan = tanf ( horizontalFov * 0.5f ); + result.RightTan = tanf ( horizontalFov * 0.5f ); + return result; + } + + static FovPort CreateFromDegrees(float horizontalFovDegrees, + float verticalFovDegrees) + { + return CreateFromRadians(DegreeToRad(horizontalFovDegrees), + DegreeToRad(verticalFovDegrees)); + } + + // Get Horizontal/Vertical components of Fov in radians. + float GetVerticalFovRadians() const { return atanf(UpTan) + atanf(DownTan); } + float GetHorizontalFovRadians() const { return atanf(LeftTan) + atanf(RightTan); } + // Get Horizontal/Vertical components of Fov in degrees. + float GetVerticalFovDegrees() const { return RadToDegree(GetVerticalFovRadians()); } + float GetHorizontalFovDegrees() const { return RadToDegree(GetHorizontalFovRadians()); } + + // Compute maximum tangent value among all four sides. + float GetMaxSideTan() const + { + return OVRMath_Max(OVRMath_Max(UpTan, DownTan), OVRMath_Max(LeftTan, RightTan)); + } + + static ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort tanHalfFov ) + { + float projXScale = 2.0f / ( tanHalfFov.LeftTan + tanHalfFov.RightTan ); + float projXOffset = ( tanHalfFov.LeftTan - tanHalfFov.RightTan ) * projXScale * 0.5f; + float projYScale = 2.0f / ( tanHalfFov.UpTan + tanHalfFov.DownTan ); + float projYOffset = ( tanHalfFov.UpTan - tanHalfFov.DownTan ) * projYScale * 0.5f; + + ScaleAndOffset2D result; + result.Scale = Vector2f(projXScale, projYScale); + result.Offset = Vector2f(projXOffset, projYOffset); + // Hey - why is that Y.Offset negated? + // It's because a projection matrix transforms from world coords with Y=up, + // whereas this is from NDC which is Y=down. + + return result; + } + + // Converts Fov Tan angle units to [-1,1] render target NDC space + Vector2f TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle) + { + ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this); + return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset; + } + + // Compute per-channel minimum and maximum of Fov. + static FovPort Min(const FovPort& a, const FovPort& b) + { + FovPort fov( OVRMath_Min( a.UpTan , b.UpTan ), + OVRMath_Min( a.DownTan , b.DownTan ), + OVRMath_Min( a.LeftTan , b.LeftTan ), + OVRMath_Min( a.RightTan, b.RightTan ) ); + return fov; + } + + static FovPort Max(const FovPort& a, const FovPort& b) + { + FovPort fov( OVRMath_Max( a.UpTan , b.UpTan ), + OVRMath_Max( a.DownTan , b.DownTan ), + OVRMath_Max( a.LeftTan , b.LeftTan ), + OVRMath_Max( a.RightTan, b.RightTan ) ); + return fov; + } +}; + + +} // Namespace OVR + + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + +#endif diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR.h new file mode 100644 index 000000000000..546e4475678b --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR.h @@ -0,0 +1,40 @@ +/************************************************************************************ + +Filename : OVR.h +Content : The main public interface to Oculus for C++ Developers. + Includes C API and helper classes. + +Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_h +#define OVR_h + +#include "OVR_Version.h" +#include "OVR_CAPI.h" + +/* The following includes are deprecated from this location and will be removed from a future version of this library. */ +#include "Kernel/OVR_Types.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Alg.h" +#include "Extras/OVR_Math.h" + +#endif + diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI.h new file mode 100644 index 000000000000..3a4f4b03cce1 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI.h @@ -0,0 +1 @@ +#include "OVR_CAPI_0_6_0.h" diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_0_6_0.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_0_6_0.h new file mode 100644 index 000000000000..54fe487fc09c --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_0_6_0.h @@ -0,0 +1,1628 @@ +/********************************************************************************//** + +\file OVR_CAPI_0_6_0.h +\brief C Interface to the Oculus PC SDK tracking and rendering library. + +\copyright Copyright 2014 Oculus VR, LLC All Rights reserved. +\n +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. +\n +You may obtain a copy of the License at +\n +http://www.oculusvr.com/licenses/LICENSE-3.2 +\n +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_CAPI_h // We don't use version numbers within this name, as all versioned variations of this file are currently mutually exclusive. +#define OVR_CAPI_h ///< Header include guard + + +#include "OVR_CAPI_Keys.h" +#include "OVR_Version.h" +#include "OVR_ErrorCode.h" + + +#include + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_OS +// +#if !defined(OVR_OS_WIN32) && defined(_WIN32) + #define OVR_OS_WIN32 +#endif + +#if !defined(OVR_OS_MAC) && defined(__APPLE__) + #define OVR_OS_MAC +#endif + +#if !defined(OVR_OS_LINUX) && defined(__linux__) + #define OVR_OS_LINUX +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_CPP +// +#if !defined(OVR_CPP) + #if defined(__cplusplus) + #define OVR_CPP(x) x + #else + #define OVR_CPP(x) /* Not C++ */ + #endif +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_CDECL +// +/// LibOVR calling convention for 32-bit Windows builds. +// +#if !defined(OVR_CDECL) + #if defined(_WIN32) + #define OVR_CDECL __cdecl + #else + #define OVR_CDECL + #endif +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_EXTERN_C +// +/// Defined as extern "C" when built from C++ code. +// +#if !defined(OVR_EXTERN_C) + #ifdef __cplusplus + #define OVR_EXTERN_C extern "C" + #else + #define OVR_EXTERN_C + #endif +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_PUBLIC_FUNCTION / OVR_PRIVATE_FUNCTION +// +// OVR_PUBLIC_FUNCTION - Functions that externally visible from a shared library. Corresponds to Microsoft __dllexport. +// OVR_PUBLIC_CLASS - C++ structs and classes that are externally visible from a shared library. Corresponds to Microsoft __dllexport. +// OVR_PRIVATE_FUNCTION - Functions that are not visible outside of a shared library. They are private to the shared library. +// OVR_PRIVATE_CLASS - C++ structs and classes that are not visible outside of a shared library. They are private to the shared library. +// +// OVR_DLL_BUILD - Used to indicate that the current compilation unit is of a shared library. +// OVR_DLL_IMPORT - Used to indicate that the current compilation unit is a user of the corresponding shared library. +// OVR_DLL_BUILD - used to indicate that the current compilation unit is not a shared library but rather statically linked code. +// +#if !defined(OVR_PUBLIC_FUNCTION) + #if defined(OVR_DLL_BUILD) + #if defined(_WIN32) + #define OVR_PUBLIC_FUNCTION(rval) OVR_EXTERN_C __declspec(dllexport) rval OVR_CDECL + #define OVR_PUBLIC_CLASS __declspec(dllexport) + #define OVR_PRIVATE_FUNCTION + #define OVR_PRIVATE_CLASS + #else + #define OVR_PUBLIC_FUNCTION(rval) OVR_EXTERN_C __attribute__((visibility("default"))) rval OVR_CDECL /* Requires GCC 4.0+ */ + #define OVR_PUBLIC_CLASS __attribute__((visibility("default"))) /* Requires GCC 4.0+ */ + #define OVR_PRIVATE_FUNCTION __attribute__((visibility("hidden"))) + #define OVR_PRIVATE_CLASS __attribute__((visibility("hidden"))) + #endif + #elif defined(OVR_DLL_IMPORT) + #if defined(_WIN32) + #define OVR_PUBLIC_FUNCTION(rval) OVR_EXTERN_C __declspec(dllimport) rval OVR_CDECL + #define OVR_PUBLIC_CLASS __declspec(dllimport) + #define OVR_PRIVATE_FUNCTION + #define OVR_PRIVATE_CLASS + #else + #define OVR_PUBLIC_FUNCTION(rval) OVR_EXTERN_C rval OVR_CDECL + #define OVR_PUBLIC_CLASS + #define OVR_PRIVATE_FUNCTION + #define OVR_PRIVATE_CLASS + #endif + #else // OVR_STATIC_BUILD + #define OVR_PUBLIC_FUNCTION(rval) OVR_EXTERN_C rval OVR_CDECL + #define OVR_PUBLIC_CLASS + #define OVR_PRIVATE_FUNCTION + #define OVR_PRIVATE_CLASS + #endif +#endif + + +//----------------------------------------------------------------------------------- +// ***** OVR_EXPORT +// +/// Provided for backward compatibility with older versions of this library. +// +#if !defined(OVR_EXPORT) + #ifdef OVR_OS_WIN32 + #define OVR_EXPORT __declspec(dllexport) + #else + #define OVR_EXPORT + #endif +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_ALIGNAS +// +#if !defined(OVR_ALIGNAS) + // C++11 alignas + #if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 408) && (defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)) + #define OVR_ALIGNAS(n) alignas(n) + #elif defined(__clang__) && !defined(__APPLE__) && (((__clang_major__ * 100) + __clang_minor__) >= 300) && (__cplusplus >= 201103L) + #define OVR_ALIGNAS(n) alignas(n) + #elif defined(__clang__) && defined(__APPLE__) && (((__clang_major__ * 100) + __clang_minor__) >= 401) && (__cplusplus >= 201103L) + #define OVR_ALIGNAS(n) alignas(n) + #elif defined(_MSC_VER) && (_MSC_VER >= 1900) + #define OVR_ALIGNAS(n) alignas(n) + #elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 408) + #define OVR_ALIGNAS(n) alignas(n) + + // Pre-C++11 alignas fallbacks + #elif defined(__GNUC__) || defined(__clang__) + #define OVR_ALIGNAS(n) __attribute__((aligned(n))) + #elif defined(_MSC_VER) || defined(__INTEL_COMPILER) + #define OVR_ALIGNAS(n) __declspec(align(n)) // For Microsoft the alignment must be a literal integer. + #elif defined(__CC_ARM) + #define OVR_ALIGNAS(n) __align(n) + #else + #error Need to define OVR_ALIGNAS + #endif +#endif + + + +//----------------------------------------------------------------------------------- +// ***** Padding +// +/// Defines explicitly unused space for a struct. +/// When used correcly, usage of this macro should not change the size of the struct. +/// Compile-time and runtime behavior with and without this defined should be identical. +/// +#if !defined(OVR_UNUSED_STRUCT_PAD) + #define OVR_UNUSED_STRUCT_PAD(padName, size) char padName[size]; +#endif + + +//----------------------------------------------------------------------------------- +// ***** Word Size +// +/// Specifies the size of a pointer on the given platform. +/// +#if !defined(OVR_PTR_SIZE) + #if defined(__WORDSIZE) + #define OVR_PTR_SIZE ((__WORDSIZE) / 8) + #elif defined(_WIN64) || defined(__LP64__) || defined(_LP64) || defined(_M_IA64) || defined(__ia64__) || defined(__arch64__) || defined(__64BIT__) || defined(__Ptr_Is_64) + #define OVR_PTR_SIZE 8 + #elif defined(__CC_ARM) && (__sizeof_ptr == 8) + #define OVR_PTR_SIZE 8 + #else + #define OVR_PTR_SIZE 4 + #endif +#endif + + +//----------------------------------------------------------------------------------- +// ***** OVR_ON32 / OVR_ON64 +// +#if OVR_PTR_SIZE == 8 + #define OVR_ON32(x) + #define OVR_ON64(x) x +#else + #define OVR_ON32(x) x + #define OVR_ON64(x) +#endif + + +//----------------------------------------------------------------------------------- +// ***** ovrBool + +typedef char ovrBool; ///< Boolean type +#define ovrFalse 0 ///< ovrBool value of false. +#define ovrTrue 1 ///< ovrBool value of true. + + +//----------------------------------------------------------------------------------- +// ***** Simple Math Structures + +/// A 2D vector with integer components. +typedef struct OVR_ALIGNAS(4) ovrVector2i_ +{ + int x, y; +} ovrVector2i; + +/// A 2D size with integer components. +typedef struct OVR_ALIGNAS(4) ovrSizei_ +{ + int w, h; +} ovrSizei; + +/// A 2D rectangle with a position and size. +/// All components are integers. +typedef struct OVR_ALIGNAS(4) ovrRecti_ +{ + ovrVector2i Pos; + ovrSizei Size; +} ovrRecti; + +/// A quaternion rotation. +typedef struct OVR_ALIGNAS(4) ovrQuatf_ +{ + float x, y, z, w; +} ovrQuatf; + +/// A 2D vector with float components. +typedef struct OVR_ALIGNAS(4) ovrVector2f_ +{ + float x, y; +} ovrVector2f; + +/// A 3D vector with float components. +typedef struct OVR_ALIGNAS(4) ovrVector3f_ +{ + float x, y, z; +} ovrVector3f; + +/// A 4x4 matrix with float elements. +typedef struct OVR_ALIGNAS(4) ovrMatrix4f_ +{ + float M[4][4]; +} ovrMatrix4f; + + +/// Position and orientation together. +typedef struct OVR_ALIGNAS(4) ovrPosef_ +{ + ovrQuatf Orientation; + ovrVector3f Position; +} ovrPosef; + +/// A full pose (rigid body) configuration with first and second derivatives. +/// +/// Body refers to any object for which ovrPoseStatef is providing data. +/// It can be the camera or something else; the context depends on the usage of the struct. +typedef struct OVR_ALIGNAS(8) ovrPoseStatef_ +{ + ovrPosef ThePose; ///< The body's position and orientation. + ovrVector3f AngularVelocity; ///< The body's angular velocity in radians per second. + ovrVector3f LinearVelocity; ///< The body's velocity in meters per second. + ovrVector3f AngularAcceleration; ///< The body's angular acceleration in radians per second per second. + ovrVector3f LinearAcceleration; ///< The body's acceleration in meters per second per second. + OVR_UNUSED_STRUCT_PAD(pad0, 4) ///< \internal struct pad. + double TimeInSeconds; ///< Absolute time of this state sample. +} ovrPoseStatef; + +/// Describes the up, down, left, and right angles of the field of view. +/// +/// Field Of View (FOV) tangent of the angle units. +/// \note For a standard 90 degree vertical FOV, we would +/// have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }. +typedef struct OVR_ALIGNAS(4) ovrFovPort_ +{ + float UpTan; ///< The tangent of the angle between the viewing vector and the top edge of the field of view. + float DownTan; ///< The tangent of the angle between the viewing vector and the bottom edge of the field of view. + float LeftTan; ///< The tangent of the angle between the viewing vector and the left edge of the field of view. + float RightTan; ///< The tangent of the angle between the viewing vector and the right edge of the field of view. +} ovrFovPort; + + +//----------------------------------------------------------------------------------- +// ***** HMD Types + +/// Enumerates all HMD types that we support. +/// +/// The currently released developer kits are ovrHmd_DK1 and ovrHmd_DK2. The other enumerations are for internal use only. +typedef enum ovrHmdType_ +{ + ovrHmd_None = 0, + ovrHmd_DK1 = 3, + ovrHmd_DKHD = 4, + ovrHmd_DK2 = 6, + ovrHmd_BlackStar = 7, + ovrHmd_CB = 8, + ovrHmd_Other = 9, + ovrHmd_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrHmdType; + + +/// HMD capability bits reported by device. +/// +/// Set (read/write) flags through ovrHmd_SetEnabledCaps() +typedef enum ovrHmdCaps_ +{ + // Read-only flags. + ovrHmdCap_DebugDevice = 0x0010, ///< (read only) Specifies that the HMD is a virtual debug device. + + /// \brief (read/write) Toggles low persistence mode on or off. + /// \details This setting reduces eye-tracking based motion blur. Eye-tracking based motion blur is caused by the viewer's focal point + /// moving more pixels than have refreshed in the same period of time.\n + /// The disadvantage of this setting is that this reduces the average brightness of the display and causes some users to perceive flicker.\n + /// There is no performance cost for this option. Oculus recommends exposing it to the user as an optional setting. + ovrHmdCap_LowPersistence = 0x0080, + ovrHmdCap_DynamicPrediction = 0x0200, ///< (read/write) Adjusts prediction dynamically based on internally measured latency. + ovrHmdCap_NoVSync = 0x1000, ///< (read/write) Supports rendering without VSync for debugging. + + /// Indicates to the developer what caps they can and cannot modify. These are processed by the client. + ovrHmdCap_Writable_Mask = ovrHmdCap_LowPersistence | + ovrHmdCap_DynamicPrediction | + ovrHmdCap_NoVSync, + + /// \internal Indicates to the developer what caps they can and cannot modify. These are processed by the service. + ovrHmdCap_Service_Mask = ovrHmdCap_LowPersistence | + ovrHmdCap_DynamicPrediction + , ovrHmdCap_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrHmdCaps; + + +/// Tracking capability bits reported by the device. +/// Used with ovrHmd_ConfigureTracking. +typedef enum ovrTrackingCaps_ +{ + ovrTrackingCap_Orientation = 0x0010, ///< Supports orientation tracking (IMU). + ovrTrackingCap_MagYawCorrection = 0x0020, ///< Supports yaw drift correction via a magnetometer or other means. + ovrTrackingCap_Position = 0x0040, ///< Supports positional tracking. + /// Overriding the other flags, this causes the application + /// to ignore tracking settings. This is the internal + /// default before ovrHmd_ConfigureTracking is called. + ovrTrackingCap_Idle = 0x0100, + ovrTrackingCap_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrTrackingCaps; + + +/// Specifies which eye is being used for rendering. +/// This type explicitly does not include a third "NoStereo" monoscopic option, as such is +/// not required for an HMD-centered API. +typedef enum ovrEyeType_ +{ + ovrEye_Left = 0, ///< The left eye, from the viewer's perspective. + ovrEye_Right = 1, ///< The right eye, from the viewer's perspective. + ovrEye_Count = 2, ///< \internal Count of enumerated elements. + ovrEye_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrEyeType; + + +/// This is a complete descriptor of the HMD. +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrHmdDesc_ +{ + struct ovrHmdStruct* Handle; ///< Internal handle of this HMD. + ovrHmdType Type; ///< The type of HMD. + OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad0, 4)) ///< \internal struct paddding. + const char* ProductName; ///< UTF8-encoded product identification string (e.g. "Oculus Rift DK1"). + const char* Manufacturer; ///< UTF8-encoded HMD manufacturer identification string. + short VendorId; ///< HID (USB) vendor identifier of the device. + short ProductId; ///< HID (USB) product identifier of the device. + char SerialNumber[24]; ///< Sensor (and display) serial number. + short FirmwareMajor; ///< Sensor firmware major version. + short FirmwareMinor; ///< Sensor firmware minor version. + float CameraFrustumHFovInRadians; ///< External tracking camera frustum horizontal field-of-view (if present). + float CameraFrustumVFovInRadians; ///< External tracking camera frustum vertical field-of-view (if present). + float CameraFrustumNearZInMeters; ///< External tracking camera frustum near Z (if present). + float CameraFrustumFarZInMeters; ///< External tracking camera frustum near Z (if present). + unsigned int HmdCaps; ///< Capability bits described by ovrHmdCaps. + unsigned int TrackingCaps; ///< Capability bits described by ovrTrackingCaps. + ovrFovPort DefaultEyeFov[ovrEye_Count]; ///< Defines the recommended FOVs for the HMD. + ovrFovPort MaxEyeFov[ovrEye_Count]; ///< Defines the maximum FOVs for the HMD. + ovrEyeType EyeRenderOrder[ovrEye_Count]; ///< Preferred eye rendering order for best performance. Can help reduce latency on sideways-scanned screens. + ovrSizei Resolution; ///< Resolution of the full HMD screen (both eyes) in pixels. + +} ovrHmdDesc; + +/// Type used by ovrHmd_* functions. +typedef const ovrHmdDesc* ovrHmd; + + +/// Bit flags describing the current status of sensor tracking. +// The values must be the same as in enum StatusBits +/// +/// \see ovrTrackingState +/// +typedef enum ovrStatusBits_ +{ + ovrStatus_OrientationTracked = 0x0001, ///< Orientation is currently tracked (connected and in use). + ovrStatus_PositionTracked = 0x0002, ///< Position is currently tracked (false if out of range). + ovrStatus_CameraPoseTracked = 0x0004, ///< Camera pose is currently tracked. + ovrStatus_PositionConnected = 0x0020, ///< Position tracking hardware is connected. + ovrStatus_HmdConnected = 0x0080, ///< HMD Display is available and connected. + ovrStatus_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrStatusBits; + + +/// Specifies a reading we can query from the sensor. +/// +/// \see ovrTrackingState +/// +typedef struct OVR_ALIGNAS(4) ovrSensorData_ +{ + ovrVector3f Accelerometer; ///< Acceleration reading in meters/second^2. + ovrVector3f Gyro; ///< Rotation rate in radians/second. + ovrVector3f Magnetometer; ///< Magnetic field in Gauss. + float Temperature; ///< Temperature of the sensor in degrees Celsius. + float TimeInSeconds; ///< Time when the reported IMU reading took place in seconds. \see ovr_GetTimeInSeconds +} ovrSensorData; + + + +/// @cond DoxygenIgnore +/// @endcond + + + +/// Tracking state at a given absolute time (describes predicted HMD pose, etc.). +/// Returned by ovrHmd_GetTrackingState. +/// +/// \see ovrHmd_GetTrackingState +/// +typedef struct OVR_ALIGNAS(8) ovrTrackingState_ +{ + /// Predicted head pose (and derivatives) at the requested absolute time. + /// The look-ahead interval is equal to (HeadPose.TimeInSeconds - RawSensorData.TimeInSeconds). + ovrPoseStatef HeadPose; + + /// Current pose of the external camera (if present). + /// This pose includes camera tilt (roll and pitch). For a leveled coordinate + /// system use LeveledCameraPose. + ovrPosef CameraPose; + + /// Camera frame aligned with gravity. + /// This value includes position and yaw of the camera, but not roll and pitch. + /// It can be used as a reference point to render real-world objects in the correct location. + ovrPosef LeveledCameraPose; + + /// The most recent sensor data received from the HMD. + ovrSensorData RawSensorData; + + /// Tracking status described by ovrStatusBits. + unsigned int StatusFlags; + + /// Tags the vision processing results to a certain frame counter number. + uint32_t LastCameraFrameCounter; + + OVR_UNUSED_STRUCT_PAD(pad0, 4) ///< \internal struct padding +} ovrTrackingState; + + +/// Frame timing data reported by ovrHmd_GetFrameTiming. +/// +/// \see ovrHmd_GetFrameTiming +/// +typedef struct OVR_ALIGNAS(8) ovrFrameTiming_ +{ + /// A point in time when the middle of the screen will be displayed. For global shutter, + /// this will be the display time. For rolling shutter this is a point at which half the image has + /// been displayed. This value can be passed as an absolute time to ovrHmd_GetTrackingState + /// to get the best predicted pose for rendering the scene. + double DisplayMidpointSeconds; + + /// Display interval between the frames. This will generally be 1 / RefreshRate of the HMD; + /// however, it may vary slightly during runtime based on video cart scan-out timing. + double FrameIntervalSeconds; + + /// Application frame index for which we requested timing. + unsigned AppFrameIndex; + + /// HW display frame index that we expect this application frame will hit; this is the frame that + /// will be displayed at DisplayMidpointSeconds. This value is monotonically increasing with each v-sync. + unsigned DisplayFrameIndex; +} ovrFrameTiming; + + +/// Rendering information for each eye. Computed by ovrHmd_GetRenderDesc() based on the +/// specified FOV. Note that the rendering viewport is not included +/// here as it can be specified separately and modified per frame by +/// passing different Viewport values in the layer structure. +/// +/// \see ovrHmd_GetRenderDesc +/// +typedef struct OVR_ALIGNAS(4) ovrEyeRenderDesc_ +{ + ovrEyeType Eye; ///< The eye index to which this instance corresponds. + ovrFovPort Fov; ///< The field of view. + ovrRecti DistortedViewport; ///< Distortion viewport. + ovrVector2f PixelsPerTanAngleAtCenter; ///< How many display pixels will fit in tan(angle) = 1. + ovrVector3f HmdToEyeViewOffset; ///< Translation of each eye. +} ovrEyeRenderDesc; + + +/// Projection information for ovrLayerEyeFovDepth. +/// +/// Use the utility function ovrTimewarpProjectionDesc_FromProjection to +/// generate this structure from the application's projection matrix. +/// +/// \see ovrLayerEyeFovDepth, ovrTimewarpProjectionDesc_FromProjection +/// +typedef struct OVR_ALIGNAS(4) ovrTimewarpProjectionDesc_ +{ + float Projection22; ///< Projection matrix element [2][2]. + float Projection23; ///< Projection matrix element [2][3]. + float Projection32; ///< Projection matrix element [3][2]. +} ovrTimewarpProjectionDesc; + + +/// Contains the data necessary to properly calculate position info for various layer types. +/// - HmdToEyeViewOffset is the same value pair provided in ovrEyeRenderDesc. +/// - HmdSpaceToWorldScaleInMeters is used to scale player motion into in-application units. +/// In other words, it is how big an in-application unit is in the player's physical meters. +/// For example, if the application uses inches as its units then HmdSpaceToWorldScaleInMeters would be 0.0254. +/// Note that if you are scaling the player in size, this must also scale. So if your application +/// units are inches, but you're shrinking the player to half their normal size, then +/// HmdSpaceToWorldScaleInMeters would be 0.0254*2.0. +/// +/// \see ovrEyeRenderDesc, ovrHmd_SubmitFrame +/// +typedef struct OVR_ALIGNAS(4) ovrViewScaleDesc_ +{ + ovrVector3f HmdToEyeViewOffset[ovrEye_Count]; ///< Translation of each eye. + float HmdSpaceToWorldScaleInMeters; ///< Ratio of viewer units to meter units. +} ovrViewScaleDesc; + + +//----------------------------------------------------------------------------------- +// ***** Platform-independent Rendering Configuration + +/// These types are used to hide platform-specific details when passing +/// render device, OS, and texture data to the API. +/// +/// The benefit of having these wrappers versus platform-specific API functions is +/// that they allow application glue code to be portable. A typical example is an +/// engine that has multiple back ends, such as GL and D3D. Portable code that calls +/// these back ends can also use LibOVR. To do this, back ends can be modified +/// to return portable types such as ovrTexture and ovrRenderAPIConfig. +typedef enum ovrRenderAPIType_ +{ + ovrRenderAPI_None, ///< No API + ovrRenderAPI_OpenGL, ///< OpenGL + ovrRenderAPI_Android_GLES, ///< OpenGL ES + ovrRenderAPI_D3D9_Obsolete, ///< DirectX 9. Obsolete + ovrRenderAPI_D3D10_Obsolete, ///< DirectX 10. Obsolete + ovrRenderAPI_D3D11, ///< DirectX 11. + ovrRenderAPI_Count, ///< \internal Count of enumerated elements. + ovrRenderAPI_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrRenderAPIType; + + +/// API-independent part of a texture descriptor. +/// +/// ovrTextureHeader is a common struct present in all ovrTexture struct types. +/// +typedef struct OVR_ALIGNAS(4) ovrTextureHeader_ +{ + ovrRenderAPIType API; ///< The API type to which this texture belongs. + ovrSizei TextureSize; ///< Size of this texture in pixels. +} ovrTextureHeader; + + +/// Contains platform-specific information about a texture. +/// Aliases to one of ovrD3D11Texture or ovrGLTexture. +/// +/// \see ovrD3D11Texture, ovrGLTexture. +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrTexture_ +{ + ovrTextureHeader Header; ///< API-independent header. + OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad0, 4)) ///< \internal struct padding + uintptr_t PlatformData[8]; ///< Specialized in ovrGLTextureData, ovrD3D11TextureData etc. +} ovrTexture; + + +/// Describes a set of textures that act as a rendered flip chain. +/// +/// An ovrSwapTextureSet per layer is passed to ovrHmd_SubmitFrame via one of the ovrLayer types. +/// The TextureCount refers to the flip chain count and not an eye count. +/// See the layer structs and functions for information about how to use ovrSwapTextureSet. +/// +/// ovrSwapTextureSets must be created by either the ovrHmd_CreateSwapTextureSetD3D11 or +/// ovrHmd_CreateSwapTextureSetGL factory function, and must be destroyed by ovrHmd_DestroySwapTextureSet. +/// +/// \see ovrHmd_CreateSwapTextureSetD3D11, ovrHmd_CreateSwapTextureSetGL, ovrHmd_DestroySwapTextureSet. +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrSwapTextureSet_ +{ + ovrTexture* Textures; ///< Points to an array of ovrTextures. + int TextureCount; ///< The number of textures referenced by the Textures array. + + /// CurrentIndex specifies which of the Textures will be used by the ovrHmd_SubmitFrame call. + /// This is manually incremented by the application, typically in a round-robin manner. + /// + /// Before selecting a Texture as a rendertarget, the application should increment CurrentIndex by + /// 1 and wrap it back to 0 if CurrentIndex == TextureCount, so that it gets a fresh rendertarget, + /// one that is not currently being used for display. It can then render to Textures[CurrentIndex]. + /// + /// After rendering, the application calls ovrHmd_SubmitFrame using that same CurrentIndex value + /// to display the new rendertarget. + /// + /// The application can submit multiple frames with the same ovrSwapTextureSet and CurrentIndex + /// value if the rendertarget does not need to be updated, for example when displaying an + /// information display whose text has not changed since the previous frame. + /// + /// Multiple layers can use the same ovrSwapTextureSet at the same time - there is no need to + /// create a unique ovrSwapTextureSet for each layer. However, all the layers using a particular + /// ovrSwapTextureSet will share the same value of CurrentIndex, so they cannot use different + /// textures within the ovrSwapTextureSet. + /// + /// Once a particular Textures[CurrentIndex] has been sent to ovrHmd_SubmitFrame, that texture + /// should not be rendered to until a subsequent ovrHmd_SubmitFrame is made (either with a + /// different CurrentIndex value, or with a different ovrSwapTextureSet, or disabling the layer). + int CurrentIndex; +} ovrSwapTextureSet; + + + +//----------------------------------------------------------------------------------- +// ***** Initialize structures + +/// Initialization flags. +/// +/// \see ovrInitParams, ovr_Initialize +/// +typedef enum ovrInitFlags_ +{ + /// When a debug library is requested, a slower debugging version of the library will + /// run which can be used to help solve problems in the library and debug application code. + ovrInit_Debug = 0x00000001, + + /// When ServerOptional is set, the ovr_Initialize() call not will block waiting for + /// the server to respond. If the server is not reachable, it might still succeed. + ovrInit_ServerOptional = 0x00000002, + + /// When a version is requested, the LibOVR runtime respects the RequestedMinorVersion + /// field and verifies that the RequestedMinorVersion is supported. + ovrInit_RequestVersion = 0x00000004, + + /// Forces debug features of LibOVR off explicitly, even if it is built in debug mode. + ovrInit_ForceNoDebug = 0x00000008, + + + ovrInit_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrInitFlags; + + +/// Logging levels +/// +/// \see ovrInitParams, ovrLogCallback +/// +typedef enum ovrLogLevel_ +{ + ovrLogLevel_Debug = 0, ///< Debug-level log event. + ovrLogLevel_Info = 1, ///< Info-level log event. + ovrLogLevel_Error = 2, ///< Error-level log event. + + ovrLogLevel_EnumSize = 0x7fffffff ///< \internal Force type int32_t. +} ovrLogLevel; + + +/// Signature of the logging callback function pointer type. +/// +/// \param[in] level is one of the ovrLogLevel constants. +/// \param[in] message is a UTF8-encoded null-terminated string. +/// \see ovrInitParams, ovrLogLevel, ovr_Initialize +/// +typedef void (OVR_CDECL* ovrLogCallback)(int level, const char* message); + + +/// Parameters for ovr_Initialize. +/// +/// \see ovr_Initialize +/// +typedef struct OVR_ALIGNAS(8) ovrInitParams_ +{ + /// Flags from ovrInitFlags to override default behavior. + /// Use 0 for the defaults. + uint32_t Flags; + + /// Requests a specific minimum minor version of the LibOVR runtime. + /// Flags must include ovrInit_RequestVersion or this will be ignored + /// and OVR_MINOR_VERSION will be used. + uint32_t RequestedMinorVersion; + + /// User-supplied log callback function, which may be called at any time + /// asynchronously from multiple threads until ovr_Shutdown completes. + /// Use NULL to specify no log callback. + ovrLogCallback LogCallback; + + /// Relative number of milliseconds to wait for a connection to the server + /// before failing. Use 0 for the default timeout. + uint32_t ConnectionTimeoutMS; + + OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad0, 4)) ///< \internal + +} ovrInitParams; + + +#ifdef __cplusplus +extern "C" { +#endif + + +// ----------------------------------------------------------------------------------- +// ***** API Interfaces + +// Overview of the API +// +// Setup: +// - ovr_Initialize(). +// - ovrHmd_Create(0, &hmd). +// - Call ovrHmd_ConfigureTracking() to configure and initialize tracking. +// - Use hmd members and ovrHmd_GetFovTextureSize() to determine graphics configuration +// and ovrHmd_GetRenderDesc() to get per-eye rendering parameters. +// - Allocate render target texture sets with ovrHmd_CreateSwapTextureSetD3D11() or +// ovrHmd_CreateSwapTextureSetGL(). +// +// Application Loop: +// - Call ovrHmd_GetFrameTiming() to get the current frame timing information. +// - Call ovrHmd_GetTrackingState() and ovr_CalcEyePoses() to obtain the predicted +// rendering pose for each eye based on timing. +// - Render the scene content into CurrentIndex of ovrTextureSet for each eye and layer +// you plan to update this frame. Increment texture set CurrentIndex. +// - Call ovrHmd_SubmitFrame() to render the distorted layers to the back buffer +// and present them on the HMD. If ovrHmd_SubmitFrame returns ovrSuccess_NotVisible, +// there is no need to render the scene for the next loop iteration. Instead, +// just call ovrHmd_SubmitFrame again until it returns ovrSuccess. +// +// Shutdown: +// - ovrHmd_Destroy(). +// - ovr_Shutdown(). + + +/// Initializes LibOVR +/// +/// Initialize LibOVR for application usage. This includes finding and loading the LibOVRRT +/// shared library. No LibOVR API functions, other than ovr_GetLastErrorInfo, can be called +/// unless ovr_Initialize succeeds. A successful call to ovr_Initialize must be eventually +/// followed by a call to ovr_Shutdown. ovr_Initialize calls are idempotent. +/// Calling ovr_Initialize twice does not require two matching calls to ovr_Shutdown. +/// If already initialized, the return value is ovr_Success. +/// +/// LibOVRRT shared library search order: +/// -# Current working directory (often the same as the application directory). +/// -# Module directory (usually the same as the application directory, +/// but not if the module is a separate shared library). +/// -# Application directory +/// -# Development directory (only if OVR_ENABLE_DEVELOPER_SEARCH is enabled, +/// which is off by default). +/// -# Standard OS shared library search location(s) (OS-specific). +/// +/// \param params Specifies custom initialization options. May be NULL to indicate default options. +/// \return Returns an ovrResult indicating success or failure. In the case of failure, use +/// ovr_GetLastErrorInfo to get more information. Example failed results include: +/// - ovrError_Initialize: Generic initialization error. +/// - ovrError_LibLoad: Couldn't load LibOVRRT. +/// - ovrError_LibVersion: LibOVRRT version incompatibility. +/// - ovrError_ServiceConnection: Couldn't connect to the OVR Service. +/// - ovrError_ServiceVersion: OVR Service version incompatibility. +/// - ovrError_IncompatibleOS: The operating system version is incompatible. +/// - ovrError_DisplayInit: Unable to initialize the HMD display. +/// - ovrError_ServerStart: Unable to start the server. Is it already running? +/// - ovrError_Reinitialization: Attempted to re-initialize with a different version. +/// +/// \see ovr_Shutdown +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovr_Initialize(const ovrInitParams* params); + + +/// Shuts down LibOVR +/// +/// A successful call to ovr_Initialize must be eventually matched by a call to ovr_Shutdown. +/// After calling ovr_Shutdown, no LibOVR functions can be called except ovr_GetLastErrorInfo +/// or another ovr_Initialize. ovr_Shutdown invalidates all pointers, references, and created objects +/// previously returned by LibOVR functions. The LibOVRRT shared library can be unloaded by +/// ovr_Shutdown. +/// +/// \see ovr_Initialize +/// +OVR_PUBLIC_FUNCTION(void) ovr_Shutdown(); + + +/// Provides information about the last error. +/// \see ovr_GetLastErrorInfo +typedef struct ovrErrorInfo_ +{ + ovrResult Result; ///< The result from the last API call that generated an error ovrResult. + char ErrorString[512]; ///< A UTF8-encoded null-terminated English string describing the problem. The format of this string is subject to change in future versions. +} ovrErrorInfo; + + +/// Returns information about the most recent failed return value by the +/// current thread for this library. +/// +/// This function itself can never generate an error. +/// The last error is never cleared by LibOVR, but will be overwritten by new errors. +/// Do not use this call to determine if there was an error in the last API +/// call as successful API calls don't clear the last ovrErrorInfo. +/// To avoid any inconsistency, ovr_GetLastErrorInfo should be called immediately +/// after an API function that returned a failed ovrResult, with no other API +/// functions called in the interim. +/// +/// \param[out] errorInfo The last ovrErrorInfo for the current thread. +/// +/// \see ovrErrorInfo +/// +OVR_PUBLIC_FUNCTION(void) ovr_GetLastErrorInfo(ovrErrorInfo* errorInfo); + + +/// Returns the version string representing the LibOVRRT version. +/// +/// The returned string pointer is valid until the next call to ovr_Shutdown. +/// +/// Note that the returned version string doesn't necessarily match the current +/// OVR_MAJOR_VERSION, etc., as the returned string refers to the LibOVRRT shared +/// library version and not the locally compiled interface version. +/// +/// The format of this string is subject to change in future versions and its contents +/// should not be interpreted. +/// +/// \return Returns a UTF8-encoded null-terminated version string. +/// +OVR_PUBLIC_FUNCTION(const char*) ovr_GetVersionString(); + + +/// Writes a message string to the LibOVR tracing mechanism (if enabled). +/// +/// This message will be passed back to the application via the ovrLogCallback if +/// it was registered. +/// +/// \param[in] level One of the ovrLogLevel constants. +/// \param[in] message A UTF8-encoded null-terminated string. +/// \return returns the strlen of the message or a negative value if the message is too large. +/// +/// \see ovrLogLevel, ovrLogCallback +/// +OVR_PUBLIC_FUNCTION(int) ovr_TraceMessage(int level, const char* message); + + +//------------------------------------------------------------------------------------- +/// @name HMD Management +/// +/// Handles the enumeration, creation, destruction, and properties of an HMD (head-mounted display). +///@{ + +/// Detects or re-detects HMDs and reports the total number detected. +/// +/// This function is useful to determine if an HMD can be created without committing to +/// creating it. For example, an application can use this information to present an HMD selection GUI. +/// +/// If one or more HMDs are present, an integer value is returned which indicates +/// the number present. The number present indicates the range of valid indexes that +/// can be passed to ovrHmd_Create. If no HMDs are present, the return +/// value is zero. If there is an error, a negative error ovrResult value is +/// returned. +/// +/// \return Returns an integer that specifies the number of HMDs currently present. Upon failure, OVR_SUCCESS(result) is false. +/// +/// \see ovrHmd_Create +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_Detect(); + +/// Creates a handle to an HMD which doubles as a description structure. +/// +/// Upon success the returned ovrHmd* must be freed with ovrHmd_Destroy. +/// A second call to ovrHmd_Create with the same index as a previously +/// successful call will result in an error return value. +/// +/// \param[in] index A value in the range of [0 .. ovrHmd_Detect()-1]. +/// \param[out] pHmd Provides a pointer to an ovrHmd which will be written to upon success. +/// \return Returns an ovrResult indicating success or failure. +/// +/// \see ovrHmd_Destroy +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_Create(int index, ovrHmd* pHmd); + +/// Creates a fake HMD used for debugging only. +/// +/// This is not tied to specific hardware, but may be used to debug some of the related rendering. +/// \param[in] type Indicates the HMD type to emulate. +/// \param[out] pHmd Provides a pointer to an ovrHmd which will be written to upon success. +/// \return Returns an ovrResult indicating success or failure. +/// +/// \see ovrHmd_Create +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateDebug(ovrHmdType type, ovrHmd* pHmd); + +/// Destroys the HMD. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \see ovrHmd_Create +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_Destroy(ovrHmd hmd); + +/// Returns ovrHmdCaps bits that are currently enabled. +/// +/// Note that this value is different from ovrHmdDesc::HmdCaps, which describes what +/// capabilities are available for that HMD. +/// +/// \return Returns a combination of zero or more ovrHmdCaps. +/// \see ovrHmdCaps +/// +OVR_PUBLIC_FUNCTION(unsigned int) ovrHmd_GetEnabledCaps(ovrHmd hmd); + +/// Modifies capability bits described by ovrHmdCaps that can be modified, +/// such as ovrHmdCap_LowPersistance. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] hmdCaps A combination of 0 or more ovrHmdCaps. +/// +/// \see ovrHmdCaps +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int hmdCaps); + +//@} + + + +//------------------------------------------------------------------------------------- +/// @name Tracking +/// +/// Tracking functions handle the position, orientation, and movement of the HMD in space. +/// +/// All tracking interface functions are thread-safe, allowing tracking state to be sampled +/// from different threads. +/// +///@{ + +/// Starts sensor sampling, enabling specified capabilities, described by ovrTrackingCaps. +/// +/// Use 0 for both supportedTrackingCaps and requiredTrackingCaps to disable tracking. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// +/// \param[in] supportedTrackingCaps Specifies support that is requested. The function will succeed +/// even if these caps are not available (i.e. sensor or camera is unplugged). Support +/// will automatically be enabled if the device is plugged in later. Software should +/// check ovrTrackingState.StatusFlags for real-time status. +/// +/// \param[in] requiredTrackingCaps Specifies sensor capabilities required at the time of the call. +/// If they are not available, the function will fail. Pass 0 if only specifying +/// supportedTrackingCaps. +/// +/// \return Returns an ovrResult indicating success or failure. In the case of failure, use +/// ovr_GetLastErrorInfo to get more information. +/// +/// \see ovrTrackingCaps +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_ConfigureTracking(ovrHmd hmd, unsigned int supportedTrackingCaps, + unsigned int requiredTrackingCaps); + +/// Re-centers the sensor position and orientation. +/// +/// This resets the (x,y,z) positional components and the yaw orientation component. +/// The Roll and pitch orientation components are always determined by gravity and cannot +/// be redefined. All future tracking will report values relative to this new reference position. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_RecenterPose(ovrHmd hmd); + + +/// Returns tracking state reading based on the specified absolute system time. +/// +/// Pass an absTime value of 0.0 to request the most recent sensor reading. In this case +/// both PredictedPose and SamplePose will have the same value. +/// +/// This may also be used for more refined timing of front buffer rendering logic, and so on. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] absTime Specifies the absolute future time to predict the return +/// ovrTrackingState value. Use 0 to request the most recent tracking state. +/// \return Returns the ovrTrackingState that is predicted for the given absTime. +/// +/// \see ovrTrackingState, ovrHmd_GetEyePoses, ovr_GetTimeInSeconds +/// +OVR_PUBLIC_FUNCTION(ovrTrackingState) ovrHmd_GetTrackingState(ovrHmd hmd, double absTime); + + + +///@} + + + +//------------------------------------------------------------------------------------- +// @name Layers +// +///@{ + +/// Describes layer types that can be passed to ovrHmd_SubmitFrame. +/// Each layer type has an associated struct, such as ovrLayerEyeFov. +/// +/// \see ovrLayerHeader +/// +typedef enum ovrLayerType_ +{ + ovrLayerType_Disabled = 0, ///< Layer is disabled. + ovrLayerType_EyeFov = 1, ///< Described by ovrLayerEyeFov. + ovrLayerType_EyeFovDepth = 2, ///< Described by ovrLayerEyeFovDepth. + ovrLayerType_QuadInWorld = 3, ///< Described by ovrLayerQuad. + ovrLayerType_QuadHeadLocked = 4, ///< Described by ovrLayerQuad. Displayed in front of your face, moving with the head. + ovrLayerType_Direct = 6, ///< Described by ovrLayerDirect. Passthrough for debugging and custom rendering. + ovrLayerType_EnumSize = 0x7fffffff ///< Force type int32_t. +} ovrLayerType; + + +/// Identifies flags used by ovrLayerHeader and which are passed to ovrHmd_SubmitFrame. +/// +/// \see ovrLayerHeader +/// +typedef enum ovrLayerFlags_ +{ + /// ovrLayerFlag_HighQuality mode costs performance, but looks better. + ovrLayerFlag_HighQuality = 0x01, + + /// ovrLayerFlag_TextureOriginAtBottomLeft: the opposite is TopLeft. + /// Generally this is false for D3D, true for OpenGL. + ovrLayerFlag_TextureOriginAtBottomLeft = 0x02 + +} ovrLayerFlags; + + +/// Defines properties shared by all ovrLayer structs, such as ovrLayerEyeFov. +/// +/// ovrLayerHeader is used as a base member in these larger structs. +/// This struct cannot be used by itself except for the case that Type is ovrLayerType_Disabled. +/// +/// \see ovrLayerType, ovrLayerFlags +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrLayerHeader_ +{ + ovrLayerType Type; ///< Described by ovrLayerType. + unsigned Flags; ///< Described by ovrLayerFlags. +} ovrLayerHeader; + + +/// Describes a layer that specifies a monoscopic or stereoscopic view. +/// This is the kind of layer that's typically used as layer 0 to ovrHmd_SubmitFrame, +/// as it is the kind of layer used to render a 3D stereoscopic view. +/// +/// Three options exist with respect to mono/stereo texture usage: +/// - ColorTexture[0] and ColorTexture[1] contain the left and right stereo renderings, respectively. +/// Viewport[0] and Viewport[1] refer to ColorTexture[0] and ColorTexture[1], respectively. +/// - ColorTexture[0] contains both the left and right renderings, ColorTexture[1] is NULL, +/// and Viewport[0] and Viewport[1] refer to sub-rects with ColorTexture[0]. +/// - ColorTexture[0] contains a single monoscopic rendering, and Viewport[0] and +/// Viewport[1] both refer to that rendering. +/// +/// \see ovrSwapTextureSet, ovrHmd_SubmitFrame +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrLayerEyeFov_ +{ + /// Header.Type must be ovrLayerType_EyeFov. + ovrLayerHeader Header; + + /// ovrSwapTextureSets for the left and right eye respectively. + /// The second one of which can be NULL for cases described above. + ovrSwapTextureSet* ColorTexture[ovrEye_Count]; + + /// Specifies the ColorTexture sub-rect UV coordinates. + /// Both Viewport[0] and Viewport[1] must be valid. + ovrRecti Viewport[ovrEye_Count]; + + /// The viewport field of view. + ovrFovPort Fov[ovrEye_Count]; + + /// Specifies the position and orientation of each eye view, with the position specified in meters. + /// RenderPose will typically be the value returned from ovr_CalcEyePoses, + /// but can be different in special cases if a different head pose is used for rendering. + ovrPosef RenderPose[ovrEye_Count]; + +} ovrLayerEyeFov; + + +/// Describes a layer that specifies a monoscopic or stereoscopic view, +/// with depth textures in addition to color textures. This is typically used to support +/// positional time warp. This struct is the same as ovrLayerEyeFov, but with the addition +/// of DepthTexture and ProjectionDesc. +/// +/// ProjectionDesc can be created using ovrTimewarpProjectionDesc_FromProjection. +/// +/// Three options exist with respect to mono/stereo texture usage: +/// - ColorTexture[0] and ColorTexture[1] contain the left and right stereo renderings, respectively. +/// Viewport[0] and Viewport[1] refer to ColorTexture[0] and ColorTexture[1], respectively. +/// - ColorTexture[0] contains both the left and right renderings, ColorTexture[1] is NULL, +/// and Viewport[0] and Viewport[1] refer to sub-rects with ColorTexture[0]. +/// - ColorTexture[0] contains a single monoscopic rendering, and Viewport[0] and +/// Viewport[1] both refer to that rendering. +/// +/// \see ovrSwapTextureSet, ovrHmd_SubmitFrame +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrLayerEyeFovDepth_ +{ + /// Header.Type must be ovrLayerType_EyeFovDepth. + ovrLayerHeader Header; + + /// ovrSwapTextureSets for the left and right eye respectively. + /// The second one can be NULL in cases described above. + ovrSwapTextureSet* ColorTexture[ovrEye_Count]; + + /// Specifies the ColorTexture sub-rect UV coordinates. + /// Both Viewport[0] and Viewport[1] must be valid. + ovrRecti Viewport[ovrEye_Count]; + + /// The viewport field of view. + ovrFovPort Fov[ovrEye_Count]; + + /// Specifies the position and orientation of each eye view, with the position specified in meters. + /// RenderPose will typically be the value returned from ovr_CalcEyePoses, + /// but can be different in special cases if a different head pose is used for rendering. + ovrPosef RenderPose[ovrEye_Count]; + + /// Depth texture for positional timewarp. + /// Must map 1:1 to the ColorTexture. + ovrSwapTextureSet* DepthTexture[ovrEye_Count]; + + /// Specifies how to convert DepthTexture information into meters. + /// \see ovrTimewarpProjectionDesc_FromProjection + ovrTimewarpProjectionDesc ProjectionDesc; + +} ovrLayerEyeFovDepth; + + +/// Describes a layer of Quad type, which is a single quad in world or viewer space. +/// It is used for both ovrLayerType_QuadInWorld and ovrLayerType_QuadHeadLocked. +/// This type of layer represents a single object placed in the world and not a stereo +/// view of the world itself. +/// +/// A typical use of ovrLayerType_QuadInWorld is to draw a television screen in a room +/// that for some reason is more convenient to draw as a layer than as part of the main +/// view in layer 0. For example, it could implement a 3D popup GUI that is drawn at a +/// higher resolution than layer 0 to improve fidelity of the GUI. +/// +/// A use of ovrLayerType_QuadHeadLocked might be to implement a debug HUD visible in +/// the HMD. +/// +/// Quad layers are visible from both sides; they are not back-face culled. +/// +/// \see ovrSwapTextureSet, ovrHmd_SubmitFrame +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrLayerQuad_ +{ + /// Header.Type must be ovrLayerType_QuadInWorld or ovrLayerType_QuadHeadLocked. + ovrLayerHeader Header; + + /// Contains a single image, never with any stereo view. + ovrSwapTextureSet* ColorTexture; + + /// Specifies the ColorTexture sub-rect UV coordinates. + ovrRecti Viewport; + + /// Position and orientation of the center of the quad. Position is specified in meters. + ovrPosef QuadPoseCenter; + + /// Width and height (respectively) of the quad in meters. + ovrVector2f QuadSize; + +} ovrLayerQuad; + + +/// Describes a layer which is copied to the HMD as-is. Neither distortion, time warp, +/// nor vignetting is applied to ColorTexture before it's copied to the HMD. The application +/// can, however implement these kinds of effects itself before submitting the layer. +/// This layer can be used for application-based distortion rendering and can also be +/// used for implementing a debug HUD that's viewed on the mirror texture. +/// +/// \see ovrSwapTextureSet, ovrHmd_SubmitFrame +/// +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrLayerDirect_ +{ + /// Header.Type must be ovrLayerType_EyeDirect. + ovrLayerHeader Header; + + /// ovrSwapTextureSets for the left and right eye respectively. + /// The second one of which can be NULL for cases described above. + ovrSwapTextureSet* ColorTexture[ovrEye_Count]; + + /// Specifies the ColorTexture sub-rect UV coordinates. + /// Both Viewport[0] and Viewport[1] must be valid. + ovrRecti Viewport[ovrEye_Count]; + +} ovrLayerDirect; + + + +/// Union that combines ovrLayer types in a way that allows them +/// to be used in a polymorphic way. +typedef union ovrLayer_Union_ +{ + ovrLayerHeader Header; + ovrLayerEyeFov EyeFov; + ovrLayerEyeFovDepth EyeFovDepth; + ovrLayerQuad Quad; + ovrLayerDirect Direct; + +} ovrLayer_Union; + +//@} + + + +/// @name SDK Distortion Rendering +/// +/// All of rendering functions including the configure and frame functions +/// are not thread safe. It is OK to use ConfigureRendering on one thread and handle +/// frames on another thread, but explicit synchronization must be done since +/// functions that depend on configured state are not reentrant. +/// +/// These functions support rendering of distortion by the SDK. +/// +//@{ + +// TextureSet creation is rendering API-specific, so the ovrHmd_CreateSwapTextureSetXX +// methods can be found in the rendering API-specific headers, such as OVR_CAPI_D3D.h and OVR_CAPI_GL.h + + +/// Destroys an ovrSwapTextureSet and frees all the resources associated with it. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] textureSet Specifies the ovrSwapTextureSet to destroy. +/// +/// \see ovrHmd_CreateSwapTextureSetD3D11, ovrHmd_CreateSwapTextureSetGL +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_DestroySwapTextureSet(ovrHmd hmd, ovrSwapTextureSet* textureSet); + + +/// Destroys a mirror texture previously created by one of the mirror texture creation functions. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] mirrorTexture Specifies the ovrTexture to destroy. +/// +/// \see ovrHmd_CreateMirrorTextureD3D11, ovrHmd_CreateMirrorTextureGL +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_DestroyMirrorTexture(ovrHmd hmd, ovrTexture* mirrorTexture); + + +/// Calculates the recommended viewport size for rendering a given eye within the HMD +/// with a given FOV cone. +/// +/// Higher FOV will generally require larger textures to maintain quality. +/// Apps packing multiple eye views together on the same texture should ensure there are +/// at least 8 pixels of padding between them to prevent texture filtering and chromatic +/// aberration causing images to leak between the two eye views. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] eye Specifies which eye (left or right) to calculate for. +/// \param[in] fov Specifies the ovrFovPort to use. +/// \param[in] pixelsPerDisplayPixel Specifies the ratio of the number of render target pixels +/// to display pixels at the center of distortion. 1.0 is the default value. Lower +/// values can improve performance, higher values give improved quality. +/// \return Returns the texture width and height size. +/// +OVR_PUBLIC_FUNCTION(ovrSizei) ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, + float pixelsPerDisplayPixel); + +/// Computes the distortion viewport, view adjust, and other rendering parameters for +/// the specified eye. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] eyeType Specifies which eye (left or right) for which to perform calculations. +/// \param[in] fov Specifies the ovrFovPort to use. +/// \return Returns the computed ovrEyeRenderDesc for the given eyeType and field of view. +/// +/// \see ovrEyeRenderDesc +/// +OVR_PUBLIC_FUNCTION(ovrEyeRenderDesc) ovrHmd_GetRenderDesc(ovrHmd hmd, + ovrEyeType eyeType, ovrFovPort fov); + +/// Submits layers for distortion and display. +/// +/// ovrHmd_SubmitFrame triggers distortion and processing which might happen asynchronously. +/// The function will return when there is room in the submission queue and surfaces +/// are available. Distortion might or might not have completed. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// +/// \param[in] frameIndex Specifies the targeted frame index, or 0, to refer to one frame after the last +/// time ovrHmd_SubmitFrame was called. +/// +/// \param[in] viewScaleDesc Provides additional information needed only if layerPtrList contains +/// a ovrLayerType_QuadInWorld or ovrLayerType_QuadHeadLocked. If NULL, a default +/// version is used based on the current configuration and a 1.0 world scale. +/// +/// \param[in] layerPtrList Specifies a list of ovrLayer pointers, which can include NULL entries to +/// indicate that any previously shown layer at that index is to not be displayed. +/// Each layer header must be a part of a layer structure such as ovrLayerEyeFov or ovrLayerQuad, +/// with Header.Type identifying its type. A NULL layerPtrList entry in the array indicates the +// absence of the given layer. +/// +/// \param[in] layerCount Indicates the number of valid elements in layerPtrList. The maximum +/// supported layerCount is not currently specified, but may be specified in a future version. +/// +/// - Layers are drawn in the order they are specified in the array, regardless of the layer type. +/// +/// - Layers are not remembered between successive calls to ovrHmd_SubmitFrame. A layer must be +/// specified in every call to ovrHmd_SubmitFrame or it won't be displayed. +/// +/// - If a layerPtrList entry that was specified in a previous call to ovrHmd_SubmitFrame is +/// passed as NULL or is of type ovrLayerType_Disabled, that layer is no longer displayed. +/// +/// - A layerPtrList entry can be of any layer type and multiple entries of the same layer type +/// are allowed. No layerPtrList entry may be duplicated (i.e. the same pointer as an earlier entry). +/// +/// Example code +/// \code{.cpp} +/// ovrLayerEyeFov layer0; +/// ovrLayerQuad layer1; +/// ... +/// ovrLayerHeader* layers[2] = { &layer0.Header, &layer1.Header }; +/// ovrResult result = ovrHmd_SubmitFrame(hmd, frameIndex, nullptr, layers, 2); +/// \endcode +/// +/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error and true +/// upon one of the possible success values: +/// - ovrSuccess: rendering completed successfully. +/// - ovrSuccess_NotVisible: rendering completed successfully but was not displayed on the HMD, +/// usually because another application currently has ownership of the HMD. Applications receiving +/// this result should stop rendering new content, but continue to call ovrHmd_SubmitFrame periodically +/// until it returns a value other than ovrSuccess_NotVisible. +/// +/// \see ovrHmd_GetFrameTiming, ovrViewScaleDesc, ovrLayerHeader +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_SubmitFrame(ovrHmd hmd, unsigned int frameIndex, + const ovrViewScaleDesc* viewScaleDesc, + ovrLayerHeader const * const * layerPtrList, unsigned int layerCount); +///@} + + + +//------------------------------------------------------------------------------------- +/// @name Frame Timing +/// +//@{ + +/// Gets the ovrFrameTiming for the given frame index. +/// +/// The application should increment frameIndex for each successively targeted frame, +/// and pass that index to any relevent OVR functions that need to apply to the frame +/// identified by that index. +/// +/// This function is thread-safe and allows for multiple application threads to target +/// their processing to the same displayed frame. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] frameIndex Identifies the frame the caller wishes to target. +/// \return Returns the ovrFrameTiming for the given frameIndex. +/// \see ovrFrameTiming, ovrHmd_ResetFrameTiming +/// +OVR_PUBLIC_FUNCTION(ovrFrameTiming) ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex); + +/// Initializes and resets frame time tracking. +/// +/// This is typically not necessary, but is helpful if the application changes vsync state or +/// video mode. vsync is assumed to be on if this isn't called. Resets internal frame index to +/// the specified number. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] frameIndex Identifies the frame the caller wishes to target. +/// \see ovrHmd_GetFrameTiming +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex); + +/// Returns global, absolute high-resolution time in seconds. +/// +/// The time frame of reference for this function is not specified and should not be +/// depended upon. +/// +/// \return Returns seconds as a floating point value. +/// \see ovrPoseStatef, ovrSensorData, ovrFrameTiming +/// +OVR_PUBLIC_FUNCTION(double) ovr_GetTimeInSeconds(); + +///@} + + + +// ----------------------------------------------------------------------------------- +/// @name Property Access +/// +/// These functions read and write OVR properties. Supported properties +/// are defined in OVR_CAPI_Keys.h +/// +//@{ + +/// Reads a boolean property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid for only the call. +/// \param[in] defaultVal specifes the value to return if the property couldn't be read. +/// \return Returns the property interpreted as a boolean value. Returns defaultVal if +/// the property doesn't exist. +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_GetBool(ovrHmd hmd, const char* propertyName, ovrBool defaultVal); + +/// Writes or creates a boolean property. +/// If the property wasn't previously a boolean property, it is changed to a boolean property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] value The value to write. +/// \return Returns true if successful, otherwise false. A false result should only occur if the property +/// name is empty or if the property is read-only. +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetBool(ovrHmd hmd, const char* propertyName, ovrBool value); + + +/// Reads an integer property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] defaultVal Specifes the value to return if the property couldn't be read. +/// \return Returns the property interpreted as an integer value. Returns defaultVal if +/// the property doesn't exist. +OVR_PUBLIC_FUNCTION(int) ovrHmd_GetInt(ovrHmd hmd, const char* propertyName, int defaultVal); + +/// Writes or creates an integer property. +/// +/// If the property wasn't previously a boolean property, it is changed to an integer property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] value The value to write. +/// \return Returns true if successful, otherwise false. A false result should only occur if the property +/// name is empty or if the property is read-only. +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetInt(ovrHmd hmd, const char* propertyName, int value); + + +/// Reads a float property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] defaultVal specifes the value to return if the property couldn't be read. +/// \return Returns the property interpreted as an float value. Returns defaultVal if +/// the property doesn't exist. +OVR_PUBLIC_FUNCTION(float) ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal); + +/// Writes or creates a float property. +/// If the property wasn't previously a float property, it's changed to a float property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] value The value to write. +/// \return Returns true if successful, otherwise false. A false result should only occur if the property +/// name is empty or if the property is read-only. +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value); + + +/// Reads a float array property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] values An array of float to write to. +/// \param[in] valuesCapacity Specifies the maximum number of elements to write to the values array. +/// \return Returns the number of elements read, or 0 if property doesn't exist or is empty. +OVR_PUBLIC_FUNCTION(unsigned int) ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName, + float values[], unsigned int valuesCapacity); + +/// Writes or creates a float array property. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] values An array of float to write from. +/// \param[in] valuesSize Specifies the number of elements to write. +/// \return Returns true if successful, otherwise false. A false result should only occur if the property +/// name is empty or if the property is read-only. +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetFloatArray(ovrHmd hmd, const char* propertyName, + const float values[], unsigned int valuesSize); + + +/// Reads a string property. +/// Strings are UTF8-encoded and null-terminated. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] defaultVal Specifes the value to return if the property couldn't be read. +/// \return Returns the string property if it exists. Otherwise returns defaultVal, which can be specified as NULL. +/// The return memory is guaranteed to be valid until next call to ovrHmd_GetString or +/// until the HMD is destroyed, whichever occurs first. +OVR_PUBLIC_FUNCTION(const char*) ovrHmd_GetString(ovrHmd hmd, const char* propertyName, + const char* defaultVal); + +/// Writes or creates a string property. +/// Strings are UTF8-encoded and null-terminated. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] propertyName The name of the property, which needs to be valid only for the call. +/// \param[in] value The string property, which only needs to be valid for the duration of the call. +/// \return Returns true if successful, otherwise false. A false result should only occur if the property +/// name is empty or if the property is read-only. +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetString(ovrHmd hmd, const char* propertyName, + const char* value); + +///@} + + + + +#ifdef __cplusplus +} // extern "C" +#endif + + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +/// @cond DoxygenIgnore +//----------------------------------------------------------------------------- +// ***** Compiler packing validation +// +// These checks ensure that the compiler settings being used will be compatible +// with with pre-built dynamic library provided with the runtime. + +OVR_CPP(static_assert(sizeof(ovrBool) == 1, "ovrBool size mismatch")); +OVR_CPP(static_assert(sizeof(ovrVector2i) == 4 * 2, "ovrVector2i size mismatch")); +OVR_CPP(static_assert(sizeof(ovrSizei) == 4 * 2, "ovrSizei size mismatch")); +OVR_CPP(static_assert(sizeof(ovrRecti) == sizeof(ovrVector2i) + sizeof(ovrSizei), "ovrRecti size mismatch")); +OVR_CPP(static_assert(sizeof(ovrQuatf) == 4 * 4, "ovrQuatf size mismatch")); +OVR_CPP(static_assert(sizeof(ovrVector2f) == 4 * 2, "ovrVector2f size mismatch")); +OVR_CPP(static_assert(sizeof(ovrVector3f) == 4 * 3, "ovrVector3f size mismatch")); +OVR_CPP(static_assert(sizeof(ovrMatrix4f) == 4 * 16, "ovrMatrix4f size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrPosef) == (7 * 4), "ovrPosef size mismatch")); +OVR_CPP(static_assert(sizeof(ovrPoseStatef) == (22 * 4), "ovrPoseStatef size mismatch")); +OVR_CPP(static_assert(sizeof(ovrFovPort) == (4 * 4), "ovrFovPort size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrHmdCaps) == 4, "ovrHmdCaps size mismatch")); +OVR_CPP(static_assert(sizeof(ovrTrackingCaps) == 4, "ovrTrackingCaps size mismatch")); +OVR_CPP(static_assert(sizeof(ovrEyeType) == 4, "ovrEyeType size mismatch")); +OVR_CPP(static_assert(sizeof(ovrHmdType) == 4, "ovrHmdType size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrSensorData) == (11 * 4), "ovrSensorData size mismatch")); +OVR_CPP(static_assert(sizeof(ovrTrackingState) == + sizeof(ovrPoseStatef) + 4 + 2 * sizeof(ovrPosef) + sizeof(ovrSensorData) + 2 * 4, + "ovrTrackingState size mismatch")); +OVR_CPP(static_assert(sizeof(ovrFrameTiming) == 3 * 8, "ovrFrameTiming size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrRenderAPIType) == 4, "ovrRenderAPIType size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrTextureHeader) == sizeof(ovrRenderAPIType) + sizeof(ovrSizei), + "ovrTextureHeader size mismatch")); +OVR_CPP(static_assert(sizeof(ovrTexture) == sizeof(ovrTextureHeader) OVR_ON64(+4) + sizeof(uintptr_t) * 8, + "ovrTexture size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrStatusBits) == 4, "ovrStatusBits size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrEyeRenderDesc) == sizeof(ovrEyeType) + sizeof(ovrFovPort) + sizeof(ovrRecti) + + sizeof(ovrVector2f) + sizeof(ovrVector3f), + "ovrEyeRenderDesc size mismatch")); +OVR_CPP(static_assert(sizeof(ovrTimewarpProjectionDesc) == 4 * 3, "ovrTimewarpProjectionDesc size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrInitFlags) == 4, "ovrInitFlags size mismatch")); +OVR_CPP(static_assert(sizeof(ovrLogLevel) == 4, "ovrLogLevel size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrInitParams) == sizeof(ovrLogCallback) + 4 * 3 OVR_ON64(+4), + "ovrInitParams size mismatch")); + +OVR_CPP(static_assert(sizeof(ovrHmdDesc) == OVR_ON64(4 +) + sizeof(struct ovrHmdStruct*) + sizeof(ovrHmdType) // Handle - Type + + sizeof(void*) * 2 + 2 + 2 + 24 // ProductName - SerialNumber + + 2 + 2 + 4 * 4 // FirmwareMajor - CameraFrustumFarZInMeters + + 4 * 2 + sizeof(ovrFovPort) * 4 // HmdCaps - MaxEyeFov + + sizeof(ovrEyeType)* 2 + sizeof(ovrSizei) // EyeRenderOrder - Resolution + , "ovrHmdDesc size mismatch")); + + +// ----------------------------------------------------------------------------------- +// ***** Backward compatibility #includes +// +// This is at the bottom of this file because the following is dependent on the +// declarations above. + +#if !defined(OVR_CAPI_NO_UTILS) + #include "OVR_CAPI_Util.h" +#endif + +/// @endcond + +#endif // OVR_CAPI_h diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_D3D.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_D3D.h new file mode 100644 index 000000000000..5c729a985649 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_D3D.h @@ -0,0 +1,113 @@ +/********************************************************************************//** + +\file OVR_CAPI_D3D.h +\brief D3D specific structures used by the CAPI interface. +\date November 7, 2013 +\author Michael Antonov + +\copyright Copyright 2014 Oculus VR, LLC All Rights reserved. +\n +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. +\n +You may obtain a copy of the License at +\n +http://www.oculusvr.com/licenses/LICENSE-3.2 +\n +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_CAPI_D3D_h +#define OVR_CAPI_D3D_h + +#include "OVR_CAPI.h" + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +#endif + +//----------------------------------------------------------------------------------- +// ***** D3D11 Specific +#if defined(_MSC_VER) + #pragma warning(push, 0) +#endif +#include +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + +/// Used to pass D3D11 eye texture data to ovrHmd_EndFrame. +typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) ovrD3D11TextureData_ +{ + ovrTextureHeader Header; ///< General device settings. + OVR_ON64(OVR_UNUSED_STRUCT_PAD(pad0, 4)) ///< \internal struct padding. + ID3D11Texture2D* pTexture; ///< The D3D11 texture containing the undistorted eye image. + ID3D11ShaderResourceView* pSRView; ///< The D3D11 shader resource view for this texture. +} ovrD3D11TextureData; + +#if defined(__cplusplus) + static_assert(sizeof(ovrTexture) >= sizeof(ovrD3D11TextureData), "Insufficient size."); + static_assert(sizeof(ovrD3D11TextureData) == sizeof(ovrTextureHeader) OVR_ON64(+ 4) + 2 * OVR_PTR_SIZE, "size mismatch"); +#endif + + +/// Contains D3D11-specific texture information. +union ovrD3D11Texture +{ + ovrTexture Texture; ///< General device settings. + ovrD3D11TextureData D3D11; ///< D3D11-specific settings. +}; + + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + + +/// Create Texture Set suitable for use with D3D11. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] device Specifies the associated ID3D11Device, which must be the one that the textures will be used with in the application's process. +/// \param[in] desc Specifies requested texture properties. +/// \param[out] outTextureSet Specifies the created ovrSwapTextureSet, which will be valid only upon a successful return value. +/// This texture set must be eventually destroyed via ovrHmd_DestroySwapTextureSet before destroying the HMD with ovrHmd_Destroy. +/// +/// \return Returns an ovrResult indicating success or failure. In the case of failure, use +/// ovr_GetLastErrorInfo to get more information. +/// +/// \see ovrHmd_DestroySwapTextureSet +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateSwapTextureSetD3D11(ovrHmd hmd, + ID3D11Device* device, + const D3D11_TEXTURE2D_DESC* desc, + ovrSwapTextureSet** outTextureSet); + +/// Create Mirror Texture which is auto-refreshed to mirror Rift contents produced by this application. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] device Specifies the associated ID3D11Device, which must be the one that the textures will be used with in the application's process. +/// \param[in] desc Specifies requested texture properties. +/// \param[out] outMirrorTexture Specifies the created ovrTexture, which will be valid only upon a successful return value. +/// This texture must be eventually destroyed via ovrHmd_DestroyMirrorTexture before destroying the HMD with ovrHmd_Destroy. +/// +/// \return Returns an ovrResult indicating success or failure. In the case of failure, use +/// ovr_GetLastErrorInfo to get more information. +/// +/// \see ovrHmd_DestroyMirrorTexture +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateMirrorTextureD3D11(ovrHmd hmd, + ID3D11Device* device, + const D3D11_TEXTURE2D_DESC* desc, + ovrTexture** outMirrorTexture); + +#endif // OVR_CAPI_h diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_GL.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_GL.h new file mode 100644 index 000000000000..3065d6d8d7a8 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_GL.h @@ -0,0 +1,100 @@ +/********************************************************************************//** + +\file OVR_CAPI_GL.h +\brief GL specific structures used by the CAPI interface. +\date November 7, 2013 +\author Lee Cooper + +\copyright Copyright 2013 Oculus VR, LLC. All Rights reserved. +\n +Use of this software is subject to the terms of the Oculus Inc license +agreement provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +************************************************************************************/ + +#ifndef OVR_CAPI_GL_h +#define OVR_CAPI_GL_h + +#include "OVR_CAPI.h" + +#if defined(OVR_OS_WIN32) + #include + #include +#elif defined(__APPLE__) + #include +#else + #include +#endif + + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +#endif + +/// Used to pass GL eye texture data to ovrHmd_EndFrame. +typedef struct ovrGLTextureData_s +{ + ovrTextureHeader Header; ///< General device settings. + GLuint TexId; ///< The OpenGL name for this texture. +} ovrGLTextureData; + +#if defined(__cplusplus) + static_assert(sizeof(ovrTexture) >= sizeof(ovrGLTextureData), "Insufficient size."); + static_assert(sizeof(ovrGLTextureData) == sizeof(ovrTextureHeader) + 4, "size mismatch"); +#endif + +/// Contains OpenGL-specific texture information. +typedef union ovrGLTexture_s +{ + ovrTexture Texture; ///< General device settings. + ovrGLTextureData OGL; ///< OpenGL-specific settings. +} ovrGLTexture; + + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + + +/// Creates a Texture Set suitable for use with OpenGL. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] format Specifies the texture format. +/// \param[in] width Specifies the requested texture width. +/// \param[in] height Specifies the requested texture height. +/// \param[out] outTextureSet Specifies the created ovrSwapTextureSet, which will be valid only upon a successful return value. +/// This texture set must be eventually destroyed via ovrHmd_DestroySwapTextureSet before destroying the HMD with ovrHmd_Destroy. +/// +/// \return Returns an ovrResult indicating success or failure. In the case of failure, use +/// ovr_GetLastErrorInfo to get more information. +/// +/// \see ovrHmd_DestroySwapTextureSet +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateSwapTextureSetGL(ovrHmd hmd, GLuint format, + int width, int height, + ovrSwapTextureSet** outTextureSet); + + +/// Creates a Mirror Texture which is auto-refreshed to mirror Rift contents produced by this application. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] format Specifies the texture format. +/// \param[in] width Specifies the requested texture width. +/// \param[in] height Specifies the requested texture height. +/// \param[out] outMirrorTexture Specifies the created ovrTexture, which will be valid only upon a successful return value. +/// This texture must be eventually destroyed via ovrHmd_DestroyMirrorTexture before destroying the HMD with ovrHmd_Destroy. +/// +/// \return Returns an ovrResult indicating success or failure. In the case of failure, use +/// ovr_GetLastErrorInfo to get more information. +/// +/// \see ovrHmd_DestroyMirrorTexture +/// +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateMirrorTextureGL(ovrHmd hmd, GLuint format, + int width, int height, + ovrTexture** outMirrorTexture); + + +#endif // OVR_CAPI_GL_h diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_Keys.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_Keys.h new file mode 100644 index 000000000000..601c8fc2bf18 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_Keys.h @@ -0,0 +1,62 @@ +/************************************************************************************ + +Filename : OVR_CAPI.h +Content : Keys for CAPI calls +Created : September 25, 2014 +Authors : + +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_CAPI_Keys_h +#define OVR_CAPI_Keys_h + +#include "OVR_Version.h" + + +#define OVR_KEY_USER "User" // string +#define OVR_KEY_NAME "Name" // string +#define OVR_KEY_GENDER "Gender" // string "Male", "Female", or "Unknown" +#define OVR_KEY_PLAYER_HEIGHT "PlayerHeight" // float meters +#define OVR_KEY_EYE_HEIGHT "EyeHeight" // float meters +#define OVR_KEY_IPD "IPD" // float meters +#define OVR_KEY_NECK_TO_EYE_DISTANCE "NeckEyeDistance" // float[2] meters +#define OVR_KEY_EYE_RELIEF_DIAL "EyeReliefDial" // int in range of 0-10 +#define OVR_KEY_EYE_TO_NOSE_DISTANCE "EyeToNoseDist" // float[2] meters +#define OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE "MaxEyeToPlateDist" // float[2] meters +#define OVR_KEY_EYE_CUP "EyeCup" // char[16] "A", "B", or "C" +#define OVR_KEY_CUSTOM_EYE_RENDER "CustomEyeRender" // bool +#define OVR_KEY_CAMERA_POSITION "CenteredFromWorld" // double[7] ovrPosef quat rotation x, y, z, w, translation x, y, z + + +// Default measurements empirically determined at Oculus to make us happy +// The neck model numbers were derived as an average of the male and female averages from ANSUR-88 +// NECK_TO_EYE_HORIZONTAL = H22 - H43 = INFRAORBITALE_BACK_OF_HEAD - TRAGION_BACK_OF_HEAD +// NECK_TO_EYE_VERTICAL = H21 - H15 = GONION_TOP_OF_HEAD - ECTOORBITALE_TOP_OF_HEAD +// These were determined to be the best in a small user study, clearly beating out the previous default values +#define OVR_DEFAULT_GENDER "Unknown" +#define OVR_DEFAULT_PLAYER_HEIGHT 1.778f +#define OVR_DEFAULT_EYE_HEIGHT 1.675f +#define OVR_DEFAULT_IPD 0.064f +#define OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL 0.0805f +#define OVR_DEFAULT_NECK_TO_EYE_VERTICAL 0.075f +#define OVR_DEFAULT_EYE_RELIEF_DIAL 3 +#define OVR_DEFAULT_CAMERA_POSITION {0,0,0,1,0,0,0} + +#endif // OVR_CAPI_Keys_h diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_Util.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_Util.h new file mode 100644 index 000000000000..3c234bd7daf0 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_CAPI_Util.h @@ -0,0 +1,160 @@ +/********************************************************************************//** + +\file OVR_CAPI_Util.h +\brief This header provides LibOVR utility function declarations + +\copyright Copyright 2015 Oculus VR, LLC All Rights reserved. +\n +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. +\n +You may obtain a copy of the License at +\n +http://www.oculusvr.com/licenses/LICENSE-3.2 +\n +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_CAPI_Util_h +#define OVR_CAPI_Util_h + + +#include "OVR_CAPI.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/// Enumerates modifications to the projection matrix based on the application's needs. +/// +/// \see ovrMatrix4f_Projection +/// +typedef enum ovrProjectionModifier_ +{ + /// Use for generating a default projection matrix that is: + /// * Left-handed. + /// * Near depth values stored in the depth buffer are smaller than far depth values. + /// * Both near and far are explicitly defined. + /// * With a clipping range that is (0 to w). + ovrProjection_None = 0x00, + + /// Enable if using right-handed transformations in your application. + ovrProjection_RightHanded = 0x01, + + /// After the projection transform is applied, far values stored in the depth buffer will be less than closer depth values. + /// NOTE: Enable only if the application is using a floating-point depth buffer for proper precision. + ovrProjection_FarLessThanNear = 0x02, + + /// When this flag is used, the zfar value pushed into ovrMatrix4f_Projection() will be ignored + /// NOTE: Enable only if ovrProjection_FarLessThanNear is also enabled where the far clipping plane will be pushed to infinity. + ovrProjection_FarClipAtInfinity = 0x04, + + /// Enable if the application is rendering with OpenGL and expects a projection matrix with a clipping range of (-w to w). + /// Ignore this flag if your application already handles the conversion from D3D range (0 to w) to OpenGL. + ovrProjection_ClipRangeOpenGL = 0x08, +} ovrProjectionModifier; + + +/// Used to generate projection from ovrEyeDesc::Fov. +/// +/// \param[in] fov Specifies the ovrFovPort to use. +/// \param[in] znear Distance to near Z limit. +/// \param[in] zfar Distance to far Z limit. +/// \param[in] projectionModFlags A combination of the ovrProjectionModifier flags. +/// +/// \return Returns the calculated projection matrix. +/// +/// \see ovrProjectionModifier +/// +OVR_PUBLIC_FUNCTION(ovrMatrix4f) ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, unsigned int projectionModFlags); + + +/// Extracts the required data from the result of ovrMatrix4f_Projection. +/// +/// \param[in] projection Specifies the project matrix from which to extract ovrTimewarpProjectionDesc. +/// \return Returns the extracted ovrTimewarpProjectionDesc. +/// \see ovrTimewarpProjectionDesc +/// +OVR_PUBLIC_FUNCTION(ovrTimewarpProjectionDesc) ovrTimewarpProjectionDesc_FromProjection(ovrMatrix4f projection); + + +/// Generates an orthographic sub-projection. +/// +/// Used for 2D rendering, Y is down. +/// +/// \param[in] projection The perspective matrix that the orthographic matrix is derived from. +/// \param[in] orthoScale Equal to 1.0f / pixelsPerTanAngleAtCenter. +/// \param[in] orthoDistance Equal to the distance from the camera in meters, such as 0.8m. +/// \param[in] hmdToEyeViewOffsetX Specifies the offset of the eye from the center. +/// +/// \return Returns the calculated projection matrix. +/// +OVR_PUBLIC_FUNCTION(ovrMatrix4f) ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale, + float orthoDistance, float hmdToEyeViewOffsetX); + + + +/// Computes offset eye poses based on headPose returned by ovrTrackingState. +/// +/// \param[in] headPose Indicates the HMD position and orientation to use for the calculation. +/// \param[in] hmdToEyeViewOffset Can be ovrEyeRenderDesc.HmdToEyeViewOffset returned from +/// ovrHmd_GetRenderDesc. For monoscopic rendering, use a vector that is the average +/// of the two vectors for both eyes. +/// \param[out] outEyePoses If outEyePoses are used for rendering, they should be passed to +/// ovrHmd_SubmitFrame in ovrLayerEyeFov::RenderPose or ovrLayerEyeFovDepth::RenderPose. +/// +OVR_PUBLIC_FUNCTION(void) ovr_CalcEyePoses(ovrPosef headPose, + const ovrVector3f hmdToEyeViewOffset[2], + ovrPosef outEyePoses[2]); + + +/// Returns the predicted head pose in outHmdTrackingState and offset eye poses in outEyePoses. +/// +/// This is a thread-safe function where caller should increment frameIndex with every frame +/// and pass that index where applicable to functions called on the rendering thread. +/// Assuming outEyePoses are used for rendering, it should be passed as a part of ovrLayerEyeFov. +/// The caller does not need to worry about applying HmdToEyeViewOffset to the returned outEyePoses variables. +/// +/// \param[in] hmd Specifies an ovrHmd previously returned by ovrHmd_Create. +/// \param[in] frameIndex Specifies the targeted frame index, or 0 to refer to one frame after +/// the last time ovrHmd_SubmitFrame was called. +/// \param[in] hmdToEyeViewOffset Can be ovrEyeRenderDesc.HmdToEyeViewOffset returned from +/// ovrHmd_GetRenderDesc. For monoscopic rendering, use a vector that is the average +/// of the two vectors for both eyes. +/// \param[out] outEyePoses The predicted eye poses. +/// \param[out] outHmdTrackingState The predicted ovrTrackingState. May be NULL, in which case it is ignored. +/// +OVR_PUBLIC_FUNCTION(void) ovrHmd_GetEyePoses(ovrHmd hmd, unsigned int frameIndex, + const ovrVector3f hmdToEyeViewOffset[2], + ovrPosef outEyePoses[2], + ovrTrackingState* outHmdTrackingState); + + + + +/// Waits until the specified absolute time. +/// +/// \deprecated This function may be removed in a future version. +/// +/// \param[in] absTime Specifies the absolute future time to wait until. +/// +/// \see ovr_GetTimeInSeconds +/// +OVR_PUBLIC_FUNCTION(double) ovr_WaitTillTime(double absTime); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif // Header include guard diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_ErrorCode.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_ErrorCode.h new file mode 100644 index 000000000000..4fdf1b2448f0 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_ErrorCode.h @@ -0,0 +1,106 @@ +/********************************************************************************//** + +\file OVR_ErrorCode.h +\brief This header provides LibOVR error code declarations + +\copyright Copyright 2015 Oculus VR, LLC All Rights reserved. +\n +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. +\n +You may obtain a copy of the License at +\n +http://www.oculusvr.com/licenses/LICENSE-3.2 +\n +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_ErrorCode_h +#define OVR_ErrorCode_h + + +#include "OVR_Version.h" +#include + + + + + + + +#ifndef OVR_RESULT_DEFINED +#define OVR_RESULT_DEFINED ///< Allows ovrResult to be independently defined. +/// API call results are represented at the highest level by a single ovrResult. +typedef int32_t ovrResult; +#endif + + +// Success is a value greater or equal to 0, while all error types are negative values. +#ifndef OVR_SUCCESS_DEFINED +#define OVR_SUCCESS_DEFINED ///< Allows ovrResult to be independently defined. +enum +{ + /// This is a general success result. Use OVR_SUCCESS to test for success. + ovrSuccess = 0, + + /// Returned from a call to SubmitFrame. The call succeeded, but what the app + /// rendered will not be visible on the HMD. Ideally the app should continue + /// calling SubmitFrame, but not do any rendering. When the result becomes + /// ovrSuccess, rendering should continue as usual. + ovrSuccess_NotVisible = 1000, +}; +#endif + + +/// \brief Indicates if an ovrResult indicates success. +/// +/// Some functions return additional successful values other than ovrSucces and +/// require usage of this macro to indicate successs. +#if !defined(OVR_SUCCESS) + #define OVR_SUCCESS(result) (result >= 0) +#endif + + +enum +{ + /* General errors */ + ovrError_MemoryAllocationFailure = -1000, ///< Failure to allocate memory. + ovrError_SocketCreationFailure = -1001, ///< Failure to create a socket. + ovrError_InvalidHmd = -1002, ///< Invalid HMD parameter provided. + ovrError_Timeout = -1003, ///< The operation timed out. + ovrError_NotInitialized = -1004, ///< The system or component has not been initialized. + ovrError_InvalidParameter = -1005, ///< Invalid parameter provided. See error info or log for details. + ovrError_ServiceError = -1006, ///< Generic service error. See error info or log for details. + ovrError_NoHmd = -1007, ///< The given HMD doesn't exist. + + /* Audio error range, reserved for Audio errors. */ + ovrError_AudioReservedBegin = -2000, ///< First Audio error. + ovrError_AudioReservedEnd = -2999, ///< Last Audio error. + + /* Initialization errors. */ + ovrError_Initialize = -3000, ///< Generic initialization error. + ovrError_LibLoad = -3001, ///< Couldn't load LibOVRRT. + ovrError_LibVersion = -3002, ///< LibOVRRT version incompatibility. + ovrError_ServiceConnection = -3003, ///< Couldn't connect to the OVR Service. + ovrError_ServiceVersion = -3004, ///< OVR Service version incompatibility. + ovrError_IncompatibleOS = -3005, ///< The operating system version is incompatible. + ovrError_DisplayInit = -3006, ///< Unable to initialize the HMD display. + ovrError_ServerStart = -3007, ///< Unable to start the server. Is it already running? + ovrError_Reinitialization = -3008, ///< Attempting to re-initialize with a different version. + + /*Hardware Errors*/ + ovrError_InvalidBundleAdjustment = -4000, ///< Headset has no bundle adjustment data. + ovrError_USBBandwidth = -4001, ///< The USB hub cannot handle the camera frame bandwidth. +}; + + +#endif /* OVR_ErrorCode_h */ + + diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_Kernel.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_Kernel.h new file mode 100644 index 000000000000..72ab48a799cb --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_Kernel.h @@ -0,0 +1,44 @@ +/************************************************************************************ + +Filename : OVRKernel.h +Content : This contains references to all OVR Kernel headers in Src folder. + Should be generated automatically based on PublicHeader tags. + +Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +/* This header file is deprecated and will be removed from a future version of this library */ + +#ifndef OVR_Kernel_h +#define OVR_Kernel_h + +#include "Kernel/OVR_Types.h" +#include "Kernel/OVR_Allocator.h" +#include "Kernel/OVR_RefCount.h" +#include "Kernel/OVR_Log.h" +#include "Kernel/OVR_System.h" +#include "Kernel/OVR_Nullptr.h" +#include "Kernel/OVR_String.h" +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_SysFile.h" +#include "Extras/OVR_Math.h" + +#endif + diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_Version.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_Version.h new file mode 100644 index 000000000000..074e8625ff0c --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Include/OVR_Version.h @@ -0,0 +1,81 @@ +/********************************************************************************//** + +\file OVR_Version.h +\brief This header provides LibOVR version identification + +\copyright Copyright 2015 Oculus VR, LLC All Rights reserved. +\n +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. +\n +You may obtain a copy of the License at +\n +http://www.oculusvr.com/licenses/LICENSE-3.2 +\n +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Version_h +#define OVR_Version_h + + + + + + +/// Conventional string-ification macro. +#if !defined(OVR_STRINGIZE) + #define OVR_STRINGIZEIMPL(x) #x + #define OVR_STRINGIZE(x) OVR_STRINGIZEIMPL(x) +#endif + + +// We are on major version 6 of the beta pre-release SDK. At some point we will +// transition to product version 1 and reset the major version back to 1 (first +// product release, version 1.0). +#define OVR_PRODUCT_VERSION 0 +#define OVR_MAJOR_VERSION 6 +#define OVR_MINOR_VERSION 0 +#define OVR_PATCH_VERSION 0 +#define OVR_BUILD_NUMBER 0 + + +/// "Product.Major.Minor.Patch" +#if !defined(OVR_VERSION_STRING) + #define OVR_VERSION_STRING OVR_STRINGIZE(OVR_PRODUCT_VERSION.OVR_MAJOR_VERSION.OVR_MINOR_VERSION.OVR_PATCH_VERSION) +#endif + + +/// "Product.Major.Minor.Patch.Build" +#if !defined(OVR_DETAILED_VERSION_STRING) + #define OVR_DETAILED_VERSION_STRING OVR_STRINGIZE(OVR_PRODUCT_VERSION.OVR_MAJOR_VERSION.OVR_MINOR_VERSION.OVR_PATCH_VERSION.OVR_BUILD_NUMBER) +#endif + + +// This is the firmware version for the DK2 headset sensor board. +//#if !defined(OVR_DK2_LATEST_FIRMWARE_MAJOR_VERSION) + #define OVR_DK2_LATEST_FIRMWARE_MAJOR_VERSION 2 + #define OVR_DK2_LATEST_FIRMWARE_MINOR_VERSION 12 +//#endif + + +/// \brief file description for version info +/// This appears in the user-visible file properties. It is intended to convey publicly +/// available additional information such as feature builds. +#if !defined(OVR_FILE_DESCRIPTION_STRING) + #if defined(_DEBUG) + #define OVR_FILE_DESCRIPTION_STRING "dev build debug" + #else + #define OVR_FILE_DESCRIPTION_STRING "dev build" + #endif +#endif + + +#endif // OVR_Version_h diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_CAPIShim.c b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_CAPIShim.c new file mode 100644 index 000000000000..4b5e1d75c7e2 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_CAPIShim.c @@ -0,0 +1,1294 @@ +/************************************************************************************ + +Filename : OVR_CAPIShim.c +Content : CAPI DLL user library +Created : November 20, 2014 +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + + +#include "OVR_CAPI.h" +#include "OVR_Version.h" +#include "OVR_ErrorCode.h" +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) + #if defined(_MSC_VER) + #pragma warning(push, 0) + #endif + #include + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + + #include "../Include/OVR_CAPI_D3D.h" +#else + #if defined(__APPLE__) + #include + #include + #include + #include + #include + #endif + #include + #include + #include +#endif +#include "../Include/OVR_CAPI_GL.h" + + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4996) // 'getenv': This function or variable may be unsafe. +#endif + + +// ----------------------------------------------------------------------------------- +// ***** OVR_ENABLE_DEVELOPER_SEARCH +// +// If defined then our shared library loading code searches for developer build +// directories. +// +#if !defined(OVR_ENABLE_DEVELOPER_SEARCH) +#endif + + +// ----------------------------------------------------------------------------------- +// ***** OVR_BUILD_DEBUG +// +// Defines OVR_BUILD_DEBUG when the compiler default debug preprocessor is set. +// +// If you want to control the behavior of these flags, then explicitly define +// either -DOVR_BUILD_RELEASE or -DOVR_BUILD_DEBUG in the compiler arguments. + +#if !defined(OVR_BUILD_DEBUG) && !defined(OVR_BUILD_RELEASE) + #if defined(_MSC_VER) + #if defined(_DEBUG) + #define OVR_BUILD_DEBUG + #endif + #else + #if defined(DEBUG) + #define OVR_BUILD_DEBUG + #endif + #endif +#endif + + +//----------------------------------------------------------------------------------- +// ***** FilePathCharType, ModuleHandleType, ModuleFunctionType +// +#if defined(_WIN32) // We need to use wchar_t on Microsoft platforms, as that's the native file system character type. + #define FilePathCharType wchar_t // #define instead of typedef because debuggers (VC++, XCode) don't recognize typedef'd types as a string type. + typedef HMODULE ModuleHandleType; + typedef FARPROC ModuleFunctionType; +#else + #define FilePathCharType char + typedef void* ModuleHandleType; + typedef void* ModuleFunctionType; +#endif + +#define ModuleHandleTypeNull ((ModuleHandleType)NULL) +#define ModuleFunctionTypeNull ((ModuleFunctionType)NULL) + + +//----------------------------------------------------------------------------------- +// ***** OVR_MAX_PATH +// +#if !defined(OVR_MAX_PATH) + #if defined(_WIN32) + #define OVR_MAX_PATH _MAX_PATH + #elif defined(__APPLE__) + #define OVR_MAX_PATH PATH_MAX + #else + #define OVR_MAX_PATH 1024 + #endif +#endif + + + +//----------------------------------------------------------------------------------- +// ***** OVR_DECLARE_IMPORT +// +// Creates typedef and pointer declaration for a function of a given signature. +// The typedef is Type, and the pointer is Ptr. +// +// Example usage: +// int MultiplyValues(float x, float y); // Assume this function exists in an external shared library. We don't actually need to redeclare it. +// OVR_DECLARE_IMPORT(int, MultiplyValues, (float x, float y)) // This creates a local typedef and pointer for it. + +#define OVR_DECLARE_IMPORT(ReturnValue, FunctionName, Arguments) \ + typedef ReturnValue (OVR_CDECL *FunctionName##Type)Arguments; \ + FunctionName##Type FunctionName##Ptr = NULL; + + + +//----------------------------------------------------------------------------------- +// ***** OVR_GETFUNCTION +// +// Loads Ptr from hLibOVR if not already loaded. +// Assumes a variable named Ptr of type Type exists which is called in LibOVR. +// +// Example usage: +// OVR_GETFUNCTION(MultiplyValues) // Normally this would be done on library init and not before every usage. +// int result = MultiplyValuesPtr(3.f, 4.f); + +#if !defined(OVR_DLSYM) + #if defined(_WIN32) + #define OVR_DLSYM(dlImage, name) GetProcAddress(dlImage, name) + #else + #define OVR_DLSYM(dlImage, name) dlsym(dlImage, name) + #endif +#endif + +#define OVR_GETFUNCTION(f) \ + if(!f##Ptr) \ + { \ + union \ + { \ + f##Type p1; \ + ModuleFunctionType p2; \ + } u; \ + u.p2 = OVR_DLSYM(hLibOVR, #f); \ + f##Ptr = u.p1; \ + } + + +static size_t OVR_strlcpy(char* dest, const char* src, size_t destsize) +{ + const char* s = src; + size_t n = destsize; + + if(n && --n) + { + do{ + if((*dest++ = *s++) == 0) + break; + } while(--n); + } + + if(!n) + { + if(destsize) + *dest = 0; + while(*s++) + { } + } + + return (size_t)((s - src) - 1); +} + + +static size_t OVR_strlcat(char* dest, const char* src, size_t destsize) +{ + const size_t d = destsize ? strlen(dest) : 0; + const size_t s = strlen(src); + const size_t t = s + d; + + if(t < destsize) + memcpy(dest + d, src, (s + 1) * sizeof(*src)); + else + { + if(destsize) + { + memcpy(dest + d, src, ((destsize - d) - 1) * sizeof(*src)); + dest[destsize - 1] = 0; + } + } + + return t; +} + + +#if defined(__APPLE__) + static ovrBool OVR_strend(const char* pStr, const char* pFind, size_t strLength, size_t findLength) + { + if(strLength == (size_t)-1) + strLength = strlen(pStr); + if(findLength == (size_t)-1) + findLength = strlen(pFind); + if(strLength >= findLength) + return (strcmp(pStr + strLength - findLength, pFind) == 0); + return ovrFalse; + } + + static ovrBool OVR_isBundleFolder(const char* filePath) + { + static const char* extensionArray[] = { ".app", ".bundle", ".framework", ".plugin", ".kext" }; + size_t i; + + for(i = 0; i < sizeof(extensionArray)/sizeof(extensionArray[0]); i++) + { + if(OVR_strend(filePath, extensionArray[i], (size_t)-1, (size_t)-1)) + return ovrTrue; + } + + return ovrFalse; + } +#endif + + +#if defined(OVR_ENABLE_DEVELOPER_SEARCH) + +// Returns true if the path begins with the given prefix. +// Doesn't support non-ASCII paths, else the return value may be incorrect. +static int OVR_PathStartsWith(const FilePathCharType* path, const char* prefix) +{ + while(*prefix) + { + if(tolower((unsigned char)*path++) != tolower((unsigned char)*prefix++)) + return ovrFalse; + } + + return ovrTrue; +} + +#endif + + +static ovrBool OVR_GetCurrentWorkingDirectory(FilePathCharType* directoryPath, size_t directoryPathCapacity) +{ + #if defined(_WIN32) + DWORD dwSize = GetCurrentDirectoryW((DWORD)directoryPathCapacity, directoryPath); + + if((dwSize > 0) && (directoryPathCapacity > 1)) // Test > 1 so we have room to possibly append a \ char. + { + size_t length = wcslen(directoryPath); + + if((length == 0) || ((directoryPath[length - 1] != L'\\') && (directoryPath[length - 1] != L'/'))) + { + directoryPath[length++] = L'\\'; + directoryPath[length] = L'\0'; + } + + return ovrTrue; + } + + #else + char* cwd = getcwd(directoryPath, directoryPathCapacity); + + if(cwd && directoryPath[0] && (directoryPathCapacity > 1)) // Test > 1 so we have room to possibly append a / char. + { + size_t length = strlen(directoryPath); + + if((length == 0) || (directoryPath[length - 1] != '/')) + { + directoryPath[length++] = '/'; + directoryPath[length] = '\0'; + } + + return ovrTrue; + } + #endif + + if(directoryPathCapacity > 0) + directoryPath[0] = '\0'; + + return ovrFalse; +} + + +// The appContainer argument is specific currently to only Macintosh. If true and the application is a .app bundle then it returns the +// location of the bundle and not the path to the executable within the bundle. Else return the path to the executable binary itself. +// The moduleHandle refers to the relevant dynamic (a.k.a. shared) library. The main executable is the main module, and each of the shared +// libraries is a module. This way you can specify that you want to know the directory of the given shared library, which may be different +// from the main executable. If the moduleHandle is NULL then the current application module is used. +static ovrBool OVR_GetCurrentApplicationDirectory(FilePathCharType* directoryPath, size_t directoryPathCapacity, ovrBool appContainer, ModuleHandleType moduleHandle) +{ + #if defined(_WIN32) + DWORD length = GetModuleFileNameW(moduleHandle, directoryPath, (DWORD)directoryPathCapacity); + DWORD pos; + + if((length != 0) && (length < (DWORD)directoryPathCapacity)) // If there wasn't an error and there was enough capacity... + { + for(pos = length; (pos > 0) && (directoryPath[pos] != '\\') && (directoryPath[pos] != '/'); --pos) + { + if((directoryPath[pos - 1] != '\\') && (directoryPath[pos - 1] != '/')) + directoryPath[pos - 1] = 0; + } + + return ovrTrue; + } + + (void)appContainer; // Not used on this platform. + + #elif defined(__APPLE__) + uint32_t directoryPathCapacity32 = (uint32_t)directoryPathCapacity; + int result = _NSGetExecutablePath(directoryPath, &directoryPathCapacity32); + + if(result == 0) // If success... + { + char realPath[OVR_MAX_PATH]; + + if(realpath(directoryPath, realPath)) // realpath returns the canonicalized absolute file path. + { + size_t length = 0; + + if(appContainer) // If the caller wants the path to the containing bundle... + { + char containerPath[OVR_MAX_PATH]; + ovrBool pathIsContainer; + + OVR_strlcpy(containerPath, realPath, sizeof(containerPath)); + pathIsContainer = OVR_isBundleFolder(containerPath); + + while(!pathIsContainer && strncmp(containerPath, ".", OVR_MAX_PATH) && strncmp(containerPath, "/", OVR_MAX_PATH)) // While the container we're looking for is not found and while the path doesn't start with a . or / + { + OVR_strlcpy(containerPath, dirname(containerPath), sizeof(containerPath)); + pathIsContainer = OVR_isBundleFolder(containerPath); + } + + if(pathIsContainer) + length = OVR_strlcpy(directoryPath, containerPath, directoryPathCapacity); + } + + if(length == 0) // If not set above in the appContainer block... + length = OVR_strlcpy(directoryPath, realPath, directoryPathCapacity); + + while(length-- && (directoryPath[length] != '/')) + directoryPath[length] = '\0'; // Strip the file name from the file path, leaving a trailing / char. + + return ovrTrue; + } + } + + (void)moduleHandle; // Not used on this platform. + + #else + ssize_t length = readlink("/proc/self/exe", directoryPath, directoryPathCapacity); + ssize_t pos; + + if(length > 0) + { + for(pos = length; (pos > 0) && (directoryPath[pos] != '/'); --pos) + { + if(directoryPath[pos - 1] != '/') + directoryPath[pos - 1] = '\0'; + } + + return ovrTrue; + } + + (void)appContainer; // Not used on this platform. + (void)moduleHandle; + #endif + + if(directoryPathCapacity > 0) + directoryPath[0] = '\0'; + + return ovrFalse; +} + + +#if defined(_WIN32) || defined(OVR_ENABLE_DEVELOPER_SEARCH) // Used only in these cases + +// Get the file path to the current module's (DLL or EXE) directory within the current process. +// Will be different from the process module handle if the current module is a DLL and is in a different directory than the EXE module. +// If successful then directoryPath will be valid and ovrTrue is returned, else directoryPath will be empty and ovrFalse is returned. +static ovrBool OVR_GetCurrentModuleDirectory(FilePathCharType* directoryPath, size_t directoryPathCapacity, ovrBool appContainer) +{ + #if defined(_WIN32) + HMODULE hModule; + BOOL result = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)(uintptr_t)OVR_GetCurrentModuleDirectory, &hModule); + if(result) + OVR_GetCurrentApplicationDirectory(directoryPath, directoryPathCapacity, ovrTrue, hModule); + else + directoryPath[0] = 0; + + (void)appContainer; + + return directoryPath[0] ? ovrTrue : ovrFalse; + #else + return OVR_GetCurrentApplicationDirectory(directoryPath, directoryPathCapacity, appContainer, NULL); + #endif +} + +#endif + + +static ModuleHandleType OVR_OpenLibrary(const FilePathCharType* libraryPath) +{ + #if defined(_WIN32) + return LoadLibraryW(libraryPath); + #else + // Don't bother trying to dlopen() a file that is not even there. + if (access(libraryPath, X_OK | R_OK ) != 0) + { + return NULL; + } + + dlerror(); // Clear any previous dlopen() errors + + // Use RTLD_NOW because we don't want unexpected stalls at runtime, and the library isn't very large. + // Use RTLD_LOCAL to avoid unilaterally exporting resolved symbols to the rest of this process. + void *lib = dlopen(libraryPath, RTLD_NOW | RTLD_LOCAL); + + if (!lib) + { + #if defined(__APPLE__) + // TODO: Output the error in whatever logging system OSX uses (jhughes) + #else // __APPLE__ + fprintf(stderr, "ERROR: Can't load '%s':\n%s\n", libraryPath, dlerror()); + #endif // __APPLE__ + } + + return lib; + #endif +} + + +/* Currently not in use, but expected to be used in the future +static void OVR_CloseLibrary(ModuleHandleType hLibrary) +{ + if (hLibrary) + { + #if defined(_WIN32) + FreeLibrary(hLibrary); + #else + dlclose(hLibrary); + #endif + } +} +*/ + + +// Returns a valid ModuleHandleType (e.g. Windows HMODULE) or returns ModuleHandleTypeNull (e.g. NULL). +// The caller is required to eventually call OVR_CloseLibrary on a valid return handle. +// +static ModuleHandleType OVR_FindLibraryPath(int requestedProductVersion, int requestedMajorVersion, + FilePathCharType* libraryPath, size_t libraryPathCapacity) +{ + ModuleHandleType moduleHandle; + int printfResult; + FilePathCharType developerDir[OVR_MAX_PATH] = { '\0' }; + + #if defined(_MSC_VER) + #if defined(_WIN64) + const char* pBitDepth = "64"; + #else + const char* pBitDepth = "32"; + #endif + #elif defined(__APPLE__) + // For Apple platforms we are using a Universal Binary LibOVRRT dylib which has both 32 and 64 in it. + #else // Other Unix. + #if defined(__x86_64__) + const char* pBitDepth = "64"; + #else + const char* pBitDepth = "32"; + #endif + #endif + + moduleHandle = ModuleHandleTypeNull; + if(libraryPathCapacity) + libraryPath[0] = '\0'; + + // Note: OVR_ENABLE_DEVELOPER_SEARCH is deprecated in favor of the simpler LIBOVR_DLL_DIR, as the edge + // case uses of the former created some complications that may be best solved by simply using a LIBOVR_DLL_DIR + // environment variable which the user can set in their debugger or system environment variables. + #if (defined(_MSC_VER) || defined(_WIN32)) && !defined(OVR_FILE_PATH_SEPARATOR) + #define OVR_FILE_PATH_SEPARATOR "\\" + #else + #define OVR_FILE_PATH_SEPARATOR "/" + #endif + + { + const char* pLibOvrDllDir = getenv("LIBOVR_DLL_DIR"); // Example value: /dev/OculusSDK/Main/LibOVR/Mac/Debug/ + + if(pLibOvrDllDir) + { + char developerDir8[OVR_MAX_PATH]; + size_t length = OVR_strlcpy(developerDir8, pLibOvrDllDir, sizeof(developerDir8)); // If missing a trailing path separator then append one. + + if((length > 0) && (length < sizeof(developerDir8)) && (developerDir8[length - 1] != OVR_FILE_PATH_SEPARATOR[0])) + { + length = OVR_strlcat(developerDir8, OVR_FILE_PATH_SEPARATOR, sizeof(developerDir8)); + + if(length < sizeof(developerDir8)) + { + #if defined(_WIN32) + size_t i; + for(i = 0; i <= length; ++i) // ASCII conversion of 8 to 16 bit text. + developerDir[i] = (FilePathCharType)(uint8_t)developerDir8[i]; + #else + OVR_strlcpy(developerDir, developerDir8, sizeof(developerDir)); + #endif + } + } + } + } + + // Support checking for a developer library location override via the OVR_SDK_ROOT environment variable. + // This pathway is deprecated in favor of using LIBOVR_DLL_DIR instead. + #if defined(OVR_ENABLE_DEVELOPER_SEARCH) + if (!developerDir[0]) // If not already set by LIBOVR_DLL_PATH... + { + // __FILE__ maps to /LibOVR/Src/OVR_CAPIShim.c + char sdkRoot[OVR_MAX_PATH]; + char* pLibOVR; + size_t i; + + // We assume that __FILE__ returns a full path, which isn't the case for some compilers. + // Need to compile with /FC under VC++ for __FILE__ to expand to the full file path. + // clang expands __FILE__ to a full path by default. + OVR_strlcpy(sdkRoot, __FILE__, sizeof(sdkRoot)); + for(i = 0; sdkRoot[i]; ++i) + sdkRoot[i] = (char)tolower(sdkRoot[i]); // Microsoft doesn't maintain case. + pLibOVR = strstr(sdkRoot, "libovr"); + if(pLibOVR && (pLibOVR > sdkRoot)) + pLibOVR[-1] = '\0'; + else + sdkRoot[0] = '\0'; + + if(sdkRoot[0]) + { + // We want to use a developer version of the library only if the application is also being executed from + // a developer location. Ideally we would do this by checking that the relative path from the executable to + // the shared library is the same at runtime as it was when the executable was first built, but we don't have + // an easy way to do that from here and it would require some runtime help from the application code. + // Instead we verify that the application is simply in the same developer tree that was was when built. + // We could put in some additional logic to make it very likely to know if the EXE is in its original location. + FilePathCharType modulePath[OVR_MAX_PATH]; + const ovrBool pathMatch = OVR_GetCurrentModuleDirectory(modulePath, OVR_MAX_PATH, ovrTrue) && + (OVR_PathStartsWith(modulePath, sdkRoot) == ovrTrue); + if(pathMatch == ovrFalse) + { + sdkRoot[0] = '\0'; // The application module is not in the developer tree, so don't try to use the developer shared library. + } + } + + if(sdkRoot[0]) + { + #if defined(OVR_BUILD_DEBUG) + const char* pConfigDirName = "Debug"; + #else + const char* pConfigDirName = "Release"; + #endif + + #if defined(_MSC_VER) + #if defined(_WIN64) + const char* pArchDirName = "x64"; + #else + const char* pArchDirName = "Win32"; + #endif + #else + #if defined(__x86_64__) + const char* pArchDirName = "x86_64"; + #else + const char* pArchDirName = "i386"; + #endif + #endif + + #if defined(_MSC_VER) && (_MSC_VER == 1600) + const char* pCompilerVersion = "VS2010"; + #elif defined(_MSC_VER) && (_MSC_VER == 1700) + const char* pCompilerVersion = "VS2012"; + #elif defined(_MSC_VER) && (_MSC_VER == 1800) + const char* pCompilerVersion = "VS2013"; + #elif defined(_MSC_VER) && (_MSC_VER == 1900) + const char* pCompilerVersion = "VS2014"; + #endif + + #if defined(_WIN32) + int count = swprintf_s(developerDir, OVR_MAX_PATH, L"%hs\\LibOVR\\Lib\\Windows\\%hs\\%hs\\%hs\\", + sdkRoot, pArchDirName, pConfigDirName, pCompilerVersion); + #elif defined(__APPLE__) + // Apple/XCode doesn't let you specify an arch in build paths, which is OK if we build a universal binary. + (void)pArchDirName; + int count = snprintf(developerDir, OVR_MAX_PATH, "%s/LibOVR/Lib/Mac/%s/", + sdkRoot, pConfigDirName); + #else + int count = snprintf(developerDir, OVR_MAX_PATH, "%s/LibOVR/Lib/Linux/%s/%s/", + sdkRoot, pArchDirName, pConfigDirName); + #endif + + if((count < 0) || (count >= (int)OVR_MAX_PATH)) // If there was an error or capacity overflow... clear the string. + { + developerDir[0] = '\0'; + } + } + } + #endif // OVR_ENABLE_DEVELOPER_SEARCH + + { + FilePathCharType cwDir[OVR_MAX_PATH]; // Will be filled in below. + FilePathCharType appDir[OVR_MAX_PATH]; + size_t i; + + #if defined(_WIN32) + FilePathCharType moduleDir[OVR_MAX_PATH]; + const FilePathCharType* directoryArray[5]; + directoryArray[0] = cwDir; + directoryArray[1] = moduleDir; + directoryArray[2] = appDir; + directoryArray[3] = developerDir; // Developer directory. + directoryArray[4] = L""; // No directory, which causes Windows to use the standard search strategy to find the DLL. + + OVR_GetCurrentModuleDirectory(moduleDir, sizeof(moduleDir)/sizeof(moduleDir[0]), ovrTrue); + + #elif defined(__APPLE__) + // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/dyld.1.html + + FilePathCharType homeDir[OVR_MAX_PATH]; + FilePathCharType homeFrameworkDir[OVR_MAX_PATH]; + const FilePathCharType* directoryArray[5]; + size_t homeDirLength = 0; + + const char* pHome = getenv("HOME"); // Try getting the HOME environment variable. + + if (pHome) + { + homeDirLength = OVR_strlcpy(homeDir, pHome, sizeof(homeDir)); + } + else + { + // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/getpwuid_r.3.html + const long pwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (pwBufferSize != -1) + { + char pwBuffer[pwBufferSize]; + struct passwd pw; + struct passwd* pwResult = NULL; + + if ((getpwuid_r(getuid(), &pw, pwBuffer, pwBufferSize, &pwResult) == 0) && pwResult) + homeDirLength = OVR_strlcpy(homeDir, pw.pw_dir, sizeof(homeDir)); + } + } + + if (homeDirLength) + { + if (homeDir[homeDirLength - 1] == '/') + homeDir[homeDirLength - 1] = '\0'; + OVR_strlcpy(homeFrameworkDir, homeDir, sizeof(homeFrameworkDir)); + OVR_strlcat(homeFrameworkDir, "/Library/Frameworks/", sizeof(homeFrameworkDir)); + } + else + { + homeFrameworkDir[0] = '\0'; + } + + directoryArray[0] = cwDir; + directoryArray[1] = appDir; + directoryArray[2] = homeFrameworkDir; // ~/Library/Frameworks/ + directoryArray[3] = "/Library/Frameworks/"; // DYLD_FALLBACK_FRAMEWORK_PATH + directoryArray[4] = developerDir; // Developer directory. + + #else + #define STR1(x) #x + #define STR(x) STR1(x) + #ifdef LIBDIR + #define TEST_LIB_DIR STR(LIBDIR) "/" + #else + #define TEST_LIB_DIR appDir + #endif + + const FilePathCharType* directoryArray[5]; + directoryArray[0] = cwDir; + directoryArray[1] = TEST_LIB_DIR; // Directory specified by LIBDIR if defined. + directoryArray[2] = developerDir; // Developer directory. + directoryArray[3] = "/usr/local/lib/"; + directoryArray[4] = "/usr/lib/"; + #endif + + OVR_GetCurrentWorkingDirectory(cwDir, sizeof(cwDir) / sizeof(cwDir[0])); + OVR_GetCurrentApplicationDirectory(appDir, sizeof(appDir) / sizeof(appDir[0]), ovrTrue, NULL); + + // Versioned file expectations. + // Windows: LibOVRRT__.dll // Example: LibOVRRT64_1_1.dll -- LibOVRRT 64 bit, product 1, major version 1, minor/patch/build numbers unspecified in the name. + // Mac: LibOVRRT_.framework/Versions//LibOVRRT_ // We are not presently using the .framework bundle's Current directory to hold the version number. This may change. + // Linux: libOVRRT_.so. // The file on disk may contain a minor version number, but a symlink is used to map this major-only version to it. + + // Since we are manually loading the LibOVR dynamic library, we need to look in various locations for a file + // that matches our requirements. The functionality required is somewhat similar to the operating system's + // dynamic loader functionality. Each OS has some differences in how this is handled. + // Future versions of this may iterate over all libOVRRT.so.* files in the directory and use the one that matches our requirements. + // + // We need to look for a library that matches the product version and major version of the caller's request, + // and that library needs to support a minor version that is >= the requested minor version. Currently we + // don't test the minor version here, as the library is named based only on the product and major version. + // Currently the minor version test is handled via the initialization of the library and the initialization + // fails if minor version cannot be supported by the library. The reason this is done during initialization + // is that the library can at runtime support multiple minor versions based on the user's request. To the + // external user, all that matters it that they call ovr_Initialize with a requested version and it succeeds + // or fails. + // + // The product version is something that is at a higher level than the major version, and is not something that's + // always seen in libraries (an example is the well-known LibXml2 library, in which the 2 is essentially the product version). + + for(i = 0; i < sizeof(directoryArray)/sizeof(directoryArray[0]); ++i) + { + #if defined(_WIN32) + printfResult = swprintf(libraryPath, libraryPathCapacity, L"%lsLibOVRRT%hs_%d_%d.dll", directoryArray[i], pBitDepth, requestedProductVersion, requestedMajorVersion); + + #elif defined(__APPLE__) + // https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/VersionInformation.html + // Macintosh application bundles have the option of embedding dependent frameworks within the application + // bundle itself. A problem with that is that it doesn't support vendor-supplied updates to the framework. + printfResult = snprintf(libraryPath, libraryPathCapacity, "%sLibOVRRT_%d.framework/Versions/%d/LibOVRRT_%d", directoryArray[i], requestedProductVersion, requestedMajorVersion, requestedProductVersion); + + #else // Unix + // Applications that depend on the OS (e.g. ld-linux / ldd) can rely on the library being in a common location + // such as /usr/lib or can rely on the -rpath linker option to embed a path for the OS to check for the library, + // or can rely on the LD_LIBRARY_PATH environment variable being set. It's generally not recommended that applications + // depend on LD_LIBRARY_PATH be globally modified, partly due to potentialy security issues. + // Currently we check the current application directory, current working directory, and then /usr/lib and possibly others. + printfResult = snprintf(libraryPath, libraryPathCapacity, "%slibOVRRT%s_%d.so.%d", directoryArray[i], pBitDepth, requestedProductVersion, requestedMajorVersion); + #endif + + if((printfResult >= 0) && (printfResult < (int)libraryPathCapacity)) + { + moduleHandle = OVR_OpenLibrary(libraryPath); + if(moduleHandle != ModuleHandleTypeNull) + return moduleHandle; + } + } + } + + return moduleHandle; +} + + + +//----------------------------------------------------------------------------------- +// ***** hLibOVR +// +// global handle to the LivOVR shared library. +// +static ModuleHandleType hLibOVR = NULL; + +// This function is currently unsupported. +ModuleHandleType ovr_GetLibOVRRTHandle() +{ + return hLibOVR; +} + + + +//----------------------------------------------------------------------------------- +// ***** Function declarations +// +// To consider: Move OVR_DECLARE_IMPORT and the declarations below to OVR_CAPI.h +// +OVR_DECLARE_IMPORT(ovrBool, ovr_InitializeRenderingShimVersion, (int requestedMinorVersion)) +OVR_DECLARE_IMPORT(ovrResult, ovr_Initialize, (const ovrInitParams* params)) +OVR_DECLARE_IMPORT(ovrBool, ovr_Shutdown, ()) +OVR_DECLARE_IMPORT(const char*, ovr_GetVersionString, ()) +OVR_DECLARE_IMPORT(void, ovr_GetLastErrorInfo, (ovrErrorInfo* errorInfo)) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_Detect, ()) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_Create, (int index, ovrHmd* pHmd)) +OVR_DECLARE_IMPORT(void, ovrHmd_Destroy, (ovrHmd hmd)) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_CreateDebug, (ovrHmdType type, ovrHmd* pHmd)) +OVR_DECLARE_IMPORT(unsigned int, ovrHmd_GetEnabledCaps, (ovrHmd hmd)) +OVR_DECLARE_IMPORT(void, ovrHmd_SetEnabledCaps, (ovrHmd hmd, unsigned int hmdCaps)) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_ConfigureTracking, (ovrHmd hmd, unsigned int supportedTrackingCaps, unsigned int requiredTrackingCaps)) +OVR_DECLARE_IMPORT(void, ovrHmd_RecenterPose, (ovrHmd hmd)) +OVR_DECLARE_IMPORT(ovrTrackingState, ovrHmd_GetTrackingState, (ovrHmd hmd, double absTime)) +OVR_DECLARE_IMPORT(ovrSizei, ovrHmd_GetFovTextureSize, (ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel)) +OVR_DECLARE_IMPORT(void, ovrHmd_SubmitFrameLegacy, (ovrHmd hmd, unsigned int frameIndex, const ovrPosef renderPose[2], const ovrTexture eyeTexture[2])) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_SubmitFrame, (ovrHmd hmd, unsigned int frameIndex, const ovrViewScaleDesc* viewScaleDesc, ovrLayerHeader const * const * layerPtrList, unsigned int layerCount)) +OVR_DECLARE_IMPORT(ovrEyeRenderDesc, ovrHmd_GetRenderDesc, (ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov)) +OVR_DECLARE_IMPORT(ovrFrameTiming, ovrHmd_GetFrameTiming, (ovrHmd hmd, unsigned int frameIndex)) +OVR_DECLARE_IMPORT(void, ovrHmd_ResetFrameTiming, (ovrHmd hmd, unsigned int frameIndex)) +OVR_DECLARE_IMPORT(double, ovr_GetTimeInSeconds, ()) +OVR_DECLARE_IMPORT(ovrBool, ovrHmd_GetBool, (ovrHmd hmd, const char* propertyName, ovrBool defaultVal)) +OVR_DECLARE_IMPORT(ovrBool, ovrHmd_SetBool, (ovrHmd hmd, const char* propertyName, ovrBool value)) +OVR_DECLARE_IMPORT(int, ovrHmd_GetInt, (ovrHmd hmd, const char* propertyName, int defaultVal)) +OVR_DECLARE_IMPORT(ovrBool, ovrHmd_SetInt, (ovrHmd hmd, const char* propertyName, int value)) +OVR_DECLARE_IMPORT(float, ovrHmd_GetFloat, (ovrHmd hmd, const char* propertyName, float defaultVal)) +OVR_DECLARE_IMPORT(ovrBool, ovrHmd_SetFloat, (ovrHmd hmd, const char* propertyName, float value)) +OVR_DECLARE_IMPORT(unsigned int, ovrHmd_GetFloatArray, (ovrHmd hmd, const char* propertyName, float values[], unsigned int arraySize)) +OVR_DECLARE_IMPORT(ovrBool, ovrHmd_SetFloatArray, (ovrHmd hmd, const char* propertyName, const float values[], unsigned int arraySize)) +OVR_DECLARE_IMPORT(const char*, ovrHmd_GetString, (ovrHmd hmd, const char* propertyName, const char* defaultVal)) +OVR_DECLARE_IMPORT(ovrBool, ovrHmd_SetString, (ovrHmd hmddesc, const char* propertyName, const char* value)) +OVR_DECLARE_IMPORT(int, ovr_TraceMessage, (int level, const char* message)) + +#if defined (_WIN32) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_CreateSwapTextureSetD3D11, (ovrHmd hmddesc, ID3D11Device* device, const D3D11_TEXTURE2D_DESC* desc, ovrSwapTextureSet** outTextureSet)) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_CreateMirrorTextureD3D11, (ovrHmd hmddesc, ID3D11Device* device, const D3D11_TEXTURE2D_DESC* desc, ovrTexture** outMirrorTexture)) +#endif + +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_CreateSwapTextureSetGL, (ovrHmd hmddesc, GLuint format, int width, int height, ovrSwapTextureSet** outTextureSet)) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_CreateMirrorTextureGL, (ovrHmd hmddesc, GLuint format, int width, int height, ovrTexture** outMirrorTexture)) + +OVR_DECLARE_IMPORT(void, ovrHmd_DestroySwapTextureSet, (ovrHmd hmddesc, ovrSwapTextureSet* textureSet)) +OVR_DECLARE_IMPORT(void, ovrHmd_DestroyMirrorTexture, (ovrHmd hmddesc, ovrTexture* textureSet)) +OVR_DECLARE_IMPORT(ovrResult, ovrHmd_SetQueueAheadFraction, (ovrHmd hmddesc, float queueAheadFraction)) + +static ovrResult OVR_LoadSharedLibrary(int requestedMinorVersion, int requestedPatchVersion) +{ + FilePathCharType filePath[OVR_MAX_PATH]; + + if(hLibOVR) + return ovrSuccess; + + hLibOVR = OVR_FindLibraryPath(requestedMinorVersion, requestedPatchVersion, + filePath, sizeof(filePath) / sizeof(filePath[0])); + if(!hLibOVR) + return ovrError_LibLoad; + + OVR_GETFUNCTION(ovr_InitializeRenderingShimVersion) + OVR_GETFUNCTION(ovr_Initialize) + OVR_GETFUNCTION(ovr_Shutdown) + OVR_GETFUNCTION(ovr_GetVersionString) + OVR_GETFUNCTION(ovr_GetLastErrorInfo) + OVR_GETFUNCTION(ovrHmd_Detect) + OVR_GETFUNCTION(ovrHmd_Create) + OVR_GETFUNCTION(ovrHmd_Destroy) + OVR_GETFUNCTION(ovrHmd_CreateDebug) + OVR_GETFUNCTION(ovrHmd_GetEnabledCaps) + OVR_GETFUNCTION(ovrHmd_SetEnabledCaps) + OVR_GETFUNCTION(ovrHmd_ConfigureTracking) + OVR_GETFUNCTION(ovrHmd_RecenterPose) + OVR_GETFUNCTION(ovrHmd_GetTrackingState) + OVR_GETFUNCTION(ovrHmd_GetFovTextureSize) + OVR_GETFUNCTION(ovrHmd_SubmitFrame) + OVR_GETFUNCTION(ovrHmd_GetRenderDesc) + OVR_GETFUNCTION(ovrHmd_GetFrameTiming) + OVR_GETFUNCTION(ovrHmd_ResetFrameTiming) + OVR_GETFUNCTION(ovr_GetTimeInSeconds) + OVR_GETFUNCTION(ovrHmd_GetBool) + OVR_GETFUNCTION(ovrHmd_SetBool) + OVR_GETFUNCTION(ovrHmd_GetInt) + OVR_GETFUNCTION(ovrHmd_SetInt) + OVR_GETFUNCTION(ovrHmd_GetFloat) + OVR_GETFUNCTION(ovrHmd_SetFloat) + OVR_GETFUNCTION(ovrHmd_GetFloatArray) + OVR_GETFUNCTION(ovrHmd_SetFloatArray) + OVR_GETFUNCTION(ovrHmd_GetString) + OVR_GETFUNCTION(ovrHmd_SetString) + OVR_GETFUNCTION(ovr_TraceMessage) +#if defined (_WIN32) + OVR_GETFUNCTION(ovrHmd_CreateSwapTextureSetD3D11) + OVR_GETFUNCTION(ovrHmd_CreateMirrorTextureD3D11) +#endif + OVR_GETFUNCTION(ovrHmd_CreateSwapTextureSetGL) + OVR_GETFUNCTION(ovrHmd_CreateMirrorTextureGL) + OVR_GETFUNCTION(ovrHmd_DestroySwapTextureSet) + OVR_GETFUNCTION(ovrHmd_DestroyMirrorTexture) + OVR_GETFUNCTION(ovrHmd_SetQueueAheadFraction) + + return ovrSuccess; +} + +static void OVR_UnloadSharedLibrary() +{ + // TBD: Currently there are some CAPI functions and code in the render shim that does + // not work with unloading the LibOVRRT. We also have the problem that LibOVR returns + // a string pointer in the GetLastError function. + //OVR_CloseLibrary(hLibOVR); + //hLibOVR = NULL; +} + + + +OVR_PUBLIC_FUNCTION(ovrBool) ovr_InitializeRenderingShim() +{ +#if 1 + return ovrTrue; +#else + return ovr_InitializeRenderingShimVersion(OVR_MINOR_VERSION); +#endif +} + + +OVR_PUBLIC_FUNCTION(ovrBool) ovr_InitializeRenderingShimVersion(int requestedMinorVersion) +{ + // By design we ignore the build version in the library search. + ovrBool initializeResult; + ovrResult result = OVR_LoadSharedLibrary(OVR_PRODUCT_VERSION, OVR_MAJOR_VERSION); + + if (result != ovrSuccess) + return ovrFalse; + + initializeResult = ovr_InitializeRenderingShimVersionPtr(requestedMinorVersion); + + if (initializeResult == ovrFalse) + OVR_UnloadSharedLibrary(); + + return initializeResult; +} + + +// These defaults are also in CAPI.cpp +static const ovrInitParams DefaultParams = { + ovrInit_RequestVersion, // Flags + OVR_MINOR_VERSION, // RequestedMinorVersion + 0, // LogCallback + 0, // ConnectionTimeoutSeconds + OVR_ON64("") // pad0 +}; + +OVR_PUBLIC_FUNCTION(ovrResult) ovr_Initialize(const ovrInitParams* inputParams) +{ + ovrResult result; + ovrInitParams params; + + if (!inputParams) + { + params = DefaultParams; + } + else + { + params = *inputParams; + + // If not requesting a particular minor version, + if (!(params.Flags & ovrInit_RequestVersion)) + { + // Enable requesting the default minor version. + params.Flags |= ovrInit_RequestVersion; + params.RequestedMinorVersion = OVR_MINOR_VERSION; + } + } + +#if defined(OVR_BUILD_DEBUG) + // If no debug setting is provided, + if (!(params.Flags & (ovrInit_Debug | ovrInit_ForceNoDebug))) + { + // Set the debug flag in debug mode. + params.Flags |= ovrInit_Debug; + } +#endif + + // By design we ignore the build version in the library search. + result = OVR_LoadSharedLibrary(OVR_PRODUCT_VERSION, OVR_MAJOR_VERSION); + if (result != ovrSuccess) + return result; + + result = ovr_InitializePtr(¶ms); + if (result != ovrSuccess) + OVR_UnloadSharedLibrary(); + + return result; +} + +OVR_PUBLIC_FUNCTION(void) ovr_Shutdown() +{ + if (!ovr_ShutdownPtr) + return; + ovr_ShutdownPtr(); + OVR_UnloadSharedLibrary(); +} + +OVR_PUBLIC_FUNCTION(const char*) ovr_GetVersionString() +{ + if (!ovr_GetVersionStringPtr) + return "(Unable to load LibOVR)"; + return ovr_GetVersionStringPtr(); +} + +OVR_PUBLIC_FUNCTION(void) ovr_GetLastErrorInfo(ovrErrorInfo* errorInfo) +{ + if (!ovr_GetLastErrorInfoPtr) + { + memset(errorInfo, 0, sizeof(ovrErrorInfo)); + errorInfo->Result = ovrError_LibLoad; + } + else + ovr_GetLastErrorInfoPtr(errorInfo); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_Detect() +{ + if (!ovrHmd_DetectPtr) + return ovrError_NotInitialized; + return ovrHmd_DetectPtr(); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_Create(int index, ovrHmd* pHmd) +{ + if (!ovrHmd_CreatePtr) + return ovrError_NotInitialized; + return ovrHmd_CreatePtr(index, pHmd); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateDebug(ovrHmdType type, ovrHmd* pHmd) +{ + if (!ovrHmd_CreateDebugPtr) + return ovrError_NotInitialized; + return ovrHmd_CreateDebugPtr(type, pHmd); +} + +OVR_PUBLIC_FUNCTION(void) ovrHmd_Destroy(ovrHmd hmd) +{ + if (!ovrHmd_DestroyPtr) + return; + ovrHmd_DestroyPtr(hmd); +} + +OVR_PUBLIC_FUNCTION(unsigned int) ovrHmd_GetEnabledCaps(ovrHmd hmd) +{ + if (!ovrHmd_GetEnabledCapsPtr) + return 0; + return ovrHmd_GetEnabledCapsPtr(hmd); +} + +OVR_PUBLIC_FUNCTION(void) ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int hmdCaps) +{ + if (!ovrHmd_SetEnabledCapsPtr) + return; + ovrHmd_SetEnabledCapsPtr(hmd, hmdCaps); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_ConfigureTracking(ovrHmd hmd, unsigned int supportedTrackingCaps, + unsigned int requiredTrackingCaps) +{ + if (!ovrHmd_ConfigureTrackingPtr) + return ovrError_NotInitialized; + return ovrHmd_ConfigureTrackingPtr(hmd, supportedTrackingCaps, requiredTrackingCaps); +} + +OVR_PUBLIC_FUNCTION(void) ovrHmd_RecenterPose(ovrHmd hmd) +{ + if (!ovrHmd_RecenterPosePtr) + return; + ovrHmd_RecenterPosePtr(hmd); +} + +OVR_PUBLIC_FUNCTION(ovrTrackingState) ovrHmd_GetTrackingState(ovrHmd hmd, double absTime) +{ + if (!ovrHmd_GetTrackingStatePtr) + { + ovrTrackingState nullTrackingState; + memset(&nullTrackingState, 0, sizeof(nullTrackingState)); + return nullTrackingState; + } + + return ovrHmd_GetTrackingStatePtr(hmd, absTime); +} + + + +OVR_PUBLIC_FUNCTION(ovrSizei) ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, + float pixelsPerDisplayPixel) +{ + if (!ovrHmd_GetFovTextureSizePtr) + { + ovrSizei nullSize; + memset(&nullSize, 0, sizeof(nullSize)); + return nullSize; + } + + return ovrHmd_GetFovTextureSizePtr(hmd, eye, fov, pixelsPerDisplayPixel); +} + +#if defined (_WIN32) +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateSwapTextureSetD3D11(ovrHmd hmd, + ID3D11Device* device, + const D3D11_TEXTURE2D_DESC* desc, + ovrSwapTextureSet** outTextureSet) +{ + if (!ovrHmd_CreateSwapTextureSetD3D11Ptr) + return ovrError_NotInitialized; + + return ovrHmd_CreateSwapTextureSetD3D11Ptr(hmd, device, desc, outTextureSet); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateMirrorTextureD3D11(ovrHmd hmd, + ID3D11Device* device, + const D3D11_TEXTURE2D_DESC* desc, + ovrTexture** outMirrorTexture) +{ + if (!ovrHmd_CreateMirrorTextureD3D11Ptr) + return ovrError_NotInitialized; + + return ovrHmd_CreateMirrorTextureD3D11Ptr(hmd, device, desc, outMirrorTexture); +} +#endif + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateSwapTextureSetGL(ovrHmd hmd, GLuint format, + int width, int height, + ovrSwapTextureSet** outTextureSet) +{ + if (!ovrHmd_CreateSwapTextureSetGLPtr) + return ovrError_NotInitialized; + + return ovrHmd_CreateSwapTextureSetGLPtr(hmd, format, width, height, outTextureSet); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_CreateMirrorTextureGL(ovrHmd hmd, GLuint format, + int width, int height, + ovrTexture** outMirrorTexture) +{ + if (!ovrHmd_CreateMirrorTextureGLPtr) + return ovrError_NotInitialized; + + return ovrHmd_CreateMirrorTextureGLPtr(hmd, format, width, height, outMirrorTexture); +} + +OVR_PUBLIC_FUNCTION(void) ovrHmd_DestroySwapTextureSet(ovrHmd hmd, ovrSwapTextureSet* textureSet) +{ + if (!ovrHmd_DestroySwapTextureSetPtr) + return; + + ovrHmd_DestroySwapTextureSetPtr(hmd, textureSet); +} + +OVR_PUBLIC_FUNCTION(void) ovrHmd_DestroyMirrorTexture(ovrHmd hmd, ovrTexture* mirrorTexture) +{ + if (!ovrHmd_DestroyMirrorTexturePtr) + return; + + ovrHmd_DestroyMirrorTexturePtr(hmd, mirrorTexture); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_SetQueueAheadFraction(ovrHmd hmd, float queueAheadFraction) +{ + if (!ovrHmd_SetQueueAheadFractionPtr) + return ovrError_NotInitialized; + + return ovrHmd_SetQueueAheadFractionPtr(hmd, queueAheadFraction); +} + +OVR_PUBLIC_FUNCTION(ovrResult) ovrHmd_SubmitFrame(ovrHmd hmd, unsigned int frameIndex, const ovrViewScaleDesc* viewScaleDesc, ovrLayerHeader const * const * layerPtrList, unsigned int layerCount) +{ + if (!ovrHmd_SubmitFramePtr) + return ovrError_NotInitialized; + + return ovrHmd_SubmitFramePtr(hmd, frameIndex, viewScaleDesc, layerPtrList, layerCount); +} + +OVR_PUBLIC_FUNCTION(ovrEyeRenderDesc) ovrHmd_GetRenderDesc(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov) +{ + if (!ovrHmd_GetRenderDescPtr) + { + ovrEyeRenderDesc nullEyeRenderDesc; + memset(&nullEyeRenderDesc, 0, sizeof(nullEyeRenderDesc)); + return nullEyeRenderDesc; + } + return ovrHmd_GetRenderDescPtr(hmd, eyeType, fov); +} + +OVR_PUBLIC_FUNCTION(ovrFrameTiming) ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex) +{ + if (!ovrHmd_GetFrameTimingPtr) + { + ovrFrameTiming nullFrameTiming; + memset(&nullFrameTiming, 0, sizeof(nullFrameTiming)); + return nullFrameTiming; + } + return ovrHmd_GetFrameTimingPtr(hmd, frameIndex); +} + +OVR_PUBLIC_FUNCTION(void) ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex) +{ + if (!ovrHmd_ResetFrameTimingPtr) + return; + ovrHmd_ResetFrameTimingPtr(hmd, frameIndex); +} + +OVR_PUBLIC_FUNCTION(double) ovr_GetTimeInSeconds() +{ + if (!ovr_GetTimeInSecondsPtr) + return 0.; + return ovr_GetTimeInSecondsPtr(); +} + +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_GetBool(ovrHmd hmd, const char* propertyName, ovrBool defaultVal) +{ + if (!ovrHmd_GetBoolPtr) + return ovrFalse; + return ovrHmd_GetBoolPtr(hmd, propertyName, defaultVal); +} + +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetBool(ovrHmd hmd, const char* propertyName, ovrBool value) +{ + if (!ovrHmd_SetBoolPtr) + return ovrFalse; + return ovrHmd_SetBoolPtr(hmd, propertyName, value); +} + +OVR_PUBLIC_FUNCTION(int) ovrHmd_GetInt(ovrHmd hmd, const char* propertyName, int defaultVal) +{ + if (!ovrHmd_GetIntPtr) + return 0; + return ovrHmd_GetIntPtr(hmd, propertyName, defaultVal); +} + +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetInt(ovrHmd hmd, const char* propertyName, int value) +{ + if (!ovrHmd_SetIntPtr) + return ovrFalse; + return ovrHmd_SetIntPtr(hmd, propertyName, value); +} + +OVR_PUBLIC_FUNCTION(float) ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal) +{ + if (!ovrHmd_GetFloatPtr) + return 0.f; + return ovrHmd_GetFloatPtr(hmd, propertyName, defaultVal); +} + +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value) +{ + if (!ovrHmd_SetFloatPtr) + return ovrFalse; + return ovrHmd_SetFloatPtr(hmd, propertyName, value); +} + +OVR_PUBLIC_FUNCTION(unsigned int) ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName, + float values[], unsigned int arraySize) +{ + if (!ovrHmd_GetFloatArrayPtr) + return 0; + return ovrHmd_GetFloatArrayPtr(hmd, propertyName, values, arraySize); +} + +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetFloatArray(ovrHmd hmd, const char* propertyName, + const float values[], unsigned int arraySize) +{ + if (!ovrHmd_SetFloatArrayPtr) + return ovrFalse; + return ovrHmd_SetFloatArrayPtr(hmd, propertyName, values, arraySize); +} + +OVR_PUBLIC_FUNCTION(const char*) ovrHmd_GetString(ovrHmd hmd, const char* propertyName, + const char* defaultVal) +{ + if (!ovrHmd_GetStringPtr) + return "(Unable to load LibOVR)"; + return ovrHmd_GetStringPtr(hmd, propertyName, defaultVal); +} + +OVR_PUBLIC_FUNCTION(ovrBool) ovrHmd_SetString(ovrHmd hmd, const char* propertyName, + const char* value) +{ + if (!ovrHmd_SetStringPtr) + return ovrFalse; + return ovrHmd_SetStringPtr(hmd, propertyName, value); +} + +OVR_PUBLIC_FUNCTION(int) ovr_TraceMessage(int level, const char* message) +{ + if (!ovr_TraceMessagePtr) + return -1; + + return ovr_TraceMessagePtr(level, message); +} + + + + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_CAPI_Util.cpp b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_CAPI_Util.cpp new file mode 100644 index 000000000000..11d7d4d8f217 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_CAPI_Util.cpp @@ -0,0 +1,171 @@ +/************************************************************************************ + +PublicHeader: OVR_CAPI_Util.c +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + + +#include +#include "OVR_StereoProjection.h" + +#if defined(_MSC_VER) + #include + #pragma intrinsic(_mm_pause) +#endif + + + +// Used to generate projection from ovrEyeDesc::Fov +ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, unsigned int projectionModFlags) +{ + bool rightHanded = (projectionModFlags & ovrProjection_RightHanded) > 0; + bool flipZ = (projectionModFlags & ovrProjection_FarLessThanNear) > 0; + bool farAtInfinity = (projectionModFlags & ovrProjection_FarClipAtInfinity) > 0; + bool isOpenGL = (projectionModFlags & ovrProjection_ClipRangeOpenGL) > 0; + + // TODO: Pass in correct eye to CreateProjection if we want to support canted displays from CAPI + return OVR::CreateProjection(rightHanded , isOpenGL, fov, OVR::StereoEye_Center, znear, zfar, flipZ, farAtInfinity); +} + +ovrTimewarpProjectionDesc ovrTimewarpProjectionDesc_FromProjection(ovrMatrix4f Projection) +{ + ovrTimewarpProjectionDesc res; + res.Projection22 = Projection.M[2][2]; + res.Projection23 = Projection.M[2][3]; + res.Projection32 = Projection.M[3][2]; + return res; +} + +ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale, + float orthoDistance, float hmdToEyeViewOffsetX) +{ + ovrMatrix4f ortho; + // Negative sign is correct! + // If the eye is offset to the left, then the ortho view needs to be offset to the right relative to the camera. + float orthoHorizontalOffset = -hmdToEyeViewOffsetX / orthoDistance; + + /* + // Current projection maps real-world vector (x,y,1) to the RT. + // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to + // the physical [-orthoHalfFov,orthoHalfFov] + // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means + // we don't have to feed in Z=1 all the time. + // The horizontal offset math is a little hinky because the destination is + // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset] + // So we need to first map [-FovPixels/2,FovPixels/2] to + // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]: + // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset; + // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset; + // But then we need the same mapping as the existing projection matrix, i.e. + // x2 = x1 * Projection.M[0][0] + Projection.M[0][2]; + // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2]; + // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels + + // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]; + // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and + // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]. + */ + + ortho.M[0][0] = projection.M[0][0] * orthoScale.x; + ortho.M[0][1] = 0.0f; + ortho.M[0][2] = 0.0f; + ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] ); + + ortho.M[1][0] = 0.0f; + ortho.M[1][1] = -projection.M[1][1] * orthoScale.y; /* Note sign flip (text rendering uses Y=down). */ + ortho.M[1][2] = 0.0f; + ortho.M[1][3] = -projection.M[1][2]; + + ortho.M[2][0] = 0.0f; + ortho.M[2][1] = 0.0f; + ortho.M[2][2] = 0.0f; + ortho.M[2][3] = 0.0f; + + /* No perspective correction for ortho. */ + ortho.M[3][0] = 0.0f; + ortho.M[3][1] = 0.0f; + ortho.M[3][2] = 0.0f; + ortho.M[3][3] = 1.0f; + + return ortho; +} + + +void ovr_CalcEyePoses(ovrPosef headPose, + const ovrVector3f hmdToEyeViewOffset[2], + ovrPosef outEyePoses[2]) +{ + if (!hmdToEyeViewOffset || !outEyePoses) + { + return; + } + + using OVR::Posef; + using OVR::Vector3f; + + // Currently HmdToEyeViewOffset is only a 3D vector + outEyePoses[0] = Posef(headPose.Orientation, ((Posef)headPose).Apply((Vector3f)hmdToEyeViewOffset[0])); + outEyePoses[1] = Posef(headPose.Orientation, ((Posef)headPose).Apply((Vector3f)hmdToEyeViewOffset[1])); +} + + +void ovrHmd_GetEyePoses(ovrHmd hmd, unsigned int frameIndex, + const ovrVector3f hmdToEyeViewOffset[2], + ovrPosef outEyePoses[2], + ovrTrackingState* outHmdTrackingState) +{ + ovrFrameTiming ftiming = ovrHmd_GetFrameTiming(hmd, frameIndex); + ovrTrackingState hmdState = ovrHmd_GetTrackingState(hmd, ftiming.DisplayMidpointSeconds); + ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewOffset, outEyePoses); + if ( outHmdTrackingState != nullptr ) + { + *outHmdTrackingState = hmdState; + } +} + + + + +double ovr_WaitTillTime(double absTime) +{ + double initialTime = ovr_GetTimeInSeconds(); + double newTime = initialTime; + + while(newTime < absTime) + { + for (int j = 0; j < 5; j++) + { + #if defined(__x86_64__) || defined(_M_AMD64) || defined(__i386__) || defined(_M_IX86) // Intel architecture... + #if defined(__GNUC__) || defined(__clang__) + asm volatile("pause" ::: "memory"); + #elif defined(_MSC_VER) + _mm_pause(); + #endif + #endif + } + + newTime = ovr_GetTimeInSeconds(); + } + + return (newTime - initialTime); +} + + + + + diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_StereoProjection.cpp b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_StereoProjection.cpp new file mode 100644 index 000000000000..f0ae034fbdbd --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_StereoProjection.cpp @@ -0,0 +1,216 @@ +/************************************************************************************ + +Filename : OVR_StereoProjection.cpp +Content : Stereo rendering functions +Created : November 30, 2013 +Authors : Tom Fosyth + +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR_StereoProjection.h" + + +namespace OVR { + + +ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort tanHalfFov ) +{ + float projXScale = 2.0f / ( tanHalfFov.LeftTan + tanHalfFov.RightTan ); + float projXOffset = ( tanHalfFov.LeftTan - tanHalfFov.RightTan ) * projXScale * 0.5f; + float projYScale = 2.0f / ( tanHalfFov.UpTan + tanHalfFov.DownTan ); + float projYOffset = ( tanHalfFov.UpTan - tanHalfFov.DownTan ) * projYScale * 0.5f; + + ScaleAndOffset2D result; + result.Scale = Vector2f(projXScale, projYScale); + result.Offset = Vector2f(projXOffset, projYOffset); + // Hey - why is that Y.Offset negated? + // It's because a projection matrix transforms from world coords with Y=up, + // whereas this is from NDC which is Y=down. + + return result; +} + + + +Matrix4f CreateProjection( bool rightHanded, bool isOpenGL, FovPort tanHalfFov, StereoEye /*eye*/, + float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/, + bool flipZ /*= false*/, bool farAtInfinity /*= false*/) +{ + if(!flipZ && farAtInfinity) + { + //OVR_ASSERT_M(false, "Cannot push Far Clip to Infinity when Z-order is not flipped"); Assertion disabled because this code no longer has access to LibOVRKernel assertion functionality. + farAtInfinity = false; + } + + // A projection matrix is very like a scaling from NDC, so we can start with that. + ScaleAndOffset2D scaleAndOffset = CreateNDCScaleAndOffsetFromFov ( tanHalfFov ); + + float handednessScale = rightHanded ? -1.0f : 1.0f; + + Matrix4f projection; + // Produces X result, mapping clip edges to [-w,+w] + projection.M[0][0] = scaleAndOffset.Scale.x; + projection.M[0][1] = 0.0f; + projection.M[0][2] = handednessScale * scaleAndOffset.Offset.x; + projection.M[0][3] = 0.0f; + + // Produces Y result, mapping clip edges to [-w,+w] + // Hey - why is that YOffset negated? + // It's because a projection matrix transforms from world coords with Y=up, + // whereas this is derived from an NDC scaling, which is Y=down. + projection.M[1][0] = 0.0f; + projection.M[1][1] = scaleAndOffset.Scale.y; + projection.M[1][2] = handednessScale * -scaleAndOffset.Offset.y; + projection.M[1][3] = 0.0f; + + // Produces Z-buffer result - app needs to fill this in with whatever Z range it wants. + // We'll just use some defaults for now. + projection.M[2][0] = 0.0f; + projection.M[2][1] = 0.0f; + + if (farAtInfinity) + { + projection.M[2][2] = 0.0f; + projection.M[2][3] = zNear; + } + else + { + if (isOpenGL) + { + projection.M[2][2] = -handednessScale * (flipZ ? 1.0f : -1.0f) * (zNear + zFar) / (zFar - zNear); + projection.M[2][3] = -2.0f * ((flipZ ? -zFar : zFar) * zNear) / (zFar - zNear); + } + else + { + projection.M[2][2] = -handednessScale * (flipZ ? -zNear : zFar) / (zNear - zFar); + projection.M[2][3] = ((flipZ ? -zFar : zFar) * zNear) / (zNear - zFar); + } + } + + // Produces W result (= Z in) + projection.M[3][0] = 0.0f; + projection.M[3][1] = 0.0f; + projection.M[3][2] = handednessScale; + projection.M[3][3] = 0.0f; + + return projection; +} + + +Matrix4f CreateOrthoSubProjection ( bool /*rightHanded*/, StereoEye eyeType, + float tanHalfFovX, float tanHalfFovY, + float unitsX, float unitsY, + float distanceFromCamera, float interpupillaryDistance, + Matrix4f const &projection, + float zNear /*= 0.0f*/, float zFar /*= 0.0f*/, + bool flipZ /*= false*/, bool farAtInfinity /*= false*/) +{ + if(!flipZ && farAtInfinity) + { + //OVR_ASSERT_M(false, "Cannot push Far Clip to Infinity when Z-order is not flipped"); Assertion disabled because this code no longer has access to LibOVRKernel assertion functionality. + farAtInfinity = false; + } + + float orthoHorizontalOffset = interpupillaryDistance * 0.5f / distanceFromCamera; + switch ( eyeType ) + { + case StereoEye_Center: + orthoHorizontalOffset = 0.0f; + break; + case StereoEye_Left: + break; + case StereoEye_Right: + orthoHorizontalOffset = -orthoHorizontalOffset; + break; + default: + break; + } + + // Current projection maps real-world vector (x,y,1) to the RT. + // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to + // the physical [-orthoHalfFov,orthoHalfFov] + // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means + // we don't have to feed in Z=1 all the time. + // The horizontal offset math is a little hinky because the destination is + // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset] + // So we need to first map [-FovPixels/2,FovPixels/2] to + // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]: + // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset; + // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset; + // But then we need the sam mapping as the existing projection matrix, i.e. + // x2 = x1 * Projection.M[0][0] + Projection.M[0][2]; + // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2]; + // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels + + // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]; + // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and + // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]. + + float orthoScaleX = 2.0f * tanHalfFovX / unitsX; + float orthoScaleY = 2.0f * tanHalfFovY / unitsY; + Matrix4f ortho; + ortho.M[0][0] = projection.M[0][0] * orthoScaleX; + ortho.M[0][1] = 0.0f; + ortho.M[0][2] = 0.0f; + ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] ); + + ortho.M[1][0] = 0.0f; + ortho.M[1][1] = -projection.M[1][1] * orthoScaleY; // Note sign flip (text rendering uses Y=down). + ortho.M[1][2] = 0.0f; + ortho.M[1][3] = -projection.M[1][2]; + + const float zDiff = zNear - zFar; + if (fabsf(zDiff) < 0.001f) + { + ortho.M[2][0] = 0.0f; + ortho.M[2][1] = 0.0f; + ortho.M[2][2] = 0.0f; + ortho.M[2][3] = flipZ ? zNear : zFar; + } + else + { + ortho.M[2][0] = 0.0f; + ortho.M[2][1] = 0.0f; + + if(farAtInfinity) + { + ortho.M[2][2] = 0.0f; + ortho.M[2][3] = zNear; + } + else if (zDiff != 0.0f) + { + ortho.M[2][2] = (flipZ ? zNear : zFar) / zDiff; + ortho.M[2][3] = ((flipZ ? -zFar : zFar) * zNear) / zDiff; + } + } + + // No perspective correction for ortho. + ortho.M[3][0] = 0.0f; + ortho.M[3][1] = 0.0f; + ortho.M[3][2] = 0.0f; + ortho.M[3][3] = 1.0f; + + return ortho; +} + + + +} //namespace OVR + + diff --git a/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_StereoProjection.h b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_StereoProjection.h new file mode 100644 index 000000000000..1ec4566c9c17 --- /dev/null +++ b/Engine/Source/ThirdParty/Oculus/LibOVR/LibOVR_0_6_0/Src/OVR_StereoProjection.h @@ -0,0 +1,70 @@ +/************************************************************************************ + +Filename : OVR_StereoProjection.h +Content : Stereo projection functions +Created : November 30, 2013 +Authors : Tom Fosyth + +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_StereoProjection_h +#define OVR_StereoProjection_h + + +#include "Extras/OVR_Math.h" + + +namespace OVR { + + +//----------------------------------------------------------------------------------- +// ***** Stereo Enumerations + +// StereoEye specifies which eye we are rendering for; it is used to +// retrieve StereoEyeParams. +enum StereoEye +{ + StereoEye_Center, + StereoEye_Left, + StereoEye_Right +}; + + + +//----------------------------------------------------------------------------------- +// ***** Propjection functions + +Matrix4f CreateProjection ( bool rightHanded, bool isOpenGL, FovPort fov, StereoEye eye, + float zNear = 0.01f, float zFar = 10000.0f, + bool flipZ = false, bool farAtInfinity = false); + +Matrix4f CreateOrthoSubProjection ( bool rightHanded, StereoEye eyeType, + float tanHalfFovX, float tanHalfFovY, + float unitsX, float unitsY, float distanceFromCamera, + float interpupillaryDistance, Matrix4f const &projection, + float zNear = 0.0f, float zFar = 0.0f, + bool flipZ = false, bool farAtInfinity = false); + +ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort fov ); + + +} //namespace OVR + +#endif // OVR_StereoProjection_h