Config for building for Quest

This commit is contained in:
2024-05-29 11:53:41 +03:00
parent 15cbcf8752
commit 0db31c34d1
353 changed files with 74095 additions and 3 deletions

View File

@@ -0,0 +1,549 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Oculus mobile plugin additions-->
<root xmlns:android="http://schemas.android.com/apk/res/android">
<!-- init section is always evaluated once per architecture -->
<init>
<log text="Oculus mobile init"/>
<setBool result="bSupported" value="false"/>
<isArch arch="armeabi-v7a">
<setBool result="bSupported" value="true"/>
</isArch>
<isArch arch="arm64-v8a">
<setBool result="bSupported" value="true"/>
</isArch>
<!-- remove Oculus Signature Files by default -->
<setBool result="bRemoveOSIG" value="true"/>
<!-- determine the XrApi libraries that need to be loaded -->
<setStringFromProperty result="XrApi" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="XrApi" default="OVRPluginOpenXR"/>
<setBoolIsEqual result="bOVRPluginOpenXR" arg1="$S(XrApi)" arg2="OVRPluginOpenXR"/>
<setBoolIsEqual result="bNativeOpenXR" arg1="$S(XrApi)" arg2="NativeOpenXR"/>
<setBoolFromProperty result="bFocusAware" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bFocusAware" default="true"/>
<!-- get package for Meta Quest from AndroidRuntimeSettings -->
<setBoolFromProperty result="bPackageForMetaQuest" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="bPackageForMetaQuest" default="false"/>
<!-- Backcompat for deprecated oculus device target setting -->
<setBoolFromPropertyContains result="bPackageForQuest1" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="PackageForOculusMobile" contains="Quest"/>
<setBoolFromPropertyContains result="bPackageForQuest2" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="PackageForOculusMobile" contains="Quest2"/>
<setBoolFromPropertyContains result="bPackageForQuestPro" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="PackageForOculusMobile" contains="QuestPro"/>
<setBoolOr result="bPackageForMetaQuest" arg1="$B(bPackageForMetaQuest)" arg2="$B(bPackageForQuest1)"/>
<setBoolOr result="bPackageForMetaQuest" arg1="$B(bPackageForMetaQuest)" arg2="$B(bPackageForQuest2)"/>
<setBoolOr result="bPackageForMetaQuest" arg1="$B(bPackageForMetaQuest)" arg2="$B(bPackageForQuestPro)"/>
<!-- get supported oculus devices from OculusXRHMDRuntimeSettings -->
<setBoolFromPropertyContains result="bSupportMetaQuest" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="Quest"/>
<setBoolFromPropertyContains result="bSupportMetaQuest2" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="Quest2"/>
<setBoolFromPropertyContains result="bSupportMetaQuestPro" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="QuestPro"/>
<setBoolFromPropertyContains result="bSupportMetaQuest3" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SupportedDevices" contains="Quest3"/>
<setBoolFromProperty result="bShowLaunchImage" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="bShowLaunchImage" default="false"/>
<setBoolFromProperty result="bRequiresSystemKeyboard" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bRequiresSystemKeyboard" default="false"/>
<setStringFromProperty result="HandTrackingSupport" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="HandTrackingSupport" default="ControllersOnly"/>
<setStringFromProperty result="HandTrackingFrequency" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="HandTrackingFrequency" default="LOW"/>
<setStringFromProperty result="HandTrackingVersion" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="HandTrackingVersion" default="Default"/>
<setBoolIsEqual result="bHandTrackingVersionDefault" arg1="$S(HandTrackingVersion)" arg2="Default"/>
<setBoolIsEqual result="bHandTrackingVersionV1" arg1="$S(HandTrackingVersion)" arg2="V1"/>
<setBoolIsEqual result="bHandTrackingVersionV2" arg1="$S(HandTrackingVersion)" arg2="V2"/>
<setStringFromProperty result="ColorSpace" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="ColorSpace" default="Rec_709"/>
<setBoolIsEqual result="bColorSpaceUnmanaged" arg1="$S(ColorSpace)" arg2="Unmanaged"/>
<setBoolIsEqual result="bColorSpaceRec2020" arg1="$S(ColorSpace)" arg2="Rec_2020"/>
<setBoolIsEqual result="bColorSpaceRec709" arg1="$S(ColorSpace)" arg2="Rec_709"/>
<setBoolIsEqual result="bColorSpaceRiftCV1" arg1="$S(ColorSpace)" arg2="Rift_CV1"/>
<setBoolIsEqual result="bColorSpaceRiftS" arg1="$S(ColorSpace)" arg2="Rift_S"/>
<setBoolIsEqual result="bColorSpaceQuest" arg1="$S(ColorSpace)" arg2="Quest"/>
<setBoolIsEqual result="bColorSpaceP3" arg1="$S(ColorSpace)" arg2="P3"/>
<setBoolIsEqual result="bColorSpaceAdobeRGB" arg1="$S(ColorSpace)" arg2="Adobe_RGB"/>
<setBoolFromProperty result="bInsightPassthroughEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bInsightPassthroughEnabled" default="false"/>
<setBoolFromProperty result="bAnchorSupportEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bAnchorSupportEnabled" default="false"/>
<setBoolFromProperty result="bAnchorSharingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bAnchorSharingEnabled" default="false"/>
<setBoolFromProperty result="bSceneSupportEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bSceneSupportEnabled" default="false"/>
<setBoolFromProperty result="bSupportEyeTrackedFoveatedRendering" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bSupportEyeTrackedFoveatedRendering" default="false"/>
<setStringFromProperty result="ProcessorFavor" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="ProcessorFavor" default="FavorEqually"/>
<setBoolIsEqual result="bProcessorFavorEqually" arg1="$S(ProcessorFavor)" arg2="FavorEqually"/>
<setStringFromProperty result="SystemSplashBackground" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="SystemSplashBackground" default="Black"/>
<setBoolIsEqual result="bSystemSplashImageContextualPassthrough" arg1="$S(SystemSplashBackground)" arg2="Contextual"/>
<setBoolIsEqual result="bSystemSplashImageBlack" arg1="$S(SystemSplashBackground)" arg2="Black"/>
<!-- check for experimental feature support from config -->
<setBoolFromProperty result="bSupportExperimentalFeatures" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bSupportExperimentalFeatures" default="false"/>
<setBoolFromProperty result="bBodyTrackingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bBodyTrackingEnabled" default="false"/>
<setBoolFromProperty result="bEyeTrackingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bEyeTrackingEnabled" default="false"/>
<setBoolFromProperty result="bFaceTrackingEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bFaceTrackingEnabled" default="false"/>
<setBoolFromProperty result="bTileTurnOffEnabled" ini="Engine" section="/Script/OculusXRHMD.OculusXRHMDRuntimeSettings" property="bTileTurnOffEnabled" default="false"/>
<!-- get packaging for Oculus Mobile from ini and reset it if architecture not supported -->
<if condition="bPackageForMetaQuest">
<true>
<if condition="bSupported">
<true>
<if condition="Distribution">
<true>
<setBoolFromProperty result="bRemoveOSIG" ini="Engine" section="/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" property="bRemoveOSIG" default="false"/>
<if condition="bRemoveOSIG">
<true>
<log text="Oculus mobile entitlement checks are enabled"/>
</true>
</if>
</true>
<false>
<!-- if not using entitlement checks need to keep the osig files -->
<setBool result="bRemoveOSIG" value="false"/>
</false>
</if>
</true>
<false>
<setBool result="bPackageForMetaQuest" value="false"/>
<log text="Oculus mobile not supported for this architecture, disabled."/>
</false>
</if>
</true>
</if>
<if condition="bRemoveOSIG">
<true>
<log text="Oculus Signature Files (osig) will be removed from APK"/>
</true>
</if>
<!-- package for Oculus and for distribution -->
<setBool result="bOculusDistribution" value="false"/>
<if condition="bPackageForMetaQuest">
<true>
<isDistribution>
<setBool result="bOculusDistribution" value="true"/>
<log text="Building with Oculus mobile for distribution"/>
</isDistribution>
</true>
</if>
<!-- entitlements check if package Oculus for distribution and removing OSIGs -->
<setBoolAnd result="bEntitlementCheck" arg1="$B(bRemoveOSIG)" arg2="$B(bOculusDistribution)"/>
</init>
<!-- optional updates applied to AndroidManifest.xml -->
<androidManifestUpdates>
<if condition="bOculusDistribution">
<true>
<!-- distribution builds can install internal or SD card -->
<addAttribute tag="manifest" name="android:installLocation" value="auto"/>
<!-- update the GameActivity activity -->
<loopElements tag="activity">
<setStringFromAttribute result="activityName" tag="$" name="android:name"/>
<setBoolIsEqual result="bGameActivity" arg1="$S(activityName)" arg2="com.epicgames.unreal.GameActivity"/>
<if condition="bGameActivity">
<true>
<!-- do not want application to show in recents -->
<addAttribute tag="$" name="android:excludeFromRecents" value="true"/>
<!-- distribution builds should not be launched from home screen so remove LAUNCHER -->
<loopElements tag="category">
<setStringFromAttribute result="categoryName" tag="$" name="android:name"/>
<setBoolIsEqual result="bLauncher" arg1="$S(categoryName)" arg2="android.intent.category.LAUNCHER"/>
<if condition="bLauncher">
<true>
<removeElement tag="$"/>
</true>
</if>
</loopElements>
<!-- add INFO intent category instead -->
<setElement result="intentInfo" value="category"/>
<addAttribute tag="$intentInfo" name="android:name" value="android.intent.category.INFO"/>
<addElement tag="intent-filter" name="intentInfo"/>
</true>
</if>
</loopElements>
</true>
</if>
<setBool result="bOculus6Dof" value="$B(bPackageForMetaQuest)"/>
<if condition="bPackageForMetaQuest">
<true>
<if condition="bOculusDistribution">
<false>
<!-- Add Extlib Flag -->
<setElement result="extlib" value="meta-data"/>
<addAttribute tag="$extlib" name="android:name" value="com.oculus.extlib"/>
<addAttribute tag="$extlib" name="android:value" value="true"/>
<addElement tag="application" name="extlib"/>
</false>
</if>
</true>
</if>
<!-- Add Quest Specific Flags -->
<if condition="bOculus6Dof">
<true>
<addFeature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true"/>
<!-- Add Hand Tracking Flag -->
<setBoolIsEqual result="bHandsOnly" arg1="$S(HandTrackingSupport)" arg2="HandsOnly"/>
<setBoolIsEqual result="bControllersAndHands" arg1="$S(HandTrackingSupport)" arg2="ControllersAndHands"/>
<setBoolOr result="bEnableHandTracking" arg1="$B(bHandsOnly)" arg2="$B(bControllersAndHands)"/>
<if condition="bEnableHandTracking">
<true>
<addPermission android:name="com.oculus.permission.HAND_TRACKING"/>
<addFeature android:name="oculus.software.handtracking" android:required="$B(bHandsOnly)"/>
</true>
</if>
<!-- Add Passthrough flag-->
<if condition="bInsightPassthroughEnabled">
<true>
<addFeature android:name="com.oculus.feature.PASSTHROUGH" android:required="true"/>
</true>
</if>
<!-- Add Eye Tracking flags-->
<setBool result="bEyeTrackingFeature" value="false"/>
<setBoolOr result="bEyeTrackingFeature" arg1="$B(bEyeTrackingFeature)" arg2="$B(bSupportEyeTrackedFoveatedRendering)"/>
<setBoolOr result="bEyeTrackingFeature" arg1="$B(bEyeTrackingFeature)" arg2="$B(bEyeTrackingEnabled)"/>
<!-- Check for other features that require eye tracking here -->
<if condition="bEyeTrackingFeature">
<true>
<log text="Adding eye tracking feature and permission tags to manifest"/>
<addPermission android:name="com.oculus.permission.EYE_TRACKING"/>
<addFeature android:name="oculus.software.eye_tracking" android:required="false"/>
</true>
</if>
<!-- Add Body Tracking flags-->
<if condition="bBodyTrackingEnabled">
<true>
<log text="Adding body tracking feature and permission tags to manifest"/>
<addPermission android:name="com.oculus.permission.BODY_TRACKING"/>
<addFeature android:name="com.oculus.software.body_tracking" android:required="true"/>
</true>
</if>
<!-- Add Face Tracking flags-->
<if condition="bFaceTrackingEnabled">
<true>
<log text="Adding face tracking feature and permission tags to manifest"/>
<addPermission android:name="com.oculus.permission.FACE_TRACKING"/>
<addPermission android:name="android.permission.RECORD_AUDIO"/>
<addFeature android:name="oculus.software.face_tracking" android:required="true"/>
</true>
</if>
<!-- Add Experimental Features flag-->
<if condition="bSupportExperimentalFeatures">
<true>
<log text="Adding experimental feature tag to manifest"/>
<addFeature android:name="com.oculus.experimental.enabled" android:required="true"/>
</true>
</if>
<!-- Add Flags for Spatial Anchors-->
<if condition="bAnchorSupportEnabled">
<true>
<addPermission android:name="com.oculus.permission.USE_ANCHOR_API"/>
</true>
</if>
<!-- Add Flag for Shared Anchors -->
<if condition="bAnchorSharingEnabled">
<true>
<addPermission android:name="com.oculus.permission.IMPORT_EXPORT_IOT_MAP_DATA"/>
</true>
</if>
<!-- Add Flags for Scene-->
<if condition="bSceneSupportEnabled">
<true>
<addPermission android:name="com.oculus.permission.USE_SCENE"/>
<addPermission android:name="com.oculus.permission.SCENE_OBJECT_TRACKING"/>
<addPermission android:name="com.oculus.permission.PLANE_TRACKING"/>
</true>
</if>
</true>
</if>
<!-- Add Activity Specific Flags -->
<loopElements tag="activity">
<setStringFromAttribute result="activityName" tag="$" name="android:name"/>
<setBoolIsEqual result="bGameActivity" arg1="$S(activityName)" arg2="com.epicgames.unreal.GameActivity"/>
<if condition="bGameActivity">
<true>
<!-- Add VR Intent Filter, Permissions, and Features -->
<if condition="bPackageForMetaQuest">
<true>
<addFeature android:name="android.hardware.usb.host"/>
</true>
</if>
<!-- Quest Specific Activity Tags -->
<if condition="bOculus6Dof">
<true>
<!-- Add System Keyboard Flag -->
<if condition="bFocusAware">
<true>
<if condition="bRequiresSystemKeyboard">
<true>
<addFeature android:name="oculus.software.overlay_keyboard" android:required="false"/>
</true>
</if>
</true>
</if>
</true>
</if>
</true>
</if>
</loopElements>
<!-- Add Application Specific Flags -->
<loopElements tag="application">
<!-- Add SupportedDevices Tag -->
<setString result="devicesString" value=""/>
<if condition="bSupportMetaQuest">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="quest|"/>
</true>
</if>
<if condition="bSupportMetaQuest2">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="quest2|"/>
</true>
</if>
<if condition="bSupportMetaQuestPro">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="cambria|"/>
</true>
</if>
<if condition="bSupportMetaQuest3">
<true>
<setStringAdd result="devicesString" arg1="$S(devicesString)" arg2="eureka|"/>
</true>
</if>
<setIntLength result="devicesStringLength" source="$S(devicesString)"/>
<setBoolIsGreater result="bDevicesSupported" arg1="$I(devicesStringLength)" arg2="0"/>
<if condition="bDevicesSupported">
<true>
<loopElements tag="meta-data">
<setStringFromAttribute result="nameString" tag="$" name="android:name"/>
<setBoolIsEqual result="bIsSupportedDevices" arg1="$S(nameString)" arg2="com.oculus.supportedDevices"/>
<if condition="bIsSupportedDevices">
<true>
<setStringFromAttribute result="existingDevicesString" tag="$" name="android:value"/>
<log text="Found existing Meta Quest supported devices tag: $S(existingDevicesString)"/>
<setStringAdd result="devicesString" arg1="$S(existingDevicesString)" arg2="|$S(devicesString)"/>
<setIntLength result="devicesStringLength" source="$S(devicesString)"/>
<removeElement tag="$"/>
</true>
</if>
</loopElements>
<setElement result="supportedDevices" value="meta-data"/>
<addAttribute tag="$supportedDevices" name="android:name" value="com.oculus.supportedDevices"/>
<setIntSubtract result="devicesStringLength" arg1="$I(devicesStringLength)" arg2="1"/>
<setStringSubstring result="devicesString" source="$S(devicesString)" start="0" length="$I(devicesStringLength)"/>
<log text="Adding Meta Quest supported devices tag: $S(devicesString)"/>
<addAttribute tag="$supportedDevices" name="android:value" value="$S(devicesString)"/>
<addElement tag="application" name="supportedDevices"/>
</true>
</if>
<!-- Add Hand Tracking Frequency -->
<if condition="bEnableHandTracking">
<true>
<setElement result="handTrackingFrequency" value="meta-data"/>
<addAttribute tag="$handTrackingFrequency" name="android:name" value="com.oculus.handtracking.frequency"/>
<addAttribute tag="$handTrackingFrequency" name="android:value" value="$S(HandTrackingFrequency)"/>
<addElement tag="application" name="handTrackingFrequency"/>
<if condition="bHandTrackingVersionDefault">
<false>
<if condition="bHandTrackingVersionV1">
<true>
<setElement result="handTrackingVersion" value="meta-data"/>
<addAttribute tag="$handTrackingVersion" name="android:name" value="com.oculus.handtracking.version"/>
<addAttribute tag="$handTrackingVersion" name="android:value" value="V1.0"/>
<addElement tag="application" name="handTrackingVersion"/>
</true>
</if>
<if condition="bHandTrackingVersionV2">
<true>
<setElement result="handTrackingVersion" value="meta-data"/>
<addAttribute tag="$handTrackingVersion" name="android:name" value="com.oculus.handtracking.version"/>
<addAttribute tag="$handTrackingVersion" name="android:value" value="V2.0"/>
<addElement tag="application" name="handTrackingVersion"/>
</true>
</if>
</false>
</if>
</true>
</if>
<!-- Add System Splash Image Background -->
<setString result="splashBackground" value=""/>
<if condition="bSystemSplashImageContextualPassthrough">
<true>
<setString result="splashBackground" value="passthrough-contextual"/>
</true>
</if>
<if condition="bSystemSplashImageBlack">
<true>
<setString result="splashBackground" value="black"/>
</true>
</if>
<setIntLength result="splashBackgroundLength" source="$S(splashBackground)"/>
<setBoolIsGreater result="bHasSplashBackground" arg1="$I(splashBackgroundLength)" arg2="0"/>
<if condition="bHasSplashBackground">
<true>
<setElement result="contextualPassthrough" value="meta-data"/>
<addAttribute tag="$contextualPassthrough" name="android:name" value="com.oculus.ossplash.background"/>
<addAttribute tag="$contextualPassthrough" name="android:value" value="$S(splashBackground)"/>
<addElement tag="application" name="contextualPassthrough"/>
</true>
</if>
<!-- Add Oculus Splash Screen -->
<if condition="bShowLaunchImage">
<true>
<setElement result="showOculusSplash" value="meta-data"/>
<addAttribute tag="$showOculusSplash" name="android:name" value="com.oculus.ossplash"/>
<addAttribute tag="$showOculusSplash" name="android:value" value="true"/>
<addElement tag="application" name="showOculusSplash"/>
<!-- Add Oculus Splash Screen colorspace setting -->
<setElement result="oculusSplashColorspace" value="meta-data"/>
<addAttribute tag="$oculusSplashColorspace" name="android:name" value="com.oculus.ossplash.colorspace"/>
<if condition="bColorSpaceUnmanaged">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!Unmanaged"/>
</true>
</if>
<if condition="bColorSpaceRec2020">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="Rec.2020"/>
</true>
</if>
<if condition="bColorSpaceRec709">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="Rec.709"/>
</true>
</if>
<if condition="bColorSpaceRiftCV1">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!RiftCV1"/>
</true>
</if>
<if condition="bColorSpaceRiftS">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!RiftS"/>
</true>
</if>
<if condition="bColorSpaceQuest">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!Quest"/>
</true>
</if>
<if condition="bColorSpaceP3">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!P3"/>
</true>
</if>
<if condition="bColorSpaceAdobeRGB">
<true>
<addAttribute tag="$oculusSplashColorspace" name="android:value" value="!Adobe"/>
</true>
</if>
<addElement tag="application" name="oculusSplashColorspace"/>
</true>
</if>
<!-- Add tradeCpuForGpuAmount Amount -->
<if condition="bProcessorFavorEqually">
<false>
<setBoolIsEqual result="bFavorGPU" arg1="$S(ProcessorFavor)" arg2="FavorGPU"/>
<setBoolIsEqual result="bFavorCPU" arg1="$S(ProcessorFavor)" arg2="FavorCPU"/>
<setElement result="tradeCpuForGpuAmount" value="meta-data"/>
<addAttribute tag="$tradeCpuForGpuAmount" name="android:name" value="com.oculus.trade_cpu_for_gpu_amount"/>
<if condition="bFavorGPU">
<true>
<addAttribute tag="$tradeCpuForGpuAmount" name="android:value" value="1"/>
</true>
</if>
<if condition="bFavorCPU">
<true>
<addAttribute tag="$tradeCpuForGpuAmount" name="android:value" value="-1"/>
</true>
</if>
<addElement tag="application" name="tradeCpuForGpuAmount"/>
</false>
</if>
<!-- Add Tile Turn Off -->
<setElement result="tileTurnOffEnabled" value="meta-data"/>
<addAttribute tag="$tileTurnOffEnabled" name="android:name" value="com.oculus.foveation.tile_turn_off"/>
<addAttribute tag="$tileTurnOffEnabled" name="android:value" value="$B(bTileTurnOffEnabled)"/>
<addElement tag="application" name="tileTurnOffEnabled"/>
</loopElements>
</androidManifestUpdates>
<!-- optional additions to proguard -->
<proguardAdditions>
<insert>
-keep class com.oculus.** {
*;
}
-keep class android.app.** {
*;
}
</insert>
</proguardAdditions>
<!-- optional files or directories to copy to Intermediate/Android/APK -->
<resourceCopies>
<isArch arch="arm64-v8a">
<if condition="bOVRPluginOpenXR">
<true>
<log text="Copying libopenxr_loader.so"/>
<copyFile src="$S(EngineDir)/Source/ThirdParty/Oculus/OculusOpenXRLoader/OculusOpenXRLoader/Lib/arm64-v8a/libopenxr_loader.so" dst="$S(BuildDir)/libs/arm64-v8a/libopenxr_loader.so"/>
<log text="Copying OpenXR libOVRPlugin.so"/>
<copyFile src="$S(PluginDir)/../ThirdParty/OVRPlugin/OVRPlugin/Lib/arm64-v8a/OpenXR/libOVRPlugin.so" dst="$S(BuildDir)/libs/arm64-v8a/libOVRPlugin.so"/>
</true>
</if>
<if condition="bNativeOpenXR">
<true>
<log text="Copying libopenxr_loader.so"/>
<copyFile src="$S(EngineDir)/Source/ThirdParty/Oculus/OculusOpenXRLoader/OculusOpenXRLoader/Lib/arm64-v8a/libopenxr_loader.so" dst="$S(BuildDir)/libs/arm64-v8a/libopenxr_loader.so"/>
</true>
</if>
</isArch>
<copyFile src="$S(PluginDir)/../ThirdParty/OVRPlugin/OVRPlugin/ExtLibs/SystemUtils.jar" dst="$S(BuildDir)/libs/SystemUtils.jar"/>
<if condition="bEntitlementCheck">
<true>
<copyFile src="$S(PluginDir)/../ThirdParty/OVRPlugin/OVRPlugin/ExtLibs/vrplatlib.jar" dst="$S(BuildDir)/libs/vrplatlib.jar"/>
</true>
</if>
<if condition="bRemoveOSIG">
<true>
<deleteFiles filespec="assets/oculussig_*"/>
</true>
</if>
</resourceCopies>
<!-- optional libraries to load in GameActivity.java before libUnreal.so -->
<soLoadLibrary>
<!-- need this if plugin enabled and supported architecture, even if not packaged for Oculus mobile -->
<if condition="bSupported">
<true>
<if condition="bOVRPluginOpenXR">
<true>
<loadLibrary name="openxr_loader" failmsg="openxr_loader library not loaded and may be required for Oculus VR."/>
<loadLibrary name="OVRPlugin" failmsg="OVRPlugin library not loaded and may be required for Oculus VR."/>
</true>
</if>
<if condition="bNativeOpenXR">
<true>
<loadLibrary name="openxr_loader" failmsg="openxr_loader library not loaded and may be required for Oculus VR."/>
</true>
</if>
</true>
</if>
</soLoadLibrary>
</root>

View File

@@ -0,0 +1,139 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OculusXRHMD : ModuleRules
{
public OculusXRHMD(ReadOnlyTargetRules Target) : base(Target)
{
bUseUnity = true;
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
PrivateIncludePaths.AddRange(
new string[] {
Path.Combine(EngineDir, "Source/Runtime/Renderer/Private"),
Path.Combine(EngineDir, "Source/Runtime/Renderer/Private"),
Path.Combine(EngineDir, "Source/Runtime/OpenGLDrv/Private"),
Path.Combine(EngineDir, "Source/Runtime/Engine/Classes/Components"),
Path.Combine(EngineDir, "Source/Runtime/Engine/Classes/Kismet"),
});
PublicIncludePathModuleNames.AddRange(
new string[] {
"Launch",
"ProceduralMeshComponent",
"AndroidPermission"
});
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"RHI",
"RHICore",
"RenderCore",
"Renderer",
"Slate",
"SlateCore",
"ImageWrapper",
"MediaAssets",
"Analytics",
"OpenGLDrv",
"VulkanRHI",
"OVRPluginXR",
"OculusOpenXRLoader",
"ProceduralMeshComponent",
"Projects",
});
PublicDependencyModuleNames.AddRange(
new string[]
{
"HeadMountedDisplay",
});
if (Target.Version.MajorVersion > 5 || (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 3))
{
PublicDependencyModuleNames.AddRange(
new string[]
{
"XRBase",
});
}
if (Target.bBuildEditor == true)
{
PrivateDependencyModuleNames.Add("UnrealEd");
}
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenGL");
if (Target.Platform == UnrealTargetPlatform.Win64)
{
// D3D
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"D3D11RHI",
"D3D12RHI",
});
PrivateIncludePaths.AddRange(
new string[]
{
"OculusXRMR/Public",
});
AddEngineThirdPartyPrivateStaticDependencies(Target, "DX11");
AddEngineThirdPartyPrivateStaticDependencies(Target, "DX12");
AddEngineThirdPartyPrivateStaticDependencies(Target, "NVAPI");
AddEngineThirdPartyPrivateStaticDependencies(Target, "DX11Audio");
AddEngineThirdPartyPrivateStaticDependencies(Target, "DirectSound");
AddEngineThirdPartyPrivateStaticDependencies(Target, "NVAftermath");
AddEngineThirdPartyPrivateStaticDependencies(Target, "IntelMetricsDiscovery");
AddEngineThirdPartyPrivateStaticDependencies(Target, "IntelExtensionsFramework");
}
// Vulkan
{
AddEngineThirdPartyPrivateStaticDependencies(Target, "Vulkan");
}
// OVRPlugin
if (Target.Platform == UnrealTargetPlatform.Win64)
{
RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/" + Target.Platform.ToString() + "/OpenXR/OVRPlugin.dll");
}
}
else if (Target.Platform == UnrealTargetPlatform.Android)
{
// We are not currently supporting Mixed Reality on Android, but we need to include IOculusXRMRModule.h for OCULUS_MR_SUPPORTED_PLATFORMS definition
PrivateIncludePaths.AddRange(
new string[]
{
"OculusXRMR/Public"
});
// Vulkan
{
AddEngineThirdPartyPrivateStaticDependencies(Target, "Vulkan");
}
// AndroidPlugin
{
string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "OculusMobile_APL.xml"));
}
}
}
}
}

View File

@@ -0,0 +1,65 @@
#include "Common.ush"
void MainVertexShader(
float4 InPosition : ATTRIBUTE0,
float2 InUV : ATTRIBUTE1,
out float2 OutUV : TEXCOORD0,
out float4 OutPosition : SV_POSITION
)
{
OutPosition = InPosition;
OutUV = InUV;
}
Texture2D<uint> TextureParameter;
#define Zoom 2
#define Pan float2(0.5, 0)
#define Aspect 1
#define Iterations int(128*PSVariables.IterationsMultiplier)
#define JuliaSeed float2(0.39, 0.2)
#define ColorScale float3(4, 5, 6)
float ComputeValue(float2 v, float2 offset)
{
float vxsquare = 0;
float vysquare = 0;
int iteration = 0;
int lastIteration = Iterations;
do
{
vxsquare = v.x * v.x;
vysquare = v.y * v.y;
v = float2(vxsquare - vysquare, v.x * v.y * 2) + offset;
iteration++;
if ((lastIteration == Iterations) && (vxsquare + vysquare) > 4.0)
{
lastIteration = iteration + 1;
}
} while (iteration < lastIteration);
return (float(iteration) - (log(log(sqrt(vxsquare + vysquare))) / log(2.0))) / float(Iterations);
}
float4 Mandelbrot_Func(float2 texCoord : TEXCOORD0) : COLOR0
{
float2 v = (texCoord - 0.5) * Zoom * float2(1, Aspect) - Pan;
float val = ComputeValue(v, v);
return float4(sin(val * ColorScale.x), sin(val * ColorScale.y), sin(val * ColorScale.z), 1);
}
void MainPixelShader(
in float2 uv : TEXCOORD0,
out float4 OutColor : SV_Target0
)
{
OutColor = Mandelbrot_Func(uv);
}

View File

@@ -0,0 +1,294 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRAssetManager.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMDModule.h"
#include "Engine/StaticMesh.h"
#include "Components/StaticMeshComponent.h"
#include "UObject/SoftObjectPath.h"
#include "Engine/SkeletalMesh.h"
#include "Components/SkeletalMeshComponent.h"
#include "OculusXRAssetDirectory.h"
#include "UObject/GCObject.h"
/* FOculusAssetDirectory
*****************************************************************************/
enum EOculusAsset
{
LeftTouchRiftS,
RightTouchRiftS,
LeftTouchQuest2,
RightTouchQuest2,
LeftTouchQuestPro,
RightTouchQuestPro,
LeftTouchQuest3,
RightTouchQuest3,
OculusAssetTotal
};
FSoftObjectPath FOculusAssetDirectory::AssetListing[OculusAssetTotal] = {
FString(TEXT("/OculusXR/Meshes/LeftTouchForQuestRiftSController.LeftTouchForQuestRiftSController")),
FString(TEXT("/OculusXR/Meshes/RightTouchForQuestRiftSController.RightTouchForQuestRiftSController")),
FString(TEXT("/OculusXR/Meshes/LeftTouchForQuest2.LeftTouchForQuest2")),
FString(TEXT("/OculusXR/Meshes/RightTouchForQuest2.RightTouchForQuest2")),
FString(TEXT("/OculusXR/Meshes/LeftMetaQuestTouchPro.LeftMetaQuestTouchPro")),
FString(TEXT("/OculusXR/Meshes/RightMetaQuestTouchPro.RightMetaQuestTouchPro")),
FString(TEXT("/OculusXR/Meshes/LeftMetaQuestTouchPlus.LeftMetaQuestTouchPlus")),
FString(TEXT("/OculusXR/Meshes/RightMetaQuestTouchPlus.RightMetaQuestTouchPlus")),
};
#if WITH_EDITORONLY_DATA
class FOculusAssetRepo : public FGCObject, public TArray<UObject*>
{
public:
// made an on-demand singleton rather than a static global, to avoid issues with FGCObject initialization
static FOculusAssetRepo& Get()
{
static FOculusAssetRepo AssetRepository;
return AssetRepository;
}
UObject* LoadAndAdd(const FSoftObjectPath& AssetPath)
{
UObject* AssetObj = AssetPath.TryLoad();
if (AssetObj != nullptr)
{
AddUnique(AssetObj);
}
return AssetObj;
}
public:
//~ FGCObject interface
virtual void AddReferencedObjects(FReferenceCollector& Collector) override
{
Collector.AddReferencedObjects(*this);
}
virtual FString GetReferencerName() const override
{
return TEXT("FOculusAssetRepo");
}
};
void FOculusAssetDirectory::LoadForCook()
{
FOculusAssetRepo& AssetRepro = FOculusAssetRepo::Get();
for (int32 AssetIndex = 0; AssetIndex < UE_ARRAY_COUNT(FOculusAssetDirectory::AssetListing); ++AssetIndex)
{
AssetRepro.LoadAndAdd(FOculusAssetDirectory::AssetListing[AssetIndex]);
}
}
void FOculusAssetDirectory::ReleaseAll()
{
FOculusAssetRepo::Get().Empty();
}
#endif // WITH_EDITORONLY_DATA
/* OculusAssetManager_Impl
*****************************************************************************/
namespace OculusAssetManager_Impl
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
struct FRenderableDevice
{
ovrpNode OVRNode;
ovrpSystemHeadset MinDeviceRange;
ovrpSystemHeadset MaxDeviceRange;
FSoftObjectPath MeshAssetRef;
};
static FRenderableDevice RenderableDevices[] = {
#if PLATFORM_ANDROID
//Quest 1 & 2
{ ovrpNode_HandLeft, ovrpSystemHeadset_Oculus_Quest, ovrpSystemHeadset_Oculus_Quest_2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Oculus_Quest, ovrpSystemHeadset_Oculus_Quest_2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
//Quest Pro
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Quest_Pro, ovrpSystemHeadset_Meta_Quest_Pro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Quest_Pro, ovrpSystemHeadset_Meta_Quest_Pro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
//Quest 3
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Quest_3, ovrpSystemHeadset_Meta_Quest_3, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Quest_3, ovrpSystemHeadset_Meta_Quest_3, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#else
//PC - Rift S
{ ovrpNode_HandLeft, ovrpSystemHeadset_Rift_S, ovrpSystemHeadset_Rift_S, FOculusAssetDirectory::AssetListing[LeftTouchRiftS] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Rift_S, ovrpSystemHeadset_Rift_S, FOculusAssetDirectory::AssetListing[RightTouchRiftS] },
//PC - Quest 1 & 2
{ ovrpNode_HandLeft, ovrpSystemHeadset_Oculus_Link_Quest, ovrpSystemHeadset_Oculus_Link_Quest_2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Oculus_Link_Quest, ovrpSystemHeadset_Oculus_Link_Quest_2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
//PC - Quest Pro
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Link_Quest_Pro, ovrpSystemHeadset_Meta_Link_Quest_Pro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Link_Quest_Pro, ovrpSystemHeadset_Meta_Link_Quest_Pro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
//Quest 3
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Link_Quest_3, ovrpSystemHeadset_Meta_Link_Quest_3, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Link_Quest_3, ovrpSystemHeadset_Meta_Link_Quest_3, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#endif
};
static uint32 RenderableDeviceCount = sizeof(RenderableDevices) / sizeof(RenderableDevices[0]);
#endif // #if OCULUS_HMD_SUPPORTED_PLATFORMS
static UObject* FindDeviceMesh(const int32 DeviceID);
}; // namespace OculusAssetManager_Impl
static UObject* OculusAssetManager_Impl::FindDeviceMesh(const int32 DeviceID)
{
UObject* DeviceMesh = nullptr;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const ovrpNode DeviceOVRNode = OculusXRHMD::ToOvrpNode(DeviceID);
bool bUseSystemHeadsetType = false;
ovrpSystemHeadset HeadsetType;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemHeadsetType2(&HeadsetType)))
{
bUseSystemHeadsetType = true;
}
if (DeviceOVRNode != ovrpNode_None)
{
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
if (RenderableDevice.OVRNode == DeviceOVRNode)
{
// If we have information about the current headset, load the model based of the headset information, otherwise load defaults.
if (bUseSystemHeadsetType)
{
if (HeadsetType >= RenderableDevice.MinDeviceRange && HeadsetType <= RenderableDevice.MaxDeviceRange)
{
DeviceMesh = RenderableDevice.MeshAssetRef.TryLoad();
break;
}
}
else
{
DeviceMesh = RenderableDevice.MeshAssetRef.TryLoad();
break;
}
}
}
}
#endif
return DeviceMesh;
}
/* FOculusAssetManager
*****************************************************************************/
FOculusAssetManager::FOculusAssetManager()
{
IModularFeatures::Get().RegisterModularFeature(IXRSystemAssets::GetModularFeatureName(), this);
ResourceHolder = NewObject<UOculusXRResourceHolder>();
ResourceHolder->AddToRoot();
}
FOculusAssetManager::~FOculusAssetManager()
{
if (ResourceHolder)
{
ResourceHolder->ConditionalBeginDestroy();
ResourceHolder = nullptr;
}
IModularFeatures::Get().UnregisterModularFeature(IXRSystemAssets::GetModularFeatureName(), this);
}
bool FOculusAssetManager::EnumerateRenderableDevices(TArray<int32>& DeviceListOut)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
using namespace OculusAssetManager_Impl;
DeviceListOut.Empty(RenderableDeviceCount);
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
const int32 ExternalDeviceId = OculusXRHMD::ToExternalDeviceId(RenderableDevice.OVRNode);
DeviceListOut.Add(ExternalDeviceId);
}
return true;
#else
return false;
#endif
}
int32 FOculusAssetManager::GetDeviceId(EControllerHand ControllerHand)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpNode ControllerOVRNode = ovrpNode_None;
switch (ControllerHand)
{
case EControllerHand::AnyHand:
// @TODO: maybe check if the right is tracking, if not choose left (if tracking)?
case EControllerHand::Right:
ControllerOVRNode = ovrpNode_HandRight;
break;
case EControllerHand::Left:
ControllerOVRNode = ovrpNode_HandLeft;
break;
case EControllerHand::ExternalCamera:
ControllerOVRNode = ovrpNode_TrackerZero;
break;
// case EControllerHand::Special_1:
// ControllerOVRNode = ovrpNode_TrackerOne;
// break;
// case EControllerHand::Special_2:
// ControllerOVRNode = ovrpNode_TrackerTwo;
// break;
// case EControllerHand::Special_3:
// ControllerOVRNode = ovrpNode_TrackerThree;
// break;
// case EControllerHand::Special_4:
// ControllerOVRNode = ovrpNode_DeviceObjectZero;
// break;
default:
// ControllerOVRNode = ovrpNode_None => returns -1
break;
}
return OculusXRHMD::ToExternalDeviceId(ControllerOVRNode);
#else
return INDEX_NONE;
#endif
}
UPrimitiveComponent* FOculusAssetManager::CreateRenderComponent(const int32 DeviceId, AActor* Owner, EObjectFlags Flags, const bool /*bForceSynchronous*/, const FXRComponentLoadComplete& OnLoadComplete)
{
UPrimitiveComponent* NewRenderComponent = nullptr;
if (UObject* DeviceMesh = OculusAssetManager_Impl::FindDeviceMesh(DeviceId))
{
if (UStaticMesh* AsStaticMesh = Cast<UStaticMesh>(DeviceMesh))
{
const FName ComponentName = MakeUniqueObjectName(Owner, UStaticMeshComponent::StaticClass(), *FString::Printf(TEXT("%s_Device%d"), TEXT("Oculus"), DeviceId));
UStaticMeshComponent* MeshComponent = NewObject<UStaticMeshComponent>(Owner, ComponentName, Flags);
MeshComponent->SetStaticMesh(AsStaticMesh);
NewRenderComponent = MeshComponent;
}
else if (USkeletalMesh* AsSkeletalMesh = Cast<USkeletalMesh>(DeviceMesh))
{
const FName ComponentName = MakeUniqueObjectName(Owner, USkeletalMeshComponent::StaticClass(), *FString::Printf(TEXT("%s_Device%d"), TEXT("Oculus"), DeviceId));
USkeletalMeshComponent* SkelMeshComponent = NewObject<USkeletalMeshComponent>(Owner, ComponentName, Flags);
SkelMeshComponent->SetSkeletalMesh(AsSkeletalMesh);
NewRenderComponent = SkelMeshComponent;
}
NewRenderComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
OnLoadComplete.ExecuteIfBound(NewRenderComponent);
return NewRenderComponent;
}

View File

@@ -0,0 +1,30 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IXRSystemAssets.h"
#include "OculusXRResourceHolder.h"
#include "UObject/SoftObjectPtr.h"
/**
*
*/
class FOculusAssetManager : public IXRSystemAssets
{
public:
FOculusAssetManager();
virtual ~FOculusAssetManager();
public:
UOculusXRResourceHolder* GetResourceHolder() { return ResourceHolder; }
//~ IXRSystemAssets interface
virtual bool EnumerateRenderableDevices(TArray<int32>& DeviceListOut) override;
virtual int32 GetDeviceId(EControllerHand ControllerHand) override;
virtual UPrimitiveComponent* CreateRenderComponent(const int32 DeviceId, AActor* Owner, EObjectFlags Flags, const bool bForceSynchronous, const FXRComponentLoadComplete& OnLoadComplete) override;
protected:
UOculusXRResourceHolder* ResourceHolder;
};

View File

@@ -0,0 +1,8 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRDelegates.h"
FOculusEventDelegates::FOculusDisplayRefreshRateChangedEvent FOculusEventDelegates::OculusDisplayRefreshRateChanged;
FOculusEventDelegates::FOculusEyeTrackingStateChangedEvent FOculusEventDelegates::OculusEyeTrackingStateChanged;

View File

@@ -0,0 +1,19 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Delegates/Delegate.h"
class FOculusEventDelegates
{
public:
/** When the display refresh rate is changed */
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusDisplayRefreshRateChangedEvent, float /*fromRefreshRate*/, float /*toRefreshRate*/);
static FOculusDisplayRefreshRateChangedEvent OculusDisplayRefreshRateChanged;
/** When the eye tracking status changes */
DECLARE_MULTICAST_DELEGATE_OneParam(FOculusEyeTrackingStateChangedEvent, bool /*bIsEyeTrackingOn*/);
static FOculusEyeTrackingStateChangedEvent OculusEyeTrackingStateChanged;
};

View File

@@ -0,0 +1,21 @@
// @lint-ignore-every LICENSELINT
// Copyright 1998-2020 Epic Games, Inc. All Rights Reserved.
#include "OculusXREventComponent.h"
#include "OculusXRHMD.h"
#include "OculusXRDelegates.h"
void UOculusXREventComponent::OnRegister()
{
Super::OnRegister();
FOculusEventDelegates::OculusDisplayRefreshRateChanged.AddUObject(this, &UOculusXREventComponent::OculusDisplayRefreshRateChanged_Handler);
FOculusEventDelegates::OculusEyeTrackingStateChanged.AddUObject(this, &UOculusXREventComponent::OculusEyeTrackingStateChanged_Handler);
}
void UOculusXREventComponent::OnUnregister()
{
Super::OnUnregister();
FOculusEventDelegates::OculusDisplayRefreshRateChanged.RemoveAll(this);
FOculusEventDelegates::OculusEyeTrackingStateChanged.RemoveAll(this);
}

View File

@@ -0,0 +1,985 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMD.h"
#include "Logging/MessageLog.h"
#define LOCTEXT_NAMESPACE "OculusFunctionLibrary"
//-------------------------------------------------------------------------------------------------
// UOculusXRFunctionLibrary
//-------------------------------------------------------------------------------------------------
UOculusXRFunctionLibrary::UOculusXRFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
OculusXRHMD::FOculusXRHMD* UOculusXRFunctionLibrary::GetOculusXRHMD()
{
return OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
}
void UOculusXRFunctionLibrary::GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector PositionScale)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD && OculusXRHMD->IsHeadTrackingAllowed())
{
FQuat HeadOrientation = FQuat::Identity;
FVector HeadPosition = FVector::ZeroVector;
OculusXRHMD->GetCurrentPose(OculusXRHMD->HMDDeviceId, HeadOrientation, HeadPosition);
DeviceRotation = HeadOrientation.Rotator();
DevicePosition = HeadPosition;
NeckPosition = OculusXRHMD->GetNeckPosition(HeadOrientation, HeadPosition);
}
else
#endif // #if OCULUS_HMD_SUPPORTED_PLATFORMS
{
DeviceRotation = FRotator::ZeroRotator;
DevicePosition = FVector::ZeroVector;
NeckPosition = FVector::ZeroVector;
}
}
void UOculusXRFunctionLibrary::SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if ((Options == EOrientPositionSelector::Orientation) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
OculusXRHMD->SetBaseRotation(Rotation);
}
if ((Options == EOrientPositionSelector::Position) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
OculusXRHMD->SetBaseOffsetInMeters(BaseOffsetInMeters);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OutRotation = OculusXRHMD->GetBaseRotation();
OutBaseOffsetInMeters = OculusXRHMD->GetBaseOffsetInMeters();
}
else
{
OutRotation = FRotator::ZeroRotator;
OutBaseOffsetInMeters = FVector::ZeroVector;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpPoseStatef state;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePoseState3(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, OculusXRHMD::ToOvrpNode(DeviceType), &state)))
{
AngularAcceleration = OculusXRHMD::ToFVector(state.AngularAcceleration);
LinearAcceleration = OculusXRHMD::ToFVector(state.Acceleration);
AngularVelocity = OculusXRHMD::ToFVector(state.AngularVelocity);
LinearVelocity = OculusXRHMD::ToFVector(state.Velocity);
TimeInSeconds = state.Time;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool UOculusXRFunctionLibrary::IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool Present;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePresent2(OculusXRHMD::ToOvrpNode(DeviceType), &Present)))
{
return Present != ovrpBool_False;
}
else
{
return false;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void UOculusXRFunctionLibrary::GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
OculusXRHMD->GetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
OculusXRHMD->SetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetCPUAndGPULevels(int CPULevel, int GPULevel)
{
// Deprecated. Please use Get/SetSuggestedCpuAndGpuPerformanceLevels instead.
}
bool UOculusXRFunctionLibrary::GetUserProfile(FOculusXRHmdUserProfile& Profile)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FOculusXRHMD::UserProfile Data;
if (OculusXRHMD->GetUserProfile(Data))
{
Profile.Name = "";
Profile.Gender = "Unknown";
Profile.PlayerHeight = 0.0f;
Profile.EyeHeight = Data.EyeHeight;
Profile.IPD = Data.IPD;
Profile.NeckToEyeDistance = FVector2D(Data.EyeDepth, 0.0f);
return true;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void UOculusXRFunctionLibrary::SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if (Options == EOrientPositionSelector::Orientation || Options == EOrientPositionSelector::OrientationAndPosition)
{
OculusXRHMD->SetBaseRotation(BaseRot);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OutRot = OculusXRHMD->GetBaseRotation();
OutPosOffset = FVector::ZeroVector;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FSplash* Splash = OculusXRHMD->GetSplash();
if (Splash)
{
if (bClearBeforeAdd)
{
Splash->ClearSplashes();
}
FOculusXRSplashDesc Desc;
Desc.LoadingTexture = Texture;
Desc.QuadSizeInMeters = SizeInMeters;
Desc.TransformInMeters = FTransform(Rotation, TranslationInMeters);
Desc.DeltaRotation = FQuat(DeltaRotation);
Splash->AddSplash(Desc);
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::ClearLoadingSplashScreens()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FSplash* Splash = OculusXRHMD->GetSplash();
if (Splash)
{
Splash->ClearSplashes();
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool UOculusXRFunctionLibrary::HasInputFocus()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool HasFocus;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppHasInputFocus(&HasFocus)))
{
return HasFocus != ovrpBool_False;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
bool UOculusXRFunctionLibrary::HasSystemOverlayPresent()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool HasFocus;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppHasInputFocus(&HasFocus)))
{
return HasFocus == ovrpBool_False;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void UOculusXRFunctionLibrary::GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization)
{
GPUUtilization = 0.0f;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bIsSupported = ovrpBool_False;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().IsPerfMetricsSupported(ovrpPerfMetrics_System_GpuUtilPercentage_Float, &bIsSupported)) && bIsSupported == ovrpBool_True)
{
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPerfMetricsFloat(ovrpPerfMetrics_System_GpuUtilPercentage_Float, &GPUUtilization)))
{
IsGPUAvailable = true;
GPUUtilization *= 100;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
float UOculusXRFunctionLibrary::GetGPUFrameTime()
{
float FrameTime = 0;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bIsSupported = ovrpBool_False;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().IsPerfMetricsSupported(ovrpPerfMetrics_App_GpuTime_Float, &bIsSupported)) && bIsSupported == ovrpBool_True)
{
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPerfMetricsFloat(ovrpPerfMetrics_App_GpuTime_Float, &FrameTime)))
{
return FrameTime * 1000;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return 0.0f;
}
EOculusXRFoveatedRenderingMethod UOculusXRFunctionLibrary::GetFoveatedRenderingMethod()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool enabled;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetFoveationEyeTracked(&enabled)))
{
return enabled == ovrpBool_True ? EOculusXRFoveatedRenderingMethod::EyeTrackedFoveatedRendering : EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
void UOculusXRFunctionLibrary::SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetFoveatedRenderingMethod(Method);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetFoveatedRenderingLevel(level, isDynamic);
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
EOculusXRFoveatedRenderingLevel UOculusXRFunctionLibrary::GetFoveatedRenderingLevel()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpTiledMultiResLevel Lvl;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetTiledMultiResLevel(&Lvl)))
{
return (EOculusXRFoveatedRenderingLevel)Lvl;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return EOculusXRFoveatedRenderingLevel::Off;
}
bool UOculusXRFunctionLibrary::GetEyeTrackedFoveatedRenderingSupported()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
// Always return false on other engine releases, since they don't have FDM offset support
#ifdef WITH_OCULUS_BRANCH
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool Supported;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetFoveationEyeTrackedSupported(&Supported)))
{
return Supported == ovrpBool_True;
}
}
#endif // WITH_OCULUS_BRANCH
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
FString UOculusXRFunctionLibrary::GetDeviceName()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
const char* NameString;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemProductName2(&NameString)) && NameString)
{
return FString(NameString);
}
}
#endif
return FString();
}
EOculusXRDeviceType UOculusXRFunctionLibrary::GetDeviceType()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if (OculusXRHMD->GetSettings())
{
switch (OculusXRHMD->GetSettings()->SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return EOculusXRDeviceType::OculusQuest_Deprecated;
case ovrpSystemHeadset_Oculus_Quest_2:
return EOculusXRDeviceType::OculusQuest2;
case ovrpSystemHeadset_Meta_Quest_Pro:
return EOculusXRDeviceType::MetaQuestPro;
case ovrpSystemHeadset_Meta_Quest_3:
return EOculusXRDeviceType::MetaQuest3;
case ovrpSystemHeadset_Rift_CV1:
return EOculusXRDeviceType::Rift;
case ovrpSystemHeadset_Rift_S:
return EOculusXRDeviceType::Rift_S;
case ovrpSystemHeadset_Oculus_Link_Quest:
return EOculusXRDeviceType::Quest_Link_Deprecated;
case ovrpSystemHeadset_Oculus_Link_Quest_2:
return EOculusXRDeviceType::Quest2_Link;
case ovrpSystemHeadset_Meta_Link_Quest_Pro:
return EOculusXRDeviceType::MetaQuestProLink;
case ovrpSystemHeadset_Meta_Link_Quest_3:
return EOculusXRDeviceType::MetaQuest3Link;
default:
break;
}
}
}
#endif
return EOculusXRDeviceType::OculusUnknown;
}
EOculusXRControllerType UOculusXRFunctionLibrary::GetControllerType(EControllerHand deviceHand)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
auto getOVRPHand = [](EControllerHand hand) {
switch (hand)
{
case EControllerHand::Left:
return ovrpHand::ovrpHand_Left;
case EControllerHand::Right:
return ovrpHand::ovrpHand_Right;
default:
return ovrpHand::ovrpHand_None;
}
return ovrpHand::ovrpHand_None;
};
auto getEControllerType = [](ovrpInteractionProfile profile) {
switch (profile)
{
case ovrpInteractionProfile::ovrpInteractionProfile_Touch:
return EOculusXRControllerType::MetaQuestTouch;
case ovrpInteractionProfile::ovrpInteractionProfile_TouchPro:
return EOculusXRControllerType::MetaQuestTouchPro;
case ovrpInteractionProfile::ovrpInteractionProfile_TouchPlus:
return EOculusXRControllerType::MetaQuestTouchPlus;
default:
return EOculusXRControllerType::None;
}
return EOculusXRControllerType::None;
};
ovrpInteractionProfile interactionProfile = ovrpInteractionProfile::ovrpInteractionProfile_None;
ovrpHand hand = getOVRPHand(deviceHand);
if (hand == ovrpHand::ovrpHand_None)
return EOculusXRControllerType::Unknown;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetCurrentInteractionProfile(hand, &interactionProfile)))
{
return getEControllerType(interactionProfile);
}
return EOculusXRControllerType::Unknown;
#endif
return EOculusXRControllerType::Unknown;
}
TArray<float> UOculusXRFunctionLibrary::GetAvailableDisplayFrequencies()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
int NumberOfFrequencies;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayAvailableFrequencies(nullptr, &NumberOfFrequencies)))
{
TArray<float> freqArray;
freqArray.SetNum(NumberOfFrequencies);
FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayAvailableFrequencies(freqArray.GetData(), &NumberOfFrequencies);
return freqArray;
}
}
#endif
return TArray<float>();
}
float UOculusXRFunctionLibrary::GetCurrentDisplayFrequency()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
float Frequency;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&Frequency)))
{
return Frequency;
}
}
#endif
return 0.0f;
}
void UOculusXRFunctionLibrary::SetDisplayFrequency(float RequestedFrequency)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetSystemDisplayFrequency(RequestedFrequency);
}
#endif
}
void UOculusXRFunctionLibrary::EnablePositionTracking(bool bPositionTracking)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetTrackingPositionEnabled2(bPositionTracking);
}
#endif
}
void UOculusXRFunctionLibrary::EnableOrientationTracking(bool bOrientationTracking)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetTrackingOrientationEnabled2(bOrientationTracking);
}
#endif
}
void UOculusXRFunctionLibrary::SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetColorScaleAndOffset(ColorScale, ColorOffset, bApplyToAllLayers);
}
#endif
}
class IStereoLayers* UOculusXRFunctionLibrary::GetStereoLayers()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
return OculusXRHMD;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return nullptr;
}
/** Helper that converts EOculusXRBoundaryType to ovrpBoundaryType */
#if OCULUS_HMD_SUPPORTED_PLATFORMS
static ovrpBoundaryType ToOvrpBoundaryType(EOculusXRBoundaryType Source)
{
switch (Source)
{
case EOculusXRBoundaryType::Boundary_PlayArea:
return ovrpBoundary_PlayArea;
case EOculusXRBoundaryType::Boundary_Outer:
default:
return ovrpBoundary_Outer;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
bool UOculusXRFunctionLibrary::IsGuardianConfigured()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool boundaryConfigured;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&boundaryConfigured)) && boundaryConfigured;
}
#endif
return false;
}
bool UOculusXRFunctionLibrary::IsGuardianDisplayed()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool boundaryVisible;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryVisible2(&boundaryVisible)) && boundaryVisible;
}
#endif
return false;
}
TArray<FVector> UOculusXRFunctionLibrary::GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */)
{
TArray<FVector> BoundaryPointList;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bBoundaryConfigured = false;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&bBoundaryConfigured)) && bBoundaryConfigured)
{
ovrpBoundaryType obt = ToOvrpBoundaryType(BoundaryType);
int NumPoints = 0;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(obt, nullptr, &NumPoints)))
{
//allocate points
const int BufferSize = NumPoints;
ovrpVector3f* BoundaryPoints = new ovrpVector3f[BufferSize];
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(obt, BoundaryPoints, &NumPoints)))
{
NumPoints = FMath::Min(BufferSize, NumPoints);
check(NumPoints <= BufferSize); // For static analyzer
BoundaryPointList.Reserve(NumPoints);
for (int i = 0; i < NumPoints; i++)
{
FVector point;
if (UsePawnSpace)
{
point = OculusXRHMD->ConvertVector_M2U(BoundaryPoints[i]);
}
else
{
point = OculusXRHMD->ScaleAndMovePointWithPlayer(BoundaryPoints[i]);
}
BoundaryPointList.Add(point);
}
}
delete[] BoundaryPoints;
}
}
}
#endif
return BoundaryPointList;
}
FVector UOculusXRFunctionLibrary::GetGuardianDimensions(EOculusXRBoundaryType BoundaryType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBoundaryType obt = ToOvrpBoundaryType(BoundaryType);
ovrpVector3f Dimensions;
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryDimensions2(obt, &Dimensions)))
return FVector::ZeroVector;
Dimensions.z *= -1.0;
return OculusXRHMD->ConvertVector_M2U(Dimensions);
}
#endif
return FVector::ZeroVector;
}
FTransform UOculusXRFunctionLibrary::GetPlayAreaTransform()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bBoundaryConfigured = false;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&bBoundaryConfigured)) && bBoundaryConfigured)
{
int NumPoints = 4;
ovrpVector3f BoundaryPoints[4];
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(ovrpBoundary_PlayArea, BoundaryPoints, &NumPoints)))
{
FVector ConvertedPoints[4];
for (int i = 0; i < NumPoints; i++)
{
ConvertedPoints[i] = OculusXRHMD->ScaleAndMovePointWithPlayer(BoundaryPoints[i]);
}
float metersScale = OculusXRHMD->GetWorldToMetersScale();
FVector Edge = ConvertedPoints[1] - ConvertedPoints[0];
float Angle = FMath::Acos((Edge).GetSafeNormal() | FVector::RightVector);
FQuat Rotation(FVector::UpVector, Edge.X < 0 ? Angle : -Angle);
FVector Position = (ConvertedPoints[0] + ConvertedPoints[1] + ConvertedPoints[2] + ConvertedPoints[3]) / 4;
FVector Scale(FVector::Distance(ConvertedPoints[3], ConvertedPoints[0]) / metersScale, FVector::Distance(ConvertedPoints[1], ConvertedPoints[0]) / metersScale, 1.0);
return FTransform(Rotation, Position, Scale);
}
}
}
#endif
return FTransform();
}
FOculusXRGuardianTestResult UOculusXRFunctionLibrary::GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpVector3f OvrpPoint = OculusXRHMD->WorldLocationToOculusPoint(Point);
ovrpBoundaryType OvrpBoundaryType = ToOvrpBoundaryType(BoundaryType);
ovrpBoundaryTestResult InteractionResult;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().TestBoundaryPoint2(OvrpPoint, OvrpBoundaryType, &InteractionResult)))
{
InteractionInfo.IsTriggering = (InteractionResult.IsTriggering != 0);
InteractionInfo.ClosestDistance = OculusXRHMD->ConvertFloat_M2U(InteractionResult.ClosestDistance);
InteractionInfo.ClosestPoint = OculusXRHMD->ScaleAndMovePointWithPlayer(InteractionResult.ClosestPoint);
InteractionInfo.ClosestPointNormal = OculusXRHMD->ConvertVector_M2U(InteractionResult.ClosestPointNormal);
InteractionInfo.DeviceType = EOculusXRTrackedDeviceType::None;
}
}
#endif
return InteractionInfo;
}
FOculusXRGuardianTestResult UOculusXRFunctionLibrary::GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpNode OvrpNode = OculusXRHMD::ToOvrpNode(DeviceType);
ovrpBoundaryType OvrpBoundaryType = ToOvrpBoundaryType(BoundaryType);
ovrpBoundaryTestResult TestResult;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().TestBoundaryNode2(OvrpNode, ovrpBoundary_PlayArea, &TestResult)) && TestResult.IsTriggering)
{
InteractionInfo.IsTriggering = true;
InteractionInfo.DeviceType = OculusXRHMD::ToEOculusXRTrackedDeviceType(OvrpNode);
InteractionInfo.ClosestDistance = OculusXRHMD->ConvertFloat_M2U(TestResult.ClosestDistance);
InteractionInfo.ClosestPoint = OculusXRHMD->ScaleAndMovePointWithPlayer(TestResult.ClosestPoint);
InteractionInfo.ClosestPointNormal = OculusXRHMD->ConvertVector_M2U(TestResult.ClosestPointNormal);
}
}
#endif
return InteractionInfo;
}
void UOculusXRFunctionLibrary::SetGuardianVisibility(bool GuardianVisible)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetBoundaryVisible2(GuardianVisible);
}
#endif
}
bool UOculusXRFunctionLibrary::GetSystemHmd3DofModeEnabled()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool enabled;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemHmd3DofModeEnabled(&enabled)) && enabled;
}
#endif
return false;
}
EOculusXRColorSpace UOculusXRFunctionLibrary::GetHmdColorDesc()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpColorSpace HmdColorSpace;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetHmdColorDesc(&HmdColorSpace)))
{
return (EOculusXRColorSpace)HmdColorSpace;
}
}
#endif
return EOculusXRColorSpace::Unknown;
}
void UOculusXRFunctionLibrary::SetClientColorDesc(EOculusXRColorSpace ColorSpace)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpColorSpace ClientColorSpace = (ovrpColorSpace)ColorSpace;
#if PLATFORM_ANDROID
if (ClientColorSpace == ovrpColorSpace_Unknown)
{
ClientColorSpace = ovrpColorSpace_Quest;
}
#endif
FOculusXRHMDModule::GetPluginWrapper().SetClientColorDesc(ClientColorSpace);
}
#endif
}
void UOculusXRFunctionLibrary::SetLocalDimmingOn(bool LocalDimmingOn)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
UE_LOG(LogHMD, Log, TEXT("SetLocalDimmingOn %d"), LocalDimmingOn);
FOculusXRHMDModule::GetPluginWrapper().SetLocalDimming(LocalDimmingOn);
}
#endif
}
bool UOculusXRFunctionLibrary::IsPassthroughSupported()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpInsightPassthroughCapabilityFlags capabilities;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilityFlags(&capabilities)))
{
return (capabilities & ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Passthrough)
== ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Passthrough;
}
return false;
}
#endif
return false;
}
bool UOculusXRFunctionLibrary::IsColorPassthroughSupported()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpInsightPassthroughCapabilityFlags capabilities;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilityFlags(&capabilities)))
{
return (capabilities & ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Color)
== ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Color;
}
return false;
}
#endif
return false;
}
void UOculusXRFunctionLibrary::StartEnvironmentDepth()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
int CreateFlags = 0;
OculusXRHMD->StartEnvironmentDepth(CreateFlags);
}
#endif
}
void UOculusXRFunctionLibrary::StopEnvironmentDepth()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->StopEnvironmentDepth();
}
#endif
}
bool UOculusXRFunctionLibrary::IsEnvironmentDepthStarted()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
return OculusXRHMD->IsEnvironmentDepthStarted();
}
#endif
return false;
}
void UOculusXRFunctionLibrary::SetEnvironmentDepthHandRemoval(bool RemoveHands)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetEnvironmentDepthHandRemoval(RemoveHands);
}
#endif
}
void UOculusXRFunctionLibrary::SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->EnableHardOcclusions(Mode == EOculusXROcclusionsMode::HardOcclusions);
}
#if defined(WITH_OCULUS_BRANCH)
WorldContextObject->GetWorld()->Scene->SetEnableXRPassthroughSoftOcclusions(Mode == EOculusXROcclusionsMode::SoftOcclusions);
#else
ensureMsgf(Mode != EOculusXROcclusionsMode::SoftOcclusions, TEXT("Soft occlusions are only supported with the Oculus branch of the Unreal Engine"));
#endif // defined(WITH_OCULUS_BRANCH)
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
void UOculusXRFunctionLibrary::SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusXRHMD::FOculusXRHMD* OculusXRHMD = GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
switch (EyeBufferSharpenType)
{
case EOculusXREyeBufferSharpenType::SLST_Normal:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_EfficientSharpen);
break;
case EOculusXREyeBufferSharpenType::SLST_Quality:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_QualitySharpen);
break;
case EOculusXREyeBufferSharpenType::SLST_Auto:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_AutoLayerFilter);
break;
default:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlags(0));
break;
}
}
#endif
}
bool UOculusXRFunctionLibrary::IsPassthroughRecommended()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const OculusXRHMD::FOculusXRHMD* OculusHMD = GetOculusXRHMD();
if (OculusHMD != nullptr)
{
ovrpPassthroughPreferences Preferences;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughPreferences(&Preferences)))
{
return (Preferences.Flags & ovrpPassthroughPreferenceFlags::ovrpPassthroughPreferenceFlags_DefaultToActive)
== ovrpPassthroughPreferenceFlags::ovrpPassthroughPreferenceFlags_DefaultToActive;
};
}
#endif
return false;
}
#undef LOCTEXT_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,637 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDModule.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "OculusXRHMD_GameFrame.h"
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMD_Layer.h"
#include "OculusXRHMD_Splash.h"
#include "OculusXRHMD_StressTester.h"
#include "OculusXRHMD_ConsoleCommands.h"
#include "OculusXRHMD_SpectatorScreenController.h"
#include "OculusXRHMD_DynamicResolutionState.h"
#include "OculusXRHMD_DeferredDeletionQueue.h"
#include "OculusXRAssetManager.h"
#include "HeadMountedDisplayBase.h"
#include "HeadMountedDisplay.h"
#include "XRRenderTargetManager.h"
#include "XRRenderBridge.h"
#include "IStereoLayers.h"
#include "Stats/Stats.h"
#include "SceneViewExtension.h"
#include "Engine/Engine.h"
#include "Engine/StaticMeshActor.h"
#include "XRThreadUtils.h"
#include "ProceduralMeshComponent.h"
#include "Shader.h"
#include "Misc/EngineVersionComparison.h"
#include "OculusXRHMD_FoveatedRendering.h"
namespace OculusXRHMD
{
DECLARE_DELEGATE_TwoParams(FOculusXRHMDEventPollingDelegate, ovrpEventDataBuffer*, bool&);
//-------------------------------------------------------------------------------------------------
// FPerformanceStats
//-------------------------------------------------------------------------------------------------
struct FPerformanceStats
{
uint64 Frames;
double Seconds;
FPerformanceStats(uint32 InFrames = 0, double InSeconds = 0.0)
: Frames(InFrames)
, Seconds(InSeconds)
{
}
FPerformanceStats operator-(const FPerformanceStats& PerformanceStats) const
{
return FPerformanceStats(
Frames - PerformanceStats.Frames,
Seconds - PerformanceStats.Seconds);
}
};
enum FRecenterTypes
{
RecenterOrientation = 0x1,
RecenterPosition = 0x2,
RecenterOrientationAndPosition = 0x3
};
//-------------------------------------------------------------------------------------------------
// FOculusXRHMD - Oculus Rift Head Mounted Display
//-------------------------------------------------------------------------------------------------
class FOculusXRHMD : public FHeadMountedDisplayBase, public FXRRenderTargetManager, public IStereoLayers, public FHMDSceneViewExtension, public FOculusAssetManager
{
friend class UOculusXRFunctionLibrary;
friend FOculusXRHMDModule;
friend class FSplash;
friend class FConsoleCommands;
public:
OCULUSXRHMD_API static const FName OculusSystemName;
// IXRSystemIdentifier
virtual FName GetSystemName() const override;
virtual int32 GetXRSystemFlags() const override;
// IXRTrackingSystem
virtual FString GetVersionString() const override;
virtual bool DoesSupportPositionalTracking() const override;
virtual bool HasValidTrackingPosition() override;
virtual bool EnumerateTrackedDevices(TArray<int32>& OutDevices, EXRTrackedDeviceType Type = EXRTrackedDeviceType::Any) override;
virtual bool GetCurrentPose(int32 InDeviceId, FQuat& OutOrientation, FVector& OutPosition) override;
virtual bool GetRelativeEyePose(int32 InDeviceId, int32 ViewIndex, FQuat& OutOrientation, FVector& OutPosition) override;
virtual bool GetTrackingSensorProperties(int32 InDeviceId, FQuat& OutOrientation, FVector& OutPosition, FXRSensorProperties& OutSensorProperties) override;
virtual void SetTrackingOrigin(EHMDTrackingOrigin::Type NewOrigin) override;
virtual EHMDTrackingOrigin::Type GetTrackingOrigin() const override;
virtual bool GetFloorToEyeTrackingTransform(FTransform& OutFloorToEye) const override;
//virtual FVector GetAudioListenerOffset(int32 InDeviceId = HMDDeviceId) const override;
virtual void ResetOrientationAndPosition(float Yaw = 0.f) override;
virtual void ResetOrientation(float Yaw = 0.f) override;
virtual void ResetPosition() override;
virtual void SetBaseRotation(const FRotator& BaseRot) override;
virtual FRotator GetBaseRotation() const override;
virtual void SetBaseOrientation(const FQuat& BaseOrient) override;
virtual FQuat GetBaseOrientation() const override;
//virtual TSharedPtr<class IXRCamera, ESPMode::ThreadSafe> GetXRCamera(int32 DeviceId = HMDDeviceId) override;
virtual class IHeadMountedDisplay* GetHMDDevice() override { return this; }
virtual class TSharedPtr<class IStereoRendering, ESPMode::ThreadSafe> GetStereoRenderingDevice() override
{
return SharedThis(this);
}
//virtual class IXRInput* GetXRInput() override;
virtual bool
IsHeadTrackingEnforced() const override;
virtual void SetHeadTrackingEnforced(bool bEnabled) override;
virtual bool IsHeadTrackingAllowed() const override;
virtual void OnBeginPlay(FWorldContext& InWorldContext) override;
virtual void OnEndPlay(FWorldContext& InWorldContext) override;
virtual bool OnStartGameFrame(FWorldContext& WorldContext) override;
virtual bool OnEndGameFrame(FWorldContext& WorldContext) override;
virtual void OnBeginRendering_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) override;
virtual void OnBeginRendering_GameThread() override;
virtual class IXRLoadingScreen* CreateLoadingScreen() override { return GetSplash(); }
virtual FVector2D GetPlayAreaBounds(EHMDTrackingOrigin::Type Origin) const override;
// IHeadMountedDisplay
virtual bool IsHMDConnected() override { return true; }
virtual bool IsHMDEnabled() const override;
virtual EHMDWornState::Type GetHMDWornState() override;
virtual void EnableHMD(bool bEnable = true) override;
virtual bool GetHMDMonitorInfo(MonitorInfo&) override;
virtual void GetFieldOfView(float& InOutHFOVInDegrees, float& InOutVFOVInDegrees) const override;
virtual void SetInterpupillaryDistance(float NewInterpupillaryDistance) override;
virtual float GetInterpupillaryDistance() const override;
//virtual void SetClippingPlanes(float NCP, float FCP) override;
//virtual FVector GetAudioListenerOffset() const override;
virtual bool GetHMDDistortionEnabled(EShadingPath ShadingPath) const override;
//virtual void BeginRendering_RenderThread(const FTransform& NewRelativeTransform, FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) override;
//virtual bool IsSpectatorScreenActive() const override;
//virtual class ISpectatorScreenController* GetSpectatorScreenController() override;
//virtual class ISpectatorScreenController const* GetSpectatorScreenController() const override;
//virtual float GetDistortionScalingFactor() const override;
//virtual float GetLensCenterOffset() const override;
//virtual void GetDistortionWarpValues(FVector4& K) const override;
virtual bool IsChromaAbCorrectionEnabled() const override;
//virtual bool GetChromaAbCorrectionValues(FVector4& K) const override;
virtual bool HasHiddenAreaMesh() const override;
virtual bool HasVisibleAreaMesh() const override;
virtual void DrawHiddenAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override;
virtual void DrawVisibleAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override;
//virtual void DrawDistortionMesh_RenderThread(struct FHeadMountedDisplayPassContext& Context, const FIntPoint& TextureSize) override;
//virtual void UpdateScreenSettings(const FViewport* InViewport) override;
//virtual void UpdatePostProcessSettings(FPostProcessSettings*) override;
//virtual FTexture* GetDistortionTextureLeft() const override;
//virtual FTexture* GetDistortionTextureRight() const override;
//virtual FVector2D GetTextureOffsetLeft() const override;
//virtual FVector2D GetTextureOffsetRight() const override;
//virtual FVector2D GetTextureScaleLeft() const override;
//virtual FVector2D GetTextureScaleRight() const override;
//virtual const float* GetRedDistortionParameters() const override;
//virtual const float* GetGreenDistortionParameters() const override;
//virtual const float* GetBlueDistortionParameters() const override;
//virtual bool NeedsUpscalePostProcessPass() override;
//virtual void RecordAnalytics() override;
//virtual bool DoesAppUseVRFocus() const override;
//virtual bool DoesAppHaveVRFocus() const override;
virtual float GetPixelDenity() const override;
virtual void SetPixelDensity(const float NewPixelDensity) override;
virtual FIntPoint GetIdealRenderTargetSize() const override;
virtual void GetMotionControllerData(UObject* WorldContext, const EControllerHand Hand, FXRMotionControllerData& MotionControllerData) override;
// IStereoRendering interface
virtual bool IsStereoEnabled() const override;
virtual bool IsStereoEnabledOnNextFrame() const override;
virtual bool EnableStereo(bool stereo = true) override;
virtual void AdjustViewRect(int32 ViewIndex, int32& X, int32& Y, uint32& SizeX, uint32& SizeY) const override;
virtual void SetFinalViewRect(FRHICommandListImmediate& RHICmdList, const int32 ViewIndex, const FIntRect& FinalViewRect) override;
//virtual FVector2D GetTextSafeRegionBounds() const override;
virtual void CalculateStereoViewOffset(const int32 ViewIndex, FRotator& ViewRotation, const float WorldToMeters, FVector& ViewLocation) override;
virtual FMatrix GetStereoProjectionMatrix(const int32 ViewIndex) const override;
virtual void InitCanvasFromView(class FSceneView* InView, class UCanvas* Canvas) override;
//virtual void GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const override;
virtual void RenderTexture_RenderThread(class FRHICommandListImmediate& RHICmdList, class FRHITexture* BackBuffer, class FRHITexture* SrcTexture, FVector2D WindowSize) const override;
//virtual void SetClippingPlanes(float NCP, float FCP) override;
virtual IStereoRenderTargetManager* GetRenderTargetManager() override { return this; }
virtual IStereoLayers* GetStereoLayers() override { return this; }
//virtual void UseImplicitHmdPosition(bool bInImplicitHmdPosition) override;
//virtual bool GetUseImplicitHmdPosition() override;
virtual bool IsStandaloneStereoOnlyDevice() const override { return bIsStandaloneStereoOnlyDevice; }
bool SupportsSpaceWarp() const;
#ifdef WITH_OCULUS_BRANCH
virtual void CalculateScissorRect(const int32 ViewIndex, const FIntRect& ViewRect, FIntRect& OutRect) override;
#endif // WITH_OCULUS_BRANCH
// FHeadMountedDisplayBase interface
virtual FVector2D GetEyeCenterPoint_RenderThread(int32 ViewIndex) const override;
virtual FIntRect GetFullFlatEyeRect_RenderThread(FTexture2DRHIRef EyeTexture) const override;
virtual void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* SrcTexture, FIntRect SrcRect, FRHITexture2D* DstTexture, FIntRect DstRect, bool bClearBlack, bool bNoAlpha) const override;
virtual bool PopulateAnalyticsAttributes(TArray<struct FAnalyticsEventAttribute>& EventAttributes) override;
// FXRRenderTargetManager interface
virtual bool ShouldUseSeparateRenderTarget() const override;
virtual void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) override;
virtual bool NeedReAllocateViewportRenderTarget(const class FViewport& Viewport) override;
virtual bool NeedReAllocateDepthTexture(const TRefCountPtr<IPooledRenderTarget>& DepthTarget) override;
virtual bool NeedReAllocateShadingRateTexture(const TRefCountPtr<IPooledRenderTarget>& FoveationTarget) override;
#ifdef WITH_OCULUS_BRANCH
virtual bool NeedReAllocateMotionVectorTexture(const TRefCountPtr<IPooledRenderTarget>& MotionVectorTarget, const TRefCountPtr<IPooledRenderTarget>& MotionVectorDepthTarget) override;
#endif // WITH_OCULUS_BRANCH
virtual bool AllocateRenderTargetTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override;
virtual bool AllocateDepthTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags TargetableTextureFlags, FTexture2DRHIRef& OutTargetableTexture, FTexture2DRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override;
virtual bool AllocateShadingRateTexture(uint32 Index, uint32 RenderSizeX, uint32 RenderSizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTexture2DRHIRef& OutTexture, FIntPoint& OutTextureSize) override;
#ifdef WITH_OCULUS_BRANCH
virtual bool AllocateMotionVectorTexture(uint32 Index, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTexture2DRHIRef& OutTexture, FIntPoint& OutTextureSize, FTexture2DRHIRef& OutDepthTexture, FIntPoint& OutDepthTextureSize) override;
virtual bool FindEnvironmentDepthTexture_RenderThread(FTextureRHIRef& OutTexture, FVector2f& OutDepthFactors, FMatrix44f OutScreenToDepthMatrices[2], FMatrix44f OutDepthViewProjMatrices[2]) override;
#endif // WITH_OCULUS_BRANCH
virtual EPixelFormat GetActualColorSwapchainFormat() const override;
virtual void UpdateViewportWidget(bool bUseSeparateRenderTarget, const class FViewport& Viewport, class SViewport* ViewportWidget) override;
virtual FXRRenderBridge* GetActiveRenderBridge_GameThread(bool bUseSeparateRenderTarget);
void AllocateEyeBuffer();
// IStereoLayers interface
virtual uint32 CreateLayer(const IStereoLayers::FLayerDesc& InLayerDesc) override;
virtual void DestroyLayer(uint32 LayerId) override;
virtual void SetLayerDesc(uint32 LayerId, const IStereoLayers::FLayerDesc& InLayerDesc) override;
virtual bool GetLayerDesc(uint32 LayerId, IStereoLayers::FLayerDesc& OutLayerDesc) override;
virtual void MarkTextureForUpdate(uint32 LayerId) override;
virtual IStereoLayers::FLayerDesc GetDebugCanvasLayerDesc(FTextureRHIRef Texture) override;
virtual void GetAllocatedTexture(uint32 LayerId, FTextureRHIRef& Texture, FTextureRHIRef& LeftTexture) override;
virtual bool ShouldCopyDebugLayersToSpectatorScreen() const override { return true; }
virtual void PushLayerState(bool) override
{ /* Todo */
}
virtual void PopLayerState() override
{ /* Todo */
}
// ISceneViewExtension
virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override;
virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
virtual void PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override;
virtual void PostRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
#ifdef WITH_OCULUS_BRANCH
virtual void PostSceneColorRenderingMobile_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
#endif
#else
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
#ifdef WITH_OCULUS_BRANCH
virtual void PostSceneColorRenderingMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
#endif
#endif
virtual void PostRenderBasePassDeferred_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView, const FRenderTargetBindingSlots& RenderTargets, TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures) override;
virtual int32 GetPriority() const override;
#ifdef WITH_OCULUS_BRANCH
virtual bool LateLatchingEnabled() const override;
virtual void PreLateLatchingViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override;
#endif
public:
FOculusXRHMD(const FAutoRegister&);
~FOculusXRHMD();
protected:
bool Startup();
void PreShutdown();
void Shutdown();
bool InitializeSession();
void ShutdownSession();
bool InitDevice();
void ReleaseDevice();
void ApplicationPauseDelegate();
void ApplicationResumeDelegate();
bool CheckEyeTrackingPermission(EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod);
void SetupOcclusionMeshes();
void UpdateStereoRenderingParams();
void UpdateHmdRenderInfo();
void InitializeEyeLayer_RenderThread(FRHICommandListImmediate& RHICmdList);
void ApplySystemOverridesOnStereo(bool force = false);
bool OnOculusStateChange(bool bIsEnabledNow);
bool ShouldDisableHiddenAndVisibileAreaMeshForSpectatorScreen_RenderThread() const;
void Recenter(FRecenterTypes RecenterType, float Yaw);
FIntRect GetAsymmetricViewRect(const int32 ViewIndex, const FIntRect& ViewRect);
#if !UE_BUILD_SHIPPING
void DrawDebug(UCanvas* InCanvas, APlayerController* InPlayerController);
#endif
class FSceneViewport* FindSceneViewport();
FOculusXRSplashDesc GetUESplashScreenDesc();
void EyeTrackedFoveatedRenderingFallback();
public:
OCULUSXRHMD_API static FOculusXRHMD* GetOculusXRHMD();
bool IsHMDActive() const;
FSplash* GetSplash() const { return Splash.Get(); }
FCustomPresent* GetCustomPresent_Internal() const { return CustomPresent; }
float GetWorldToMetersScale() const;
ESpectatorScreenMode GetSpectatorScreenMode_RenderThread() const;
FVector GetNeckPosition(const FQuat& HeadOrientation, const FVector& HeadPosition);
/**
* Sets base position offset (in meters). The base position offset is the distance from the physical (0, 0, 0) position
* to current HMD position (bringing the (0, 0, 0) point to the current HMD position)
* Note, this vector is set by ResetPosition call; use this method with care.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param BaseOffset (in) the vector to be set as base offset, in meters.
*/
void SetBaseOffsetInMeters(const FVector& BaseOffset);
/**
* Returns the currently used base position offset, previously set by the
* ResetPosition or SetBasePositionOffset calls. It represents a vector that translates the HMD's position
* into (0,0,0) point, in meters.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @return Base position offset, in meters.
*/
FVector GetBaseOffsetInMeters() const;
OCULUSXRHMD_API bool ConvertPose(const ovrpPosef& InPose, FPose& OutPose) const;
OCULUSXRHMD_API bool ConvertPose(const FPose& InPose, ovrpPosef& OutPose) const;
OCULUSXRHMD_API bool ConvertPose_RenderThread(const ovrpPosef& InPose, FPose& OutPose) const;
OCULUSXRHMD_API static bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FSettings* Settings, float WorldToMetersScale = 100.0f);
OCULUSXRHMD_API static bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FSettings* Settings, float WorldToMetersScale = 100.0f);
/** Turns ovrVector3f in Unreal World space to a scaled FVector and applies translation and rotation corresponding to player movement */
FVector ScaleAndMovePointWithPlayer(ovrpVector3f& OculusXRHMDPoint);
/** The inverse of ScaleAndMovePointWithPlayer */
ovrpVector3f WorldLocationToOculusPoint(const FVector& InUnrealPosition);
/** Convert dimension of a float (e.g., a distance) from meters to Unreal Units */
float ConvertFloat_M2U(float OculusFloat) const;
FVector ConvertVector_M2U(ovrpVector3f OculusPoint) const;
struct UserProfile
{
float IPD;
float EyeDepth;
float EyeHeight;
};
bool GetUserProfile(UserProfile& OutProfile);
float GetVsyncToNextVsync() const;
FPerformanceStats GetPerformanceStats() const;
bool DoEnableStereo(bool bStereo);
void ResetControlRotation() const;
void UpdateFoveationOffsets_RenderThread();
bool ComputeEnvironmentDepthParameters_RenderThread(FVector2f& DepthFactors, FMatrix44f ScreenToDepth[ovrpEye_Count], FMatrix44f DepthViewProj[ovrpEye_Count], int& SwapchainIndex);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
void RenderHardOcclusions_RenderThread(FRHICommandListImmediate& RHICmdList, const FSceneView& InView);
#else
void RenderHardOcclusions_RenderThread(FRHICommandList& RHICmdList, const FSceneView& InView);
#endif
FSettingsPtr CreateNewSettings() const;
FGameFramePtr CreateNewGameFrame() const;
FGameFrame* GetFrame()
{
CheckInGameThread();
return Frame.Get();
}
const FGameFrame* GetFrame() const
{
CheckInGameThread();
return Frame.Get();
}
FGameFrame* GetFrame_RenderThread()
{
CheckInRenderThread();
return Frame_RenderThread.Get();
}
const FGameFrame* GetFrame_RenderThread() const
{
CheckInRenderThread();
return Frame_RenderThread.Get();
}
FGameFrame* GetFrame_RHIThread()
{
CheckInRHIThread();
return Frame_RHIThread.Get();
}
const FGameFrame* GetFrame_RHIThread() const
{
CheckInRHIThread();
return Frame_RHIThread.Get();
}
FGameFrame* GetNextFrameToRender()
{
CheckInGameThread();
return NextFrameToRender.Get();
}
const FGameFrame* GetNextFrameToRender() const
{
CheckInGameThread();
return NextFrameToRender.Get();
}
FSettings* GetSettings()
{
CheckInGameThread();
return Settings.Get();
}
const FSettings* GetSettings() const
{
CheckInGameThread();
return Settings.Get();
}
FSettings* GetSettings_RenderThread()
{
CheckInRenderThread();
return Settings_RenderThread.Get();
}
const FSettings* GetSettings_RenderThread() const
{
CheckInRenderThread();
return Settings_RenderThread.Get();
}
FSettings* GetSettings_RHIThread()
{
CheckInRHIThread();
return Settings_RHIThread.Get();
}
const FSettings* GetSettings_RHIThread() const
{
CheckInRHIThread();
return Settings_RHIThread.Get();
}
const int GetNextFrameNumber() const { return NextFrameNumber; }
const FRotator GetSplashRotation() const { return SplashRotation; }
void SetSplashRotationToForward();
OCULUSXRHMD_API void StartGameFrame_GameThread(); // Called from OnStartGameFrame or from FOculusXRInput::SendControllerEvents (first actual call of the frame)
void FinishGameFrame_GameThread(); // Called from OnEndGameFrame
void StartRenderFrame_GameThread(); // Called from BeginRenderViewFamily
void FinishRenderFrame_RenderThread(FRDGBuilder& GraphBuilder); // Called from PostRenderViewFamily_RenderThread
void StartRHIFrame_RenderThread(); // Called from PreRenderViewFamily_RenderThread
void FinishRHIFrame_RHIThread(); // Called from FinishRendering_RHIThread
void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel);
void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel);
void SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod InFoveationMethod);
void SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel InFoveationLevel, bool isDynamic);
void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers);
void SetEnvironmentDepthHandRemoval(bool RemoveHands);
void StartEnvironmentDepth(int CreateFlags);
void StopEnvironmentDepth();
bool IsEnvironmentDepthStarted();
void EnableHardOcclusions(bool bEnable);
OCULUSXRHMD_API void UpdateRTPoses();
FTransform GetLastTrackingToWorld() const { return LastTrackingToWorld; }
OCULUSXRHMD_API void AddEventPollingDelegate(const FOculusXRHMDEventPollingDelegate& NewDelegate);
protected:
FConsoleCommands ConsoleCommands;
void UpdateOnRenderThreadCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void PixelDensityMinCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void PixelDensityMaxCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void HQBufferCommandHandler(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar);
void HQDistortionCommandHandler(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar);
void ShowGlobalMenuCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void ShowQuitMenuCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#if !UE_BUILD_SHIPPING
void StatsCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void ShowSettingsCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void IPDCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#endif
void LoadFromSettings();
void CheckMultiPlayer();
void DoSessionShutdown();
protected:
void UpdateHMDWornState();
EHMDWornState::Type HMDWornState = EHMDWornState::Unknown;
void UpdateHMDEvents();
void EnableInsightPassthrough_RenderThread(bool bEnablePassthrough);
void DrawHmdViewMesh(
FRHICommandList& RHICmdList,
float X,
float Y,
float SizeX,
float SizeY,
float U,
float V,
float SizeU,
float SizeV,
FIntPoint TargetSize,
FIntPoint TextureSize,
int32 StereoView,
const TShaderRef<class FShader>& VertexShader);
union
{
struct
{
uint64 bApplySystemOverridesOnStereo : 1;
uint64 bNeedEnableStereo : 1;
uint64 bNeedDisableStereo : 1;
};
uint64 Raw;
} Flags;
union
{
struct
{
// set to true when origin was set while OvrSession == null; the origin will be set ASA OvrSession != null
uint64 NeedSetTrackingOrigin : 1;
// enforces exit; used mostly for testing
uint64 EnforceExit : 1;
// set if a game is paused by the plug-in
uint64 AppIsPaused : 1;
// set to indicate that DisplayLost was detected by game thread.
uint64 DisplayLostDetected : 1;
// set to true once new session is created; being handled and reset as soon as session->IsVisible.
uint64 NeedSetFocusToGameViewport : 1;
};
uint64 Raw;
} OCFlags;
TRefCountPtr<FCustomPresent> CustomPresent;
FSplashPtr Splash;
IRendererModule* RendererModule;
FDeferredDeletionQueue DeferredDeletion;
EHMDTrackingOrigin::Type TrackingOrigin;
// Stores difference between ViewRotation and EyeOrientation from previous frame
FQuat LastPlayerOrientation;
// Stores GetFrame()->PlayerLocation (i.e., ViewLocation) from the previous frame
FVector LastPlayerLocation;
FRotator DeltaControlRotation; // used from ApplyHmdRotation
TWeakPtr<SWidget> CachedViewportWidget;
TWeakPtr<SWindow> CachedWindow;
FIntPoint CachedWindowSize;
float CachedWorldToMetersScale;
bool bIsStandaloneStereoOnlyDevice;
// Stores TrackingToWorld from previous frame
FTransform LastTrackingToWorld;
std::atomic<bool> bHardOcclusionsEnabled;
std::atomic<bool> bEnvironmentDepthHandRemovalEnabled;
// These three properties indicate the current state of foveated rendering, which may differ from what's in Settings
// due to cases such as falling back to FFR when eye tracked foveated rendering isn't enabled. Will allow us to resume
// ETFR from situations such as when ET gets paused.
std::atomic<EOculusXRFoveatedRenderingMethod> FoveatedRenderingMethod;
std::atomic<EOculusXRFoveatedRenderingLevel> FoveatedRenderingLevel;
std::atomic<bool> bDynamicFoveatedRendering;
// Game thread
FSettingsPtr Settings;
uint32 NextFrameNumber;
uint32 WaitFrameNumber;
FGameFramePtr Frame; // Valid from OnStartGameFrame to OnEndGameFrame
FGameFramePtr NextFrameToRender; // Valid from OnStartGameFrame to BeginRenderViewFamily
FGameFramePtr LastFrameToRender; // Valid from OnStartGameFrame to BeginRenderViewFamily
uint32 NextLayerId;
TMap<uint32, FLayerPtr> LayerMap;
bool bNeedReAllocateViewportRenderTarget;
// Render thread
FSettingsPtr Settings_RenderThread;
FGameFramePtr Frame_RenderThread; // Valid from BeginRenderViewFamily to PostRenderViewFamily_RenderThread
TArray<FLayerPtr> Layers_RenderThread;
FLayerPtr EyeLayer_RenderThread; // Valid to be accessed from game thread, since updated only when game thread is waiting
bool bNeedReAllocateDepthTexture_RenderThread;
bool bNeedReAllocateFoveationTexture_RenderThread;
bool bNeedReAllocateMotionVectorTexture_RenderThread;
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
TSharedPtr<FOculusXRFoveatedRenderingImageGenerator, ESPMode::ThreadSafe> FoveationImageGenerator;
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)
// RHI thread
FSettingsPtr Settings_RHIThread;
FGameFramePtr Frame_RHIThread; // Valid from PreRenderViewFamily_RenderThread to FinishRendering_RHIThread
TArray<FLayerPtr> Layers_RHIThread;
FHMDViewMesh HiddenAreaMeshes[2];
FHMDViewMesh VisibleAreaMeshes[2];
FPerformanceStats PerformanceStats;
FRotator SplashRotation; // rotation applied to all splash screens (dependent on HMD orientation as the splash is shown)
TArray<FTextureRHIRef> EnvironmentDepthSwapchain;
#if !UE_BUILD_SHIPPING
FDelegateHandle DrawDebugDelegateHandle;
#endif
enum class FInsightInitStatus
{
NotInitialized,
Initialized,
Failed,
};
FInsightInitStatus InsightInitStatus;
bool bShutdownRequestQueued;
bool bEyeTrackedFoveatedRenderingSupported;
TArray<FOculusXRHMDEventPollingDelegate> EventPollingDelegates;
// MultiPlayer
bool bMultiPlayer;
bool bShouldWait_GameThread;
bool bIsRendering_RenderThread;
};
typedef TSharedPtr<FOculusXRHMD, ESPMode::ThreadSafe> FOculusXRHMDPtr;
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,509 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDModule.h"
#include "OculusXRHMD.h"
#include "OculusXRHMDPrivateRHI.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "Containers/StringConv.h"
#include "Misc/EngineVersion.h"
#include "Misc/Paths.h"
#if PLATFORM_ANDROID
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#include "Interfaces/IPluginManager.h"
#include "ShaderCore.h"
#include "OculusXRTelemetry.h"
#if PLATFORM_WINDOWS
#include "OculusXRSimulator.h"
#include "OculusXRSyntheticEnvironmentServer.h"
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
namespace
{
void __cdecl OvrpLogCallback2(ovrpLogLevel InLevel, const char* Message, int Length)
{
ELogVerbosity::Type OutLevel;
switch (InLevel)
{
case ovrpLogLevel_Debug:
OutLevel = ELogVerbosity::Log;
break;
case ovrpLogLevel_Info:
OutLevel = ELogVerbosity::Display;
break;
case ovrpLogLevel_Error:
OutLevel = ELogVerbosity::Error;
break;
default:
OutLevel = ELogVerbosity::NoLogging;
}
const FString MessageStr(Length, Message);
GLog->CategorizedLogf(TEXT("LogOVRPlugin"), OutLevel, TEXT("%s"), *MessageStr);
}
} // namespace
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
OculusPluginWrapper FOculusXRHMDModule::PluginWrapper{};
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusPluginWrapper& FOculusXRHMDModule::GetPluginWrapper()
{
return PluginWrapper;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
FOculusXRHMDModule::FOculusXRHMDModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
bPreInit = false;
bPreInitCalled = false;
OVRPluginHandle = nullptr;
GraphicsAdapterLuid = 0;
#endif
}
void FOculusXRHMDModule::StartupModule()
{
IHeadMountedDisplayModule::StartupModule();
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/Plugin/OculusXR"), PluginShaderDir);
}
void FOculusXRHMDModule::ShutdownModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (PluginWrapper.IsInitialized())
{
OculusXRTelemetry::FTelemetryBackend::OnEditorShutdown();
PluginWrapper.Shutdown2();
OculusPluginWrapper::DestroyOculusPluginWrapper(&PluginWrapper);
}
if (OVRPluginHandle)
{
FPlatformProcess::FreeDllHandle(OVRPluginHandle);
OVRPluginHandle = nullptr;
}
#endif
}
#if PLATFORM_ANDROID
extern bool AndroidThunkCpp_IsOculusMobileApplication();
#endif
FString FOculusXRHMDModule::GetModuleKeyName() const
{
return FString(TEXT("OculusXRHMD"));
}
void FOculusXRHMDModule::GetModuleAliases(TArray<FString>& AliasesOut) const
{
// Pre-OculusXR rename (5.0.3 v44)
AliasesOut.Add(TEXT("OculusHMD"));
}
bool FOculusXRHMDModule::PreInit()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (!bPreInitCalled)
{
bPreInit = false;
#if PLATFORM_ANDROID
bPreInitCalled = true;
if (!AndroidThunkCpp_IsOculusMobileApplication())
{
UE_LOG(LogHMD, Log, TEXT("App is not packaged for Oculus Mobile"));
return false;
}
#endif
// Init module if app can render
if (FApp::CanEverRender())
{
// Load OVRPlugin
OVRPluginHandle = GetOVRPluginHandle();
if (!OVRPluginHandle)
{
UE_LOG(LogHMD, Log, TEXT("Failed loading OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
return false;
}
if (!OculusPluginWrapper::InitializeOculusPluginWrapper(&PluginWrapper))
{
UE_LOG(LogHMD, Log, TEXT("Failed InitializeOculusPluginWrapper"));
return false;
}
// Initialize OVRPlugin
ovrpRenderAPIType PreinitApiType = ovrpRenderAPI_None;
#if PLATFORM_ANDROID
void* Activity = (void*)FAndroidApplication::GetGameActivityThis();
PreinitApiType = ovrpRenderAPI_Vulkan;
#else
void* Activity = nullptr;
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
PluginWrapper.SetLogCallback2(OvrpLogCallback2);
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
if (OVRP_FAILURE(PluginWrapper.PreInitialize5(Activity, PreinitApiType, ovrpPreinitializeFlags::ovrpPreinitializeFlag_None)))
{
UE_LOG(LogHMD, Log, TEXT("Failed initializing OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
#if WITH_EDITOR && PLATFORM_WINDOWS
// In the editor, we want to allow the headset to connect after the editor has booted.
// To do this, we must have PreInit() return true, to prevent the HMD module from being unloaded.
return GIsEditor;
#else
return false;
#endif
}
#if PLATFORM_WINDOWS
bPreInitCalled = true;
const LUID* DisplayAdapterId;
if (OVRP_SUCCESS(PluginWrapper.GetDisplayAdapterId2((const void**)&DisplayAdapterId)) && DisplayAdapterId)
{
SetGraphicsAdapterLuid(*(const uint64*)DisplayAdapterId);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD display adapter"));
}
const WCHAR* AudioInDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioInDeviceId2((const void**)&AudioInDeviceId)) && AudioInDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio input device"));
}
const WCHAR* AudioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&AudioOutDeviceId)) && AudioOutDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio output device"));
}
#endif
float ModulePriority;
if (!GConfig->GetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni))
{
// if user doesn't set priority set it for them to allow this hmd to be used if enabled
ModulePriority = 45.0f;
GConfig->SetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni);
}
UE_LOG(LogHMD, Log, TEXT("FOculusXRHMDModule PreInit successfully"));
bPreInit = true;
}
}
return bPreInit;
#else
return false;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool FOculusXRHMDModule::IsHMDConnected()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
if (FApp::CanEverRender() && HMDSettings->XrApi != EOculusXRXrApi::NativeOpenXR)
{
return true;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
uint64 FOculusXRHMDModule::GetGraphicsAdapterLuid()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
if (!GraphicsAdapterLuid)
{
int GraphicsAdapter;
if (GConfig->GetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), GraphicsAdapter, GEngineIni) && GraphicsAdapter >= 0)
{
TRefCountPtr<IDXGIFactory> DXGIFactory;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())) && SUCCEEDED(DXGIFactory->EnumAdapters(GraphicsAdapter, DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
FMemory::Memcpy(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid));
}
}
}
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return GraphicsAdapterLuid;
#else
return 0;
#endif
}
FString FOculusXRHMDModule::GetAudioInputDevice()
{
FString AudioInputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInputDevice, GEngineIni);
#endif
return AudioInputDevice;
}
FString FOculusXRHMDModule::GetAudioOutputDevice()
{
FString AudioOutputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#if PLATFORM_WINDOWS
if (bPreInit)
{
if (FApp::CanEverRender())
{
const WCHAR* audioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&audioOutDeviceId)) && audioOutDeviceId)
{
AudioOutputDevice = audioOutDeviceId;
}
}
}
#else
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutputDevice, GEngineIni);
#endif
#endif
return AudioOutputDevice;
}
TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> FOculusXRHMDModule::CreateTrackingSystem()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit || (GIsEditor && PLATFORM_WINDOWS))
{
//If -HMDSimulator is used as the command option to launch UE, use simulator runtime instead of the physical HMD runtime (like PC-Link).
if (FParse::Param(FCommandLine::Get(), TEXT("HMDSimulator")) && GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MetaXRJsonPath.FilePath.Len())
{
if (!IsSimulatorActivated())
{
ToggleOpenXRRuntime();
}
}
OculusXRHMD::FOculusXRHMDPtr OculusXRHMD = FSceneViewExtensions::NewExtension<OculusXRHMD::FOculusXRHMD>();
if (OculusXRHMD->Startup())
{
HeadMountedDisplay = OculusXRHMD;
return OculusXRHMD;
}
}
HeadMountedDisplay = nullptr;
#endif
return nullptr;
}
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> FOculusXRHMDModule::GetVulkanExtensions()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit)
{
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FVulkanExtensions);
}
}
#if WITH_EDITOR && PLATFORM_WINDOWS
else if (GIsEditor)
{
// OpenXR has no ability to query for possible vulkan extensions without connecting a HMD.
// This is a problem, because we need to create our VkInstance and VkDevice to render in 2D and there's no HMD.
// For now, as a workaround, we hardcode the extensions that Oculus's OpenXR implementation needs.
// Eventually, one of three things has to happen for a proper fix:
//
// 1. OculusXRHMD (or, better, OVRPlugin) maintains a separate VkInstance that has the right extensions,
// and uses the vk_external extensions to transfer data between them when needed.
// 2. OpenXR changes to allow querying instance and device extensions without an active HMD.
// It may still require a physical device handle to list device extensions.
// 3. Oculus's Link implementation for OpenXR changes to allow an XrSystemId to be created before a headset
// is connected (possibly as an opt-in OpenXR extension for backwards compatibility).
//
// (2) or (3) are preferable, but if OpenXR is held constant we will have to do (1).
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FEditorVulkanExtensions);
}
}
#endif
return VulkanExtensions;
#endif
return nullptr;
}
FString FOculusXRHMDModule::GetDeviceSystemName()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpSystemHeadset SystemHeadset;
if (PluginWrapper.IsInitialized() && OVRP_SUCCESS(PluginWrapper.GetSystemHeadsetType2(&SystemHeadset)))
{
switch (SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return FString("Oculus Quest");
case ovrpSystemHeadset_Oculus_Quest_2:
default:
return FString("Oculus Quest2");
#ifdef WITH_OCULUS_BRANCH
case ovrpSystemHeadset_Meta_Quest_Pro:
return FString("Meta Quest Pro");
case ovrpSystemHeadset_Meta_Quest_3:
return FString("Meta Quest 3");
#endif // WITH_OCULUS_BRANCH
}
}
return FString();
#else
return FString();
#endif
}
bool FOculusXRHMDModule::IsStandaloneStereoOnlyDevice()
{
#if PLATFORM_ANDROID
return FAndroidMisc::GetDeviceMake() == FString("Oculus");
#else
return false;
#endif
}
bool FOculusXRHMDModule::IsSimulatorActivated()
{
#if PLATFORM_WINDOWS
return FMetaXRSimulator::IsSimulatorActivated();
#else
return false;
#endif
}
void FOculusXRHMDModule::ToggleOpenXRRuntime()
{
#if PLATFORM_WINDOWS
FMetaXRSimulator::ToggleOpenXRRuntime();
#endif
}
void FOculusXRHMDModule::LaunchEnvironment(FString EnvironmentName)
{
#if PLATFORM_WINDOWS
FMetaXRSES::LaunchEnvironment(EnvironmentName);
#endif
}
void FOculusXRHMDModule::StopServer()
{
#if PLATFORM_WINDOWS
FMetaXRSES::StopServer();
#endif
}
#if OCULUS_HMD_SUPPORTED_PLATFORMS
void* FOculusXRHMDModule::GetOVRPluginHandle()
{
void* OVRPluginHandle = nullptr;
#if PLATFORM_WINDOWS
FString XrApi;
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD") || !GConfig->GetString(TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings"), TEXT("XrApi"), XrApi, GEngineIni) || XrApi.Equals(FString("OVRPluginOpenXR")))
{
FString BinariesPath = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/Win64"));
FPlatformProcess::PushDllDirectory(*BinariesPath);
OVRPluginHandle = FPlatformProcess::GetDllHandle(*(BinariesPath / "OpenXR/OVRPlugin.dll"));
FPlatformProcess::PopDllDirectory(*BinariesPath);
}
#elif PLATFORM_ANDROID
OVRPluginHandle = FPlatformProcess::GetDllHandle(TEXT("libOVRPlugin.so"));
#endif // PLATFORM_ANDROID
return OVRPluginHandle;
}
bool FOculusXRHMDModule::PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const
{
OculusXRHMD::CheckInGameThread();
OculusXRHMD::FOculusXRHMD* OculusXRHMD = static_cast<OculusXRHMD::FOculusXRHMD*>(HeadMountedDisplay.Pin().Get());
if (OculusXRHMD)
{
ovrpPosef InPose;
InPose.Orientation = OculusXRHMD::ToOvrpQuatf(InOrientation);
InPose.Position = OculusXRHMD::ToOvrpVector3f(InPosition);
OculusXRHMD::FPose OutPose;
if (OculusXRHMD->ConvertPose(InPose, OutPose))
{
OutOrientation = OutPose.Orientation;
OutPosition = OutPose.Position;
return true;
}
}
return false;
}
void FOculusXRHMDModule::SetGraphicsAdapterLuid(uint64 InLuid)
{
GraphicsAdapterLuid = InLuid;
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
TRefCountPtr<IDXGIFactory> DXGIFactory;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())))
{
for (int32 adapterIndex = 0;; adapterIndex++)
{
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (FAILED(DXGIFactory->EnumAdapters(adapterIndex, DXGIAdapter.GetInitReference())) || FAILED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
break;
}
if (!FMemory::Memcmp(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid)))
{
// Remember this adapterIndex so we use the right adapter, even when we startup without HMD connected
GConfig->SetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), adapterIndex, GEngineIni);
break;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRHMDModule, OculusXRHMD)

View File

@@ -0,0 +1,121 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IHeadMountedDisplay.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMD_VulkanExtensions.h"
#include "OculusXRPluginWrapper.h"
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
class FOculusXRHMDModule : public IOculusXRHMDModule
{
public:
FOculusXRHMDModule();
static inline FOculusXRHMDModule& Get()
{
return FModuleManager::LoadModuleChecked<FOculusXRHMDModule>("OculusXRHMD");
}
// IModuleInterface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
// IHeadMountedDisplayModule
virtual FString GetModuleKeyName() const override;
virtual void GetModuleAliases(TArray<FString>& AliasesOut) const override;
virtual bool PreInit() override;
virtual bool IsHMDConnected() override;
virtual uint64 GetGraphicsAdapterLuid() override;
virtual FString GetAudioInputDevice() override;
virtual FString GetAudioOutputDevice() override;
virtual FString GetDeviceSystemName() override;
virtual TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> CreateTrackingSystem() override;
virtual TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> GetVulkanExtensions() override;
virtual bool IsStandaloneStereoOnlyDevice() override;
// IOculusXRHMDModule
virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false, const FVector PositionScale = FVector::ZeroVector) override
{
UOculusXRFunctionLibrary::GetPose(DeviceRotation, DevicePosition, NeckPosition, bUseOrienationForPlayerCamera, bUsePositionForPlayerCamera, PositionScale);
}
virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds) override
{
UOculusXRFunctionLibrary::GetRawSensorData(AngularAcceleration, LinearAcceleration, AngularVelocity, LinearVelocity, TimeInSeconds, EOculusXRTrackedDeviceType::HMD);
}
virtual bool GetUserProfile(struct FOculusXRHmdUserProfile& Profile) override
{
return UOculusXRFunctionLibrary::GetUserProfile(Profile);
}
virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) override
{
UOculusXRFunctionLibrary::SetBaseRotationAndBaseOffsetInMeters(Rotation, BaseOffsetInMeters, Options);
}
virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) override
{
UOculusXRFunctionLibrary::GetBaseRotationAndBaseOffsetInMeters(OutRotation, OutBaseOffsetInMeters);
}
virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) override
{
UOculusXRFunctionLibrary::SetBaseRotationAndPositionOffset(BaseRot, PosOffset, Options);
}
virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) override
{
UOculusXRFunctionLibrary::GetBaseRotationAndPositionOffset(OutRot, OutPosOffset);
}
virtual class IStereoLayers* GetStereoLayers() override
{
return UOculusXRFunctionLibrary::GetStereoLayers();
}
bool IsOVRPluginAvailable() const
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return OVRPluginHandle != nullptr;
#else
return false;
#endif
}
// FMetaXRSimulator
OCULUSXRHMD_API static bool IsSimulatorActivated();
OCULUSXRHMD_API static void ToggleOpenXRRuntime();
// FMetaXRSES
OCULUSXRHMD_API static void LaunchEnvironment(FString EnvironmentName);
OCULUSXRHMD_API static void StopServer();
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OCULUSXRHMD_API static void* GetOVRPluginHandle();
OCULUSXRHMD_API static OculusPluginWrapper& GetPluginWrapper();
virtual bool PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const override;
protected:
void SetGraphicsAdapterLuid(uint64 InLuid);
static OculusPluginWrapper PluginWrapper;
bool bPreInit;
bool bPreInitCalled;
void* OVRPluginHandle;
uint64 GraphicsAdapterLuid;
TWeakPtr<IHeadMountedDisplay, ESPMode::ThreadSafe> HeadMountedDisplay;
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> VulkanExtensions;
friend class ::OculusXRHMD::FOculusXRHMD;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
};

View File

@@ -0,0 +1,95 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDPrivate.h"
#include "RHICommandList.h"
#include "RenderingThread.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------------------------------
// TODO: Change in case of parallel game threads
bool InGameThread()
{
if (GIsGameThreadIdInitialized)
{
return FPlatformTLS::GetCurrentThreadId() == GGameThreadId;
}
else
{
return true;
}
}
bool InRenderThread()
{
if (GIsThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
{
return IsInParallelRenderingThread();
}
else
{
return InGameThread();
}
}
// TODO: Change in case of parallel RHI threads
bool InRHIThread()
{
if (GIsThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
{
if (IsRHIThreadRunning())
{
if (IsInRHIThread())
{
return true;
}
if (IsInParallelRenderingThread())
{
return GetImmediateCommandList_ForRenderCommand().Bypass();
}
return false;
}
else
{
return IsInParallelRenderingThread();
}
}
else
{
return InGameThread();
}
}
bool ConvertPose_Internal(const FPose& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
// apply base orientation correction
OutPose.Orientation = BaseOrientation.Inverse() * InPose.Orientation;
OutPose.Orientation.Normalize();
// correct position according to BaseOrientation and BaseOffset.
OutPose.Position = (InPose.Position - BaseOffset) * WorldToMetersScale;
OutPose.Position = BaseOrientation.Inverse().RotateVector(OutPose.Position);
return true;
}
bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
return ConvertPose_Internal(FPose(ToFQuat(InPose.Orientation), ToFVector(InPose.Position)), OutPose, BaseOrientation, BaseOffset, WorldToMetersScale);
}
bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
OutPose.Orientation = ToOvrpQuatf(BaseOrientation * InPose.Orientation);
OutPose.Position = ToOvrpVector3f(BaseOrientation.RotateVector(InPose.Position) / WorldToMetersScale + BaseOffset);
return true;
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,311 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/WorldSettings.h"
#include "IOculusXRHMDModule.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRPassthroughLayerShapes.h"
#include "StereoRendering.h"
#include "HAL/RunnableThread.h"
#include "RHI.h"
#include <functional>
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 PLATFORM_WINDOWS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12 PLATFORM_WINDOWS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN (PLATFORM_WINDOWS || PLATFORM_ANDROID)
#else
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 0
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12 0
#define OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN 0
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// OVRPlugin
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRPluginWrapper.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------------------------------
namespace OculusXRHMD
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
struct FPose
{
FQuat Orientation;
FVector Position;
FPose()
: Orientation(EForceInit::ForceInit)
, Position(EForceInit::ForceInit)
{
}
FPose(const FQuat& InOrientation, const FVector& InPosition)
: Orientation(InOrientation), Position(InPosition) {}
FPose Inverse() const
{
FQuat InvOrientation = Orientation.Inverse();
FVector InvPosition = InvOrientation.RotateVector(-Position);
return FPose(InvOrientation, InvPosition);
}
FPose operator*(const FPose& other) const
{
return FPose(Orientation * other.Orientation, Orientation.RotateVector(other.Position) + Position);
}
};
/** Converts ovrpQuatf to FQuat */
FORCEINLINE FQuat ToFQuat(const ovrpQuatf& InQuat)
{
return FQuat(-InQuat.z, InQuat.x, InQuat.y, -InQuat.w);
}
/** Converts FQuat to ovrpQuatf */
FORCEINLINE ovrpQuatf ToOvrpQuatf(const FQuat& InQuat)
{
return ovrpQuatf{ static_cast<float>(InQuat.Y), static_cast<float>(InQuat.Z), static_cast<float>(-InQuat.X), static_cast<float>(-InQuat.W) };
}
/** Converts vector from Oculus to Unreal */
FORCEINLINE FVector ToFVector(const ovrpVector3f& InVec)
{
return FVector(-InVec.z, InVec.x, InVec.y);
}
/** Converts vector from Unreal to Oculus. */
FORCEINLINE ovrpVector3f ToOvrpVector3f(const FVector& InVec)
{
return ovrpVector3f{ static_cast<float>(InVec.Y), static_cast<float>(InVec.Z), static_cast<float>(-InVec.X) };
}
FORCEINLINE FMatrix ToFMatrix(const ovrpMatrix4f& vtm)
{
// Rows and columns are swapped between ovrpMatrix4f 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 ovrpVector4f LinearColorToOvrpVector4f(const FLinearColor& InColor)
{
return ovrpVector4f{ InColor.R, InColor.G, InColor.B, InColor.A };
}
FORCEINLINE ovrpRecti ToOvrpRecti(const FIntRect& rect)
{
return ovrpRecti{ { rect.Min.X, rect.Min.Y }, { rect.Size().X, rect.Size().Y } };
}
FORCEINLINE ovrpColorf ToOvrpColorf(const FLinearColor LinearColor)
{
return ovrpColorf{ LinearColor.R, LinearColor.G, LinearColor.B, LinearColor.A };
}
FORCEINLINE ovrpMatrix4f ToOvrpMatrix(FMatrix Matrix)
{
ovrpMatrix4f Result;
Result.M[0][0] = Matrix.M[0][0];
Result.M[0][1] = Matrix.M[0][1];
Result.M[0][2] = Matrix.M[0][2];
Result.M[0][3] = Matrix.M[0][3];
Result.M[1][0] = Matrix.M[1][0];
Result.M[1][1] = Matrix.M[1][1];
Result.M[1][2] = Matrix.M[1][2];
Result.M[1][3] = Matrix.M[1][3];
Result.M[2][0] = Matrix.M[2][0];
Result.M[2][1] = Matrix.M[2][1];
Result.M[2][2] = Matrix.M[2][2];
Result.M[2][3] = Matrix.M[2][3];
Result.M[3][0] = Matrix.M[3][0];
Result.M[3][1] = Matrix.M[3][1];
Result.M[3][2] = Matrix.M[3][2];
Result.M[3][3] = Matrix.M[3][3];
return Result;
}
/** Helper that converts ovrTrackedDeviceType to EOculusXRTrackedDeviceType */
FORCEINLINE EOculusXRTrackedDeviceType ToEOculusXRTrackedDeviceType(ovrpNode Source)
{
EOculusXRTrackedDeviceType Destination = EOculusXRTrackedDeviceType::All; // Best attempt at initialization
switch (Source)
{
case ovrpNode_None:
Destination = EOculusXRTrackedDeviceType::None;
break;
case ovrpNode_Head:
Destination = EOculusXRTrackedDeviceType::HMD;
break;
case ovrpNode_HandLeft:
Destination = EOculusXRTrackedDeviceType::LTouch;
break;
case ovrpNode_HandRight:
Destination = EOculusXRTrackedDeviceType::RTouch;
break;
case ovrpNode_DeviceObjectZero:
Destination = EOculusXRTrackedDeviceType::DeviceObjectZero;
break;
default:
break;
}
return Destination;
}
/** Helper that converts EOculusXRTrackedDeviceType to ovrTrackedDeviceType */
FORCEINLINE ovrpNode ToOvrpNode(EOculusXRTrackedDeviceType Source)
{
ovrpNode Destination = ovrpNode_None; // Best attempt at initialization
switch (Source)
{
case EOculusXRTrackedDeviceType::None:
Destination = ovrpNode_None;
break;
case EOculusXRTrackedDeviceType::HMD:
Destination = ovrpNode_Head;
break;
case EOculusXRTrackedDeviceType::LTouch:
Destination = ovrpNode_HandLeft;
break;
case EOculusXRTrackedDeviceType::RTouch:
Destination = ovrpNode_HandRight;
break;
case EOculusXRTrackedDeviceType::DeviceObjectZero:
Destination = ovrpNode_DeviceObjectZero;
break;
default:
break;
}
return Destination;
}
FORCEINLINE int32 ToExternalDeviceId(const ovrpNode Source)
{
int32 ExternalDeviceId = INDEX_NONE;
switch (Source)
{
case ovrpNode_Head:
// required to be zero (see IXRTrackingSystem::HMDDeviceId)
ExternalDeviceId = 0;
break;
case ovrpNode_None:
case ovrpNode_Count:
case ovrpNode_EnumSize:
// ExternalDeviceId = INDEX_NONE;
break;
default:
// add one, in case the enum value is zero (conflicting with the HMD)
ExternalDeviceId = 1 + (int32)Source;
break;
}
return ExternalDeviceId;
}
FORCEINLINE ovrpNode ToOvrpNode(const int32 ExternalDeviceId)
{
ovrpNode Destination = ovrpNode_None;
switch (ExternalDeviceId)
{
case 0:
// zero implies HMD (see ToExternalDeviceId/IXRTrackingSystem::HMDDeviceId)
Destination = ovrpNode_Head;
break;
case -1:
// Destination = ovrpNode_None;
break;
default:
// we added one to avoid collision with the HMD's ID (see ToExternalDeviceId)
Destination = ovrpNode(ExternalDeviceId - 1);
break;
}
return Destination;
}
bool ConvertPose_Internal(const FPose& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
FORCEINLINE ovrpInsightPassthroughColorMapType ToOVRPColorMapType(EOculusXRColorMapType InColorMapType)
{
switch (InColorMapType)
{
case ColorMapType_GrayscaleToColor:
return ovrpInsightPassthroughColorMapType_MonoToRgba;
case ColorMapType_Grayscale:
return ovrpInsightPassthroughColorMapType_MonoToMono;
case ColorMapType_ColorAdjustment:
return ovrpInsightPassthroughColorMapType_BrightnessContrastSaturation;
case ColorMapType_ColorLut:
return ovrpInsightPassthroughColorMapType_ColorLut;
case ColorMapType_ColorLut_Interpolated:
return ovrpInsightPassthroughColorMapType_InterpolatedColorLut;
default:
return ovrpInsightPassthroughColorMapType_None;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
/** Check currently executing from Game thread */
OCULUSXRHMD_API bool InGameThread();
FORCEINLINE void CheckInGameThread()
{
#if DO_CHECK
check(InGameThread());
#endif
}
/** Check currently executing from Render thread */
OCULUSXRHMD_API bool InRenderThread();
FORCEINLINE void CheckInRenderThread()
{
#if DO_CHECK
check(InRenderThread());
#endif
}
/** Check currently executing from RHI thread */
OCULUSXRHMD_API bool InRHIThread();
FORCEINLINE void CheckInRHIThread()
{
#if DO_CHECK
check(InRHIThread());
#endif
}
FORCEINLINE bool GetUnitScaleFactorFromSettings(UWorld* World, float& outWorldToMeters)
{
if (IsValid(World))
{
const auto* WorldSettings = World->GetWorldSettings();
if (IsValid(WorldSettings))
{
outWorldToMeters = WorldSettings->WorldToMeters;
return true;
}
}
return false;
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,64 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#if OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// D3D11
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
#include "ID3D11DynamicRHI.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
//-------------------------------------------------------------------------------------------------
// D3D12
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
#define GetD3D11CubeFace GetD3D12CubeFace
#define VerifyD3D11Result VerifyD3D12Result
#define GetD3D11TextureFromRHITexture GetD3D12TextureFromRHITexture
#define FRingAllocation FRingAllocation_D3D12
#define GetRenderTargetFormat GetRenderTargetFormat_D3D12
#define ED3D11ShaderOffsetBuffer ED3D12ShaderOffsetBuffer
#define FindShaderResourceDXGIFormat FindShaderResourceDXGIFormat_D3D12
#define FindUnorderedAccessDXGIFormat FindUnorderedAccessDXGIFormat_D3D12
#define FindDepthStencilDXGIFormat FindDepthStencilDXGIFormat_D3D12
#define HasStencilBits HasStencilBits_D3D12
#define FVector4VertexDeclaration FVector4VertexDeclaration_D3D12
#define GLOBAL_CONSTANT_BUFFER_INDEX GLOBAL_CONSTANT_BUFFER_INDEX_D3D12
#define MAX_CONSTANT_BUFFER_SLOTS MAX_CONSTANT_BUFFER_SLOTS_D3D12
#define FD3DGPUProfiler FD3D12GPUProfiler
#define FRangeAllocator FRangeAllocator_D3D12
#include "ID3D12DynamicRHI.h"
#undef GetD3D11CubeFace
#undef VerifyD3D11Result
#undef GetD3D11TextureFromRHITexture
#undef FRingAllocation
#undef GetRenderTargetFormat
#undef ED3D11ShaderOffsetBuffer
#undef FindShaderResourceDXGIFormat
#undef FindUnorderedAccessDXGIFormat
#undef FindDepthStencilDXGIFormat
#undef HasStencilBits
#undef FVector4VertexDeclaration
#undef GLOBAL_CONSTANT_BUFFER_INDEX
#undef MAX_CONSTANT_BUFFER_SLOTS
#undef FD3DGPUProfiler
#undef FRangeAllocator
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
//-------------------------------------------------------------------------------------------------
// Vulkan
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#include "IVulkanDynamicRHI.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,299 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDRuntimeSettings.h"
//////////////////////////////////////////////////////////////////////////
// UOculusXRHMDRuntimeSettings
#include "OculusXRHMD_Settings.h"
UOculusXRHMDRuntimeSettings::UOculusXRHMDRuntimeSettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bAutoEnabled(false)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
// FSettings is the sole source of truth for Oculus default settings
OculusXRHMD::FSettings DefaultSettings;
SystemSplashBackground = DefaultSettings.SystemSplashBackground;
bSupportsDash = DefaultSettings.Flags.bSupportsDash;
bCompositesDepth = DefaultSettings.Flags.bCompositeDepth;
bHQDistortion = DefaultSettings.Flags.bHQDistortion;
SuggestedCpuPerfLevel = DefaultSettings.SuggestedCpuPerfLevel;
SuggestedGpuPerfLevel = DefaultSettings.SuggestedGpuPerfLevel;
FoveatedRenderingMethod = DefaultSettings.FoveatedRenderingMethod;
FoveatedRenderingLevel = DefaultSettings.FoveatedRenderingLevel;
bDynamicFoveatedRendering = DefaultSettings.bDynamicFoveatedRendering;
bSupportEyeTrackedFoveatedRendering = DefaultSettings.bSupportEyeTrackedFoveatedRendering;
PixelDensityMin = DefaultSettings.PixelDensityMin;
PixelDensityMax = DefaultSettings.PixelDensityMax;
bFocusAware = DefaultSettings.Flags.bFocusAware;
bDynamicResolution = DefaultSettings.Flags.bPixelDensityAdaptive;
XrApi = DefaultSettings.XrApi;
ColorSpace = DefaultSettings.ColorSpace;
ControllerPoseAlignment = DefaultSettings.ControllerPoseAlignment;
bRequiresSystemKeyboard = DefaultSettings.Flags.bRequiresSystemKeyboard;
HandTrackingSupport = DefaultSettings.HandTrackingSupport;
HandTrackingFrequency = DefaultSettings.HandTrackingFrequency;
HandTrackingVersion = DefaultSettings.HandTrackingVersion;
bInsightPassthroughEnabled = DefaultSettings.Flags.bInsightPassthroughEnabled;
bBodyTrackingEnabled = DefaultSettings.Flags.bBodyTrackingEnabled;
bEyeTrackingEnabled = DefaultSettings.Flags.bEyeTrackingEnabled;
bFaceTrackingEnabled = DefaultSettings.Flags.bFaceTrackingEnabled;
bSupportExperimentalFeatures = DefaultSettings.bSupportExperimentalFeatures;
bAnchorSupportEnabled = DefaultSettings.Flags.bAnchorSupportEnabled;
bAnchorSharingEnabled = DefaultSettings.Flags.bAnchorSharingEnabled;
bSceneSupportEnabled = DefaultSettings.Flags.bSceneSupportEnabled;
ProcessorFavor = DefaultSettings.ProcessorFavor;
bTileTurnOffEnabled = DefaultSettings.Flags.bTileTurnOffEnabled;
FaceTrackingDataSource.Empty(static_cast<int8>(EFaceTrackingDataSourceConfig::MAX));
FaceTrackingDataSource.Append(DefaultSettings.FaceTrackingDataSource);
// Default this to false, FSettings doesn't have a separate composite depth flag for mobile
bCompositeDepthMobile = false;
#else
// Some set of reasonable defaults, since blueprints are still available on non-Oculus platforms.
SystemSplashBackground = ESystemSplashBackgroundType::Black;
bSupportsDash = false;
bCompositesDepth = false;
bHQDistortion = false;
SuggestedCpuPerfLevel = EOculusXRProcessorPerformanceLevel::SustainedLow;
SuggestedGpuPerfLevel = EOculusXRProcessorPerformanceLevel::SustainedHigh;
FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
bDynamicFoveatedRendering = false;
bSupportEyeTrackedFoveatedRendering = false;
PixelDensityMin = 0.8f;
PixelDensityMax = 1.2f;
bDynamicResolution = false;
bCompositeDepthMobile = false;
bFocusAware = true;
XrApi = EOculusXRXrApi::OVRPluginOpenXR;
bLateLatching = false;
ColorSpace = EOculusXRColorSpace::P3;
ControllerPoseAlignment = EOculusXRControllerPoseAlignment::Default;
bRequiresSystemKeyboard = false;
HandTrackingSupport = EOculusXRHandTrackingSupport::ControllersOnly;
HandTrackingFrequency = EOculusXRHandTrackingFrequency::Low;
HandTrackingVersion = EOculusXRHandTrackingVersion::Default;
bInsightPassthroughEnabled = false;
bSupportExperimentalFeatures = false;
bBodyTrackingEnabled = false;
bEyeTrackingEnabled = false;
bFaceTrackingEnabled = false;
bAnchorSupportEnabled = false;
bAnchorSharingEnabled = false;
bSceneSupportEnabled = false;
ProcessorFavor = EProcessorFavor::FavorEqually;
bTileTurnOffEnabled = false;
#endif
LoadFromIni();
}
#if WITH_EDITOR
bool UOculusXRHMDRuntimeSettings::CanEditChange(const FProperty* InProperty) const
{
bool bIsEditable = Super::CanEditChange(InProperty);
if (bIsEditable && InProperty)
{
const FName PropertyName = InProperty->GetFName();
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, XrApi) && !FModuleManager::Get().IsModuleLoaded("OpenXRHMD"))
{
bIsEditable = false;
}
// Disable settings for marketplace release that are only compatible with the Oculus engine fork
#ifndef WITH_OCULUS_BRANCH
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod) || PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering) || PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicResolution))
{
bIsEditable = false;
}
#endif // WITH_OCULUS_BRANCH
}
return bIsEditable;
}
void UOculusXRHMDRuntimeSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property != nullptr)
{
// Automatically switch to Fixed Foveated Rendering when removing Eye Tracked Foveated rendering support
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering) && !bSupportEyeTrackedFoveatedRendering)
{
FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod)), GetDefaultConfigFilename());
}
// Automatically enable support for eye tracked foveated rendering when selecting the Eye Tracked Foveated Rendering method
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod) && FoveatedRenderingMethod == EOculusXRFoveatedRenderingMethod::EyeTrackedFoveatedRendering)
{
bSupportEyeTrackedFoveatedRendering = true;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering)), GetDefaultConfigFilename());
}
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, SupportedDevices))
{
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
{
// Get a list of all available devices
TArray<EOculusXRSupportedDevices> deviceList;
#define OCULUS_DEVICE_LOOP(device) deviceList.Add(device);
FOREACH_ENUM_EOCULUSXRSUPPORTEDDEVICES(OCULUS_DEVICE_LOOP);
#undef OCULUS_DEVICE_LOOP
// Add last device that isn't already in the list
for (int i = deviceList.Num() - 1; i >= 0; --i)
{
if (!SupportedDevices.Contains(deviceList[i]))
{
SupportedDevices.Last() = deviceList[i];
break;
}
// Just add another copy of the first device if nothing was available
SupportedDevices.Last() = deviceList[deviceList.Num() - 1];
}
}
}
}
}
#endif // WITH_EDITOR
void UOculusXRHMDRuntimeSettings::PostInitProperties()
{
Super::PostInitProperties();
RenameProperties();
const TCHAR* OculusSettings = TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings");
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD"))
{
XrApi = EOculusXRXrApi::OVRPluginOpenXR;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, XrApi)), GetDefaultConfigFilename());
}
}
void UOculusXRHMDRuntimeSettings::LoadFromIni()
{
const TCHAR* OculusSettings = TEXT("Oculus.Settings");
bool v;
float f;
FVector vec;
if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensityMax"), f, GEngineIni))
{
check(!FMath::IsNaN(f));
PixelDensityMax = f;
}
if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensityMin"), f, GEngineIni))
{
check(!FMath::IsNaN(f));
PixelDensityMin = f;
}
if (GConfig->GetBool(OculusSettings, TEXT("bHQDistortion"), v, GEngineIni))
{
bHQDistortion = v;
}
if (GConfig->GetBool(OculusSettings, TEXT("bCompositeDepth"), v, GEngineIni))
{
bCompositesDepth = v;
}
}
/** This essentially acts like redirects for plugin settings saved in the engine config.
Anything added here should check for the current setting in the config so that if the dev changes the setting manually, we don't overwrite it with the old setting.
Note: Do not use UpdateSinglePropertyInConfigFile() here, since that uses a temp config to save the single property,
it'll get overwritten when GConfig->RemoveKey() marks the main config as dirty and it gets saved again **/
void UOculusXRHMDRuntimeSettings::RenameProperties()
{
const TCHAR* OculusSettings = TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings");
bool v = false;
FString str;
// FFRLevel was renamed to FoveatedRenderingLevel
if (!GConfig->GetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingLevel), str, GetDefaultConfigFilename()) && GConfig->GetString(OculusSettings, TEXT("FFRLevel"), str, GetDefaultConfigFilename()))
{
if (str.Equals(TEXT("FFR_Off")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
}
else if (str.Equals(TEXT("FFR_Low")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Low;
}
else if (str.Equals(TEXT("FFR_Medium")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Medium;
}
else if (str.Equals(TEXT("FFR_High")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::High;
}
else if (str.Equals(TEXT("FFR_HighTop")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::HighTop;
}
// Use GetNameStringByValue() here because GetValueAsString() includes the type name as well
GConfig->SetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingLevel), *StaticEnum<EOculusXRFoveatedRenderingLevel>()->GetNameStringByValue((int64)FoveatedRenderingLevel), GetDefaultConfigFilename());
GConfig->RemoveKey(OculusSettings, TEXT("FFRLevel"), GetDefaultConfigFilename());
}
// FFRDynamic was renamed to bDynamicFoveatedRendering
if (!GConfig->GetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering), str, GetDefaultConfigFilename()) && GConfig->GetBool(OculusSettings, TEXT("FFRDynamic"), v, GetDefaultConfigFilename()))
{
bDynamicFoveatedRendering = v;
GConfig->SetBool(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering), bDynamicFoveatedRendering, GetDefaultConfigFilename());
GConfig->RemoveKey(OculusSettings, TEXT("FFRDynamic"), GetDefaultConfigFilename());
}
const FString Quest = TEXT("Quest");
#ifndef WITH_OCULUS_BRANCH
const TCHAR* AndroidSettings = TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings");
TArray<FString> PackageList;
const TCHAR* PackageForMobileKey = TEXT("+PackageForOculusMobile");
if (GConfig->GetArray(AndroidSettings, PackageForMobileKey, PackageList, GetDefaultConfigFilename()))
{
const FString Quest2 = TEXT("Quest2");
if (PackageList.Contains(Quest))
{
PackageList.Remove(Quest);
if (!PackageList.Contains(Quest2))
{
PackageList.Add(Quest2);
}
GConfig->SetArray(AndroidSettings, PackageForMobileKey, PackageList, GetDefaultConfigFilename());
}
}
#endif // WITH_OCULUS_BRANCH
TArray<FString> DeviceList;
const TCHAR* SupportedDevicesKey = *FString("+").Append(GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, SupportedDevices));
if (GConfig->GetArray(OculusSettings, SupportedDevicesKey, DeviceList, GetDefaultConfigFilename()))
{
const EOculusXRSupportedDevices LastSupportedDevice = EOculusXRSupportedDevices::Quest2;
const FString LastSupportedDeviceString = StaticEnum<EOculusXRSupportedDevices>()->GetNameStringByValue((int64)LastSupportedDevice);
if (DeviceList.Contains(Quest))
{
DeviceList.Remove(Quest);
if (!DeviceList.Contains(LastSupportedDeviceString))
{
DeviceList.Add(LastSupportedDeviceString);
}
GConfig->SetArray(OculusSettings, SupportedDevicesKey, DeviceList, GetDefaultConfigFilename());
// Reflect the config changes in the Project Settings UI
SupportedDevices.Remove((EOculusXRSupportedDevices)0); // Enums that don't exist just have a value of 0
if (!SupportedDevices.Contains(LastSupportedDevice))
{
SupportedDevices.Add(LastSupportedDevice);
}
}
}
}

View File

@@ -0,0 +1,90 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_ConsoleCommands.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "OculusXRSceneCaptureCubemap.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FConsoleCommands
//-------------------------------------------------------------------------------------------------
/// @cond DOXYGEN_WARNINGS
FConsoleCommands::FConsoleCommands(class FOculusXRHMD* InHMDPtr)
: UpdateOnRenderThreadCommand(TEXT("vr.oculus.bUpdateOnRenderThread"),
*NSLOCTEXT("OculusRift", "CCommandText_UpdateRT", "Oculus Rift specific extension.\nEnables or disables updating on the render thread.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::UpdateOnRenderThreadCommandHandler))
, PixelDensityMinCommand(TEXT("vr.oculus.PixelDensity.min"),
*NSLOCTEXT("OculusRift", "CCommandText_PixelDensityMin", "Oculus Rift specific extension.\nMinimum pixel density when adaptive pixel density is enabled").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::PixelDensityMinCommandHandler))
, PixelDensityMaxCommand(TEXT("vr.oculus.PixelDensity.max"),
*NSLOCTEXT("OculusRift", "CCommandText_PixelDensityMax", "Oculus Rift specific extension.\nMaximum pixel density when adaptive pixel density is enabled").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::PixelDensityMaxCommandHandler))
, HQBufferCommand(TEXT("vr.oculus.bHQBuffer"),
*NSLOCTEXT("OculusRift", "CCommandText_HQBuffer", "Oculus Rift specific extension.\nEnable or disable using floating point texture format for the eye layer.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::HQBufferCommandHandler))
, HQDistortionCommand(TEXT("vr.oculus.bHQDistortion"),
*NSLOCTEXT("OculusRift", "CCommandText_HQDistortion", "Oculus Rift specific extension.\nEnable or disable using multiple mipmap levels for the eye layer.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::HQDistortionCommandHandler))
, ShowGlobalMenuCommand(TEXT("vr.oculus.ShowGlobalMenu"),
*NSLOCTEXT("OculusRift", "CCommandText_GlobalMenu", "Oculus Rift specific extension.\nOpens the global menu.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowGlobalMenuCommandHandler))
, ShowQuitMenuCommand(TEXT("vr.oculus.ShowQuitMenu"),
*NSLOCTEXT("OculusRift", "CCommandText_QuitMenu", "Oculus Rift specific extension.\nOpens the quit menu.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowQuitMenuCommandHandler))
#if !UE_BUILD_SHIPPING
, StatsCommand(TEXT("vr.oculus.Debug.bShowStats"),
*NSLOCTEXT("OculusRift", "CCommandText_Stats", "Oculus Rift specific extension.\nEnable or disable rendering of stats.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::StatsCommandHandler))
, CubemapCommand(TEXT("vr.oculus.Debug.CaptureCubemap"),
*NSLOCTEXT("OculusRift", "CCommandText_Cubemap", "Oculus Rift specific extension.\nCaptures a cubemap for Oculus Home.\nOptional arguments (default is zero for all numeric arguments):\n xoff=<float> -- X axis offset from the origin\n yoff=<float> -- Y axis offset\n zoff=<float> -- Z axis offset\n yaw=<float> -- the direction to look into (roll and pitch is fixed to zero)\n mobile -- Generate a Mobile format cubemap\n (height of the captured cubemap will be 1024 instead of 2048 pixels)\n").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(&UOculusXRSceneCaptureCubemap::CaptureCubemapCommandHandler))
, ShowSettingsCommand(TEXT("vr.oculus.Debug.Show"),
*NSLOCTEXT("OculusRift", "CCommandText_Show", "Oculus Rift specific extension.\nShows the current value of various stereo rendering params.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowSettingsCommandHandler))
, IPDCommand(TEXT("vr.oculus.Debug.IPD"),
*NSLOCTEXT("OculusRift", "CCommandText_IPD", "Oculus Rift specific extension.\nShows or changes the current interpupillary distance in meters.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::IPDCommandHandler))
#endif // !UE_BUILD_SHIPPING
{
}
bool FConsoleCommands::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
const TCHAR* OrigCmd = Cmd;
FString AliasedCommand;
if (FParse::Command(&Cmd, TEXT("OVRGLOBALMENU")))
{
AliasedCommand = TEXT("vr.oculus.ShowGlobalMenu");
}
else if (FParse::Command(&Cmd, TEXT("OVRQUITMENU")))
{
AliasedCommand = TEXT("vr.oculus.ShowQuitMenu");
}
#if !UE_BUILD_SHIPPING
else if (FParse::Command(&Cmd, TEXT("vr.oculus.Debug.EnforceHeadTracking")))
{
AliasedCommand = TEXT("vr.HeadTracking.bEnforced");
}
#endif // !UE_BUILD_SHIPPING
if (!AliasedCommand.IsEmpty())
{
Ar.Logf(ELogVerbosity::Warning, TEXT("%s is deprecated. Use %s instead"), OrigCmd, *AliasedCommand);
return IConsoleManager::Get().ProcessUserConsoleInput(*AliasedCommand, Ar, InWorld);
}
return false;
}
/// @endcond
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,45 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "HAL/IConsoleManager.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FConsoleCommands
//-------------------------------------------------------------------------------------------------
class FConsoleCommands : private FSelfRegisteringExec
{
public:
FConsoleCommands(class FOculusXRHMD* InHMDPtr);
// FSelfRegisteringExec interface
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
private:
FAutoConsoleCommand UpdateOnRenderThreadCommand;
FAutoConsoleCommand PixelDensityMinCommand;
FAutoConsoleCommand PixelDensityMaxCommand;
FAutoConsoleCommand HQBufferCommand;
FAutoConsoleCommand HQDistortionCommand;
FAutoConsoleCommand ShowGlobalMenuCommand;
FAutoConsoleCommand ShowQuitMenuCommand;
#if !UE_BUILD_SHIPPING
// Debug console commands
FAutoConsoleCommand StatsCommand;
FAutoConsoleCommand CubemapCommand;
FAutoConsoleCommand ShowSettingsCommand;
FAutoConsoleCommand IPDCommand;
#endif // !UE_BUILD_SHIPPING
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,657 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "ScreenRendering.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "OculusShaders.h"
#include "CommonRenderResources.h"
#include "RHIStaticStates.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidEGL.h"
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#define VULKAN_CUBEMAP_POSITIVE_Y 2
#define VULKAN_CUBEMAP_NEGATIVE_Y 3
namespace OculusXRHMD
{
/**
* A pixel shader for rendering a textured screen element with mip maps and array slice.
*/
class FScreenPSMipLevelArray : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSMipLevelArray, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; }
FScreenPSMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel"));
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
FScreenPSMipLevelArray() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InMipLevelParameter);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
IMPLEMENT_SHADER_TYPE(, FScreenPSMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainMipLevel"), SF_Pixel);
/**
* A pixel shader for rendering a textured screen element with mip maps and array slice.
*/
class FScreenPSsRGBSourceMipLevelArray : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSsRGBSourceMipLevelArray, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; }
FScreenPSsRGBSourceMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel"));
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
FScreenPSsRGBSourceMipLevelArray() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InMipLevelParameter);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
IMPLEMENT_SHADER_TYPE(, FScreenPSsRGBSourceMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainsRGBSourceMipLevel"), SF_Pixel);
//-------------------------------------------------------------------------------------------------
// FCustomPresent
//-------------------------------------------------------------------------------------------------
FCustomPresent::FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool bInSupportsSRGB)
: OculusXRHMD(InOculusXRHMD)
, RenderAPI(InRenderAPI)
, DefaultPixelFormat(InDefaultPixelFormat)
, bSupportsSRGB(bInSupportsSRGB)
, bSupportsSubsampled(false)
, bIsStandaloneStereoDevice(false)
{
CheckInGameThread();
DefaultOvrpTextureFormat = GetOvrpTextureFormat(GetDefaultPixelFormat());
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_None;
#if PLATFORM_ANDROID
bIsStandaloneStereoDevice = FAndroidMisc::GetDeviceMake() == FString("Oculus");
#endif
// grab a pointer to the renderer module for displaying our mirror window
static const FName RendererModuleName("Renderer");
RendererModule = FModuleManager::GetModulePtr<IRendererModule>(RendererModuleName);
}
void FCustomPresent::ReleaseResources_RHIThread()
{
CheckInRHIThread();
if (MirrorTextureRHI.IsValid())
{
FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2();
MirrorTextureRHI = nullptr;
}
}
void FCustomPresent::Shutdown()
{
CheckInGameThread();
// OculusXRHMD is going away, but this object can live on until viewport is destroyed
ExecuteOnRenderThread([this]() {
ExecuteOnRHIThread([this]() {
OculusXRHMD = nullptr;
});
});
}
bool FCustomPresent::NeedsNativePresent()
{
return !bIsStandaloneStereoDevice;
}
bool FCustomPresent::Present(int32& SyncInterval)
{
CheckInRHIThread();
if (OculusXRHMD)
{
FGameFrame* Frame_RHIThread = OculusXRHMD->GetFrame_RHIThread();
if (Frame_RHIThread)
{
FinishRendering_RHIThread();
}
}
SyncInterval = 0; // VSync off
return NeedsNativePresent();
}
void FCustomPresent::UpdateMirrorTexture_RenderThread()
{
SCOPE_CYCLE_COUNTER(STAT_BeginRendering);
CheckInRenderThread();
const ESpectatorScreenMode MirrorWindowMode = OculusXRHMD->GetSpectatorScreenMode_RenderThread();
const FIntPoint MirrorWindowSize = OculusXRHMD->GetFrame_RenderThread()->WindowSize;
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
{
// Need to destroy mirror texture?
if (MirrorTextureRHI.IsValid())
{
const auto MirrorTextureSize = FIntPoint(MirrorTextureRHI->GetDesc().Extent.X, MirrorTextureRHI->GetDesc().Extent.Y);
if (MirrorWindowMode != ESpectatorScreenMode::Distorted || MirrorWindowSize != MirrorTextureSize)
{
ExecuteOnRHIThread([]() {
FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2();
});
MirrorTextureRHI = nullptr;
}
}
// Need to create mirror texture?
if (!MirrorTextureRHI.IsValid() && MirrorWindowMode == ESpectatorScreenMode::Distorted && MirrorWindowSize.X != 0 && MirrorWindowSize.Y != 0)
{
const int Width = MirrorWindowSize.X;
const int Height = MirrorWindowSize.Y;
ovrpTextureHandle TextureHandle;
ExecuteOnRHIThread([&]() {
FOculusXRHMDModule::GetPluginWrapper().SetupMirrorTexture2(GetOvrpDevice(), Height, Width, GetDefaultOvrpTextureFormat(), &TextureHandle);
});
UE_LOG(LogHMD, Log, TEXT("Allocated a new mirror texture (size %d x %d)"), Width, Height);
ETextureCreateFlags TexCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable;
MirrorTextureRHI = CreateTexture_RenderThread(Width, Height, GetDefaultPixelFormat(), FClearValueBinding::None, 1, 1, 1, RRT_Texture2D, TextureHandle, TexCreateFlags)->GetTexture2D();
}
}
}
void FCustomPresent::FinishRendering_RHIThread()
{
SCOPE_CYCLE_COUNTER(STAT_FinishRendering);
CheckInRHIThread();
#if STATS
if (OculusXRHMD->GetFrame_RHIThread()->ShowFlags.Rendering)
{
ovrpAppLatencyTimings AppLatencyTimings;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppLatencyTimings2(&AppLatencyTimings)))
{
SET_FLOAT_STAT(STAT_LatencyRender, AppLatencyTimings.LatencyRender * 1000.0f);
SET_FLOAT_STAT(STAT_LatencyTimewarp, AppLatencyTimings.LatencyTimewarp * 1000.0f);
SET_FLOAT_STAT(STAT_LatencyPostPresent, AppLatencyTimings.LatencyPostPresent * 1000.0f);
SET_FLOAT_STAT(STAT_ErrorRender, AppLatencyTimings.ErrorRender * 1000.0f);
SET_FLOAT_STAT(STAT_ErrorTimewarp, AppLatencyTimings.ErrorTimewarp * 1000.0f);
}
}
#endif
OculusXRHMD->FinishRHIFrame_RHIThread();
#if PLATFORM_ANDROID
float GPUFrameTime = 0.0f;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetGPUFrameTime(&GPUFrameTime)))
{
SubmitGPUFrameTime(GPUFrameTime);
}
#endif
}
EPixelFormat FCustomPresent::GetPixelFormat(EPixelFormat Format) const
{
switch (Format)
{
// case PF_B8G8R8A8:
case PF_FloatRGBA:
case PF_FloatR11G11B10:
// case PF_R8G8B8A8:
case PF_G16:
case PF_R16F:
case PF_R32_FLOAT:
case PF_ShadowDepth:
case PF_D24:
return Format;
}
return GetDefaultPixelFormat();
}
EPixelFormat FCustomPresent::GetPixelFormat(ovrpTextureFormat Format) const
{
switch (Format)
{
// case ovrpTextureFormat_R8G8B8A8_sRGB:
// case ovrpTextureFormat_R8G8B8A8:
// return PF_R8G8B8A8;
case ovrpTextureFormat_R16G16B16A16_FP:
return PF_FloatRGBA;
case ovrpTextureFormat_R11G11B10_FP:
return PF_FloatR11G11B10;
// case ovrpTextureFormat_B8G8R8A8_sRGB:
// case ovrpTextureFormat_B8G8R8A8:
// return PF_B8G8R8A8;
case ovrpTextureFormat_R16:
return PF_G16; // G stands for grey here, not green, and is actually R16 in RHI
case ovrpTextureFormat_R16_FP:
return PF_R16F;
case ovrpTextureFormat_R32_FP:
return PF_R32_FLOAT;
case ovrpTextureFormat_D16:
return PF_ShadowDepth; // ShadowDepth maps to D16 in Vulkan
case ovrpTextureFormat_D24_S8:
return PF_D24;
}
return GetDefaultPixelFormat();
}
ovrpTextureFormat FCustomPresent::GetOvrpTextureFormat(EPixelFormat Format, bool usesRGB) const
{
switch (GetPixelFormat(Format))
{
case PF_B8G8R8A8:
return bSupportsSRGB && usesRGB ? ovrpTextureFormat_B8G8R8A8_sRGB : ovrpTextureFormat_B8G8R8A8;
case PF_FloatRGBA:
return ovrpTextureFormat_R16G16B16A16_FP;
case PF_FloatR11G11B10:
return ovrpTextureFormat_R11G11B10_FP;
case PF_R8G8B8A8:
return bSupportsSRGB && usesRGB ? ovrpTextureFormat_R8G8B8A8_sRGB : ovrpTextureFormat_R8G8B8A8;
case PF_G16:
return ovrpTextureFormat_R16;
case PF_R16F:
return ovrpTextureFormat_R16_FP;
case PF_R32_FLOAT:
return ovrpTextureFormat_R32_FP;
case PF_ShadowDepth:
return ovrpTextureFormat_D16;
case PF_D24:
return ovrpTextureFormat_D24_S8;
}
return ovrpTextureFormat_None;
}
bool FCustomPresent::IsSRGB(ovrpTextureFormat InFormat)
{
switch (InFormat)
{
case ovrpTextureFormat_B8G8R8A8_sRGB:
case ovrpTextureFormat_R8G8B8A8_sRGB:
return true;
}
return false;
}
int FCustomPresent::GetSystemRecommendedMSAALevel() const
{
int SystemRecommendedMSAALevel = 1;
FOculusXRHMDModule::GetPluginWrapper().GetSystemRecommendedMSAALevel2(&SystemRecommendedMSAALevel);
return SystemRecommendedMSAALevel;
}
FXRSwapChainPtr FCustomPresent::CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName)
{
TArray<FTextureRHIRef> RHITextureSwapChain = CreateSwapChainTextures_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures, InTexCreateFlags, DebugName);
FTextureRHIRef RHITexture = GDynamicRHI->RHICreateAliasedTexture(RHITextureSwapChain[0]);
return CreateXRSwapChain(MoveTemp(RHITextureSwapChain), RHITexture);
}
TArray<FTextureRHIRef> FCustomPresent::CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName)
{
CheckInRenderThread();
TArray<FTextureRHIRef> RHITextureSwapChain;
{
for (int32 TextureIndex = 0; TextureIndex < InTextures.Num(); ++TextureIndex)
{
FTextureRHIRef TexRef = CreateTexture_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures[TextureIndex], InTexCreateFlags);
FString TexName = FString::Printf(TEXT("%s (%d/%d)"), DebugName, TextureIndex, InTextures.Num());
TexRef->SetName(*TexName);
RHIBindDebugLabelName(TexRef, *TexName);
RHITextureSwapChain.Add(TexRef);
}
}
return RHITextureSwapChain;
}
void FCustomPresent::CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture,
FIntRect DstRect, FIntRect SrcRect, bool bAlphaPremultiply, bool bNoAlphaWrite, bool bInvertY, bool sRGBSource, bool bInvertAlpha) const
{
CheckInRenderThread();
FIntPoint DstSize;
FIntPoint SrcSize;
if (DstTexture->GetDesc().IsTexture2D() && SrcTexture->GetDesc().IsTexture2D())
{
DstSize = FIntPoint(DstTexture->GetSizeX(), DstTexture->GetSizeY());
SrcSize = FIntPoint(SrcTexture->GetSizeX(), SrcTexture->GetSizeY());
}
else if (DstTexture->GetDesc().IsTextureCube() && SrcTexture->GetDesc().IsTextureCube())
{
DstSize = FIntPoint(DstTexture->GetSize(), DstTexture->GetSize());
SrcSize = FIntPoint(SrcTexture->GetSize(), SrcTexture->GetSize());
}
else
{
return;
}
if (DstRect.IsEmpty())
{
DstRect = FIntRect(FIntPoint::ZeroValue, DstSize);
}
if (SrcRect.IsEmpty())
{
SrcRect = FIntRect(FIntPoint::ZeroValue, SrcSize);
}
const uint32 ViewportWidth = DstRect.Width();
const uint32 ViewportHeight = DstRect.Height();
const FIntPoint TargetSize(ViewportWidth, ViewportHeight);
float U = SrcRect.Min.X / (float)SrcSize.X;
float V = SrcRect.Min.Y / (float)SrcSize.Y;
float USize = SrcRect.Width() / (float)SrcSize.X;
float VSize = SrcRect.Height() / (float)SrcSize.Y;
#if PLATFORM_ANDROID // on android, top-left isn't 0/0 but 1/0.
if (bInvertY)
{
V = 1.0f - V;
VSize = -VSize;
}
#endif
FRHITexture* SrcTextureRHI = SrcTexture;
RHICmdList.Transition(FRHITransitionInfo(SrcTextureRHI, ERHIAccess::Unknown, ERHIAccess::SRVGraphics));
FGraphicsPipelineStateInitializer GraphicsPSOInit;
if (bInvertAlpha)
{
// write RGBA, RGB = src.rgb * 1 + dst.rgb * 0, A = src.a * 0 + dst.a * (1 - src.a)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
}
else if (bAlphaPremultiply)
{
if (bNoAlphaWrite)
{
// for quads, write RGB, RGB = src.rgb * 1 + dst.rgb * 0
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
else
{
// for quads, write RGBA, RGB = src.rgb * src.a + dst.rgb * 0, A = src.a + dst.a * 0
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
}
else
{
if (bNoAlphaWrite)
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB>::GetRHI();
}
else
{
// for mirror window, write RGBA, RGB = src.rgb * src.a + dst.rgb * (1 - src.a), A = src.a * 1 + dst.a * (1 - src a)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
}
}
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const auto FeatureLevel = OculusXRHMD->GetSettings_RenderThread()->CurrentFeatureLevel;
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
if (DstTexture->GetDesc().IsTexture2D())
{
sRGBSource &= EnumHasAnyFlags(SrcTexture->GetFlags(), TexCreate_SRGB);
// Need to copy over mip maps on Android since they are not generated like they are on PC
#if PLATFORM_ANDROID
uint32 NumMips = SrcTexture->GetNumMips();
#else
uint32 NumMips = 1;
#endif
const bool bUseTexArrayShader = SrcTexture->GetDesc().IsTextureArray() && DstTexture->GetDesc().IsTextureArray();
const int32 SliceCount = bUseTexArrayShader ? FMath::Min(SrcTexture->GetDesc().ArraySize, DstTexture->GetDesc().ArraySize) : 1;
for (uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store);
RPInfo.ColorRenderTargets[0].MipIndex = MipIndex;
for (int32 SliceIndex = 0; SliceIndex < SliceCount; ++SliceIndex)
{
RPInfo.ColorRenderTargets[0].ArraySlice = SliceIndex;
RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTexture"));
{
const uint32 MipViewportWidth = ViewportWidth >> MipIndex;
const uint32 MipViewportHeight = ViewportHeight >> MipIndex;
const FIntPoint MipTargetSize(MipViewportWidth, MipViewportHeight);
if (bNoAlphaWrite || bInvertAlpha)
{
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f);
DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White);
}
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState<SF_Point>::GetRHI() : TStaticSamplerState<SF_Bilinear>::GetRHI();
if (!sRGBSource)
{
if (bUseTexArrayShader)
{
TShaderMapRef<FScreenPSMipLevelArray> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
}
else
{
TShaderMapRef<FScreenPSMipLevel> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
}
}
else
{
if (bUseTexArrayShader)
{
TShaderMapRef<FScreenPSsRGBSourceMipLevelArray> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
}
else
{
TShaderMapRef<FScreenPSsRGBSourceMipLevel> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
}
}
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Min.X + MipViewportWidth, DstRect.Min.Y + MipViewportHeight, 1.0f);
RendererModule->DrawRectangle(
RHICmdList,
0, 0, MipViewportWidth, MipViewportHeight,
U, V, USize, VSize,
MipTargetSize,
FIntPoint(1, 1),
VertexShader,
EDRF_Default);
}
RHICmdList.EndRenderPass();
}
}
}
else
{
for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++)
{
FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store);
// On Vulkan the positive and negative Y faces of the cubemap need to be flipped
if (RenderAPI == ovrpRenderAPI_Vulkan)
{
int NewFaceIndex = 0;
if (FaceIndex == VULKAN_CUBEMAP_POSITIVE_Y)
NewFaceIndex = VULKAN_CUBEMAP_NEGATIVE_Y;
else if (FaceIndex == VULKAN_CUBEMAP_NEGATIVE_Y)
NewFaceIndex = VULKAN_CUBEMAP_POSITIVE_Y;
else
NewFaceIndex = FaceIndex;
RPInfo.ColorRenderTargets[0].ArraySlice = NewFaceIndex;
}
else
{
RPInfo.ColorRenderTargets[0].ArraySlice = FaceIndex;
}
RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTextureFace"));
{
if (bNoAlphaWrite)
{
DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White);
}
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
TShaderMapRef<FOculusCubemapPS> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState<SF_Point>::GetRHI() : TStaticSamplerState<SF_Bilinear>::GetRHI();
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, FaceIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, FaceIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f);
RendererModule->DrawRectangle(
RHICmdList,
0, 0, ViewportWidth, ViewportHeight,
#if PLATFORM_ANDROID
U, V, USize, VSize,
#else
U, 1.0 - V, USize, -VSize,
#endif
TargetSize,
FIntPoint(1, 1),
VertexShader,
EDRF_Default);
}
RHICmdList.EndRenderPass();
}
}
}
void FCustomPresent::SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList)
{
CheckInRenderThread();
RHICmdList.SubmitCommandsHint();
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,117 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "OculusXRHMD_GameFrame.h"
#include "XRSwapChain.h"
#include "RHI.h"
#include "RendererInterface.h"
#include "IStereoLayers.h"
#include "XRRenderBridge.h"
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
DECLARE_STATS_GROUP(TEXT("OculusXRHMD"), STATGROUP_OculusXRHMD, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("BeginRendering"), STAT_BeginRendering, STATGROUP_OculusXRHMD);
DECLARE_CYCLE_STAT(TEXT("FinishRendering"), STAT_FinishRendering, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyRender"), STAT_LatencyRender, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyTimewarp"), STAT_LatencyTimewarp, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyPostPresent"), STAT_LatencyPostPresent, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("ErrorRender"), STAT_ErrorRender, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("ErrorTimewarp"), STAT_ErrorTimewarp, STATGROUP_OculusXRHMD);
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresent
//-------------------------------------------------------------------------------------------------
class FCustomPresent : public FXRRenderBridge
{
public:
FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool InSupportsSRGB);
// FXRRenderBridge/FRHICustomPresent
virtual bool NeedsNativePresent() override;
virtual bool Present(int32& SyncInterval) override;
virtual void FinishRendering_RHIThread();
ovrpRenderAPIType GetRenderAPI() const { return RenderAPI; }
virtual bool IsUsingCorrectDisplayAdapter() const { return true; }
void UpdateMirrorTexture_RenderThread();
void ReleaseResources_RHIThread();
void Shutdown();
FTexture2DRHIRef GetMirrorTexture() { return MirrorTextureRHI; }
virtual void* GetOvrpInstance() const { return nullptr; }
virtual void* GetOvrpPhysicalDevice() const { return nullptr; }
virtual void* GetOvrpDevice() const { return nullptr; }
virtual void* GetOvrpCommandQueue() const { return nullptr; }
EPixelFormat GetPixelFormat(EPixelFormat InFormat) const;
EPixelFormat GetPixelFormat(ovrpTextureFormat InFormat) const;
EPixelFormat GetDefaultPixelFormat() const { return DefaultPixelFormat; }
ovrpTextureFormat GetOvrpTextureFormat(EPixelFormat InFormat, bool usesRGB = true) const;
ovrpTextureFormat GetDefaultOvrpTextureFormat() const { return DefaultOvrpTextureFormat; }
ovrpTextureFormat GetDefaultDepthOvrpTextureFormat() const { return DefaultDepthOvrpTextureFormat; }
static bool IsSRGB(ovrpTextureFormat InFormat);
virtual int GetSystemRecommendedMSAALevel() const;
virtual int GetLayerFlags() const { return 0; }
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags TexCreateFlags) = 0;
FXRSwapChainPtr CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName);
TArray<FTextureRHIRef> CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName);
void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture, FIntRect DstRect = FIntRect(), FIntRect SrcRect = FIntRect(), bool bAlphaPremultiply = false, bool bNoAlphaWrite = false, bool bInvertY = true, bool sRGBSource = false, bool bInvertAlpha = false) const;
void SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList);
virtual void SubmitGPUFrameTime(float GPUFrameTime) {}
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
virtual void UseFragmentDensityMapOverShadingRate_RHIThread(){};
#ifdef WITH_OCULUS_BRANCH
virtual void UpdateFoveationOffsets_RHIThread(bool bUseOffsets, FIntPoint Offsets[2]){};
#endif // WITH_OCULUS_BRANCH
bool SupportsSRGB()
{
return bSupportsSRGB;
}
bool SupportsSubsampled() { return bSupportsSubsampled; }
protected:
FOculusXRHMD* OculusXRHMD;
ovrpRenderAPIType RenderAPI;
EPixelFormat DefaultPixelFormat;
bool bSupportsSRGB;
bool bSupportsSubsampled;
ovrpTextureFormat DefaultOvrpTextureFormat;
ovrpTextureFormat DefaultDepthOvrpTextureFormat;
IRendererModule* RendererModule;
FTexture2DRHIRef MirrorTextureRHI;
bool bIsStandaloneStereoDevice;
};
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
FCustomPresent* CreateCustomPresent_D3D11(FOculusXRHMD* InOculusXRHMD);
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
FCustomPresent* CreateCustomPresent_D3D12(FOculusXRHMD* InOculusXRHMD);
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
FCustomPresent* CreateCustomPresent_Vulkan(FOculusXRHMD* InOculusXRHMD);
#endif
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,118 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
#include "OculusXRHMD.h"
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FD3D11CustomPresent
//-------------------------------------------------------------------------------------------------
class FD3D11CustomPresent : public FCustomPresent
{
public:
FD3D11CustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpDevice() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
};
FD3D11CustomPresent::FD3D11CustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_D3D11, PF_B8G8R8A8, true)
{
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case DXGI_FORMAT_R24G8_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case DXGI_FORMAT_R32G8X24_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
}
bool FD3D11CustomPresent::IsUsingCorrectDisplayAdapter() const
{
const void* luid;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&luid)) && luid)
{
TRefCountPtr<ID3D11Device> D3D11Device;
ExecuteOnRenderThread([&D3D11Device]() {
D3D11Device = (ID3D11Device*)RHIGetNativeDevice();
});
if (D3D11Device)
{
TRefCountPtr<IDXGIDevice> DXGIDevice;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(D3D11Device->QueryInterface(__uuidof(IDXGIDevice), (void**)DXGIDevice.GetInitReference())) && SUCCEEDED(DXGIDevice->GetAdapter(DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
return !FMemory::Memcmp(luid, &DXGIAdapterDesc.AdapterLuid, sizeof(LUID));
}
}
}
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FD3D11CustomPresent::GetOvrpDevice() const
{
return GetID3D11DynamicRHI()->RHIGetDevice();
}
FTextureRHIRef FD3D11CustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
switch (InResourceType)
{
case RRT_Texture2D:
return GetID3D11DynamicRHI()->RHICreateTexture2DFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
case RRT_Texture2DArray:
return GetID3D11DynamicRHI()->RHICreateTexture2DArrayFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
case RRT_TextureCube:
return GetID3D11DynamicRHI()->RHICreateTextureCubeFromResource(InFormat, InTexCreateFlags | TexCreate_TargetArraySlicesIndependently, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
default:
return nullptr;
}
}
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_D3D11(FOculusXRHMD* InOculusXRHMD)
{
return new FD3D11CustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11

View File

@@ -0,0 +1,114 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
#include "OculusXRHMD.h"
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresentD3D12
//-------------------------------------------------------------------------------------------------
class FD3D12CustomPresent : public FCustomPresent
{
public:
FD3D12CustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpDevice() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
};
FD3D12CustomPresent::FD3D12CustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_D3D12, PF_B8G8R8A8, true)
{
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case DXGI_FORMAT_R24G8_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case DXGI_FORMAT_R32G8X24_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
}
bool FD3D12CustomPresent::IsUsingCorrectDisplayAdapter() const
{
const void* luid;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&luid)) && luid)
{
TRefCountPtr<ID3D12Device> D3DDevice;
ExecuteOnRenderThread([&D3DDevice]() {
D3DDevice = (ID3D12Device*)RHIGetNativeDevice();
});
if (D3DDevice)
{
LUID AdapterLuid = D3DDevice->GetAdapterLuid();
return !FMemory::Memcmp(luid, &AdapterLuid, sizeof(LUID));
}
}
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FD3D12CustomPresent::GetOvrpDevice() const
{
return GetID3D12DynamicRHI()->RHIGetCommandQueue();
}
FTextureRHIRef FD3D12CustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
switch (InResourceType)
{
case RRT_Texture2D:
return DynamicRHI->RHICreateTexture2DFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
case RRT_Texture2DArray:
return DynamicRHI->RHICreateTexture2DArrayFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
case RRT_TextureCube:
return DynamicRHI->RHICreateTextureCubeFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
default:
return nullptr;
}
}
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_D3D12(FOculusXRHMD* InOculusXRHMD)
{
return new FD3D12CustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12

View File

@@ -0,0 +1,171 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#include "OculusXRHMD.h"
#include "IVulkanDynamicRHI.h"
#if PLATFORM_WINDOWS
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresentVulkan
//-------------------------------------------------------------------------------------------------
class FVulkanCustomPresent : public FCustomPresent
{
public:
FVulkanCustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpInstance() const override;
virtual void* GetOvrpPhysicalDevice() const override;
virtual void* GetOvrpDevice() const override;
virtual void* GetOvrpCommandQueue() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
virtual void UseFragmentDensityMapOverShadingRate_RHIThread() override;
#ifdef WITH_OCULUS_BRANCH
virtual void UpdateFoveationOffsets_RHIThread(bool bUseTileOffsets, FIntPoint TileOffsets[2]) override;
#endif // WITH_OCULUS_BRANCH
};
FVulkanCustomPresent::FVulkanCustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_Vulkan, PF_R8G8B8A8, true)
{
#if PLATFORM_ANDROID
if (GRHISupportsRHIThread && GIsThreadedRendering && GUseRHIThread_InternalUseOnly)
{
SetRHIThreadEnabled(false, false);
}
#endif
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case VK_FORMAT_D24_UNORM_S8_UINT:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
bSupportsSubsampled = GetIVulkanDynamicRHI()->RHISupportsEXTFragmentDensityMap2();
}
bool FVulkanCustomPresent::IsUsingCorrectDisplayAdapter() const
{
#if PLATFORM_WINDOWS
const void* AdapterId = nullptr;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&AdapterId)) && AdapterId)
{
return GetIVulkanDynamicRHI()->RHIDoesAdapterMatchDevice(AdapterId);
}
#endif
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FVulkanCustomPresent::GetOvrpInstance() const
{
return GetIVulkanDynamicRHI()->RHIGetVkInstance();
}
void* FVulkanCustomPresent::GetOvrpPhysicalDevice() const
{
return GetIVulkanDynamicRHI()->RHIGetVkPhysicalDevice();
}
void* FVulkanCustomPresent::GetOvrpDevice() const
{
return GetIVulkanDynamicRHI()->RHIGetVkDevice();
}
void* FVulkanCustomPresent::GetOvrpCommandQueue() const
{
return GetIVulkanDynamicRHI()->RHIGetGraphicsVkQueue();
}
FTextureRHIRef FVulkanCustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
const VkImageSubresourceRange SubresourceRangeAll = { VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS };
if (EnumHasAnyFlags(InTexCreateFlags, TexCreate_RenderTargetable))
{
VulkanRHI->RHISetImageLayout((VkImage)InTexture, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, SubresourceRangeAll);
}
else if (EnumHasAnyFlags(InTexCreateFlags, TexCreate_Foveation))
{
VulkanRHI->RHISetImageLayout((VkImage)InTexture, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT, SubresourceRangeAll);
}
switch (InResourceType)
{
case RRT_Texture2D:
return VulkanRHI->RHICreateTexture2DFromResource(InFormat, InSizeX, InSizeY, InNumMips, InNumSamples, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
case RRT_Texture2DArray:
return VulkanRHI->RHICreateTexture2DArrayFromResource(InFormat, InSizeX, InSizeY, 2, InNumMips, InNumSamples, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
case RRT_TextureCube:
return VulkanRHI->RHICreateTextureCubeFromResource(InFormat, InSizeX, false, 1, InNumMips, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
default:
return nullptr;
}
}
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
void FVulkanCustomPresent::UseFragmentDensityMapOverShadingRate_RHIThread()
{
CheckInRHIThread();
SCOPED_NAMED_EVENT(UseFragmentDensityMapOverShadingRate_RHIThread, FColor::Red);
GRHIVariableRateShadingImageDataType = VRSImage_Fractional;
GRHIVariableRateShadingImageFormat = PF_R8G8;
}
#ifdef WITH_OCULUS_BRANCH
void FVulkanCustomPresent::UpdateFoveationOffsets_RHIThread(bool bUseOffsets, FIntPoint Offsets[2])
{
CheckInRHIThread();
SCOPED_NAMED_EVENT(UpdateFoveationOffsets_RHIThread, FColor::Red);
GetIVulkanDynamicRHI()->RHISetQcomFragmentDensityMapOffsets(bUseOffsets, Offsets);
}
#endif // WITH_OCULUS_BRANCH
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_Vulkan(FOculusXRHMD* InOculusXRHMD)
{
return new FVulkanCustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN

View File

@@ -0,0 +1,71 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_DeferredDeletionQueue.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMDPrivate.h"
#include "XRThreadUtils.h"
#include "OculusXRHMDModule.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDeferredDeletionQueue
//-------------------------------------------------------------------------------------------------
uint32 GOculusXRHMDLayerDeletionFrameNumber = 0;
const uint32 NUM_FRAMES_TO_WAIT_FOR_LAYER_DELETE = 3;
const uint32 NUM_FRAMES_TO_WAIT_FOR_OVRP_LAYER_DELETE = 7;
void FDeferredDeletionQueue::AddLayerToDeferredDeletionQueue(const FLayerPtr& ptr)
{
DeferredDeletionEntry Entry;
Entry.Layer = ptr;
Entry.FrameEnqueued = GOculusXRHMDLayerDeletionFrameNumber;
Entry.EntryType = DeferredDeletionEntry::DeferredDeletionEntryType::Layer;
DeferredDeletionArray.Add(Entry);
}
void FDeferredDeletionQueue::AddOVRPLayerToDeferredDeletionQueue(const uint32 layerID)
{
DeferredDeletionEntry Entry;
Entry.OvrpLayerId = layerID;
Entry.FrameEnqueued = GOculusXRHMDLayerDeletionFrameNumber;
Entry.EntryType = DeferredDeletionEntry::DeferredDeletionEntryType::OvrpLayer;
DeferredDeletionArray.Add(Entry);
}
void FDeferredDeletionQueue::HandleLayerDeferredDeletionQueue_RenderThread(bool bDeleteImmediately)
{
// Traverse list backwards so the swap switches to elements already tested
for (int32 Index = DeferredDeletionArray.Num() - 1; Index >= 0; --Index)
{
DeferredDeletionEntry* Entry = &DeferredDeletionArray[Index];
if (Entry->EntryType == DeferredDeletionEntry::DeferredDeletionEntryType::Layer)
{
if (bDeleteImmediately || GOculusXRHMDLayerDeletionFrameNumber > Entry->FrameEnqueued + NUM_FRAMES_TO_WAIT_FOR_LAYER_DELETE)
{
DeferredDeletionArray.RemoveAtSwap(Index, 1, false);
}
}
else if (Entry->EntryType == DeferredDeletionEntry::DeferredDeletionEntryType::OvrpLayer)
{
if (bDeleteImmediately || GOculusXRHMDLayerDeletionFrameNumber > Entry->FrameEnqueued + NUM_FRAMES_TO_WAIT_FOR_OVRP_LAYER_DELETE)
{
ExecuteOnRHIThread_DoNotWait([OvrpLayerId = Entry->OvrpLayerId]() {
UE_LOG(LogHMD, Warning, TEXT("Destroying layer %d"), OvrpLayerId);
FOculusXRHMDModule::GetPluginWrapper().DestroyLayer(OvrpLayerId);
});
DeferredDeletionArray.RemoveAtSwap(Index, 1, false);
}
}
}
// if the function is to be called multiple times, move this increment somewhere unique!
++GOculusXRHMDLayerDeletionFrameNumber;
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,45 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Layer.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDeferredDeletionQueue
//-------------------------------------------------------------------------------------------------
class FDeferredDeletionQueue
{
public:
void AddLayerToDeferredDeletionQueue(const FLayerPtr& ptr);
void AddOVRPLayerToDeferredDeletionQueue(const uint32 layerID);
void HandleLayerDeferredDeletionQueue_RenderThread(bool bDeleteImmediately = false);
private:
struct DeferredDeletionEntry
{
enum class DeferredDeletionEntryType
{
Layer,
OvrpLayer
};
FLayerPtr Layer;
uint32 OvrpLayerId;
uint32 FrameEnqueued;
DeferredDeletionEntryType EntryType;
};
TArray<DeferredDeletionEntry> DeferredDeletionArray;
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,90 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_DynamicResolutionState.h"
#include "LegacyScreenPercentageDriver.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "SceneView.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDynamicResolutionState implementation
//-------------------------------------------------------------------------------------------------
FDynamicResolutionState::FDynamicResolutionState(const OculusXRHMD::FSettingsPtr InSettings)
: Settings(InSettings)
, ResolutionFraction(-1.0f)
, ResolutionFractionUpperBound(-1.0f)
{
check(Settings.IsValid());
}
void FDynamicResolutionState::ResetHistory(){
// Empty - Oculus drives resolution fraction externally
};
bool FDynamicResolutionState::IsSupported() const
{
return true;
}
void FDynamicResolutionState::SetupMainViewFamily(class FSceneViewFamily& ViewFamily)
{
check(IsInGameThread());
check(ViewFamily.EngineShowFlags.ScreenPercentage == true);
if (IsEnabled())
{
// Compute desired resolution fraction range
float MinResolutionFraction = Settings->PixelDensityMin;
float MaxResolutionFraction = Settings->PixelDensityMax;
// Clamp resolution fraction to what the renderer can do.
MinResolutionFraction = FMath::Max(MinResolutionFraction, ISceneViewFamilyScreenPercentage::kMinResolutionFraction);
MaxResolutionFraction = FMath::Min(MaxResolutionFraction, ISceneViewFamilyScreenPercentage::kMaxResolutionFraction);
ResolutionFraction = FMath::Clamp(Settings->PixelDensity, MinResolutionFraction, MaxResolutionFraction);
ResolutionFractionUpperBound = MaxResolutionFraction;
ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(ViewFamily, ResolutionFraction, ResolutionFractionUpperBound));
}
}
DynamicRenderScaling::TMap<float> FDynamicResolutionState::GetResolutionFractionsApproximation() const
{
DynamicRenderScaling::TMap<float> ResolutionFractions;
ResolutionFractions.SetAll(1.0f);
ResolutionFractions[GDynamicPrimaryResolutionFraction] = ResolutionFraction;
return ResolutionFractions;
}
DynamicRenderScaling::TMap<float> FDynamicResolutionState::GetResolutionFractionsUpperBound() const
{
DynamicRenderScaling::TMap<float> ResolutionFractions;
ResolutionFractions.SetAll(1.0f);
ResolutionFractions[GDynamicPrimaryResolutionFraction] = ResolutionFractionUpperBound;
return ResolutionFractionUpperBound;
}
void FDynamicResolutionState::SetEnabled(bool bEnable)
{
check(IsInGameThread());
Settings->Flags.bPixelDensityAdaptive = bEnable;
}
bool FDynamicResolutionState::IsEnabled() const
{
check(IsInGameThread());
return Settings->Flags.bPixelDensityAdaptive;
}
void FDynamicResolutionState::ProcessEvent(EDynamicResolutionStateEvent Event){
// Empty - Oculus drives resolution fraction externally
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,43 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "DynamicResolutionState.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDynamicResolutionState
//-------------------------------------------------------------------------------------------------
class FDynamicResolutionState : public IDynamicResolutionState
{
public:
FDynamicResolutionState(const OculusXRHMD::FSettingsPtr InSettings);
// ISceneViewFamilyScreenPercentage
virtual void ResetHistory() override;
virtual bool IsSupported() const override;
virtual void SetupMainViewFamily(class FSceneViewFamily& ViewFamily) override;
protected:
virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsApproximation() const override;
virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsUpperBound() const override;
virtual void SetEnabled(bool bEnable) override;
virtual bool IsEnabled() const override;
virtual void ProcessEvent(EDynamicResolutionStateEvent Event) override;
private:
const OculusXRHMD::FSettingsPtr Settings;
float ResolutionFraction;
float ResolutionFractionUpperBound;
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,53 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_FoveatedRendering.h"
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
#include "RenderGraphBuilder.h"
#include "HeadMountedDisplayTypes.h" // For the LogHMD log category
FOculusXRFoveatedRenderingImageGenerator::FOculusXRFoveatedRenderingImageGenerator(const FXRSwapChainPtr& Swapchain)
: FoveationSwapchain(Swapchain)
{
GVRSImageManager.RegisterExternalImageGenerator(this);
}
FOculusXRFoveatedRenderingImageGenerator::~FOculusXRFoveatedRenderingImageGenerator()
{
GVRSImageManager.UnregisterExternalImageGenerator(this);
}
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType)
{
if (!FoveationSwapchain.IsValid())
{
return nullptr;
}
FTexture2DRHIRef SwapchainTexture = FoveationSwapchain->GetTexture2DArray() ? FoveationSwapchain->GetTexture2DArray() : FoveationSwapchain->GetTexture2D();
FIntPoint TexSize = SwapchainTexture->GetSizeXY();
// Only set texture and return true if we have a valid texture of compatible size
if (SwapchainTexture->IsValid() && TexSize.X > 0 && TexSize.Y > 0)
{
TRefCountPtr<IPooledRenderTarget> PooledRenderTarget = CreateRenderTarget(SwapchainTexture, *SwapchainTexture->GetName().ToString());
return GraphBuilder.RegisterExternalTexture(PooledRenderTarget, *SwapchainTexture->GetName().ToString(), ERDGTextureFlags::SkipTracking);
}
return nullptr;
}
void FOculusXRFoveatedRenderingImageGenerator::PrepareImages(FRDGBuilder& GraphBuilder, const FSceneViewFamily& ViewFamily, const FMinimalSceneTextures& SceneTextures)
{
return;
}
bool FOculusXRFoveatedRenderingImageGenerator::IsEnabledForView(const FSceneView& View) const
{
return View.StereoPass != EStereoscopicPass::eSSP_FULL;
}
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetDebugImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType)
{
return nullptr;
}
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)

View File

@@ -0,0 +1,31 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
#include "VariableRateShadingImageManager.h"
#include "XRSwapchain.h"
class FOculusXRFoveatedRenderingImageGenerator : public IVariableRateShadingImageGenerator
{
public:
FOculusXRFoveatedRenderingImageGenerator(const FXRSwapChainPtr& Swapchain);
virtual ~FOculusXRFoveatedRenderingImageGenerator() override;
// IVariableRateShadingImageGenerator interface
virtual FRDGTextureRef GetImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType) override;
virtual void PrepareImages(FRDGBuilder& GraphBuilder, const FSceneViewFamily& ViewFamily, const FMinimalSceneTextures& SceneTextures) override;
virtual bool IsEnabledForView(const FSceneView& View) const override;
virtual FRDGTextureRef GetDebugImage(FRDGBuilder& GraphBuilder, const FViewInfo& ViewInfo, FVariableRateShadingImageManager::EVRSImageType ImageType) override;
virtual FVariableRateShadingImageManager::EVRSSourceType GetType() const override
{
return FVariableRateShadingImageManager::EVRSSourceType::FixedFoveation;
}
private:
const FXRSwapChainPtr& FoveationSwapchain;
};
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)

View File

@@ -0,0 +1,33 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_GameFrame.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "GameFramework/WorldSettings.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FGameFrame
//-------------------------------------------------------------------------------------------------
FGameFrame::FGameFrame()
: FrameNumber(0), WorldToMetersScale(100.f), ShowFlags(ESFIM_All0), PlayerOrientation(FQuat::Identity), PlayerLocation(FVector::ZeroVector), FoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering), FoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel::Off), bDynamicFoveatedRendering(false)
{
Flags.Raw = 0;
Fov[0] = Fov[1] = SymmetricFov[0] = SymmetricFov[1] = ovrpFovf{ 0, 0, 0, 0 };
}
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> FGameFrame::Clone() const
{
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> NewFrame = MakeShareable(new FGameFrame(*this));
return NewFrame;
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,65 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "ShowFlags.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FGameFrame
//-------------------------------------------------------------------------------------------------
class FGameFrame : public TSharedFromThis<FGameFrame, ESPMode::ThreadSafe>
{
public:
uint32 FrameNumber; // current frame number. (StartGameFrame_GameThread)
float WorldToMetersScale; // World units (UU) to Meters scale. (OnStartGameFrame)
FIntPoint WindowSize; // actual window size (StartGameFrame_GameThread)
FEngineShowFlags ShowFlags; // (PreRenderViewFamily_RenderThread)
FQuat HeadOrientation; // (CalculateStereoViewOffset)
FQuat PlayerOrientation; // (CalculateStereoViewOffset)
FVector PlayerLocation; // (CalculateStereoViewOffset)
float NearClippingPlane; // (GetStereoProjectionMatrix)
FTransform TrackingToWorld; // (OnEndGameFrame)
FTransform LastTrackingToWorld; // (OnEndGameFrame)
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod; // OnStartGameFrame
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel; // OnStartGameFrame
bool bDynamicFoveatedRendering; // OnStartGameFrame
ovrpFovf Fov[ovrpEye_Count]; // UpdateStereoRenderingParams
ovrpFovf SymmetricFov[ovrpEye_Count]; // UpdateStereoRenderingParams, symmetric FOV if frame is using symmetricFOV.
union
{
struct
{
/** True, if splash is shown */
uint64 bSplashIsShown : 1;
/** True, if spectator screen is active */
uint64 bSpectatorScreenActive : 1;
/** True if the frame's positions have been updated on the render thread */
uint64 bRTLateUpdateDone : 1;
};
uint64 Raw;
} Flags;
public:
FGameFrame();
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> Clone() const;
};
typedef TSharedPtr<FGameFrame, ESPMode::ThreadSafe> FGameFramePtr;
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "ProceduralMeshComponent.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_CustomPresent.h"
#include "XRSwapChain.h"
#include "OculusXRPassthroughLayerShapes.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FOvrpLayer
//-------------------------------------------------------------------------------------------------
class FDeferredDeletionQueue;
class FOvrpLayer : public TSharedFromThis<FOvrpLayer, ESPMode::ThreadSafe>
{
public:
FOvrpLayer(uint32 InOvrpLayerId, FDeferredDeletionQueue* InDeferredDeletion);
~FOvrpLayer();
protected:
uint32 OvrpLayerId;
private:
FDeferredDeletionQueue* DeferredDeletion; // necessary for deferred deletion queue of the actual OvrpLayer
};
typedef TSharedPtr<FOvrpLayer, ESPMode::ThreadSafe> FOvrpLayerPtr;
//-------------------------------------------------------------------------------------------------
// FLayer
//-------------------------------------------------------------------------------------------------
class FLayer : public TSharedFromThis<FLayer, ESPMode::ThreadSafe>
{
public:
FLayer(uint32 InId);
FLayer(const FLayer& InLayer);
~FLayer();
uint32 GetId() const { return Id; }
int GetOvrpId() const { return OvrpLayerId; }
void SetDesc(const IStereoLayers::FLayerDesc& InDesc);
void SetDesc(const FSettings* Settings, const IStereoLayers::FLayerDesc& InDesc);
const IStereoLayers::FLayerDesc& GetDesc() const { return Desc; }
void SetEyeLayerDesc(const ovrpLayerDesc_EyeFov& InEyeLayerDesc);
const FXRSwapChainPtr& GetSwapChain() const { return SwapChain; }
const FXRSwapChainPtr& GetRightSwapChain() const { return RightSwapChain; }
const FXRSwapChainPtr& GetDepthSwapChain() const { return DepthSwapChain; }
const FXRSwapChainPtr& GetFoveationSwapChain() const { return FoveationSwapChain; }
const FXRSwapChainPtr& GetMotionVectorSwapChain() const { return MotionVectorSwapChain; }
const FXRSwapChainPtr& GetMotionVectorDepthSwapChain() const { return MotionVectorDepthSwapChain; }
void MarkTextureForUpdate() { bUpdateTexture = true; }
bool NeedsPokeAHole();
void HandlePokeAHoleComponent();
void BuildPokeAHoleMesh(TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector2D>& UV0);
bool NeedsPassthroughPokeAHole();
bool ShapeNeedsTextures(ovrpShape shape);
FTextureRHIRef GetTexture() { return Desc.Texture; }
TSharedPtr<FLayer, ESPMode::ThreadSafe> Clone() const;
bool CanReuseResources(const FLayer* InLayer) const;
bool Initialize_RenderThread(const FSettings* Settings, FCustomPresent* CustomPresent, FDeferredDeletionQueue* DeferredDeletion, FRHICommandListImmediate& RHICmdList, const FLayer* InLayer = nullptr);
void UpdateTexture_RenderThread(const FSettings* Settings, FCustomPresent* CustomPresent, FRHICommandListImmediate& RHICmdList);
void UpdatePassthrough_RenderThread(FCustomPresent* CustomPresent, FRHICommandListImmediate& RHICmdList, const FGameFrame* Frame);
const ovrpLayerSubmit* UpdateLayer_RHIThread(const FSettings* Settings, const FGameFrame* Frame, const int LayerIndex);
void IncrementSwapChainIndex_RHIThread(FCustomPresent* CustomPresent);
void ReleaseResources_RHIThread();
bool IsVisible() { return (Desc.Flags & IStereoLayers::LAYER_FLAG_HIDDEN) == 0; }
bool bNeedsTexSrgbCreate;
void AddPassthroughMesh_RenderThread(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, FMatrix Transformation, uint64_t& OutMeshHandle, uint64_t& OutInstanceHandle);
void UpdatePassthroughMeshTransform_RenderThread(uint64_t InstanceHandle, FMatrix Transformation);
void RemovePassthroughMesh_RenderThread(uint64_t MeshHandle, uint64_t InstanceHandle);
void DestroyLayer();
protected:
struct FPassthroughMesh
{
FPassthroughMesh(uint64_t MeshHandle, uint64_t InstanceHandle)
: MeshHandle(MeshHandle)
, InstanceHandle(InstanceHandle)
{
}
uint64_t MeshHandle;
uint64_t InstanceHandle;
};
typedef TSharedPtr<TMap<FString, FPassthroughMesh>, ESPMode::ThreadSafe> FUserDefinedGeometryMapPtr;
void UpdatePassthroughStyle_RenderThread(const FEdgeStyleParameters& EdgeStyleParameters);
struct FPassthroughPokeActor
{
FPassthroughPokeActor(){};
FPassthroughPokeActor(UProceduralMeshComponent* PokeAHoleComponentPtr, AActor* PokeAHoleActor)
: PokeAHoleComponentPtr(PokeAHoleComponentPtr)
, PokeAHoleActor(PokeAHoleActor){};
UProceduralMeshComponent* PokeAHoleComponentPtr;
AActor* PokeAHoleActor;
};
typedef TSharedPtr<TMap<FString, FPassthroughPokeActor>, ESPMode::ThreadSafe> FPassthroughPokeActorMapPtr;
bool BuildPassthroughPokeActor(FOculusPassthroughMeshRef PassthroughMesh, FPassthroughPokeActor& OutPassthroughPokeActor);
void UpdatePassthroughPokeActors_GameThread();
uint32 Id;
IStereoLayers::FLayerDesc Desc;
int OvrpLayerId;
ovrpLayerDescUnion OvrpLayerDesc;
ovrpLayerSubmitUnion OvrpLayerSubmit;
FOvrpLayerPtr OvrpLayer;
FXRSwapChainPtr SwapChain;
FXRSwapChainPtr DepthSwapChain;
FXRSwapChainPtr FoveationSwapChain;
FXRSwapChainPtr RightSwapChain;
FXRSwapChainPtr RightDepthSwapChain;
FXRSwapChainPtr MotionVectorSwapChain;
FXRSwapChainPtr MotionVectorDepthSwapChain;
FTextureRHIRef InvAlphaTexture;
bool bUpdateTexture;
bool bInvertY;
bool bHasDepth;
bool bSupportDepthComposite;
UProceduralMeshComponent* PokeAHoleComponentPtr;
AActor* PokeAHoleActor;
FUserDefinedGeometryMapPtr UserDefinedGeometryMap;
FPassthroughPokeActorMapPtr PassthroughPokeActorMap;
};
typedef TSharedPtr<FLayer, ESPMode::ThreadSafe> FLayerPtr;
//-------------------------------------------------------------------------------------------------
// FLayerPtr_CompareId
//-------------------------------------------------------------------------------------------------
struct FLayerPtr_CompareId
{
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
return A->GetId() < B->GetId();
}
};
//-------------------------------------------------------------------------------------------------
// FLayerPtr_ComparePriority
//-------------------------------------------------------------------------------------------------
struct FLayerPtr_ComparePriority
{
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
if (A->GetDesc().Priority < B->GetDesc().Priority)
return true;
if (A->GetDesc().Priority > B->GetDesc().Priority)
return false;
return A->GetId() < B->GetId();
}
};
struct FLayerPtr_CompareTotal
{
FORCEINLINE int32 GetLayerTypePriority(const FLayerPtr& A) const
{
// Draw FReconstructedLayer, PoleAHole layers (Android only), EyeFov layer, followed by other layers
const bool IsEyeFov = (A->GetId() == 0);
const bool IsPokeAHole = A->NeedsPokeAHole() || A->NeedsPassthroughPokeAHole();
bool IsUnderlay = false;
if (A->GetDesc().HasShape<FReconstructedLayer>())
{
const FReconstructedLayer& ReconstructedLayerProps = A->GetDesc().GetShape<FReconstructedLayer>();
IsUnderlay = (ReconstructedLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Underlay);
}
else if (A->GetDesc().HasShape<FUserDefinedLayer>())
{
const FUserDefinedLayer& UserDefinedLayerProps = A->GetDesc().GetShape<FUserDefinedLayer>();
IsUnderlay = (UserDefinedLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Underlay);
}
const int32 Priority = IsUnderlay ? -2 : IsPokeAHole ? -1
: IsEyeFov ? 0
: 1;
return Priority;
}
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
// First order layers by type
int32 PassA = GetLayerTypePriority(A);
int32 PassB = GetLayerTypePriority(B);
if (PassA != PassB)
return PassA < PassB;
// Draw non-FaceLocked layers first
const IStereoLayers::FLayerDesc& DescA = A->GetDesc();
const IStereoLayers::FLayerDesc& DescB = B->GetDesc();
bool bFaceLockedA = (DescA.PositionType == IStereoLayers::ELayerType::FaceLocked);
bool bFaceLockedB = (DescB.PositionType == IStereoLayers::ELayerType::FaceLocked);
if (bFaceLockedA != bFaceLockedB)
return !bFaceLockedA;
// Draw layers by ascending priority
if (DescA.Priority != DescB.Priority)
return DescA.Priority < DescB.Priority;
// Draw layers by ascending id
return A->GetId() < B->GetId();
}
};
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,135 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_Settings.h"
#include "Engine/Engine.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSettings
//-------------------------------------------------------------------------------------------------
FSettings::FSettings()
: BaseOffset(0, 0, 0)
, BaseOrientation(FQuat::Identity)
, PixelDensity(1.0f)
, PixelDensityMin(0.8f)
, PixelDensityMax(1.2f)
, SystemHeadset(ovrpSystemHeadset_None)
, SuggestedCpuPerfLevel(EOculusXRProcessorPerformanceLevel::SustainedLow)
, SuggestedGpuPerfLevel(EOculusXRProcessorPerformanceLevel::SustainedHigh)
, FoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering)
, FoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel::Off)
, bDynamicFoveatedRendering(true)
, bSupportEyeTrackedFoveatedRendering(false)
, SystemSplashBackground(ESystemSplashBackgroundType::Black)
, XrApi(EOculusXRXrApi::OVRPluginOpenXR)
, ColorSpace(EOculusXRColorSpace::P3)
, ControllerPoseAlignment(EOculusXRControllerPoseAlignment::Default)
, HandTrackingSupport(EOculusXRHandTrackingSupport::ControllersOnly)
, HandTrackingFrequency(EOculusXRHandTrackingFrequency::LOW)
, HandTrackingVersion(EOculusXRHandTrackingVersion::Default)
, ColorScale(ovrpVector4f{ 1, 1, 1, 1 })
, ColorOffset(ovrpVector4f{ 0, 0, 0, 0 })
, bApplyColorScaleAndOffsetToAllLayers(false)
, CurrentFeatureLevel(GMaxRHIFeatureLevel)
, bLateLatching(false)
, bSupportExperimentalFeatures(false)
, ProcessorFavor(EProcessorFavor::FavorEqually)
, BodyTrackingFidelity(EOculusXRHMDBodyTrackingFidelity::Low)
, BodyTrackingJointSet(EOculusXRHMDBodyJointSet::UpperBody)
{
Flags.Raw = 0;
Flags.bHMDEnabled = true;
Flags.bUpdateOnRT = true;
Flags.bHQBuffer = false;
Flags.bCompositeDepth = true;
#if PLATFORM_ANDROID
Flags.bsRGBEyeBuffer = true;
//oculus mobile is always-on stereo, no need for enableStereo codepaths
Flags.bStereoEnabled = true;
#else
Flags.bsRGBEyeBuffer = false;
Flags.bStereoEnabled = false;
#endif
CurrentFeatureLevel = GEngine ? GEngine->GetDefaultWorldFeatureLevel() : GMaxRHIFeatureLevel;
CurrentShaderPlatform = GShaderPlatformForFeatureLevel[CurrentFeatureLevel];
Flags.bSupportsDash = true;
Flags.bFocusAware = true;
Flags.bRequiresSystemKeyboard = false;
Flags.bInsightPassthroughEnabled = false;
Flags.bAnchorSupportEnabled = false;
Flags.bAnchorSharingEnabled = false;
Flags.bSceneSupportEnabled = false;
Flags.bBodyTrackingEnabled = false;
Flags.bEyeTrackingEnabled = false;
Flags.bFaceTrackingEnabled = false;
EyeRenderViewport[0] = EyeRenderViewport[1] = FIntRect(0, 0, 0, 0);
RenderTargetSize = FIntPoint(0, 0);
#ifdef WITH_OCULUS_BRANCH
Flags.bTileTurnOffEnabled = false;
#else
Flags.bTileTurnOffEnabled = true;
#endif
}
TSharedPtr<FSettings, ESPMode::ThreadSafe> FSettings::Clone() const
{
TSharedPtr<FSettings, ESPMode::ThreadSafe> NewSettings = MakeShareable(new FSettings(*this));
return NewSettings;
}
void FSettings::SetPixelDensity(float NewPixelDensity)
{
if (Flags.bPixelDensityAdaptive)
{
PixelDensity = FMath::Clamp(NewPixelDensity, PixelDensityMin, PixelDensityMax);
}
else
{
PixelDensity = FMath::Clamp(NewPixelDensity, ClampPixelDensityMin, ClampPixelDensityMax);
}
}
void FSettings::SetPixelDensitySmooth(float NewPixelDensity)
{
// Pixel Density changes need to be smooth both for artifacts with FFR/TTO (FFR/tile-turnoff is one frame late so shouldn't change too fast)
// but also so that if the developer uses the CVar and not the runtime (which is already smooth) there is no jump artifacts.
constexpr float MaxPerFrameIncrease = 0.010;
constexpr float MaxPerFrameDecrease = 0.045;
float NewClampedPixelDensity = FMath::Clamp(NewPixelDensity, PixelDensity - MaxPerFrameDecrease, PixelDensity + MaxPerFrameIncrease);
if (Flags.bPixelDensityAdaptive)
{
PixelDensity = FMath::Clamp(NewClampedPixelDensity, PixelDensityMin, PixelDensityMax);
}
else
{
PixelDensity = FMath::Clamp(NewClampedPixelDensity, ClampPixelDensityMin, ClampPixelDensityMax);
}
}
void FSettings::SetPixelDensityMin(float NewPixelDensityMin)
{
PixelDensityMin = FMath::Clamp(NewPixelDensityMin, ClampPixelDensityMin, ClampPixelDensityMax);
PixelDensityMax = FMath::Max(PixelDensityMin, PixelDensityMax);
SetPixelDensity(PixelDensity);
}
void FSettings::SetPixelDensityMax(float NewPixelDensityMax)
{
PixelDensityMax = FMath::Clamp(NewPixelDensityMax, ClampPixelDensityMin, ClampPixelDensityMax);
PixelDensityMin = FMath::Min(PixelDensityMin, PixelDensityMax);
SetPixelDensity(PixelDensity);
}
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,174 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
static const float ClampPixelDensityMin = 0.5f;
static const float ClampPixelDensityMax = 2.0f;
//-------------------------------------------------------------------------------------------------
// FSettings
//-------------------------------------------------------------------------------------------------
class FSettings : public TSharedFromThis<FSettings, ESPMode::ThreadSafe>
{
public:
union
{
struct
{
/** Whether stereo is currently on or off. */
uint64 bStereoEnabled : 1;
/** Whether or not switching to stereo is allowed */
uint64 bHMDEnabled : 1;
/** Turns on/off updating view's orientation/position on a RenderThread. When it is on,
latency should be significantly lower.
See 'HMD UPDATEONRT ON|OFF' console command.
*/
uint64 bUpdateOnRT : 1;
/** Enforces headtracking to work even in non-stereo mode (for debugging or screenshots).
See 'MOTION ENFORCE' console command. */
uint64 bHeadTrackingEnforced : 1;
/** Allocate an high quality OVR_FORMAT_R11G11B10_FLOAT buffer for Rift */
uint64 bHQBuffer : 1;
/** Rendering should be (could be) paused */
uint64 bPauseRendering : 1;
/** HQ Distortion */
uint64 bHQDistortion : 1;
/** Send the depth buffer to the compositor */
uint64 bCompositeDepth : 1;
/** Supports Dash in-game compositing */
uint64 bSupportsDash : 1;
#if !UE_BUILD_SHIPPING
/** Show status / statistics on screen. See 'hmd stats' cmd */
uint64 bShowStats : 1;
#endif
/** Dynamically update pixel density to maintain framerate */
uint64 bPixelDensityAdaptive : 1;
/** All future eye buffers will need to be created with TexSRGB_Create flag due to the current feature level (ES31) */
uint64 bsRGBEyeBuffer : 1;
/** Supports Focus Aware state on Quest */
uint64 bFocusAware : 1;
/** Requires the Oculus system keyboard */
uint64 bRequiresSystemKeyboard : 1;
/** Whether passthrough functionality can be used with the app */
uint64 bInsightPassthroughEnabled : 1;
/** Whether Anchors can be used with the app */
uint64 bAnchorSupportEnabled : 1;
/** Whether Anchor Sharing can be used with the app */
uint64 bAnchorSharingEnabled : 1;
/** Whether Scene can be used with the app */
uint64 bSceneSupportEnabled : 1;
/** Whether body tracking functionality can be used with the app */
uint64 bBodyTrackingEnabled : 1;
/** Whether eye tracking functionality can be used with the app */
uint64 bEyeTrackingEnabled : 1;
/** Whether face tracking functionality can be used with the app */
uint64 bFaceTrackingEnabled : 1;
/** Whether tile turn off can be used with the app */
uint64 bTileTurnOffEnabled : 1;
};
uint64 Raw;
} Flags;
/** HMD base values, specify forward orientation and zero pos offset */
FVector BaseOffset; // base position, in meters, relatively to the sensor //@todo hmd: clients need to stop using oculus space
FQuat BaseOrientation; // base orientation
/** Viewports for each eye, in render target texture coordinates */
FIntRect EyeRenderViewport[ovrpEye_Count];
/** Viewports for each eye, without DynamicResolution scaling applied */
FIntRect EyeUnscaledRenderViewport[ovrpEye_Count];
ovrpMatrix4f EyeProjectionMatrices[ovrpEye_Count]; // 0 - left, 1 - right same as Views
ovrpMatrix4f MonoProjectionMatrix;
FIntPoint RenderTargetSize;
float PixelDensity;
float PixelDensityMin;
float PixelDensityMax;
ovrpSystemHeadset SystemHeadset;
float VsyncToNextVsync;
EOculusXRProcessorPerformanceLevel SuggestedCpuPerfLevel;
EOculusXRProcessorPerformanceLevel SuggestedGpuPerfLevel;
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod;
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel;
bool bDynamicFoveatedRendering;
bool bSupportEyeTrackedFoveatedRendering;
ESystemSplashBackgroundType SystemSplashBackground;
EOculusXRXrApi XrApi;
EOculusXRColorSpace ColorSpace;
EOculusXRControllerPoseAlignment ControllerPoseAlignment;
EOculusXRHandTrackingSupport HandTrackingSupport;
EOculusXRHandTrackingFrequency HandTrackingFrequency;
EOculusXRHandTrackingVersion HandTrackingVersion;
ovrpVector4f ColorScale, ColorOffset;
bool bApplyColorScaleAndOffsetToAllLayers;
FStaticFeatureLevel CurrentFeatureLevel;
EShaderPlatform CurrentShaderPlatform;
bool bLateLatching;
bool bSupportExperimentalFeatures;
EProcessorFavor ProcessorFavor;
EOculusXRHMDBodyTrackingFidelity BodyTrackingFidelity;
EOculusXRHMDBodyJointSet BodyTrackingJointSet;
TSet<EFaceTrackingDataSourceConfig> FaceTrackingDataSource;
public:
FSettings();
virtual ~FSettings() {}
bool IsStereoEnabled() const { return Flags.bStereoEnabled && Flags.bHMDEnabled; }
void SetPixelDensity(float NewPixelDensity);
void SetPixelDensitySmooth(float NewPixelDensity);
void SetPixelDensityMin(float NewPixelDensityMin);
void SetPixelDensityMax(float NewPixelDensityMax);
TSharedPtr<FSettings, ESPMode::ThreadSafe> Clone() const;
};
typedef TSharedPtr<FSettings, ESPMode::ThreadSafe> FSettingsPtr;
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,121 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_SpectatorScreenController.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "TextureResource.h"
#include "Engine/TextureRenderTarget2D.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSpectatorScreenController
//-------------------------------------------------------------------------------------------------
FSpectatorScreenController::FSpectatorScreenController(FOculusXRHMD* InOculusXRHMD)
: FDefaultSpectatorScreenController(InOculusXRHMD)
, OculusXRHMD(InOculusXRHMD)
, SpectatorMode(EMRSpectatorScreenMode::Default)
, ForegroundRenderTexture(nullptr)
, BackgroundRenderTexture(nullptr)
{
}
void FSpectatorScreenController::RenderSpectatorScreen_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* BackBuffer, FTexture2DRHIRef RenderTexture, FVector2D WindowSize)
{
CheckInRenderThread();
if (OculusXRHMD->GetCustomPresent_Internal())
{
if (SpectatorMode == EMRSpectatorScreenMode::ExternalComposition)
{
auto ForegroundResource = ForegroundRenderTexture->GetRenderTargetResource();
auto BackgroundResource = BackgroundRenderTexture->GetRenderTargetResource();
if (ForegroundResource && BackgroundResource)
{
RenderSpectatorModeExternalComposition(
RHICmdList,
FTexture2DRHIRef(BackBuffer),
ForegroundResource->GetRenderTargetTexture(),
BackgroundResource->GetRenderTargetTexture());
return;
}
}
else if (SpectatorMode == EMRSpectatorScreenMode::DirectComposition)
{
auto BackgroundResource = BackgroundRenderTexture->GetRenderTargetResource();
if (BackgroundResource)
{
RenderSpectatorModeDirectComposition(
RHICmdList,
FTexture2DRHIRef(BackBuffer),
BackgroundRenderTexture->GetRenderTargetResource()->GetRenderTargetTexture());
return;
}
}
FDefaultSpectatorScreenController::RenderSpectatorScreen_RenderThread(RHICmdList, BackBuffer, RenderTexture, WindowSize);
}
}
void FSpectatorScreenController::RenderSpectatorModeUndistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FSettings* Settings = OculusXRHMD->GetSettings_RenderThread();
FIntRect DestRect(0, 0, TargetTexture->GetSizeX() / 2, TargetTexture->GetSizeY());
for (int i = 0; i < 2; ++i)
{
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, EyeTexture, Settings->EyeRenderViewport[i], TargetTexture, DestRect, false, true);
DestRect.Min.X += TargetTexture->GetSizeX() / 2;
DestRect.Max.X += TargetTexture->GetSizeX() / 2;
}
}
void FSpectatorScreenController::RenderSpectatorModeDistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FCustomPresent* CustomPresent = OculusXRHMD->GetCustomPresent_Internal();
FTexture2DRHIRef MirrorTexture = CustomPresent->GetMirrorTexture();
if (MirrorTexture)
{
FIntRect SrcRect(0, 0, MirrorTexture->GetSizeX(), MirrorTexture->GetSizeY());
FIntRect DestRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, MirrorTexture, SrcRect, TargetTexture, DestRect, false, true);
}
}
void FSpectatorScreenController::RenderSpectatorModeSingleEye(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FSettings* Settings = OculusXRHMD->GetSettings_RenderThread();
const FIntRect SrcRect = Settings->EyeRenderViewport[0];
const FIntRect DstRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, EyeTexture, SrcRect, TargetTexture, DstRect, false, true);
}
void FSpectatorScreenController::RenderSpectatorModeDirectComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef SrcTexture) const
{
CheckInRenderThread();
const FIntRect SrcRect(0, 0, SrcTexture->GetSizeX(), SrcTexture->GetSizeY());
const FIntRect DstRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, SrcTexture, SrcRect, TargetTexture, DstRect, false, true);
}
void FSpectatorScreenController::RenderSpectatorModeExternalComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef FrontTexture, const FTexture2DRHIRef BackTexture) const
{
CheckInRenderThread();
const FIntRect FrontSrcRect(0, 0, FrontTexture->GetSizeX(), FrontTexture->GetSizeY());
const FIntRect FrontDstRect(0, 0, TargetTexture->GetSizeX() / 2, TargetTexture->GetSizeY());
const FIntRect BackSrcRect(0, 0, BackTexture->GetSizeX(), BackTexture->GetSizeY());
const FIntRect BackDstRect(TargetTexture->GetSizeX() / 2, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, FrontTexture, FrontSrcRect, TargetTexture, FrontDstRect, false, true);
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, BackTexture, BackSrcRect, TargetTexture, BackDstRect, false, true);
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,53 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "DefaultSpectatorScreenController.h"
class UTextureRenderTarget2D;
namespace OculusXRHMD
{
// Oculus specific spectator screen modes that override the regular VR spectator screens
enum class EMRSpectatorScreenMode : uint8
{
Default,
ExternalComposition,
DirectComposition
};
//-------------------------------------------------------------------------------------------------
// FSpectatorScreenController
//-------------------------------------------------------------------------------------------------
class FSpectatorScreenController : public FDefaultSpectatorScreenController
{
public:
FSpectatorScreenController(class FOculusXRHMD* InOculusXRHMD);
void SetMRSpectatorScreenMode(EMRSpectatorScreenMode Mode) { SpectatorMode = Mode; }
void SetMRForeground(UTextureRenderTarget2D* Texture) { ForegroundRenderTexture = Texture; }
void SetMRBackground(UTextureRenderTarget2D* Texture) { BackgroundRenderTexture = Texture; }
virtual void RenderSpectatorScreen_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* BackBuffer, FTexture2DRHIRef RenderTarget, FVector2D WindowSize) override;
virtual void RenderSpectatorModeUndistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize) override;
virtual void RenderSpectatorModeDistorted(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize) override;
virtual void RenderSpectatorModeSingleEye(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, FTexture2DRHIRef EyeTexture, FTexture2DRHIRef OtherTexture, FVector2D WindowSize) override;
private:
FOculusXRHMD* OculusXRHMD;
EMRSpectatorScreenMode SpectatorMode;
UTextureRenderTarget2D* ForegroundRenderTexture;
UTextureRenderTarget2D* BackgroundRenderTexture;
void RenderSpectatorModeDirectComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef SrcTexture) const;
void RenderSpectatorModeExternalComposition(FRHICommandListImmediate& RHICmdList, FTexture2DRHIRef TargetTexture, const FTexture2DRHIRef FrontTexture, const FTexture2DRHIRef BackTexture) const;
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,666 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_Splash.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "RenderingThread.h"
#include "Misc/ScopeLock.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "StereoLayerFunctionLibrary.h"
#include "TextureResource.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidEGL.h"
#include "Android/AndroidApplication.h"
#include "OculusXRHMDTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSplash
//-------------------------------------------------------------------------------------------------
FSplash::FSplash(FOculusXRHMD* InOculusXRHMD)
: OculusXRHMD(InOculusXRHMD), CustomPresent(InOculusXRHMD->GetCustomPresent_Internal()), FramesOutstanding(0), NextLayerId(1), bInitialized(false), bIsShown(false), bNeedSplashUpdate(false), bShouldShowSplash(false), SystemDisplayInterval(1 / 90.0f)
{
// Create empty quad layer for UE layer
{
IStereoLayers::FLayerDesc LayerDesc;
LayerDesc.QuadSize = FVector2D(0.01f, 0.01f);
LayerDesc.Priority = 0;
LayerDesc.PositionType = IStereoLayers::TrackerLocked;
LayerDesc.Texture = nullptr;
UELayer = MakeShareable(new FLayer(NextLayerId++));
UELayer->SetDesc(LayerDesc);
}
}
FSplash::~FSplash()
{
// Make sure RenTicker is freed in Shutdown
check(!Ticker.IsValid())
}
void FSplash::Tick_RenderThread(float DeltaTime)
{
CheckInRenderThread();
if (FramesOutstanding > 0)
{
UE_LOG(LogHMD, VeryVerbose, TEXT("Splash skipping frame; too many frames outstanding"));
return;
}
const double TimeInSeconds = FPlatformTime::Seconds();
const double DeltaTimeInSeconds = TimeInSeconds - LastTimeInSeconds;
if (DeltaTimeInSeconds > 2.f * SystemDisplayInterval && Layers_RenderThread_DeltaRotation.Num() > 0)
{
FScopeLock ScopeLock(&RenderThreadLock);
for (TTuple<FLayerPtr, FQuat>& Info : Layers_RenderThread_DeltaRotation)
{
FLayerPtr Layer = Info.Key;
const FQuat& DeltaRotation = Info.Value;
check(Layer.IsValid());
check(!DeltaRotation.Equals(FQuat::Identity)); // Only layers with non-zero delta rotation should be in the DeltaRotation array.
IStereoLayers::FLayerDesc LayerDesc = Layer->GetDesc();
LayerDesc.Transform.SetRotation(LayerDesc.Transform.GetRotation() * DeltaRotation);
LayerDesc.Transform.NormalizeRotation();
Layer->SetDesc(LayerDesc);
}
LastTimeInSeconds = TimeInSeconds;
}
RenderFrame_RenderThread(FRHICommandListExecutor::GetImmediateCommandList());
}
void FSplash::LoadSettings()
{
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
check(HMDSettings);
ClearSplashes();
for (const FOculusXRSplashDesc& SplashDesc : HMDSettings->SplashDescs)
{
AddSplash(SplashDesc);
}
if (HMDSettings->bAutoEnabled)
{
if (!PreLoadLevelDelegate.IsValid())
{
PreLoadLevelDelegate = FCoreUObjectDelegates::PreLoadMap.AddSP(this, &FSplash::OnPreLoadMap);
}
if (!PostLoadLevelDelegate.IsValid())
{
PostLoadLevelDelegate = FCoreUObjectDelegates::PostLoadMapWithWorld.AddSP(this, &FSplash::OnPostLoadMap);
}
}
else
{
if (PreLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
PreLoadLevelDelegate.Reset();
}
if (PostLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
PostLoadLevelDelegate.Reset();
}
}
}
void FSplash::OnPreLoadMap(const FString&)
{
DoShow();
}
void FSplash::OnPostLoadMap(UWorld* LoadedWorld)
{
// Don't auto-hide splash if show loading screen is called explicitly
if (!bShouldShowSplash)
{
UE_LOG(LogHMD, Log, TEXT("FSplash::OnPostLoadMap Hide Auto Splash"));
HideLoadingScreen();
}
}
#if WITH_EDITOR
void FSplash::OnPieBegin(bool bIsSimulating)
{
LoadSettings();
}
#endif
void FSplash::Startup()
{
CheckInGameThread();
if (!bInitialized)
{
Settings = OculusXRHMD->CreateNewSettings();
Frame = OculusXRHMD->CreateNewGameFrame();
// keep units in meters rather than UU (because UU make not much sense).
Frame->WorldToMetersScale = 1.0f;
float SystemDisplayFrequency;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&SystemDisplayFrequency)))
{
SystemDisplayInterval = 1.0f / SystemDisplayFrequency;
}
LoadSettings();
OculusXRHMD->InitDevice();
#if WITH_EDITOR
PieBeginDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FSplash::OnPieBegin);
#else
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
check(HMDSettings);
if (HMDSettings->bAutoEnabled)
{
UE_LOG(LogHMD, Log, TEXT("FSplash::Startup Show Splash on Startup"));
DoShow();
}
#endif
OculusXRHMD->Settings_RenderThread = OculusXRHMD->Settings->Clone();
bInitialized = true;
}
}
void FSplash::StopTicker()
{
CheckInGameThread();
if (!bIsShown)
{
ExecuteOnRenderThread([this]() {
if (Ticker.IsValid())
{
Ticker->Unregister();
Ticker = nullptr;
}
});
UnloadTextures();
}
}
void FSplash::StartTicker()
{
CheckInGameThread();
if (!Ticker.IsValid())
{
Ticker = MakeShareable(new FTicker(this));
ExecuteOnRenderThread([this]() {
LastTimeInSeconds = FPlatformTime::Seconds();
Ticker->Register();
});
}
}
void FSplash::RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList)
{
CheckInRenderThread();
FScopeLock ScopeLock(&RenderThreadLock);
// RenderFrame
FSettingsPtr XSettings = Settings->Clone();
FGameFramePtr XFrame = Frame->Clone();
XFrame->FrameNumber = OculusXRHMD->NextFrameNumber;
XFrame->ShowFlags.Rendering = true;
TArray<FLayerPtr> XLayers = Layers_RenderThread_Input;
ensure(XLayers.Num() != 0);
ovrpResult Result;
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OculusXRHMD->WaitFrameNumber != XFrame->FrameNumber)
{
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame(XFrame->FrameNumber)))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u failed (%d)"), XFrame->FrameNumber, Result);
XFrame->ShowFlags.Rendering = false;
}
else
{
OculusXRHMD->WaitFrameNumber = XFrame->FrameNumber;
OculusXRHMD->NextFrameNumber = XFrame->FrameNumber + 1;
FPlatformAtomics::InterlockedIncrement(&FramesOutstanding);
}
}
else
{
XFrame->ShowFlags.Rendering = false;
}
if (XFrame->ShowFlags.Rendering)
{
if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().Update3(ovrpStep_Render, XFrame->FrameNumber, 0.0)))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().Update3 %u failed (%d)"), XFrame->FrameNumber, Result);
}
}
{
int32 LayerIndex = 0;
int32 LayerIndex_RenderThread = 0;
while (LayerIndex < XLayers.Num() && LayerIndex_RenderThread < Layers_RenderThread.Num())
{
uint32 LayerIdA = XLayers[LayerIndex]->GetId();
uint32 LayerIdB = Layers_RenderThread[LayerIndex_RenderThread]->GetId();
if (LayerIdA < LayerIdB)
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
}
else if (LayerIdA > LayerIdB)
{
OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
}
else
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList, Layers_RenderThread[LayerIndex_RenderThread++].Get());
}
}
while (LayerIndex < XLayers.Num())
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
}
while (LayerIndex_RenderThread < Layers_RenderThread.Num())
{
OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
}
}
Layers_RenderThread = XLayers;
for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
{
Layers_RenderThread[LayerIndex]->UpdateTexture_RenderThread(XSettings.Get(), CustomPresent, RHICmdList);
}
// This submit is required since splash happens before the game is rendering, so layers won't be submitted with game render commands
CustomPresent->SubmitGPUCommands_RenderThread(RHICmdList);
// RHIFrame
for (int32 LayerIndex = 0; LayerIndex < XLayers.Num(); LayerIndex++)
{
XLayers[LayerIndex] = XLayers[LayerIndex]->Clone();
}
ExecuteOnRHIThread_DoNotWait([this, XSettings, XFrame, XLayers]() {
ovrpResult ResultT;
if (XFrame->ShowFlags.Rendering)
{
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().BeginFrame4(XFrame->FrameNumber, CustomPresent->GetOvrpCommandQueue())))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
XFrame->ShowFlags.Rendering = false;
}
}
FPlatformAtomics::InterlockedDecrement(&FramesOutstanding);
Layers_RHIThread = XLayers;
Layers_RHIThread.Sort(FLayerPtr_ComparePriority());
if (XFrame->ShowFlags.Rendering)
{
TArray<const ovrpLayerSubmit*> LayerSubmitPtr;
LayerSubmitPtr.SetNum(Layers_RHIThread.Num());
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
LayerSubmitPtr[LayerIndex] = Layers_RHIThread[LayerIndex]->UpdateLayer_RHIThread(XSettings.Get(), XFrame.Get(), LayerIndex);
}
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().EndFrame4(XFrame->FrameNumber, LayerSubmitPtr.GetData(), LayerSubmitPtr.Num(), CustomPresent->GetOvrpCommandQueue())))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
}
else
{
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
Layers_RHIThread[LayerIndex]->IncrementSwapChainIndex_RHIThread(CustomPresent);
}
}
}
});
}
void FSplash::ReleaseResources_RHIThread()
{
for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
{
Layers_RenderThread[LayerIndex]->ReleaseResources_RHIThread();
}
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
Layers_RHIThread[LayerIndex]->ReleaseResources_RHIThread();
}
Layers_RenderThread.Reset();
Layers_RHIThread.Reset();
}
void FSplash::PreShutdown()
{
CheckInGameThread();
}
void FSplash::Shutdown()
{
CheckInGameThread();
#if WITH_EDITOR
if (PieBeginDelegateHandle.IsValid())
{
FEditorDelegates::BeginPIE.Remove(PieBeginDelegateHandle);
PieBeginDelegateHandle.Reset();
}
#endif
if (PreLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
PreLoadLevelDelegate.Reset();
}
if (PostLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
PostLoadLevelDelegate.Reset();
}
if (bInitialized)
{
ExecuteOnRenderThread([this]() {
if (Ticker)
{
Ticker->Unregister();
Ticker = nullptr;
}
ExecuteOnRHIThread([this]() {
SplashLayers.Reset();
Layers_RenderThread.Reset();
Layers_RenderThread_Input.Reset();
Layers_RHIThread.Reset();
});
});
bInitialized = false;
}
}
int FSplash::AddSplash(const FOculusXRSplashDesc& Desc)
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
return SplashLayers.Add(FSplashLayer(Desc));
}
void FSplash::AddSplash(const FSplashDesc& Splash)
{
FOculusXRSplashDesc OculusDesc;
OculusDesc.TransformInMeters = Splash.Transform;
OculusDesc.QuadSizeInMeters = Splash.QuadSize;
OculusDesc.DeltaRotation = Splash.DeltaRotation;
OculusDesc.bNoAlphaChannel = Splash.bIgnoreAlpha;
OculusDesc.bIsDynamic = Splash.bIsDynamic || Splash.bIsExternal;
OculusDesc.TextureOffset = Splash.UVRect.Min;
OculusDesc.TextureScale = Splash.UVRect.Max;
OculusDesc.LoadedTexture = Splash.Texture;
AddSplash(OculusDesc);
}
void FSplash::ClearSplashes()
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
SplashLayers.Reset();
}
bool FSplash::GetSplash(unsigned InSplashLayerIndex, FOculusXRSplashDesc& OutDesc)
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
if (InSplashLayerIndex < unsigned(SplashLayers.Num()))
{
OutDesc = SplashLayers[int32(InSplashLayerIndex)].Desc;
return true;
}
return false;
}
IStereoLayers::FLayerDesc FSplash::StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc)
{
IStereoLayers::FLayerDesc LayerDesc;
if (OculusDesc.LoadedTexture->GetTextureCube() != nullptr)
{
LayerDesc.SetShape<FCubemapLayer>();
}
// else LayerDesc.Shape defaults to FQuadLayer
LayerDesc.Transform = OculusDesc.TransformInMeters * FTransform(OculusXRHMD->GetSplashRotation().Quaternion());
LayerDesc.QuadSize = OculusDesc.QuadSizeInMeters;
LayerDesc.UVRect = FBox2D(OculusDesc.TextureOffset, OculusDesc.TextureOffset + OculusDesc.TextureScale);
LayerDesc.Priority = INT32_MAX - (int32)(OculusDesc.TransformInMeters.GetTranslation().X * 1000.f);
LayerDesc.PositionType = IStereoLayers::TrackerLocked;
LayerDesc.Texture = OculusDesc.LoadedTexture;
LayerDesc.Flags = IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO | (OculusDesc.bNoAlphaChannel ? IStereoLayers::LAYER_FLAG_TEX_NO_ALPHA_CHANNEL : 0) | (OculusDesc.bIsDynamic ? IStereoLayers::LAYER_FLAG_TEX_CONTINUOUS_UPDATE : 0);
return LayerDesc;
}
void FSplash::DoShow()
{
CheckInGameThread();
OculusXRHMD->SetSplashRotationToForward();
// Create new textures
UnloadTextures();
// Make sure all UTextures are loaded and contain Resource->TextureRHI
bool bWaitForRT = false;
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
if (SplashLayer.Desc.TexturePath.IsValid())
{
// load temporary texture (if TexturePath was specified)
LoadTexture(SplashLayer);
}
if (SplashLayer.Desc.LoadingTexture && SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
{
SplashLayer.Desc.LoadingTexture->UpdateResource();
bWaitForRT = true;
}
}
FlushRenderingCommands();
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
//@DBG BEGIN
if (SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
{
if (SplashLayer.Desc.LoadingTexture->GetResource() && SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI)
{
SplashLayer.Desc.LoadedTexture = SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI;
}
else
{
UE_LOG(LogHMD, Warning, TEXT("Splash, %s - no Resource"), *SplashLayer.Desc.LoadingTexture->GetDesc());
}
}
//@DBG END
if (SplashLayer.Desc.LoadedTexture)
{
SplashLayer.Layer = MakeShareable(new FLayer(NextLayerId++));
SplashLayer.Layer->SetDesc(StereoLayerDescFromOculusSplashDesc(SplashLayer.Desc));
}
}
{
//add oculus-generated layers through the OculusVR settings area
FScopeLock ScopeLock(&RenderThreadLock);
Layers_RenderThread_DeltaRotation.Reset();
Layers_RenderThread_Input.Reset();
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); SplashLayerIndex++)
{
const FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
if (SplashLayer.Layer.IsValid())
{
FLayerPtr ClonedLayer = SplashLayer.Layer->Clone();
Layers_RenderThread_Input.Add(ClonedLayer);
// Register layers that need to be rotated every n ticks
if (!SplashLayer.Desc.DeltaRotation.Equals(FQuat::Identity))
{
Layers_RenderThread_DeltaRotation.Emplace(ClonedLayer, SplashLayer.Desc.DeltaRotation);
}
}
}
//add UE VR splash screen
FOculusXRSplashDesc UESplashDesc = OculusXRHMD->GetUESplashScreenDesc();
if (UESplashDesc.LoadedTexture != nullptr)
{
UELayer.Reset();
UELayer = MakeShareable(new FLayer(NextLayerId++));
UELayer->SetDesc(StereoLayerDescFromOculusSplashDesc(UESplashDesc));
Layers_RenderThread_Input.Add(UELayer->Clone());
}
Layers_RenderThread_Input.Sort(FLayerPtr_CompareId());
}
if (Layers_RenderThread_Input.Num() > 0)
{
// If no textures are loaded, this will push black frame
StartTicker();
bIsShown = true;
UE_LOG(LogHMD, Log, TEXT("FSplash::DoShow"));
}
else
{
UE_LOG(LogHMD, Log, TEXT("No splash layers in FSplash::DoShow"));
}
}
void FSplash::DoHide()
{
CheckInGameThread();
UE_LOG(LogHMD, Log, TEXT("FSplash::DoHide"));
bIsShown = false;
StopTicker();
}
void FSplash::UpdateLoadingScreen_GameThread()
{
if (bNeedSplashUpdate)
{
if (bShouldShowSplash)
{
DoShow();
}
else
{
DoHide();
}
bNeedSplashUpdate = false;
}
}
void FSplash::ShowLoadingScreen()
{
bShouldShowSplash = true;
// DoShow will be called from UpdateSplashScreen_Gamethread().
// This can can happen if the splashes are already being shown, as it will reset the relative positions and delta rotations of the layers.
bNeedSplashUpdate = true;
}
void FSplash::HideLoadingScreen()
{
bShouldShowSplash = false;
bNeedSplashUpdate = bIsShown; // no need to call DoHide when the splash is already hidden
}
void FSplash::UnloadTextures()
{
CheckInGameThread();
// unload temporary loaded textures
FScopeLock ScopeLock(&RenderThreadLock);
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
if (SplashLayers[SplashLayerIndex].Desc.TexturePath.IsValid())
{
UnloadTexture(SplashLayers[SplashLayerIndex]);
}
}
}
void FSplash::LoadTexture(FSplashLayer& InSplashLayer)
{
CheckInGameThread();
UnloadTexture(InSplashLayer);
UE_LOG(LogLoadingSplash, Log, TEXT("Loading texture for splash %s..."), *InSplashLayer.Desc.TexturePath.GetAssetName());
InSplashLayer.Desc.LoadingTexture = Cast<UTexture>(InSplashLayer.Desc.TexturePath.TryLoad());
if (InSplashLayer.Desc.LoadingTexture != nullptr)
{
UE_LOG(LogLoadingSplash, Log, TEXT("...Success. "));
}
InSplashLayer.Desc.LoadedTexture = nullptr;
InSplashLayer.Layer.Reset();
}
void FSplash::UnloadTexture(FSplashLayer& InSplashLayer)
{
CheckInGameThread();
InSplashLayer.Desc.LoadingTexture = nullptr;
InSplashLayer.Desc.LoadedTexture = nullptr;
InSplashLayer.Layer.Reset();
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,146 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IXRLoadingScreen.h"
#if WITH_EDITOR
#include "Editor.h"
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_GameFrame.h"
#include "OculusXRHMD_Layer.h"
#include "TickableObjectRenderThread.h"
#include "OculusXRHMDTypes.h"
namespace OculusXRHMD
{
class FOculusXRHMD;
//-------------------------------------------------------------------------------------------------
// FSplashLayer
//-------------------------------------------------------------------------------------------------
struct FSplashLayer
{
FOculusXRSplashDesc Desc;
FLayerPtr Layer;
public:
FSplashLayer(const FOculusXRSplashDesc& InDesc)
: Desc(InDesc) {}
FSplashLayer(const FSplashLayer& InSplashLayer)
: Desc(InSplashLayer.Desc), Layer(InSplashLayer.Layer) {}
};
//-------------------------------------------------------------------------------------------------
// FSplash
//-------------------------------------------------------------------------------------------------
class FSplash : public IXRLoadingScreen, public TSharedFromThis<FSplash>
{
protected:
class FTicker : public FTickableObjectRenderThread, public TSharedFromThis<FTicker>
{
public:
FTicker(FSplash* InSplash)
: FTickableObjectRenderThread(false, true), pSplash(InSplash) {}
virtual void Tick(float DeltaTime) override { pSplash->Tick_RenderThread(DeltaTime); }
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(FSplash, STATGROUP_Tickables); }
virtual bool IsTickable() const override { return true; }
protected:
FSplash* pSplash;
};
public:
FSplash(FOculusXRHMD* InPlugin);
virtual ~FSplash();
void Tick_RenderThread(float DeltaTime);
void Startup();
void LoadSettings();
void ReleaseResources_RHIThread();
void PreShutdown();
void Shutdown();
void OnPreLoadMap(const FString&);
void OnPostLoadMap(UWorld* LoadedWorld);
#if WITH_EDITOR
void OnPieBegin(bool bIsSimulating);
#endif
// Called from FOculusXRHMD
void UpdateLoadingScreen_GameThread();
// Internal extended API
int AddSplash(const FOculusXRSplashDesc&);
bool GetSplash(unsigned index, FOculusXRSplashDesc& OutDesc);
void StopTicker();
void StartTicker();
// The standard IXRLoadingScreen interface
virtual void ShowLoadingScreen() override;
virtual void HideLoadingScreen() override;
virtual void ClearSplashes() override;
virtual void AddSplash(const FSplashDesc& Splash) override;
virtual bool IsShown() const override { return bIsShown; }
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
virtual bool IsPlayingLoadingMovie() const override
{
return false;
}
#endif
protected:
void DoShow();
void DoHide();
void UnloadTextures();
void LoadTexture(FSplashLayer& InSplashLayer);
void UnloadTexture(FSplashLayer& InSplashLayer);
void RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList);
IStereoLayers::FLayerDesc StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc);
protected:
FOculusXRHMD* OculusXRHMD;
FCustomPresent* CustomPresent;
TSharedPtr<FTicker> Ticker;
int32 FramesOutstanding;
FCriticalSection RenderThreadLock;
FSettingsPtr Settings;
FGameFramePtr Frame;
TArray<FSplashLayer> SplashLayers;
uint32 NextLayerId;
FLayerPtr BlackLayer;
FLayerPtr UELayer;
TArray<TTuple<FLayerPtr, FQuat>> Layers_RenderThread_DeltaRotation;
TArray<FLayerPtr> Layers_RenderThread_Input;
TArray<FLayerPtr> Layers_RenderThread;
TArray<FLayerPtr> Layers_RHIThread;
// All these flags are only modified from the Game thread
bool bInitialized;
bool bIsShown;
bool bNeedSplashUpdate;
bool bShouldShowSplash;
float SystemDisplayInterval;
double LastTimeInSeconds;
FDelegateHandle PreLoadLevelDelegate;
FDelegateHandle PostLoadLevelDelegate;
#if WITH_EDITOR
FDelegateHandle PieBeginDelegateHandle;
#endif
};
typedef TSharedPtr<FSplash> FSplashPtr;
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,309 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_StressTester.h"
#if OCULUS_STRESS_TESTS_ENABLED
#include "OculusXRHMD.h"
#include "GlobalShader.h"
#include "UniformBuffer.h"
#include "RHICommandList.h"
#include "ShaderParameterUtils.h"
#include "RHIStaticStates.h"
#include "PipelineStateCache.h"
#include "OculusShaders.h"
#include "SceneUtils.h" // for SCOPED_DRAW_EVENT()
DECLARE_STATS_GROUP(TEXT("Oculus"), STATGROUP_Oculus, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("GPUStressRendering"), STAT_GPUStressRendering, STATGROUP_Oculus);
//-------------------------------------------------------------------------------------------------
// Uniform buffers
//-------------------------------------------------------------------------------------------------
//This buffer should contain variables that never, or rarely change
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderConstantParameters, )
//SHADER_PARAMETER(FVector4, Name)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderConstantParameters, "PSConstants");
typedef TUniformBufferRef<FOculusPixelShaderConstantParameters> FOculusPixelShaderConstantParametersRef;
//This buffer is for variables that change very often (each frame for example)
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderVariableParameters, )
SHADER_PARAMETER(int, IterationsMultiplier)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderVariableParameters, "PSVariables");
typedef TUniformBufferRef<FOculusPixelShaderVariableParameters> FOculusPixelShaderVariableParametersRef;
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FTextureVertexDeclaration
//-------------------------------------------------------------------------------------------------
struct FTextureVertex
{
FVector4 Position;
FVector2f UV;
};
class FTextureVertexDeclaration : public FRenderResource
{
public:
FVertexDeclarationRHIRef VertexDeclarationRHI;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual void InitRHI() override
#else
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
#endif
{
FVertexDeclarationElementList Elements;
uint32 Stride = sizeof(FTextureVertex);
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, Position), VET_Float4, 0, Stride));
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, UV), VET_Float2, 1, Stride));
VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
virtual void ReleaseRHI() override
{
VertexDeclarationRHI.SafeRelease();
}
};
static TGlobalResource<FTextureVertexDeclaration> GOculusTextureVertexDeclaration;
//-------------------------------------------------------------------------------------------------
// FStressTester
//-------------------------------------------------------------------------------------------------
TSharedPtr<FStressTester, ESPMode::ThreadSafe> FStressTester::SharedInstance;
TSharedRef<class FStressTester, ESPMode::ThreadSafe> FStressTester::Get()
{
CheckInGameThread();
if (!SharedInstance.IsValid())
{
SharedInstance = TSharedPtr<class FStressTester, ESPMode::ThreadSafe>(new FStressTester());
check(SharedInstance.IsValid());
}
return SharedInstance.ToSharedRef();
}
FStressTester::FStressTester()
: Mode(STM_None)
, CPUSpinOffInSeconds(0.011 / 3.) // one third of the frame (default value)
, PDsTimeLimitInSeconds(10.) // 10 secs
, CPUsTimeLimitInSeconds(10.) // 10 secs
, GPUsTimeLimitInSeconds(10.) // 10 secs
, GPUIterationsMultiplier(0.)
, CPUStartTimeInSeconds(0.)
, GPUStartTimeInSeconds(0.)
, PDStartTimeInSeconds(0.)
{
}
// multiple masks could be set, see EStressTestMode
void FStressTester::SetStressMode(uint32 InStressMask)
{
check((InStressMask & (~STM__All)) == 0);
Mode = InStressMask;
for (uint32 m = 1; m < STM__All; m <<= 1)
{
if (InStressMask & m)
{
switch (m)
{
case STM_EyeBufferRealloc:
UE_LOG(LogHMD, Log, TEXT("PD of EyeBuffer stress test is started"));
break;
case STM_CPUSpin:
UE_LOG(LogHMD, Log, TEXT("CPU stress test is started"));
break;
case STM_GPU:
UE_LOG(LogHMD, Log, TEXT("GPU stress test is started"));
break;
}
}
}
}
void FStressTester::DoTickCPU_GameThread(FOculusXRHMD* pPlugin)
{
CheckInGameThread();
if (Mode & STM_EyeBufferRealloc)
{
// Change PixelDensity every frame within MinPixelDensity..MaxPixelDensity range
if (PDStartTimeInSeconds == 0.)
{
PDStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - PDStartTimeInSeconds >= PDsTimeLimitInSeconds)
{
PDStartTimeInSeconds = 0.;
Mode &= ~STM_EyeBufferRealloc;
UE_LOG(LogHMD, Log, TEXT("PD of EyeBuffer stress test is finished"));
}
}
const int divisor = int((MaxPixelDensity - MinPixelDensity) * 10.f);
float NewPD = float(uint64(FPlatformTime::Seconds() * 1000) % divisor) / 10.f + MinPixelDensity;
pPlugin->SetPixelDensity(NewPD);
}
if (Mode & STM_CPUSpin)
{
// Simulate heavy CPU load within specified time limits
if (CPUStartTimeInSeconds == 0.)
{
CPUStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - CPUStartTimeInSeconds >= CPUsTimeLimitInSeconds)
{
CPUStartTimeInSeconds = 0.;
Mode &= ~STM_CPUSpin;
UE_LOG(LogHMD, Log, TEXT("CPU stress test is finished"));
}
}
const double StartSeconds = FPlatformTime::Seconds();
int i, num = 1, primes = 0;
bool bFinish = false;
while (!bFinish)
{
i = 2;
while (i <= num)
{
if (num % i == 0)
{
break;
}
i++;
const double NowSeconds = FPlatformTime::Seconds();
if (NowSeconds - StartSeconds >= CPUSpinOffInSeconds)
{
bFinish = true;
}
}
if (i == num)
{
++primes;
}
++num;
}
}
if (Mode & STM_GPU)
{
// Simulate heavy CPU load within specified time limits
if (GPUStartTimeInSeconds == 0.)
{
GPUStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - GPUStartTimeInSeconds >= GPUsTimeLimitInSeconds)
{
GPUStartTimeInSeconds = 0.;
Mode &= ~STM_GPU;
UE_LOG(LogHMD, Log, TEXT("GPU stress test is finished"));
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Console commands for managing the stress tester:
//-------------------------------------------------------------------------------------------------
static void StressGPUCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_GPU | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const int GpuMult = FCString::Atoi(*Args[0]);
StressTester->SetGPULoadMultiplier(GpuMult);
}
if (Args.Num() > 1)
{
const float GpuTimeLimit = FCString::Atof(*Args[1]);
StressTester->SetGPUsTimeLimitInSeconds(GpuTimeLimit);
}
}
static FAutoConsoleCommand CStressGPUCmd(
TEXT("vr.oculus.Stress.GPU"),
*NSLOCTEXT("OculusRift", "CCommandText_StressGPU", "Initiates a GPU stress test.\n Usage: vr.oculus.Stress.GPU [LoadMultiplier [TimeLimit]]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressGPUCmdHandler));
static void StressCPUCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_CPUSpin | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const float CpuLimit = FCString::Atof(*Args[0]);
StressTester->SetCPUSpinOffPerFrameInSeconds(CpuLimit);
}
if (Args.Num() > 1)
{
const float CpuTimeLimit = FCString::Atof(*Args[1]);
StressTester->SetCPUsTimeLimitInSeconds(CpuTimeLimit);
}
}
static FAutoConsoleCommand CStressCPUCmd(
TEXT("vr.oculus.Stress.CPU"),
*NSLOCTEXT("OculusRift", "CCommandText_StressCPU", "Initiates a CPU stress test.\n Usage: vr.oculus.Stress.CPU [PerFrameTime [TotalTimeLimit]]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressCPUCmdHandler));
static void StressPDCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_EyeBufferRealloc | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const float TimeLimit = FCString::Atof(*Args[0]);
StressTester->SetPDsTimeLimitInSeconds(TimeLimit);
}
}
static FAutoConsoleCommand CStressPDCmd(
TEXT("vr.oculus.Stress.PD"),
*NSLOCTEXT("OculusRift", "CCommandText_StressPD", "Initiates a pixel density stress test wher pixel density is changed every frame for TotalTimeLimit seconds.\n Usage: vr.oculus.Stress.PD [TotalTimeLimit]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressPDCmdHandler));
static void StressResetCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(0);
}
static FAutoConsoleCommand CStressResetCmd(
TEXT("vr.oculus.Stress.Reset"),
*NSLOCTEXT("OculusRift", "CCommandText_StressReset", "Resets the stress tester and stops all currently running stress tests.\n Usage: vr.oculus.Stress.Reset").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressResetCmdHandler));
} // namespace OculusXRHMD
#endif // #if OCULUS_STRESS_TESTS_ENABLED

View File

@@ -0,0 +1,91 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#define OCULUS_STRESS_TESTS_ENABLED (OCULUS_HMD_SUPPORTED_PLATFORMS && !UE_BUILD_SHIPPING && !PLATFORM_ANDROID)
#if OCULUS_STRESS_TESTS_ENABLED
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FStressTester
//-------------------------------------------------------------------------------------------------
class FStressTester
{
public:
const float MinPixelDensity = 0.4f;
const float MaxPixelDensity = 2.0f;
enum EStressTestMode
{
STM_None,
STM_EyeBufferRealloc = 0x01,
STM_CPUSpin = 0x02,
STM_GPU = 0x04,
STM__All = ((STM_GPU << 1) - 1)
};
// multiple masks could be set, see EStressTestMode
void SetStressMode(uint32 InStressMask);
uint32 GetStressMode() const { return Mode; }
// sets limits for CPUSpin mode, per frame
void SetCPUSpinOffPerFrameInSeconds(double InCPUSpinOffInSeconds) { CPUSpinOffInSeconds = InCPUSpinOffInSeconds; }
// set GPU load multiplier
// if IterationsMultiplier is 0 then the multiplier will be randomly changed in 1..20 range.
// the bigger the multiplier the longer it takes GPU to draw the quad.
void SetGPULoadMultiplier(int IterationsMultiplier) { GPUIterationsMultiplier = IterationsMultiplier; }
// sets time limit for STM_EyeBufferRealloc mode; 0 - unlimited
void SetPDsTimeLimitInSeconds(double InSeconds) { PDsTimeLimitInSeconds = InSeconds; }
// sets time limit for STM_CPUSpin mode; 0 - unlimited
void SetCPUsTimeLimitInSeconds(double InSeconds) { CPUsTimeLimitInSeconds = InSeconds; }
// sets time limit for STM_GPU mode; 0 - unlimited
void SetGPUsTimeLimitInSeconds(double InSeconds) { GPUsTimeLimitInSeconds = InSeconds; }
static TSharedRef<class FStressTester, ESPMode::ThreadSafe> Get();
static void TickCPU_GameThread(class FOculusXRHMD* pPlugin)
{
CheckInGameThread();
if (SharedInstance.IsValid())
{
SharedInstance->DoTickCPU_GameThread(pPlugin);
}
}
protected:
void DoTickCPU_GameThread(class FOculusXRHMD* pPlugin);
FStressTester();
uint32 Mode; // bit mask, see EStressTestMode
double CPUSpinOffInSeconds; // limit of additional CPU load per frame, STM_CPUSpin
double PDsTimeLimitInSeconds; // time limit for STM_EyeBufferRealloc mode; 0 - unlimited
double CPUsTimeLimitInSeconds; // time limit for STM_CPUSpin mode; 0 - unlimited
double GPUsTimeLimitInSeconds; // time limit for STM_GPU mode; 0 - unlimited
// the higher multiplier the longer it takes GPU to draw
int GPUIterationsMultiplier; // if 0 - then it is dynamically changed.
double CPUStartTimeInSeconds;
double GPUStartTimeInSeconds;
double PDStartTimeInSeconds;
static TSharedPtr<class FStressTester, ESPMode::ThreadSafe> SharedInstance;
};
} // namespace OculusXRHMD
#endif // #if OCULUS_STRESS_TESTS_ENABLED

View File

@@ -0,0 +1,120 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_VulkanExtensions.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMDPrivateRHI.h"
#include "OculusXRHMDModule.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FVulkanExtensions
//-------------------------------------------------------------------------------------------------
bool FVulkanExtensions::GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
//TArray<VkExtensionProperties> Properties = GetIVulkanDynamicRHI()->RHIGetAllInstanceExtensions();
TArray<const char*> Extensions;
{
int32 ExtensionCount = 0;
FOculusXRHMDModule::GetPluginWrapper().GetInstanceExtensionsVk(nullptr, &ExtensionCount);
Extensions.SetNum(ExtensionCount);
FOculusXRHMDModule::GetPluginWrapper().GetInstanceExtensionsVk(Extensions.GetData(), &ExtensionCount);
}
// int32 ExtensionsFound = 0;
for (int32 ExtensionIndex = 0; ExtensionIndex < Extensions.Num(); ExtensionIndex++)
{
// for (int32 PropertyIndex = 0; PropertyIndex < Properties.Num(); PropertyIndex++)
{
// const char* PropertyExtensionName = Properties[PropertyIndex].extensionName;
// if (!FCStringAnsi::Strcmp(PropertyExtensionName, Extensions[ExtensionIndex]))
{
Out.Add(Extensions[ExtensionIndex]);
// ExtensionsFound++;
// break;
}
}
}
return true;
// return ExtensionsFound == Extensions.Num();
#endif
return true;
}
bool FVulkanExtensions::GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
//TArray<VkExtensionProperties> Properties = GetIVulkanDynamicRHI()->RHIGetAllDeviceExtensions((VkPhysicalDevice)pPhysicalDevice);
TArray<const char*> Extensions;
{
int32 ExtensionCount = 0;
FOculusXRHMDModule::GetPluginWrapper().GetDeviceExtensionsVk(nullptr, &ExtensionCount);
Extensions.SetNum(ExtensionCount);
FOculusXRHMDModule::GetPluginWrapper().GetDeviceExtensionsVk(Extensions.GetData(), &ExtensionCount);
}
// int32 ExtensionsFound = 0;
for (int32 ExtensionIndex = 0; ExtensionIndex < Extensions.Num(); ExtensionIndex++)
{
// for (int32 PropertyIndex = 0; PropertyIndex < Properties.Num(); PropertyIndex++)
{
// const char* PropertyExtensionName = Properties[PropertyIndex].extensionName;
// if (!FCStringAnsi::Strcmp(PropertyExtensionName, Extensions[ExtensionIndex]))
{
Out.Add(Extensions[ExtensionIndex]);
// ExtensionsFound++;
// break;
}
}
}
return true;
// return ExtensionsFound == Extensions.Num();
#endif
return true;
}
#if WITH_EDITOR
bool FEditorVulkanExtensions::GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN && PLATFORM_WINDOWS
Out.Append({ "VK_KHR_surface",
"VK_KHR_external_memory_capabilities",
"VK_KHR_win32_surface",
"VK_KHR_external_fence_capabilities",
"VK_KHR_external_semaphore_capabilities",
"VK_KHR_get_physical_device_properties2" });
#endif
return true;
}
bool FEditorVulkanExtensions::GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN && PLATFORM_WINDOWS
Out.Append({ "VK_KHR_swapchain",
"VK_KHR_external_memory",
"VK_KHR_external_memory_win32",
"VK_KHR_external_fence",
"VK_KHR_external_fence_win32",
"VK_KHR_external_semaphore",
"VK_KHR_external_semaphore_win32",
"VK_KHR_get_memory_requirements2",
"VK_KHR_dedicated_allocation" });
#endif
return true;
}
#endif
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,43 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IHeadMountedDisplayVulkanExtensions.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FVulkanExtensions
//-------------------------------------------------------------------------------------------------
class FVulkanExtensions : public IHeadMountedDisplayVulkanExtensions, public TSharedFromThis<FVulkanExtensions, ESPMode::ThreadSafe>
{
public:
FVulkanExtensions() {}
virtual ~FVulkanExtensions() {}
// IHeadMountedDisplayVulkanExtensions
virtual bool GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out) override;
virtual bool GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out) override;
};
#if WITH_EDITOR
class FEditorVulkanExtensions : public IHeadMountedDisplayVulkanExtensions, public TSharedFromThis<FEditorVulkanExtensions, ESPMode::ThreadSafe>
{
public:
FEditorVulkanExtensions() {}
virtual ~FEditorVulkanExtensions() {}
// IHeadMountedDisplayVulkanExtensions
virtual bool GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out) override;
virtual bool GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out) override;
};
#endif
} // namespace OculusXRHMD
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,152 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRPassthroughLayerShapes.h"
#include "OculusXRHMDPrivate.h"
#include "Curves/CurveLinearColor.h"
#include "OculusXRPluginWrapper.h"
const FName FReconstructedLayer::ShapeName = FName("ReconstructedLayer");
const FName FUserDefinedLayer::ShapeName = FName("UserDefinedLayer");
FColorLutDesc::FColorLutDesc()
: Weight(0)
, ColorLuts{}
{
}
FColorLutDesc::FColorLutDesc(const TArray<uint64>& InColorLuts, float InWeight)
: Weight(InWeight)
, ColorLuts(InColorLuts)
{
}
FEdgeStyleParameters::FEdgeStyleParameters()
: bEnableEdgeColor(false)
, bEnableColorMap(false)
, bUseColorLuts(false)
, TextureOpacityFactor(1.0f)
, EdgeColor{}
, ColorMapType{}
, ColorMapData{}
, ColorLutDesc{} {
};
FEdgeStyleParameters::FEdgeStyleParameters(
bool bEnableEdgeColor,
bool bEnableColorMap,
float TextureOpacityFactor,
float Brightness,
float Contrast,
float Posterize,
float Saturation,
FLinearColor EdgeColor,
FLinearColor ColorScale,
FLinearColor ColorOffset,
EOculusXRColorMapType InColorMapType,
const TArray<FLinearColor>& InColorMapGradient,
const FColorLutDesc& InLutDesc)
: bEnableEdgeColor(bEnableEdgeColor)
, bEnableColorMap(bEnableColorMap)
, TextureOpacityFactor(TextureOpacityFactor)
, Brightness(Brightness)
, Contrast(Contrast)
, Posterize(Posterize)
, Saturation(Saturation)
, EdgeColor(EdgeColor)
, ColorScale(ColorScale)
, ColorOffset(ColorOffset)
, ColorMapType(InColorMapType)
, ColorLutDesc(InLutDesc)
{
bUseColorLuts = (InColorMapType == ColorMapType_ColorLut && InLutDesc.ColorLuts.Num() == 1)
|| (InColorMapType == ColorMapType_ColorLut_Interpolated && InLutDesc.ColorLuts.Num() == 2);
if ((InColorMapType == ColorMapType_ColorLut || InColorMapType == ColorMapType_ColorLut_Interpolated)
&& !bUseColorLuts)
{
ColorMapType = ColorMapType_None;
}
ColorMapData = GenerateColorMapData(InColorMapType, InColorMapGradient);
};
TArray<uint8> FEdgeStyleParameters::GenerateColorMapData(EOculusXRColorMapType InColorMapType, const TArray<FLinearColor>& InColorMapGradient)
{
switch (InColorMapType)
{
case ColorMapType_GrayscaleToColor:
{
TArray<uint8> NewColorMapData = GenerateMonoBrightnessContrastPosterizeMap();
return GenerateMonoToRGBA(InColorMapGradient, NewColorMapData);
}
case ColorMapType_Grayscale:
return GenerateMonoBrightnessContrastPosterizeMap();
case ColorMapType_ColorAdjustment:
return GenerateBrightnessContrastSaturationColorMap();
default:
return TArray<uint8>();
}
}
TArray<uint8> FEdgeStyleParameters::GenerateMonoToRGBA(const TArray<FLinearColor>& InColorMapGradient, const TArray<uint8>& InColorMapData)
{
TArray<uint8> NewColorMapData;
FInterpCurveLinearColor InterpCurve;
const uint32 TotalEntries = 256;
for (int32 Index = 0; Index < InColorMapGradient.Num(); ++Index)
{
InterpCurve.AddPoint(Index, (InColorMapGradient[Index] * ColorScale) + ColorOffset);
}
NewColorMapData.SetNum(TotalEntries * sizeof(ovrpColorf));
uint8* Dest = NewColorMapData.GetData();
for (int32 Index = 0; Index < TotalEntries; ++Index)
{
const ovrpColorf Color = OculusXRHMD::ToOvrpColorf(InterpCurve.Eval(InColorMapData[Index]));
FMemory::Memcpy(Dest, &Color, sizeof(Color));
Dest += sizeof(ovrpColorf);
}
return NewColorMapData;
}
TArray<uint8> FEdgeStyleParameters::GenerateMonoBrightnessContrastPosterizeMap()
{
TArray<uint8> NewColorMapData;
const int32 TotalEntries = 256;
NewColorMapData.SetNum(TotalEntries * sizeof(uint8));
for (int32 Index = 0; Index < TotalEntries; ++Index)
{
float Alpha = ((float)Index / TotalEntries);
float ContrastFactor = Contrast + 1.0;
Alpha = (Alpha - 0.5) * ContrastFactor + 0.5 + Brightness;
if (Posterize > 0.0f)
{
const float PosterizationBase = 50.0f;
float FinalPosterize = (FMath::Pow(PosterizationBase, Posterize) - 1.0) / (PosterizationBase - 1.0);
Alpha = FMath::RoundToFloat(Alpha / FinalPosterize) * FinalPosterize;
}
NewColorMapData[Index] = (uint8)(FMath::Min(FMath::Max(Alpha, 0.0f), 1.0f) * 255.0f);
}
return NewColorMapData;
}
TArray<uint8> FEdgeStyleParameters::GenerateBrightnessContrastSaturationColorMap()
{
TArray<uint8> NewColorMapData;
NewColorMapData.SetNum(3 * sizeof(float));
float newB = Brightness * 100.0f;
float newC = Contrast + 1.0f;
float newS = Saturation + 1.0f;
uint8* Dest = NewColorMapData.GetData();
FMemory::Memcpy(Dest, &newB, sizeof(float));
Dest += sizeof(float);
FMemory::Memcpy(Dest, &newC, sizeof(float));
Dest += sizeof(float);
FMemory::Memcpy(Dest, &newS, sizeof(float));
return NewColorMapData;
}

View File

@@ -0,0 +1,461 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRPluginWrapper.h"
#include "OculusXRHMDModule.h"
#if PLATFORM_ANDROID
#include <dlfcn.h>
#define MIN_SDK_VERSION 29
#endif
DEFINE_LOG_CATEGORY(LogOculusPluginWrapper);
static void* LoadEntryPoint(void* handle, const char* EntryPointName);
bool OculusPluginWrapper::InitializeOculusPluginWrapper(OculusPluginWrapper* wrapper)
{
if (wrapper->IsInitialized())
{
UE_LOG(LogOculusPluginWrapper, Warning, TEXT("wrapper already initialized"));
return true;
}
#if OCULUS_HMD_SUPPORTED_PLATFORMS
void* LibraryHandle = nullptr;
#if PLATFORM_ANDROID
const bool VersionValid = FAndroidMisc::GetAndroidBuildVersion() >= MIN_SDK_VERSION;
#else
const bool VersionValid = true;
#endif
if (VersionValid)
{
LibraryHandle = FOculusXRHMDModule::GetOVRPluginHandle();
if (LibraryHandle == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Warning, TEXT("GetOVRPluginHandle() returned NULL"));
return false;
}
}
else
{
return false;
}
#else
return false;
#endif
struct OculusEntryPoint
{
const char* EntryPointName;
void** EntryPointPtr;
};
#define OCULUS_BIND_ENTRY_POINT(Func) { "ovrp_" #Func, (void**)&wrapper->Func }
OculusEntryPoint entryPointArray[] = {
// OVR_Plugin.h
OCULUS_BIND_ENTRY_POINT(PreInitialize5),
OCULUS_BIND_ENTRY_POINT(GetInitialized),
OCULUS_BIND_ENTRY_POINT(Initialize7),
OCULUS_BIND_ENTRY_POINT(Shutdown2),
OCULUS_BIND_ENTRY_POINT(SetLogCallback2),
OCULUS_BIND_ENTRY_POINT(GetVersion2),
OCULUS_BIND_ENTRY_POINT(GetNativeSDKVersion2),
OCULUS_BIND_ENTRY_POINT(GetNativeSDKPointer2),
OCULUS_BIND_ENTRY_POINT(GetDisplayAdapterId2),
OCULUS_BIND_ENTRY_POINT(GetAudioOutId2),
OCULUS_BIND_ENTRY_POINT(GetAudioOutDeviceId2),
OCULUS_BIND_ENTRY_POINT(GetAudioInId2),
OCULUS_BIND_ENTRY_POINT(GetAudioInDeviceId2),
OCULUS_BIND_ENTRY_POINT(GetInstanceExtensionsVk),
OCULUS_BIND_ENTRY_POINT(GetDeviceExtensionsVk),
OCULUS_BIND_ENTRY_POINT(SetupDistortionWindow3),
OCULUS_BIND_ENTRY_POINT(DestroyDistortionWindow2),
OCULUS_BIND_ENTRY_POINT(GetDominantHand),
OCULUS_BIND_ENTRY_POINT(SetRemoteHandedness),
OCULUS_BIND_ENTRY_POINT(SetColorScaleAndOffset),
OCULUS_BIND_ENTRY_POINT(SetupLayer),
OCULUS_BIND_ENTRY_POINT(SetupLayerDepth),
OCULUS_BIND_ENTRY_POINT(SetEyeFovPremultipliedAlphaMode),
OCULUS_BIND_ENTRY_POINT(GetEyeFovLayerId),
OCULUS_BIND_ENTRY_POINT(GetLayerTextureStageCount),
OCULUS_BIND_ENTRY_POINT(GetLayerTexture2),
OCULUS_BIND_ENTRY_POINT(GetLayerTextureFoveation),
OCULUS_BIND_ENTRY_POINT(GetLayerOcclusionMesh),
OCULUS_BIND_ENTRY_POINT(GetLayerAndroidSurfaceObject),
OCULUS_BIND_ENTRY_POINT(GetLayerTextureSpaceWarp),
OCULUS_BIND_ENTRY_POINT(CalculateEyeLayerDesc3),
OCULUS_BIND_ENTRY_POINT(DestroyLayer),
OCULUS_BIND_ENTRY_POINT(CalculateLayerDesc),
OCULUS_BIND_ENTRY_POINT(CalculateEyeLayerDesc2),
OCULUS_BIND_ENTRY_POINT(CalculateEyePreviewRect),
OCULUS_BIND_ENTRY_POINT(SetupMirrorTexture2),
OCULUS_BIND_ENTRY_POINT(DestroyMirrorTexture2),
OCULUS_BIND_ENTRY_POINT(GetAdaptiveGpuPerformanceScale2),
OCULUS_BIND_ENTRY_POINT(GetAppCpuStartToGpuEndTime2),
OCULUS_BIND_ENTRY_POINT(GetEyePixelsPerTanAngleAtCenter2),
OCULUS_BIND_ENTRY_POINT(GetHmdToEyeOffset2),
OCULUS_BIND_ENTRY_POINT(Update3),
OCULUS_BIND_ENTRY_POINT(WaitToBeginFrame),
OCULUS_BIND_ENTRY_POINT(BeginFrame4),
OCULUS_BIND_ENTRY_POINT(UpdateFoveation),
OCULUS_BIND_ENTRY_POINT(EndFrame4),
OCULUS_BIND_ENTRY_POINT(GetTrackingOrientationSupported2),
OCULUS_BIND_ENTRY_POINT(GetTrackingOrientationEnabled2),
OCULUS_BIND_ENTRY_POINT(SetTrackingOrientationEnabled2),
OCULUS_BIND_ENTRY_POINT(GetTrackingPositionSupported2),
OCULUS_BIND_ENTRY_POINT(GetTrackingPositionEnabled2),
OCULUS_BIND_ENTRY_POINT(SetTrackingPositionEnabled2),
OCULUS_BIND_ENTRY_POINT(GetTrackingIPDEnabled2),
OCULUS_BIND_ENTRY_POINT(SetTrackingIPDEnabled2),
OCULUS_BIND_ENTRY_POINT(GetTrackingCalibratedOrigin2),
OCULUS_BIND_ENTRY_POINT(SetTrackingCalibratedOrigin2),
OCULUS_BIND_ENTRY_POINT(GetTrackingOriginType2),
OCULUS_BIND_ENTRY_POINT(SetTrackingOriginType2),
OCULUS_BIND_ENTRY_POINT(RecenterTrackingOrigin2),
OCULUS_BIND_ENTRY_POINT(GetNodePresent2),
OCULUS_BIND_ENTRY_POINT(GetNodeOrientationTracked2),
OCULUS_BIND_ENTRY_POINT(GetNodeOrientationValid),
OCULUS_BIND_ENTRY_POINT(GetNodePositionTracked2),
OCULUS_BIND_ENTRY_POINT(GetNodePositionValid),
OCULUS_BIND_ENTRY_POINT(SetNodePositionTracked2),
OCULUS_BIND_ENTRY_POINT(GetNodePoseState3),
OCULUS_BIND_ENTRY_POINT(GetNodePoseStateRaw),
OCULUS_BIND_ENTRY_POINT(GetNodeFrustum2),
OCULUS_BIND_ENTRY_POINT(SetHeadPoseModifier),
OCULUS_BIND_ENTRY_POINT(GetHeadPoseModifier),
OCULUS_BIND_ENTRY_POINT(GetControllerState4),
OCULUS_BIND_ENTRY_POINT(GetControllerState5),
OCULUS_BIND_ENTRY_POINT(GetControllerState6),
OCULUS_BIND_ENTRY_POINT(GetActiveController2),
OCULUS_BIND_ENTRY_POINT(GetConnectedControllers2),
OCULUS_BIND_ENTRY_POINT(SetControllerVibration2),
OCULUS_BIND_ENTRY_POINT(SetControllerLocalizedVibration),
OCULUS_BIND_ENTRY_POINT(SetControllerHapticsAmplitudeEnvelope),
OCULUS_BIND_ENTRY_POINT(SetControllerHapticsPcm),
OCULUS_BIND_ENTRY_POINT(GetControllerHapticsDesc2),
OCULUS_BIND_ENTRY_POINT(GetControllerHapticsState2),
OCULUS_BIND_ENTRY_POINT(GetControllerSampleRateHz),
OCULUS_BIND_ENTRY_POINT(SetControllerHaptics2),
OCULUS_BIND_ENTRY_POINT(SetSuggestedCpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(GetSuggestedCpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(SetSuggestedGpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(GetSuggestedGpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(GetAppCPUPriority2),
OCULUS_BIND_ENTRY_POINT(SetAppCPUPriority2),
OCULUS_BIND_ENTRY_POINT(GetSystemPowerSavingMode2),
OCULUS_BIND_ENTRY_POINT(GetSystemDisplayFrequency2),
OCULUS_BIND_ENTRY_POINT(GetSystemDisplayAvailableFrequencies),
OCULUS_BIND_ENTRY_POINT(SetSystemDisplayFrequency),
OCULUS_BIND_ENTRY_POINT(GetSystemVSyncCount2),
OCULUS_BIND_ENTRY_POINT(SetSystemVSyncCount2),
OCULUS_BIND_ENTRY_POINT(GetSystemProductName2),
OCULUS_BIND_ENTRY_POINT(GetSystemRegion2),
OCULUS_BIND_ENTRY_POINT(ShowSystemUI2),
OCULUS_BIND_ENTRY_POINT(GetAppHasVrFocus2),
OCULUS_BIND_ENTRY_POINT(GetAppHasInputFocus),
OCULUS_BIND_ENTRY_POINT(GetAppHasSystemOverlayPresent),
OCULUS_BIND_ENTRY_POINT(GetAppShouldQuit2),
OCULUS_BIND_ENTRY_POINT(GetAppShouldRecenter2),
OCULUS_BIND_ENTRY_POINT(GetAppShouldRecreateDistortionWindow2),
OCULUS_BIND_ENTRY_POINT(GetAppLatencyTimings2),
OCULUS_BIND_ENTRY_POINT(SetAppEngineInfo2),
OCULUS_BIND_ENTRY_POINT(GetUserPresent2),
OCULUS_BIND_ENTRY_POINT(GetUserIPD2),
OCULUS_BIND_ENTRY_POINT(SetUserIPD2),
OCULUS_BIND_ENTRY_POINT(GetUserEyeHeight2),
OCULUS_BIND_ENTRY_POINT(SetUserEyeHeight2),
OCULUS_BIND_ENTRY_POINT(GetUserNeckEyeDistance2),
OCULUS_BIND_ENTRY_POINT(SetUserNeckEyeDistance2),
OCULUS_BIND_ENTRY_POINT(SetupDisplayObjects2),
OCULUS_BIND_ENTRY_POINT(GetSystemMultiViewSupported2),
OCULUS_BIND_ENTRY_POINT(GetEyeTextureArraySupported2),
OCULUS_BIND_ENTRY_POINT(GetBoundaryConfigured2),
OCULUS_BIND_ENTRY_POINT(GetDepthCompositingSupported),
OCULUS_BIND_ENTRY_POINT(TestBoundaryNode2),
OCULUS_BIND_ENTRY_POINT(TestBoundaryPoint2),
OCULUS_BIND_ENTRY_POINT(GetBoundaryGeometry3),
OCULUS_BIND_ENTRY_POINT(GetBoundaryDimensions2),
OCULUS_BIND_ENTRY_POINT(GetBoundaryVisible2),
OCULUS_BIND_ENTRY_POINT(SetBoundaryVisible2),
OCULUS_BIND_ENTRY_POINT(GetSystemHeadsetType2),
OCULUS_BIND_ENTRY_POINT(GetAppPerfStats2),
OCULUS_BIND_ENTRY_POINT(ResetAppPerfStats2),
OCULUS_BIND_ENTRY_POINT(GetAppFramerate2),
OCULUS_BIND_ENTRY_POINT(IsPerfMetricsSupported),
OCULUS_BIND_ENTRY_POINT(GetPerfMetricsFloat),
OCULUS_BIND_ENTRY_POINT(GetPerfMetricsInt),
OCULUS_BIND_ENTRY_POINT(SetHandNodePoseStateLatency),
OCULUS_BIND_ENTRY_POINT(GetHandNodePoseStateLatency),
OCULUS_BIND_ENTRY_POINT(GetSystemRecommendedMSAALevel2),
OCULUS_BIND_ENTRY_POINT(SetInhibitSystemUX2),
OCULUS_BIND_ENTRY_POINT(GetTiledMultiResSupported),
OCULUS_BIND_ENTRY_POINT(GetTiledMultiResLevel),
OCULUS_BIND_ENTRY_POINT(SetTiledMultiResLevel),
OCULUS_BIND_ENTRY_POINT(GetTiledMultiResDynamic),
OCULUS_BIND_ENTRY_POINT(SetTiledMultiResDynamic),
OCULUS_BIND_ENTRY_POINT(GetFoveationEyeTrackedSupported),
OCULUS_BIND_ENTRY_POINT(GetFoveationEyeTracked),
OCULUS_BIND_ENTRY_POINT(SetFoveationEyeTracked),
OCULUS_BIND_ENTRY_POINT(GetFoveationEyeTrackedCenter),
OCULUS_BIND_ENTRY_POINT(GetGPUUtilSupported),
OCULUS_BIND_ENTRY_POINT(GetGPUUtilLevel),
OCULUS_BIND_ENTRY_POINT(SetThreadPerformance),
OCULUS_BIND_ENTRY_POINT(AutoThreadScheduling),
OCULUS_BIND_ENTRY_POINT(GetGPUFrameTime),
OCULUS_BIND_ENTRY_POINT(GetViewportStencil),
OCULUS_BIND_ENTRY_POINT(SetDeveloperTelemetryConsent),
OCULUS_BIND_ENTRY_POINT(SendEvent),
OCULUS_BIND_ENTRY_POINT(SendEvent2),
OCULUS_BIND_ENTRY_POINT(AddCustomMetadata),
OCULUS_BIND_ENTRY_POINT(SetDeveloperMode),
OCULUS_BIND_ENTRY_POINT(GetCurrentTrackingTransformPose),
OCULUS_BIND_ENTRY_POINT(GetTrackingTransformRawPose),
OCULUS_BIND_ENTRY_POINT(GetTrackingTransformRelativePose),
OCULUS_BIND_ENTRY_POINT(GetTimeInSeconds),
//OCULUS_BIND_ENTRY_POINT(GetPTWNear),
OCULUS_BIND_ENTRY_POINT(GetASWVelocityScale),
OCULUS_BIND_ENTRY_POINT(GetASWDepthScale),
OCULUS_BIND_ENTRY_POINT(GetASWAdaptiveMode),
OCULUS_BIND_ENTRY_POINT(SetASWAdaptiveMode),
OCULUS_BIND_ENTRY_POINT(IsRequestingASWData),
OCULUS_BIND_ENTRY_POINT(GetPredictedDisplayTime),
OCULUS_BIND_ENTRY_POINT(GetHandTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(GetHandState),
OCULUS_BIND_ENTRY_POINT(GetHandState2),
OCULUS_BIND_ENTRY_POINT(GetSkeleton2),
OCULUS_BIND_ENTRY_POINT(GetMesh),
OCULUS_BIND_ENTRY_POINT(GetLocalTrackingSpaceRecenterCount),
OCULUS_BIND_ENTRY_POINT(GetSystemHmd3DofModeEnabled),
OCULUS_BIND_ENTRY_POINT(SetClientColorDesc),
OCULUS_BIND_ENTRY_POINT(GetHmdColorDesc),
OCULUS_BIND_ENTRY_POINT(PollEvent),
OCULUS_BIND_ENTRY_POINT(GetNativeXrApiType),
OCULUS_BIND_ENTRY_POINT(GetLocalDimmingSupported),
OCULUS_BIND_ENTRY_POINT(SetLocalDimming),
OCULUS_BIND_ENTRY_POINT(GetCurrentInteractionProfile),
OCULUS_BIND_ENTRY_POINT(GetLayerRecommendedResolution),
OCULUS_BIND_ENTRY_POINT(IsLayerShapeSupported),
OCULUS_BIND_ENTRY_POINT(SetEyeBufferSharpenType),
OCULUS_BIND_ENTRY_POINT(InitializeEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(DestroyEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthTextureDesc),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthTextureStageCount),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthTexture),
OCULUS_BIND_ENTRY_POINT(SetEnvironmentDepthHandRemoval),
OCULUS_BIND_ENTRY_POINT(StartEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(StopEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthFrameDesc),
#ifndef OVRPLUGIN_JNI_LIB_EXCLUDED
OCULUS_BIND_ENTRY_POINT(GetSystemVolume2),
OCULUS_BIND_ENTRY_POINT(GetSystemHeadphonesPresent2),
#endif
// Anchors
OCULUS_BIND_ENTRY_POINT(LocateSpace),
OCULUS_BIND_ENTRY_POINT(LocateSpace2),
OCULUS_BIND_ENTRY_POINT(CreateSpatialAnchor),
OCULUS_BIND_ENTRY_POINT(DestroySpace),
OCULUS_BIND_ENTRY_POINT(SetSpaceComponentStatus),
OCULUS_BIND_ENTRY_POINT(GetSpaceComponentStatus),
OCULUS_BIND_ENTRY_POINT(EnumerateSpaceSupportedComponents),
OCULUS_BIND_ENTRY_POINT(QuerySpaces),
OCULUS_BIND_ENTRY_POINT(RetrieveSpaceQueryResults),
OCULUS_BIND_ENTRY_POINT(SaveSpace),
OCULUS_BIND_ENTRY_POINT(EraseSpace),
OCULUS_BIND_ENTRY_POINT(GetSpaceUuid),
OCULUS_BIND_ENTRY_POINT(SaveSpaceList),
OCULUS_BIND_ENTRY_POINT(ShareSpaces),
OCULUS_BIND_ENTRY_POINT(CreateSpaceUser),
OCULUS_BIND_ENTRY_POINT(DestroySpaceUser),
// Scene
OCULUS_BIND_ENTRY_POINT(GetSpaceContainer),
OCULUS_BIND_ENTRY_POINT(GetSpaceBoundingBox2D),
OCULUS_BIND_ENTRY_POINT(GetSpaceBoundingBox3D),
OCULUS_BIND_ENTRY_POINT(GetSpaceSemanticLabels),
OCULUS_BIND_ENTRY_POINT(GetSpaceRoomLayout),
OCULUS_BIND_ENTRY_POINT(GetSpaceBoundary2D),
OCULUS_BIND_ENTRY_POINT(RequestSceneCapture),
OCULUS_BIND_ENTRY_POINT(GetSpaceTriangleMesh),
// MovementSDK
OCULUS_BIND_ENTRY_POINT(GetBodyTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(GetBodyTrackingSupported),
OCULUS_BIND_ENTRY_POINT(StopBodyTracking),
OCULUS_BIND_ENTRY_POINT(GetBodyState4),
OCULUS_BIND_ENTRY_POINT(GetFullBodyTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(StartBodyTracking2),
OCULUS_BIND_ENTRY_POINT(RequestBodyTrackingFidelity),
OCULUS_BIND_ENTRY_POINT(ResetBodyTrackingCalibration),
OCULUS_BIND_ENTRY_POINT(SuggestBodyTrackingCalibrationOverride),
OCULUS_BIND_ENTRY_POINT(GetFaceTracking2Enabled),
OCULUS_BIND_ENTRY_POINT(GetFaceTracking2Supported),
OCULUS_BIND_ENTRY_POINT(GetFaceState2),
OCULUS_BIND_ENTRY_POINT(StartFaceTracking2),
OCULUS_BIND_ENTRY_POINT(StopFaceTracking2),
OCULUS_BIND_ENTRY_POINT(GetEyeTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(GetEyeTrackingSupported),
OCULUS_BIND_ENTRY_POINT(GetEyeGazesState),
OCULUS_BIND_ENTRY_POINT(StartEyeTracking),
OCULUS_BIND_ENTRY_POINT(StopEyeTracking),
// QPL
OCULUS_BIND_ENTRY_POINT(QplMarkerStart),
OCULUS_BIND_ENTRY_POINT(QplMarkerEnd),
OCULUS_BIND_ENTRY_POINT(QplMarkerPoint),
OCULUS_BIND_ENTRY_POINT(QplMarkerPointCached),
OCULUS_BIND_ENTRY_POINT(QplMarkerAnnotation),
OCULUS_BIND_ENTRY_POINT(QplCreateMarkerHandle),
OCULUS_BIND_ENTRY_POINT(QplDestroyMarkerHandle),
OCULUS_BIND_ENTRY_POINT(OnEditorShutdown),
OCULUS_BIND_ENTRY_POINT(QplSetConsent),
// OVR_Plugin_Insight.h
OCULUS_BIND_ENTRY_POINT(InitializeInsightPassthrough),
OCULUS_BIND_ENTRY_POINT(ShutdownInsightPassthrough),
OCULUS_BIND_ENTRY_POINT(GetInsightPassthroughInitialized),
OCULUS_BIND_ENTRY_POINT(GetInsightPassthroughInitializationState),
OCULUS_BIND_ENTRY_POINT(CreateInsightTriangleMesh),
OCULUS_BIND_ENTRY_POINT(DestroyInsightTriangleMesh),
OCULUS_BIND_ENTRY_POINT(AddInsightPassthroughSurfaceGeometry),
OCULUS_BIND_ENTRY_POINT(DestroyInsightPassthroughGeometryInstance),
OCULUS_BIND_ENTRY_POINT(UpdateInsightPassthroughGeometryTransform),
OCULUS_BIND_ENTRY_POINT(SetInsightPassthroughStyle),
OCULUS_BIND_ENTRY_POINT(SetInsightPassthroughStyle2),
OCULUS_BIND_ENTRY_POINT(GetPassthroughCapabilityFlags),
OCULUS_BIND_ENTRY_POINT(CreatePassthroughColorLut),
OCULUS_BIND_ENTRY_POINT(DestroyPassthroughColorLut),
OCULUS_BIND_ENTRY_POINT(UpdatePassthroughColorLut),
OCULUS_BIND_ENTRY_POINT(GetPassthroughCapabilities),
OCULUS_BIND_ENTRY_POINT(GetPassthroughPreferences),
// OVR_Plugin_MixedReality.h
OCULUS_BIND_ENTRY_POINT(InitializeMixedReality),
OCULUS_BIND_ENTRY_POINT(ShutdownMixedReality),
OCULUS_BIND_ENTRY_POINT(GetMixedRealityInitialized),
OCULUS_BIND_ENTRY_POINT(UpdateExternalCamera),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraCount),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraName),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraIntrinsics),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraExtrinsics),
// OVR_Plugin_Media.h
OCULUS_BIND_ENTRY_POINT(Media_Initialize),
OCULUS_BIND_ENTRY_POINT(Media_Shutdown),
OCULUS_BIND_ENTRY_POINT(Media_GetInitialized),
OCULUS_BIND_ENTRY_POINT(Media_Update),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcActivationMode),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcActivationMode),
OCULUS_BIND_ENTRY_POINT(Media_IsMrcEnabled),
OCULUS_BIND_ENTRY_POINT(Media_IsMrcActivated),
OCULUS_BIND_ENTRY_POINT(Media_UseMrcDebugCamera),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcInputVideoBufferType),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcInputVideoBufferType),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcFrameSize),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcFrameSize),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcAudioSampleRate),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcAudioSampleRate),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcFrameImageFlipped),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcFrameImageFlipped),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcFrameInverseAlpha),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcFrameInverseAlpha),
OCULUS_BIND_ENTRY_POINT(Media_SetAvailableQueueIndexVulkan),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrame),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrameWithDualTextures),
OCULUS_BIND_ENTRY_POINT(Media_SyncMrcFrame),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrameWithPoseTime),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrameDualTexturesWithPoseTime),
OCULUS_BIND_ENTRY_POINT(Media_SetHeadsetControllerPose),
OCULUS_BIND_ENTRY_POINT(Media_EnumerateCameraAnchorHandles),
OCULUS_BIND_ENTRY_POINT(Media_GetCurrentCameraAnchorHandle),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraAnchorName),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraAnchorHandle),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraAnchorType),
OCULUS_BIND_ENTRY_POINT(Media_CreateCustomCameraAnchor),
OCULUS_BIND_ENTRY_POINT(Media_DestroyCustomCameraAnchor),
OCULUS_BIND_ENTRY_POINT(Media_GetCustomCameraAnchorPose),
OCULUS_BIND_ENTRY_POINT(Media_SetCustomCameraAnchorPose),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraMinMaxDistance),
OCULUS_BIND_ENTRY_POINT(Media_SetCameraMinMaxDistance),
OCULUS_BIND_ENTRY_POINT(SetControllerDrivenHandPoses),
OCULUS_BIND_ENTRY_POINT(SetControllerDrivenHandPosesAreNatural),
};
#undef OCULUS_BIND_ENTRY_POINT
bool result = true;
for (int i = 0; i < UE_ARRAY_COUNT(entryPointArray); ++i)
{
*(entryPointArray[i].EntryPointPtr) = LoadEntryPoint(LibraryHandle, entryPointArray[i].EntryPointName);
if (*entryPointArray[i].EntryPointPtr == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Error, TEXT("OculusPlugin EntryPoint could not be loaded: %s"), ANSI_TO_TCHAR(entryPointArray[i].EntryPointName));
result = false;
}
}
wrapper->Initialized = true;
if (result)
{
UE_LOG(LogOculusPluginWrapper, Log, TEXT("OculusPlugin initialized successfully"));
}
else
{
DestroyOculusPluginWrapper(wrapper);
}
return result;
}
void OculusPluginWrapper::DestroyOculusPluginWrapper(OculusPluginWrapper* wrapper)
{
if (!wrapper->Initialized)
return;
wrapper->Reset();
UE_LOG(LogOculusPluginWrapper, Log, TEXT("OculusPlugin destroyed successfully"));
}
static void* LoadEntryPoint(void* Handle, const char* EntryPointName)
{
if (Handle == nullptr)
return nullptr;
#if PLATFORM_WINDOWS
void* ptr = GetProcAddress((HMODULE)Handle, EntryPointName);
if (ptr == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Error, TEXT("Unable to load entry point: %s"), ANSI_TO_TCHAR(EntryPointName));
}
return ptr;
#elif PLATFORM_ANDROID
void* ptr = dlsym(Handle, EntryPointName);
if (ptr == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Error, TEXT("Unable to load entry point: %s, error %s"), ANSI_TO_TCHAR(EntryPointName), ANSI_TO_TCHAR(dlerror()));
}
return ptr;
#else
UE_LOG(LogOculusPluginWrapper, Error, TEXT("LoadEntryPoint: Unsupported platform"));
return nullptr;
#endif
}

View File

@@ -0,0 +1,414 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include <memory.h>
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(push, 8)
#endif
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#pragma warning(push)
#pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union
//#pragma warning(disable:4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
#define OVRP_EXPORT typedef
#include "OVR_Plugin.h"
#include "OVR_Plugin_Insight.h"
#include "OVR_Plugin_MixedReality.h"
#include "OVR_Plugin_Media.h"
#undef OVRP_EXPORT
#pragma warning(pop)
#if PLATFORM_WINDOWS
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(pop)
#endif
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
DECLARE_LOG_CATEGORY_EXTERN(LogOculusPluginWrapper, Log, All);
#define OCULUS_DECLARE_ENTRY_POINT(Func) ovrp_##Func* Func
struct OculusPluginWrapper
{
OculusPluginWrapper()
{
Reset();
}
void Reset()
{
memset(this, 0, sizeof(OculusPluginWrapper));
ovrpHeaderVersion.MajorVersion = OVRP_MAJOR_VERSION;
ovrpHeaderVersion.MinorVersion = OVRP_MINOR_VERSION;
ovrpHeaderVersion.PatchVersion = OVRP_PATCH_VERSION;
}
bool IsInitialized() const
{
return Initialized;
}
// OVR_Plugin.h
OCULUS_DECLARE_ENTRY_POINT(PreInitialize5);
OCULUS_DECLARE_ENTRY_POINT(GetInitialized);
OCULUS_DECLARE_ENTRY_POINT(Initialize7);
OCULUS_DECLARE_ENTRY_POINT(Shutdown2);
OCULUS_DECLARE_ENTRY_POINT(SetLogCallback2);
OCULUS_DECLARE_ENTRY_POINT(GetVersion2);
OCULUS_DECLARE_ENTRY_POINT(GetNativeSDKVersion2);
OCULUS_DECLARE_ENTRY_POINT(GetNativeSDKPointer2);
OCULUS_DECLARE_ENTRY_POINT(GetDisplayAdapterId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioOutId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioOutDeviceId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioInId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioInDeviceId2);
OCULUS_DECLARE_ENTRY_POINT(GetInstanceExtensionsVk);
OCULUS_DECLARE_ENTRY_POINT(GetDeviceExtensionsVk);
OCULUS_DECLARE_ENTRY_POINT(SetupDistortionWindow3);
OCULUS_DECLARE_ENTRY_POINT(DestroyDistortionWindow2);
OCULUS_DECLARE_ENTRY_POINT(GetDominantHand);
OCULUS_DECLARE_ENTRY_POINT(SetRemoteHandedness);
OCULUS_DECLARE_ENTRY_POINT(SetColorScaleAndOffset);
OCULUS_DECLARE_ENTRY_POINT(SetupLayer);
OCULUS_DECLARE_ENTRY_POINT(SetupLayerDepth);
OCULUS_DECLARE_ENTRY_POINT(SetEyeFovPremultipliedAlphaMode);
OCULUS_DECLARE_ENTRY_POINT(GetEyeFovLayerId);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTextureStageCount);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTexture2);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTextureFoveation);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTextureSpaceWarp);
OCULUS_DECLARE_ENTRY_POINT(CalculateEyeLayerDesc3);
OCULUS_DECLARE_ENTRY_POINT(GetLayerAndroidSurfaceObject);
OCULUS_DECLARE_ENTRY_POINT(GetLayerOcclusionMesh);
OCULUS_DECLARE_ENTRY_POINT(DestroyLayer);
OCULUS_DECLARE_ENTRY_POINT(CalculateLayerDesc);
OCULUS_DECLARE_ENTRY_POINT(CalculateEyeLayerDesc2);
OCULUS_DECLARE_ENTRY_POINT(CalculateEyePreviewRect);
OCULUS_DECLARE_ENTRY_POINT(SetupMirrorTexture2);
OCULUS_DECLARE_ENTRY_POINT(DestroyMirrorTexture2);
OCULUS_DECLARE_ENTRY_POINT(GetAdaptiveGpuPerformanceScale2);
OCULUS_DECLARE_ENTRY_POINT(GetAppCpuStartToGpuEndTime2);
OCULUS_DECLARE_ENTRY_POINT(GetEyePixelsPerTanAngleAtCenter2);
OCULUS_DECLARE_ENTRY_POINT(GetHmdToEyeOffset2);
OCULUS_DECLARE_ENTRY_POINT(Update3);
OCULUS_DECLARE_ENTRY_POINT(WaitToBeginFrame);
OCULUS_DECLARE_ENTRY_POINT(BeginFrame4);
OCULUS_DECLARE_ENTRY_POINT(UpdateFoveation);
OCULUS_DECLARE_ENTRY_POINT(EndFrame4);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingOrientationSupported2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingOrientationEnabled2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingOrientationEnabled2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingPositionSupported2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingPositionEnabled2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingPositionEnabled2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingIPDEnabled2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingIPDEnabled2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingCalibratedOrigin2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingCalibratedOrigin2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingOriginType2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingOriginType2);
OCULUS_DECLARE_ENTRY_POINT(RecenterTrackingOrigin2);
OCULUS_DECLARE_ENTRY_POINT(GetNodePresent2);
OCULUS_DECLARE_ENTRY_POINT(GetNodeOrientationTracked2);
OCULUS_DECLARE_ENTRY_POINT(GetNodeOrientationValid);
OCULUS_DECLARE_ENTRY_POINT(GetNodePositionTracked2);
OCULUS_DECLARE_ENTRY_POINT(GetNodePositionValid);
OCULUS_DECLARE_ENTRY_POINT(SetNodePositionTracked2);
OCULUS_DECLARE_ENTRY_POINT(GetNodePoseState3);
OCULUS_DECLARE_ENTRY_POINT(GetNodePoseStateRaw);
OCULUS_DECLARE_ENTRY_POINT(GetNodeFrustum2);
OCULUS_DECLARE_ENTRY_POINT(SetHeadPoseModifier);
OCULUS_DECLARE_ENTRY_POINT(GetHeadPoseModifier);
OCULUS_DECLARE_ENTRY_POINT(GetControllerState4);
OCULUS_DECLARE_ENTRY_POINT(GetControllerState5);
OCULUS_DECLARE_ENTRY_POINT(GetControllerState6);
OCULUS_DECLARE_ENTRY_POINT(GetActiveController2);
OCULUS_DECLARE_ENTRY_POINT(GetConnectedControllers2);
OCULUS_DECLARE_ENTRY_POINT(SetControllerVibration2);
OCULUS_DECLARE_ENTRY_POINT(SetControllerLocalizedVibration);
OCULUS_DECLARE_ENTRY_POINT(SetControllerHapticsAmplitudeEnvelope);
OCULUS_DECLARE_ENTRY_POINT(SetControllerHapticsPcm);
OCULUS_DECLARE_ENTRY_POINT(GetControllerHapticsDesc2);
OCULUS_DECLARE_ENTRY_POINT(GetControllerHapticsState2);
OCULUS_DECLARE_ENTRY_POINT(GetControllerSampleRateHz);
OCULUS_DECLARE_ENTRY_POINT(SetControllerHaptics2);
OCULUS_DECLARE_ENTRY_POINT(SetSuggestedCpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(GetSuggestedCpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(SetSuggestedGpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(GetSuggestedGpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(GetAppCPUPriority2);
OCULUS_DECLARE_ENTRY_POINT(SetAppCPUPriority2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemPowerSavingMode2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemDisplayFrequency2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemDisplayAvailableFrequencies);
OCULUS_DECLARE_ENTRY_POINT(SetSystemDisplayFrequency);
OCULUS_DECLARE_ENTRY_POINT(GetSystemVSyncCount2);
OCULUS_DECLARE_ENTRY_POINT(SetSystemVSyncCount2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemProductName2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemRegion2);
OCULUS_DECLARE_ENTRY_POINT(ShowSystemUI2);
OCULUS_DECLARE_ENTRY_POINT(GetAppHasVrFocus2);
OCULUS_DECLARE_ENTRY_POINT(GetAppHasInputFocus);
OCULUS_DECLARE_ENTRY_POINT(GetAppHasSystemOverlayPresent);
OCULUS_DECLARE_ENTRY_POINT(GetAppShouldQuit2);
OCULUS_DECLARE_ENTRY_POINT(GetAppShouldRecenter2);
OCULUS_DECLARE_ENTRY_POINT(GetAppShouldRecreateDistortionWindow2);
OCULUS_DECLARE_ENTRY_POINT(GetAppLatencyTimings2);
OCULUS_DECLARE_ENTRY_POINT(SetAppEngineInfo2);
OCULUS_DECLARE_ENTRY_POINT(GetUserPresent2);
OCULUS_DECLARE_ENTRY_POINT(GetUserIPD2);
OCULUS_DECLARE_ENTRY_POINT(SetUserIPD2);
OCULUS_DECLARE_ENTRY_POINT(GetUserEyeHeight2);
OCULUS_DECLARE_ENTRY_POINT(SetUserEyeHeight2);
OCULUS_DECLARE_ENTRY_POINT(GetUserNeckEyeDistance2);
OCULUS_DECLARE_ENTRY_POINT(SetUserNeckEyeDistance2);
OCULUS_DECLARE_ENTRY_POINT(SetupDisplayObjects2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemMultiViewSupported2);
OCULUS_DECLARE_ENTRY_POINT(GetEyeTextureArraySupported2);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryConfigured2);
OCULUS_DECLARE_ENTRY_POINT(GetDepthCompositingSupported);
OCULUS_DECLARE_ENTRY_POINT(TestBoundaryNode2);
OCULUS_DECLARE_ENTRY_POINT(TestBoundaryPoint2);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryGeometry3);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryDimensions2);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryVisible2);
OCULUS_DECLARE_ENTRY_POINT(SetBoundaryVisible2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemHeadsetType2);
OCULUS_DECLARE_ENTRY_POINT(GetAppPerfStats2);
OCULUS_DECLARE_ENTRY_POINT(ResetAppPerfStats2);
OCULUS_DECLARE_ENTRY_POINT(GetAppFramerate2);
OCULUS_DECLARE_ENTRY_POINT(IsPerfMetricsSupported);
OCULUS_DECLARE_ENTRY_POINT(GetPerfMetricsFloat);
OCULUS_DECLARE_ENTRY_POINT(GetPerfMetricsInt);
OCULUS_DECLARE_ENTRY_POINT(SetHandNodePoseStateLatency);
OCULUS_DECLARE_ENTRY_POINT(GetHandNodePoseStateLatency);
OCULUS_DECLARE_ENTRY_POINT(GetSystemRecommendedMSAALevel2);
OCULUS_DECLARE_ENTRY_POINT(SetInhibitSystemUX2);
OCULUS_DECLARE_ENTRY_POINT(GetTiledMultiResSupported);
OCULUS_DECLARE_ENTRY_POINT(GetTiledMultiResLevel);
OCULUS_DECLARE_ENTRY_POINT(SetTiledMultiResLevel);
OCULUS_DECLARE_ENTRY_POINT(GetTiledMultiResDynamic);
OCULUS_DECLARE_ENTRY_POINT(SetTiledMultiResDynamic);
OCULUS_DECLARE_ENTRY_POINT(GetFoveationEyeTrackedSupported);
OCULUS_DECLARE_ENTRY_POINT(GetFoveationEyeTracked);
OCULUS_DECLARE_ENTRY_POINT(SetFoveationEyeTracked);
OCULUS_DECLARE_ENTRY_POINT(GetFoveationEyeTrackedCenter);
OCULUS_DECLARE_ENTRY_POINT(GetGPUUtilSupported);
OCULUS_DECLARE_ENTRY_POINT(GetGPUUtilLevel);
OCULUS_DECLARE_ENTRY_POINT(SetThreadPerformance);
OCULUS_DECLARE_ENTRY_POINT(AutoThreadScheduling);
OCULUS_DECLARE_ENTRY_POINT(GetGPUFrameTime);
OCULUS_DECLARE_ENTRY_POINT(GetViewportStencil);
OCULUS_DECLARE_ENTRY_POINT(SetDeveloperTelemetryConsent);
OCULUS_DECLARE_ENTRY_POINT(SendEvent);
OCULUS_DECLARE_ENTRY_POINT(SendEvent2);
OCULUS_DECLARE_ENTRY_POINT(AddCustomMetadata);
OCULUS_DECLARE_ENTRY_POINT(SetDeveloperMode);
OCULUS_DECLARE_ENTRY_POINT(GetCurrentTrackingTransformPose);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingTransformRawPose);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingTransformRelativePose);
OCULUS_DECLARE_ENTRY_POINT(GetTimeInSeconds);
//OCULUS_DECLARE_ENTRY_POINT(GetPTWNear);
OCULUS_DECLARE_ENTRY_POINT(GetASWVelocityScale);
OCULUS_DECLARE_ENTRY_POINT(GetASWDepthScale);
OCULUS_DECLARE_ENTRY_POINT(GetASWAdaptiveMode);
OCULUS_DECLARE_ENTRY_POINT(SetASWAdaptiveMode);
OCULUS_DECLARE_ENTRY_POINT(IsRequestingASWData);
OCULUS_DECLARE_ENTRY_POINT(GetPredictedDisplayTime);
OCULUS_DECLARE_ENTRY_POINT(GetHandTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetHandState);
OCULUS_DECLARE_ENTRY_POINT(GetHandState2);
OCULUS_DECLARE_ENTRY_POINT(GetSkeleton2);
OCULUS_DECLARE_ENTRY_POINT(GetMesh);
OCULUS_DECLARE_ENTRY_POINT(GetLocalTrackingSpaceRecenterCount);
OCULUS_DECLARE_ENTRY_POINT(GetSystemHmd3DofModeEnabled);
OCULUS_DECLARE_ENTRY_POINT(SetClientColorDesc);
OCULUS_DECLARE_ENTRY_POINT(GetHmdColorDesc);
OCULUS_DECLARE_ENTRY_POINT(PollEvent);
OCULUS_DECLARE_ENTRY_POINT(GetNativeXrApiType);
OCULUS_DECLARE_ENTRY_POINT(GetLocalDimmingSupported);
OCULUS_DECLARE_ENTRY_POINT(SetLocalDimming);
OCULUS_DECLARE_ENTRY_POINT(GetCurrentInteractionProfile);
OCULUS_DECLARE_ENTRY_POINT(GetLayerRecommendedResolution);
OCULUS_DECLARE_ENTRY_POINT(IsLayerShapeSupported);
OCULUS_DECLARE_ENTRY_POINT(SetEyeBufferSharpenType);
OCULUS_DECLARE_ENTRY_POINT(InitializeEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(DestroyEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthTextureDesc);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthTextureStageCount);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthTexture);
OCULUS_DECLARE_ENTRY_POINT(SetEnvironmentDepthHandRemoval);
OCULUS_DECLARE_ENTRY_POINT(StartEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(StopEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthFrameDesc);
#ifndef OVRPLUGIN_JNI_LIB_EXCLUDED
OCULUS_DECLARE_ENTRY_POINT(GetSystemVolume2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemHeadphonesPresent2);
#endif
// Anchors
OCULUS_DECLARE_ENTRY_POINT(LocateSpace);
OCULUS_DECLARE_ENTRY_POINT(LocateSpace2);
OCULUS_DECLARE_ENTRY_POINT(CreateSpatialAnchor);
OCULUS_DECLARE_ENTRY_POINT(DestroySpace);
OCULUS_DECLARE_ENTRY_POINT(SetSpaceComponentStatus);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceComponentStatus);
OCULUS_DECLARE_ENTRY_POINT(EnumerateSpaceSupportedComponents);
OCULUS_DECLARE_ENTRY_POINT(QuerySpaces);
OCULUS_DECLARE_ENTRY_POINT(RetrieveSpaceQueryResults);
OCULUS_DECLARE_ENTRY_POINT(SaveSpace);
OCULUS_DECLARE_ENTRY_POINT(EraseSpace);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceUuid);
OCULUS_DECLARE_ENTRY_POINT(SaveSpaceList);
OCULUS_DECLARE_ENTRY_POINT(ShareSpaces);
OCULUS_DECLARE_ENTRY_POINT(CreateSpaceUser);
OCULUS_DECLARE_ENTRY_POINT(DestroySpaceUser);
// Scene
OCULUS_DECLARE_ENTRY_POINT(GetSpaceContainer);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceBoundingBox2D);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceBoundingBox3D);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceSemanticLabels);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceRoomLayout);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceBoundary2D);
OCULUS_DECLARE_ENTRY_POINT(RequestSceneCapture);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceTriangleMesh);
// Local Groups
// MovementSDK
OCULUS_DECLARE_ENTRY_POINT(GetBodyTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetBodyTrackingSupported);
OCULUS_DECLARE_ENTRY_POINT(StopBodyTracking);
OCULUS_DECLARE_ENTRY_POINT(GetBodyState4);
OCULUS_DECLARE_ENTRY_POINT(GetFullBodyTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(StartBodyTracking2);
OCULUS_DECLARE_ENTRY_POINT(RequestBodyTrackingFidelity);
OCULUS_DECLARE_ENTRY_POINT(ResetBodyTrackingCalibration);
OCULUS_DECLARE_ENTRY_POINT(SuggestBodyTrackingCalibrationOverride);
OCULUS_DECLARE_ENTRY_POINT(GetFaceTracking2Enabled);
OCULUS_DECLARE_ENTRY_POINT(GetFaceTracking2Supported);
OCULUS_DECLARE_ENTRY_POINT(GetFaceState2);
OCULUS_DECLARE_ENTRY_POINT(StartFaceTracking2);
OCULUS_DECLARE_ENTRY_POINT(StopFaceTracking2);
OCULUS_DECLARE_ENTRY_POINT(GetEyeTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetEyeTrackingSupported);
OCULUS_DECLARE_ENTRY_POINT(GetEyeGazesState);
OCULUS_DECLARE_ENTRY_POINT(StartEyeTracking);
OCULUS_DECLARE_ENTRY_POINT(StopEyeTracking);
// QPL
OCULUS_DECLARE_ENTRY_POINT(QplMarkerStart);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerEnd);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerPoint);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerPointCached);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerAnnotation);
OCULUS_DECLARE_ENTRY_POINT(QplCreateMarkerHandle);
OCULUS_DECLARE_ENTRY_POINT(QplDestroyMarkerHandle);
OCULUS_DECLARE_ENTRY_POINT(OnEditorShutdown);
OCULUS_DECLARE_ENTRY_POINT(QplSetConsent);
//OVR_Plugin_Insight.h
OCULUS_DECLARE_ENTRY_POINT(InitializeInsightPassthrough);
OCULUS_DECLARE_ENTRY_POINT(ShutdownInsightPassthrough);
OCULUS_DECLARE_ENTRY_POINT(GetInsightPassthroughInitialized);
OCULUS_DECLARE_ENTRY_POINT(GetInsightPassthroughInitializationState);
OCULUS_DECLARE_ENTRY_POINT(CreateInsightTriangleMesh);
OCULUS_DECLARE_ENTRY_POINT(DestroyInsightTriangleMesh);
OCULUS_DECLARE_ENTRY_POINT(AddInsightPassthroughSurfaceGeometry);
OCULUS_DECLARE_ENTRY_POINT(DestroyInsightPassthroughGeometryInstance);
OCULUS_DECLARE_ENTRY_POINT(UpdateInsightPassthroughGeometryTransform);
OCULUS_DECLARE_ENTRY_POINT(SetInsightPassthroughStyle);
OCULUS_DECLARE_ENTRY_POINT(SetInsightPassthroughStyle2);
OCULUS_DECLARE_ENTRY_POINT(GetPassthroughCapabilityFlags);
OCULUS_DECLARE_ENTRY_POINT(CreatePassthroughColorLut);
OCULUS_DECLARE_ENTRY_POINT(DestroyPassthroughColorLut);
OCULUS_DECLARE_ENTRY_POINT(UpdatePassthroughColorLut);
OCULUS_DECLARE_ENTRY_POINT(GetPassthroughCapabilities);
OCULUS_DECLARE_ENTRY_POINT(GetPassthroughPreferences);
//OVR_Plugin_MixedReality.h
OCULUS_DECLARE_ENTRY_POINT(InitializeMixedReality);
OCULUS_DECLARE_ENTRY_POINT(ShutdownMixedReality);
OCULUS_DECLARE_ENTRY_POINT(GetMixedRealityInitialized);
OCULUS_DECLARE_ENTRY_POINT(UpdateExternalCamera);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraCount);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraName);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraIntrinsics);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraExtrinsics);
// OVR_Plugin_Media.h
OCULUS_DECLARE_ENTRY_POINT(Media_Initialize);
OCULUS_DECLARE_ENTRY_POINT(Media_Shutdown);
OCULUS_DECLARE_ENTRY_POINT(Media_GetInitialized);
OCULUS_DECLARE_ENTRY_POINT(Media_Update);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcActivationMode);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcActivationMode);
OCULUS_DECLARE_ENTRY_POINT(Media_IsMrcEnabled);
OCULUS_DECLARE_ENTRY_POINT(Media_IsMrcActivated);
OCULUS_DECLARE_ENTRY_POINT(Media_UseMrcDebugCamera);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcInputVideoBufferType);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcInputVideoBufferType);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcFrameSize);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcFrameSize);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcAudioSampleRate);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcAudioSampleRate);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcFrameImageFlipped);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcFrameImageFlipped);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcFrameInverseAlpha);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcFrameInverseAlpha);
OCULUS_DECLARE_ENTRY_POINT(Media_SetAvailableQueueIndexVulkan);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrame);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrameWithDualTextures);
OCULUS_DECLARE_ENTRY_POINT(Media_SyncMrcFrame);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrameWithPoseTime);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrameDualTexturesWithPoseTime);
OCULUS_DECLARE_ENTRY_POINT(Media_SetHeadsetControllerPose);
OCULUS_DECLARE_ENTRY_POINT(Media_EnumerateCameraAnchorHandles);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCurrentCameraAnchorHandle);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraAnchorName);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraAnchorHandle);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraAnchorType);
OCULUS_DECLARE_ENTRY_POINT(Media_CreateCustomCameraAnchor);
OCULUS_DECLARE_ENTRY_POINT(Media_DestroyCustomCameraAnchor);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCustomCameraAnchorPose);
OCULUS_DECLARE_ENTRY_POINT(Media_SetCustomCameraAnchorPose);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraMinMaxDistance);
OCULUS_DECLARE_ENTRY_POINT(Media_SetCameraMinMaxDistance);
OCULUS_DECLARE_ENTRY_POINT(SetControllerDrivenHandPoses);
OCULUS_DECLARE_ENTRY_POINT(SetControllerDrivenHandPosesAreNatural);
static bool InitializeOculusPluginWrapper(OculusPluginWrapper* wrapper);
static void DestroyOculusPluginWrapper(OculusPluginWrapper* wrapper);
private:
ovrpVersion ovrpHeaderVersion;
bool Initialized;
};
#undef OCULUS_DECLARE_ENTRY_POINT

View File

@@ -0,0 +1,106 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRQPL.h"
#include "OculusXRHMDModule.h"
#include "OculusXRPluginWrapper.h"
namespace OculusXRTelemetry
{
namespace QPL
{
bool MarkerStart(const int MarkerId, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerStart(
MarkerId,
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerEnd(const int MarkerId, const EAction Action, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerEnd(
MarkerId,
static_cast<short>(Action),
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerPoint(const int MarkerId, const char* Name, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerPoint(
MarkerId,
Name,
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerPointCached(const int MarkerId, const int NameHandle, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerPointCached(
MarkerId,
NameHandle,
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerAnnotation(const int MarkerId, const char* AnnotationKey, const char* AnnotationValue, const FTelemetryInstanceKey InstanceKey)
{
if (nullptr == AnnotationValue)
{
return false;
}
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerAnnotation(
MarkerId,
AnnotationKey,
AnnotationValue,
InstanceKey.GetValue());
return OVRP_SUCCESS(Result);
}
bool CreateMarkerHandle(const char* Name, int* NameHandle)
{
if (nullptr == NameHandle)
{
return false;
}
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplCreateMarkerHandle(
Name,
NameHandle);
return OVRP_SUCCESS(Result);
}
bool DestroyMarkerHandle(const int NameHandle)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplDestroyMarkerHandle(
NameHandle);
return OVRP_SUCCESS(Result);
}
bool OnEditorShutdown()
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().OnEditorShutdown();
return OVRP_SUCCESS(Result);
}
} // namespace QPL
bool FQPLBackend::MarkerStart(const int MarkerId, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp) { return QPL::MarkerStart(MarkerId, InstanceKey, Timestamp); }
bool FQPLBackend::MarkerEnd(const int MarkerId, const EAction Action, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp) { return QPL::MarkerEnd(MarkerId, Action, InstanceKey, Timestamp); };
bool FQPLBackend::MarkerPoint(const int MarkerId, const char* Name, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp) { return QPL::MarkerPoint(MarkerId, Name, InstanceKey, Timestamp); };
bool FQPLBackend::MarkerPointCached(const int MarkerId, const int NameHandle, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp) { return QPL::MarkerPointCached(MarkerId, NameHandle, InstanceKey, Timestamp); };
bool FQPLBackend::MarkerAnnotation(const int MarkerId, const char* AnnotationKey, const char* AnnotationValue, const FTelemetryInstanceKey InstanceKey) { return QPL::MarkerAnnotation(MarkerId, AnnotationKey, AnnotationValue, InstanceKey); };
bool FQPLBackend::CreateMarkerHandle(const char* Name, int* NameHandle) { return QPL::CreateMarkerHandle(Name, NameHandle); };
bool FQPLBackend::DestroyMarkerHandle(const int NameHandle) { return QPL::DestroyMarkerHandle(NameHandle); };
bool FQPLBackend::OnEditorShutdown() { return QPL::OnEditorShutdown(); };
} // namespace OculusXRTelemetry

View File

@@ -0,0 +1,27 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRResourceHolder.h"
#include "HeadMountedDisplayTypes.h" // for LogHMD
#include "UObject/ConstructorHelpers.h"
#include "Materials/Material.h"
//////////////////////////////////////////////////////////////////////////
// UOculusResourceManager
UOculusXRResourceHolder::UOculusXRResourceHolder(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
static ConstructorHelpers::FObjectFinder<UMaterial> StaticPokeAHoleMaterial(TEXT("/OculusXR/Materials/PokeAHoleMaterial"));
PokeAHoleMaterial = StaticPokeAHoleMaterial.Object;
if (!PokeAHoleMaterial)
{
UE_LOG(LogHMD, Error, TEXT("Unable to load PokeAHoleMaterial"));
}
else
{
UE_LOG(LogHMD, Log, TEXT("PokeAHoleMaterial loaded successfully"));
}
}

View File

@@ -0,0 +1,22 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Materials/MaterialInterface.h"
#include "OculusXRResourceHolder.generated.h"
/**
*
*/
UCLASS()
class UOculusXRResourceHolder : public UObject
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY()
UMaterial* PokeAHoleMaterial;
};

View File

@@ -0,0 +1,193 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSceneCaptureCubemap.h"
#include "OculusXRHMDPrivate.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/World.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/TextureRenderTarget2D.h"
#include "TextureResource.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "XRThreadUtils.h"
#include "RenderingThread.h"
//-------------------------------------------------------------------------------------------------
// UOculusXRSceneCaptureCubemap
//-------------------------------------------------------------------------------------------------
UOculusXRSceneCaptureCubemap::UOculusXRSceneCaptureCubemap()
: Stage(None)
, CaptureBoxSideRes(2048)
, CaptureFormat(EPixelFormat::PF_A16B16G16R16)
, OverriddenLocation(FVector::ZeroVector)
, OverriddenOrientation(FQuat::Identity)
, CaptureOffset(FVector::ZeroVector)
{
}
void UOculusXRSceneCaptureCubemap::StartCapture(UWorld* World, uint32 InCaptureBoxSideRes, EPixelFormat InFormat)
{
CaptureBoxSideRes = InCaptureBoxSideRes;
CaptureFormat = InFormat;
FVector Location = OverriddenLocation;
FQuat Orientation = OverriddenOrientation;
APlayerController* CapturePlayerController = UGameplayStatics::GetPlayerController(GWorld, 0);
if (CapturePlayerController)
{
FRotator Rotation;
CapturePlayerController->GetPlayerViewPoint(Location, Rotation);
Rotation.Pitch = Rotation.Roll = 0;
Orientation = FQuat(Rotation);
Location += CaptureOffset;
}
if (!OverriddenOrientation.IsIdentity())
{
Orientation = OverriddenOrientation;
}
if (!OverriddenLocation.IsZero())
{
Location = OverriddenLocation;
}
const FVector ZAxis(0, 0, 1);
const FVector YAxis(0, 1, 0);
const FQuat FaceOrientations[] = { { ZAxis, PI / 2 }, { ZAxis, -PI / 2 }, // right, left
{ YAxis, -PI / 2 }, { YAxis, PI / 2 }, // top, bottom
{ ZAxis, 0 }, { ZAxis, -PI } }; // front, back
for (int i = 0; i < 6; ++i)
{
USceneCaptureComponent2D* CaptureComponent = NewObject<USceneCaptureComponent2D>();
CaptureComponent->SetVisibility(true);
CaptureComponent->SetHiddenInGame(false);
CaptureComponent->FOVAngle = 90.f;
CaptureComponent->bCaptureEveryFrame = true;
CaptureComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
const FName TargetName = MakeUniqueObjectName(this, UTextureRenderTarget2D::StaticClass(), TEXT("SceneCaptureTextureTarget"));
CaptureComponent->TextureTarget = NewObject<UTextureRenderTarget2D>(this, TargetName);
CaptureComponent->TextureTarget->InitCustomFormat(CaptureBoxSideRes, CaptureBoxSideRes, CaptureFormat, false);
CaptureComponents.Add(CaptureComponent);
CaptureComponent->RegisterComponentWithWorld(GWorld);
CaptureComponent->SetWorldLocationAndRotation(Location, Orientation * FaceOrientations[i]);
CaptureComponent->UpdateContent();
}
Stage = SettingPos;
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.bNoFail = true;
SpawnInfo.ObjectFlags = RF_Transient;
AStaticMeshActor* InGameActor;
InGameActor = World->SpawnActor<AStaticMeshActor>(SpawnInfo);
OutputDir = FPaths::ProjectSavedDir() + TEXT("/Cubemaps");
IFileManager::Get().MakeDirectory(*OutputDir);
}
void UOculusXRSceneCaptureCubemap::Tick(float DeltaTime)
{
ExecuteOnRenderThread([]() {
TickRenderingTickables();
});
if (Stage == SettingPos)
{
Stage = Capturing;
return;
}
//Read Whole Capture Buffer
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
TArray<FColor> OneFaceSurface, WholeCubemapData;
OneFaceSurface.AddUninitialized(CaptureBoxSideRes * CaptureBoxSideRes);
WholeCubemapData.AddUninitialized(CaptureBoxSideRes * 6 * CaptureBoxSideRes);
// Read pixels
for (int cubeFaceIdx = 0; cubeFaceIdx < 6; ++cubeFaceIdx)
{
auto RenderTarget = CaptureComponents[cubeFaceIdx]->TextureTarget->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixelsPtr(OneFaceSurface.GetData(), FReadSurfaceDataFlags());
// enforce alpha to be 1
for (FColor& Color : OneFaceSurface)
{
Color.A = 255;
}
// copy subimage into whole cubemap array
const uint32 Stride = CaptureBoxSideRes * 6;
const uint32 XOff = cubeFaceIdx * CaptureBoxSideRes;
const uint32 StripSizeInBytes = CaptureBoxSideRes * sizeof(FColor);
for (uint32 y = 0; y < CaptureBoxSideRes; ++y)
{
FMemory::Memcpy(WholeCubemapData.GetData() + XOff + y * Stride, OneFaceSurface.GetData() + y * CaptureBoxSideRes, StripSizeInBytes);
}
}
ImageWrapper->SetRaw(WholeCubemapData.GetData(), WholeCubemapData.GetAllocatedSize(), CaptureBoxSideRes * 6, CaptureBoxSideRes, ERGBFormat::BGRA, 8);
const TArray64<uint8>& PNGData = ImageWrapper->GetCompressed(100);
const FString Filename = OutputDir + FString::Printf(TEXT("/Cubemap-%d-%s.png"), CaptureBoxSideRes, *FDateTime::Now().ToString(TEXT("%m.%d-%H.%M.%S")));
FFileHelper::SaveArrayToFile(PNGData, *Filename);
check(Stage == Capturing);
Stage = Finished;
for (int i = 0; i < CaptureComponents.Num(); ++i)
{
CaptureComponents[i]->UnregisterComponent();
}
CaptureComponents.SetNum(0);
RemoveFromRoot(); // We're done here, so remove ourselves from the root set. @TODO: Fix this later
}
#if !UE_BUILD_SHIPPING
void UOculusXRSceneCaptureCubemap::CaptureCubemapCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
bool bCreateOculusMobileCubemap = false;
FVector CaptureOffset(FVector::ZeroVector);
float Yaw = 0.f;
for (const FString& Arg : Args)
{
FParse::Value(*Arg, TEXT("XOFF="), CaptureOffset.X);
FParse::Value(*Arg, TEXT("YOFF="), CaptureOffset.Y);
FParse::Value(*Arg, TEXT("ZOFF="), CaptureOffset.Z);
FParse::Value(*Arg, TEXT("YAW="), Yaw);
if (Arg.Equals(TEXT("MOBILE"), ESearchCase::IgnoreCase))
{
bCreateOculusMobileCubemap = true;
}
}
UOculusXRSceneCaptureCubemap* CubemapCapturer = NewObject<UOculusXRSceneCaptureCubemap>();
CubemapCapturer->AddToRoot(); // TODO: Don't add the object to the GC root
CubemapCapturer->SetOffset((FVector)CaptureOffset);
if (Yaw != 0.f)
{
FRotator Rotation(FRotator::ZeroRotator);
Rotation.Yaw = Yaw;
const FQuat Orient(Rotation);
CubemapCapturer->SetInitialOrientation(Orient);
}
const uint32 CaptureHeight = 2048;
CubemapCapturer->StartCapture(World, bCreateOculusMobileCubemap ? CaptureHeight / 2 : CaptureHeight);
}
#endif

View File

@@ -0,0 +1,79 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "UObject/ObjectMacros.h"
#include "Tickable.h"
#include "OculusXRSceneCaptureCubemap.generated.h"
//-------------------------------------------------------------------------------------------------
// UOculusXRSceneCaptureCubemap
//-------------------------------------------------------------------------------------------------
class USceneCaptureComponent2D;
UCLASS()
class UOculusXRSceneCaptureCubemap : public UObject, public FTickableGameObject
{
GENERATED_BODY()
public:
UOculusXRSceneCaptureCubemap();
virtual void Tick(float DeltaTime) override;
virtual bool IsTickable() const override
{
return CaptureComponents.Num() != 0 && Stage != None;
}
virtual bool IsTickableWhenPaused() const override
{
return IsTickable();
}
virtual TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(USceneCapturer, STATGROUP_Tickables);
}
// init capture params and start
void StartCapture(UWorld* World, uint32 InCaptureBoxSideRes, EPixelFormat InFormat = EPixelFormat::PF_A16B16G16R16);
// sets offset for the capture, in UU, relatively to current player 0 location
void SetOffset(FVector InOffset) { CaptureOffset = InOffset; }
// overrides player's 0 orientation for the capture.
void SetInitialOrientation(const FQuat& InOrientation) { OverriddenOrientation = InOrientation; }
// overrides player's 0 location for the capture.
void SetInitialLocation(FVector InLocation) { OverriddenLocation = InLocation; }
bool IsFinished() const { return Stage == Finished; }
bool IsCapturing() const { return Stage == Capturing || Stage == SettingPos; }
#if !UE_BUILD_SHIPPING
static void CaptureCubemapCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#endif // UE_BUILD_SHIPPING
private:
enum EStage
{
None,
SettingPos,
Capturing,
Finished
} Stage;
UPROPERTY()
TArray<USceneCaptureComponent2D*> CaptureComponents;
uint32 CaptureBoxSideRes;
EPixelFormat CaptureFormat;
FString OutputDir;
FVector OverriddenLocation; // overridden location of the capture, world coordinates, UU
FQuat OverriddenOrientation; // overridden orientation of the capture. Full orientation is used (not only yaw, like with player's rotation).
FVector CaptureOffset; // offset relative to current player's 0 location
};

View File

@@ -0,0 +1,66 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSimulator.h"
#if PLATFORM_WINDOWS
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRTelemetryEvents.h"
#include "Misc/MessageDialog.h"
#include "Windows/WindowsPlatformMisc.h"
const FString OpenXrRuntimeEnvKey = "XR_RUNTIME_JSON";
const FString PreviousOpenXrRuntimeEnvKey = "XR_RUNTIME_JSON_PREV";
bool FMetaXRSimulator::IsSimulatorActivated()
{
FString MetaXRSimPath = GetSimulatorJsonPath();
FString CurRuntimePath = FWindowsPlatformMisc::GetEnvironmentVariable(*OpenXrRuntimeEnvKey);
return (!MetaXRSimPath.IsEmpty() && MetaXRSimPath == CurRuntimePath);
}
void FMetaXRSimulator::ToggleOpenXRRuntime()
{
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FSimulator> Event;
FString MetaXRSimPath = GetSimulatorJsonPath();
if (!IFileManager::Get().FileExists(*MetaXRSimPath))
{
FString Message("Meta XR Simulator Not Found.\nPlease set its path in Project Settings/Meta XR Plugin/PC.");
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message));
UE_LOG(LogMetaXRSim, Error, TEXT("%s"), *Message);
const auto& NotEnd = Event.SetResult(OculusXRTelemetry::EAction::Fail).AddAnnotation("reason", "not found");
return;
}
if (IsSimulatorActivated())
{
//Deactivate MetaXR Simulator
FString PrevOpenXrRuntimeEnvKey = FWindowsPlatformMisc::GetEnvironmentVariable(*PreviousOpenXrRuntimeEnvKey);
FWindowsPlatformMisc::SetEnvironmentVar(*PreviousOpenXrRuntimeEnvKey,
TEXT(""));
FWindowsPlatformMisc::SetEnvironmentVar(*OpenXrRuntimeEnvKey, *PrevOpenXrRuntimeEnvKey);
UE_LOG(LogMetaXRSim, Log, TEXT("Meta XR Simulator is deactivated. (%s : %s)"), *OpenXrRuntimeEnvKey, *PrevOpenXrRuntimeEnvKey);
const auto& NotEnd = Event.AddAnnotation("action", "deactivated");
}
else
{
//Activate MetaXR Simulator
FString CurOpenXrRuntimeEnvKey = FWindowsPlatformMisc::GetEnvironmentVariable(*OpenXrRuntimeEnvKey);
FWindowsPlatformMisc::SetEnvironmentVar(*PreviousOpenXrRuntimeEnvKey,
*CurOpenXrRuntimeEnvKey);
FWindowsPlatformMisc::SetEnvironmentVar(*OpenXrRuntimeEnvKey, *MetaXRSimPath);
UE_LOG(LogMetaXRSim, Log, TEXT("Meta XR Simulator is activated. (%s : %s)"), *OpenXrRuntimeEnvKey, *MetaXRSimPath);
const auto& NotEnd = Event.AddAnnotation("action", "activated");
}
}
FString FMetaXRSimulator::GetSimulatorJsonPath()
{
return GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MetaXRJsonPath.FilePath;
}
#endif

View File

@@ -0,0 +1,21 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#if PLATFORM_WINDOWS
DEFINE_LOG_CATEGORY_STATIC(LogMetaXRSim, Log, All);
/** */
class FMetaXRSimulator
{
public:
static bool IsSimulatorActivated();
static void ToggleOpenXRRuntime();
private:
static FString GetSimulatorJsonPath();
};
#endif

View File

@@ -0,0 +1,108 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSyntheticEnvironmentServer.h"
#if PLATFORM_WINDOWS
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRTelemetryEvents.h"
#include "Misc/MessageDialog.h"
#include "Windows/WindowsPlatformMisc.h"
const FString SynthEnvServer = "Synthetic Environment Server";
const FString LocalSharingServer = "Local Sharing Server";
FProcHandle FMetaXRSES::EnvProcHandle;
FProcHandle FMetaXRSES::LSSProcHandle;
void FMetaXRSES::StopServer()
{
StopProcess(EnvProcHandle, SynthEnvServer);
StopProcess(LSSProcHandle, LocalSharingServer);
}
void FMetaXRSES::LaunchEnvironment(FString EnvironmentName)
{
if (GetMetaXRSimPackagePath().IsEmpty())
{
return;
}
StopServer();
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FSimulator> Event;
FString SESPath = GetSynthEnvServerPath();
const bool bLaunched = LaunchProcess(SESPath, EnvironmentName, LocalSharingServer, EnvProcHandle);
const auto& _ = Event.SetResult(bLaunched ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail).AddAnnotation("launch", StringCast<ANSICHAR>(*EnvironmentName).Get());
LaunchLocalSharingServer();
}
void FMetaXRSES::LaunchLocalSharingServer()
{
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FSimulator> Event;
FString LSSPath = GetLocalSharingServerPath();
const bool bLaunched = LaunchProcess(LSSPath, "", LocalSharingServer, LSSProcHandle);
const auto& _ = Event.SetResult(bLaunched ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail).AddAnnotation("launch", "localsharingserver");
}
bool FMetaXRSES::LaunchProcess(FString BinaryPath, FString Arguments, FString LogContext, FProcHandle& OutProcHandle)
{
if (!IFileManager::Get().FileExists(*BinaryPath))
{
UE_LOG(LogMetaXRSES, Error, TEXT("Failed to find %s."), *BinaryPath);
return false;
}
UE_LOG(LogMetaXRSES, Log, TEXT("Launching %s."), *BinaryPath);
uint32 OutProcessId = 0;
OutProcHandle = FPlatformProcess::CreateProc(*BinaryPath, *Arguments, false, false, false, &OutProcessId, 0, NULL, NULL);
if (!OutProcHandle.IsValid())
{
UE_LOG(LogMetaXRSES, Error, TEXT("Failed to launch %s."), *BinaryPath);
FPlatformProcess::CloseProc(OutProcHandle);
return false;
}
UE_LOG(LogMetaXRSES, Log, TEXT("Launched %s."), *BinaryPath);
return true;
}
void FMetaXRSES::StopProcess(FProcHandle& ProcHandle, FString LogContext)
{
if (ProcHandle.IsValid())
{
if (FPlatformProcess::IsProcRunning(ProcHandle))
{
UE_LOG(LogMetaXRSES, Log, TEXT("Stopping %s."), *LogContext);
FPlatformProcess::TerminateProc(ProcHandle);
}
FPlatformProcess::CloseProc(ProcHandle);
}
else
{
UE_LOG(LogMetaXRSES, Warning, TEXT("Failed to stop process %s because it is not active anymore."), *LogContext);
}
}
FString FMetaXRSES::GetMetaXRSimPackagePath()
{
FString JsonPath = GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MetaXRJsonPath.FilePath;
if (JsonPath.IsEmpty() || !IFileManager::Get().FileExists(*JsonPath))
{
FString Message("Meta XR Simulator Not Found.\nPlease set its path in Project Settings/Meta XR Plugin/PC.");
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message));
UE_LOG(LogMetaXRSES, Error, TEXT("%s"), *Message);
}
return FPaths::GetPath(JsonPath);
}
FString FMetaXRSES::GetSynthEnvServerPath()
{
return GetMetaXRSimPackagePath() + "/.synth_env_server/synth_env_server.exe";
}
FString FMetaXRSES::GetLocalSharingServerPath()
{
return GetMetaXRSimPackagePath() + "/.local_sharing_server/local_sharing_server.exe";
}
#endif

View File

@@ -0,0 +1,31 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#if PLATFORM_WINDOWS
DEFINE_LOG_CATEGORY_STATIC(LogMetaXRSES, Log, All);
struct FProcHandle;
/** */
class FMetaXRSES
{
public:
static void LaunchEnvironment(FString EnvironmentName);
static void StopServer();
private:
static void LaunchLocalSharingServer();
static bool LaunchProcess(FString BinaryPath, FString Arguments, FString LogContext, FProcHandle& OutProcHandle);
static void StopProcess(FProcHandle& ProcHandle, FString LogContext);
static FString GetMetaXRSimPackagePath();
static FString GetSynthEnvServerPath();
static FString GetLocalSharingServerPath();
static FProcHandle EnvProcHandle;
static FProcHandle LSSProcHandle;
};
#endif

View File

@@ -0,0 +1,49 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRTelemetry.h"
#include "OculusXRHMDModule.h"
#include "OculusXRTelemetryPrivacySettings.h"
#include "Async/Async.h"
namespace OculusXRTelemetry
{
bool IsActive()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if constexpr (FTelemetryBackend::IsNullBackend())
{
return false;
}
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
return true;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void IfActiveThen(TUniqueFunction<void()> Function)
{
AsyncTask(ENamedThreads::GameThread, [F = MoveTemp(Function)]() {
if (IsActive())
{
F();
}
});
}
void PropagateTelemetryConsent()
{
bool HasConsent = true;
#ifdef WITH_EDITOR
if (const auto EditorPrivacySettings = GetDefault<UOculusXRTelemetryPrivacySettings>())
{
HasConsent = EditorPrivacySettings->bIsEnabled;
}
#endif
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
FOculusXRHMDModule::GetPluginWrapper().QplSetConsent(HasConsent);
}
}
} // namespace OculusXRTelemetry

View File

@@ -0,0 +1,12 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRTelemetry.h"
namespace OculusXRTelemetry::Events
{
using FEditorConsent = TMarker<191965622>;
using FSimulator = TMarker<191963436>;
constexpr const char* ConsentOriginKey = "Origin";
} // namespace OculusXRTelemetry::Events

View File

@@ -0,0 +1,82 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRTelemetryPrivacySettings.h"
#include "OculusXRHMDModule.h"
#include "OculusXRTelemetry.h"
#include "OculusXRTelemetryEvents.h"
#define LOCTEXT_NAMESPACE "OculusXRTelemetryPrivacySettings"
UOculusXRTelemetryPrivacySettings::UOculusXRTelemetryPrivacySettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UOculusXRTelemetryPrivacySettings::GetToggleCategoryAndPropertyNames(FName& OutCategory, FName& OutProperty) const
{
OutCategory = FName("Options");
OutProperty = FName("bIsEnabled");
};
FText UOculusXRTelemetryPrivacySettings::GetFalseStateLabel() const
{
return LOCTEXT("FalseStateLabel", "Don't Send");
};
FText UOculusXRTelemetryPrivacySettings::GetFalseStateTooltip() const
{
return LOCTEXT("FalseStateTooltip", "Don't send MetaXR plugin usage data to Meta.");
};
FText UOculusXRTelemetryPrivacySettings::GetFalseStateDescription() const
{
return LOCTEXT("FalseStateDescription", "By opting out you don't allow Meta to collect usage data on its SDKs, such as package name, class names and plugin configuration in your projects using Meta SDKs on this machine. This data helps improve the Meta SDKs and is collected in accordance with Meta's Privacy Policy.");
};
FText UOculusXRTelemetryPrivacySettings::GetTrueStateLabel() const
{
return LOCTEXT("TrueStateLabel", "Send Usage Data");
};
FText UOculusXRTelemetryPrivacySettings::GetTrueStateTooltip() const
{
return LOCTEXT("TrueStateTooltip", "Send MetaXR plugin usage data to Meta.");
};
FText UOculusXRTelemetryPrivacySettings::GetTrueStateDescription() const
{
return LOCTEXT("TrueStateDescription", "By opting in you allow Meta to collect usage data on its SDKs, such as package name, class names and plugin configuration in your projects using Meta SDKs on this machine. This data helps improve the Meta SDKs and is collected in accordance with Meta's Privacy Policy.");
};
FString UOculusXRTelemetryPrivacySettings::GetAdditionalInfoUrl() const
{
return FString(TEXT("https://www.meta.com/legal/quest/privacy-policy/"));
};
FText UOculusXRTelemetryPrivacySettings::GetAdditionalInfoUrlLabel() const
{
return LOCTEXT("HyperlinkLabel", "Meta Platforms Technologies Privacy Policy");
};
#if WITH_EDITOR
void UOculusXRTelemetryPrivacySettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRTelemetryPrivacySettings, bIsEnabled))
{
using namespace OculusXRTelemetry;
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
PropagateTelemetryConsent();
Events::FEditorConsent().Start() //
.AddAnnotation(Events::ConsentOriginKey, "Settings") //
.End(bIsEnabled ? EAction::Success : EAction::Fail);
}
}
}
#endif
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,39 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Engine/ImportantToggleSettingInterface.h"
#include "OculusXRTelemetryPrivacySettings.generated.h"
UCLASS(MinimalAPI, hidecategories = Object, config = EditorSettings)
class UOculusXRTelemetryPrivacySettings : public UObject, public IImportantToggleSettingInterface
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, config, Category = Options)
bool bIsEnabled = false;
UPROPERTY(config)
bool bHasNotified = false;
public:
// BEGIN IImportantToggleSettingInterface
virtual void GetToggleCategoryAndPropertyNames(FName& OutCategory, FName& OutProperty) const override;
virtual FText GetFalseStateLabel() const override;
virtual FText GetFalseStateTooltip() const override;
virtual FText GetFalseStateDescription() const override;
virtual FText GetTrueStateLabel() const override;
virtual FText GetTrueStateTooltip() const override;
virtual FText GetTrueStateDescription() const override;
virtual FString GetAdditionalInfoUrl() const override;
virtual FText GetAdditionalInfoUrlLabel() const override;
// END IImportantToggleSettingInterface
#if WITH_EDITOR
//~ Begin UObject Interface
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
//~ End UObject Interface
#endif // WITH_EDITOR
};

View File

@@ -0,0 +1,132 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
#include "IHeadMountedDisplayModule.h"
#include "HeadMountedDisplayTypes.h"
// Oculus support is not available on Windows XP
#define OCULUS_HMD_SUPPORTED_PLATFORMS (PLATFORM_WINDOWS && WINVER > 0x0502) || (PLATFORM_ANDROID_ARM || PLATFORM_ANDROID_ARM64)
//-------------------------------------------------------------------------------------------------
// IOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
/**
* The public interface to this module. In most cases, this interface is only public to sibling modules
* within this plugin.
*/
class IOculusXRHMDModule : public IHeadMountedDisplayModule
{
public:
/**
* Singleton-like access to this module's interface. This is just for convenience!
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
*
* @return Returns singleton instance, loading the module on demand if needed
*/
static inline IOculusXRHMDModule& Get()
{
return FModuleManager::LoadModuleChecked<IOculusXRHMDModule>("OculusXRHMD");
}
/**
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
*
* @return True if the module is loaded and ready to use
*/
static inline bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("OculusXRHMD");
}
/**
* Grabs the current orientation and position for the HMD. If positional tracking is not available, DevicePosition will be a zero vector
*
* @param DeviceRotation (out) The device's current rotation
* @param DevicePosition (out) The device's current position, in its own tracking space
* @param NeckPosition (out) The estimated neck position, calculated using NeckToEye vector from User Profile. Same coordinate space as DevicePosition.
* @param bUseOrienationForPlayerCamera (in) Should be set to 'true' if the orientation is going to be used to update orientation of the camera manually.
* @param bUsePositionForPlayerCamera (in) Should be set to 'true' if the position is going to be used to update position of the camera manually.
* @param PositionScale (in) The 3D scale that will be applied to position.
*/
virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false, const FVector PositionScale = FVector::ZeroVector) = 0;
/**
* Reports raw sensor data. If HMD doesn't support any of the parameters then it will be set to zero.
*
* @param AngularAcceleration (out) Angular acceleration in radians per second per second.
* @param LinearAcceleration (out) Acceleration in meters per second per second.
* @param AngularVelocity (out) Angular velocity in radians per second.
* @param LinearVelocity (out) Velocity in meters per second.
* @param TimeInSeconds (out) Time when the reported IMU reading took place, in seconds.
*/
virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds) = 0;
/**
* Returns current user profile.
*
* @param Profile (out) Structure to hold current user profile.
* @return (boolean) True, if user profile was acquired.
*/
virtual bool GetUserProfile(struct FOculusXRHmdUserProfile& Profile) = 0;
/**
* Sets 'base rotation' - the rotation that will be subtracted from
* the actual HMD orientation.
* Sets base position offset (in meters). The base position offset is the distance from the physical (0, 0, 0) position
* to current HMD position (bringing the (0, 0, 0) point to the current HMD position)
* Note, this vector is set by ResetPosition call; use this method with care.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param Rotation (in) Rotator object with base rotation
* @param BaseOffsetInMeters (in) the vector to be set as base offset, in meters.
* @param Options (in) specifies either position, orientation or both should be set.
*/
virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) = 0;
/**
* Returns current base rotation and base offset.
* The base offset is currently used base position offset, previously set by the
* ResetPosition or SetBasePositionOffset calls. It represents a vector that translates the HMD's position
* into (0,0,0) point, in meters.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param OutRotation (out) Rotator object with base rotation
* @param OutBaseOffsetInMeters (out) base position offset, vector, in meters.
*/
virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) = 0;
/**
* Sets 'base rotation' - the rotation that will be subtracted from
* the actual HMD orientation.
* The position offset might be added to current HMD position,
* effectively moving the virtual camera by the specified offset. The addition
* occurs after the HMD orientation and position are applied.
*
* @param BaseRot (in) Rotator object with base rotation
* @param PosOffset (in) the vector to be added to HMD position.
* @param Options (in) specifies either position, orientation or both should be set.
*/
virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) = 0;
/**
* Returns current base rotation and position offset.
*
* @param OutRot (out) Rotator object with base rotation
* @param OutPosOffset (out) the vector with previously set position offset.
*/
virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) = 0;
/**
* Returns IStereoLayers interface to work with overlays.
*/
virtual class IStereoLayers* GetStereoLayers() = 0;
virtual FString GetDeviceSystemName() = 0;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
virtual bool PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const = 0;
#endif //OCULUS_HMD_SUPPORTED_PLATFORMS
};

View File

@@ -0,0 +1,18 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/SoftObjectPath.h"
class FOculusAssetDirectory
{
public:
#if WITH_EDITORONLY_DATA
OCULUSXRHMD_API static void LoadForCook();
OCULUSXRHMD_API static void ReleaseAll();
#endif
static FSoftObjectPath AssetListing[];
};

View File

@@ -0,0 +1,34 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
// OculusEventComponent.h: Component to handle receiving events from Oculus HMDs
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Components/ActorComponent.h"
#include "OculusXREventComponent.generated.h"
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = OculusXRHMD)
class OCULUSXRHMD_API UOculusXREventComponent : public UActorComponent
{
GENERATED_BODY()
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOculusDisplayRefreshRateChangedEventDelegate, float, fromRefreshRate, float, toRefreshRate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOculusEyeTrackingStateChangedEventDelegate, bool, bEyeTrackingOn);
UPROPERTY(BlueprintAssignable)
FOculusDisplayRefreshRateChangedEventDelegate OculusDisplayRefreshRateChanged;
UPROPERTY(BlueprintAssignable)
FOculusEyeTrackingStateChangedEventDelegate OculusEyeTrackingStateChanged;
void OnRegister() override;
void OnUnregister() override;
private:
/** Native handlers that get registered with the actual FCoreDelegates, and then proceed to broadcast to the delegates above */
void OculusDisplayRefreshRateChanged_Handler(float fromRefresh, float toRefresh) { OculusDisplayRefreshRateChanged.Broadcast(fromRefresh, toRefresh); }
void OculusEyeTrackingStateChanged_Handler(bool bEyeTrackingOn) { OculusEyeTrackingStateChanged.Broadcast(bEyeTrackingOn); }
};

View File

@@ -0,0 +1,436 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRHMDTypes.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "IOculusXRHMDModule.h"
#include "OculusXRFunctionLibrary.generated.h"
namespace OculusXRHMD
{
class FOculusXRHMD;
}
UCLASS()
class OCULUSXRHMD_API UOculusXRFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_UCLASS_BODY()
/**
* Grabs the current orientation and position for the HMD. If positional tracking is not available, DevicePosition will be a zero vector
*
* @param DeviceRotation (out) The device's current rotation
* @param DevicePosition (out) The device's current position, in its own tracking space
* @param NeckPosition (out) The estimated neck position, calculated using NeckToEye vector from User Profile. Same coordinate space as DevicePosition.
* @param bUseOrienationForPlayerCamera (in) Should be set to 'true' if the orientation is going to be used to update orientation of the camera manually.
* @param bUsePositionForPlayerCamera (in) Should be set to 'true' if the position is going to be used to update position of the camera manually.
* @param PositionScale (in) The 3D scale that will be applied to position.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false, const FVector PositionScale = FVector::ZeroVector);
/**
* Reports raw sensor data. If HMD doesn't support any of the parameters then it will be set to zero.
*
* @param AngularAcceleration (out) Angular acceleration in radians per second per second.
* @param LinearAcceleration (out) Acceleration in meters per second per second.
* @param AngularVelocity (out) Angular velocity in radians per second.
* @param LinearVelocity (out) Velocity in meters per second.
* @param TimeInSeconds (out) Time when the reported IMU reading took place, in seconds.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType = EOculusXRTrackedDeviceType::HMD);
/**
* Returns if the device is currently tracked by the runtime or not.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static bool IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType);
/**
* Set the CPU and GPU levels as hints to the Oculus device (Deprecated).
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecatedMessage = "Deprecated. Please use Get/SetSuggestedCpuAndGpuPerformanceLevels instead"))
static void SetCPUAndGPULevels(int CPULevel, int GPULevel);
/**
* Get the suggested CPU and GPU levels to the Oculus device.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel);
/**
* Set the suggested CPU and GPU levels to the Oculus device.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel);
/**
* Returns current user profile.
*
* @param Profile (out) Structure to hold current user profile.
* @return (boolean) True, if user profile was acquired.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static bool GetUserProfile(FOculusXRHmdUserProfile& Profile);
/**
* Sets 'base rotation' - the rotation that will be subtracted from
* the actual HMD orientation.
* Sets base position offset (in meters). The base position offset is the distance from the physical (0, 0, 0) position
* to current HMD position (bringing the (0, 0, 0) point to the current HMD position)
* Note, this vector is set by ResetPosition call; use this method with care.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param Rotation (in) Rotator object with base rotation
* @param BaseOffsetInMeters (in) the vector to be set as base offset, in meters.
* @param Options (in) specifies either position, orientation or both should be set.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options);
/**
* Returns current base rotation and base offset.
* The base offset is currently used base position offset, previously set by the
* ResetPosition or SetBasePositionOffset calls. It represents a vector that translates the HMD's position
* into (0,0,0) point, in meters.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param OutRotation (out) Rotator object with base rotation
* @param OutBaseOffsetInMeters (out) base position offset, vector, in meters.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters);
/**
* Scales the HMD position that gets added to the virtual camera position.
*
* @param PosScale3D (in) the scale to apply to the HMD position.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecationMessage = "This feature is no longer supported."))
static void SetPositionScale3D(FVector PosScale3D) {}
/**
* Sets 'base rotation' - the rotation that will be subtracted from
* the actual HMD orientation.
* The position offset might be added to current HMD position,
* effectively moving the virtual camera by the specified offset. The addition
* occurs after the HMD orientation and position are applied.
*
* @param BaseRot (in) Rotator object with base rotation
* @param PosOffset (in) the vector to be added to HMD position.
* @param Options (in) specifies either position, orientation or both should be set.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecationMessage = "A hack, proper camera positioning should be used"))
static void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options);
/**
* Returns current base rotation and position offset.
*
* @param OutRot (out) Rotator object with base rotation
* @param OutPosOffset (out) the vector with previously set position offset.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecationMessage = "A hack, proper camera positioning should be used"))
static void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset);
/**
* Adds loading splash screen with parameters
*
* @param Texture (in) A texture asset to be used for the splash.
* @param TranslationInMeters (in) Initial translation of the center of the splash screen (in meters).
* @param Rotation (in) Initial rotation of the splash screen, with the origin at the center of the splash screen.
* @param SizeInMeters (in) Size, in meters, of the quad with the splash screen.
* @param DeltaRotation (in) Incremental rotation, that is added each 2nd frame to the quad transform. The quad is rotated around the center of the quad.
* @param bClearBeforeAdd (in) If true, clears splashes before adding a new one.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecationMessage = "Use Add Loading Screen Splash from the Head Mounted Display Loading Screen functions instead."))
static void AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters = FVector2D(1.0f, 1.0f), FRotator DeltaRotation = FRotator::ZeroRotator, bool bClearBeforeAdd = false);
/**
* Removes all the splash screens.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecationMessage = "Use Clear Loading Screen Splashes from the Head Mounted Display Loading Screen functions instead."))
static void ClearLoadingSplashScreens();
/**
* Returns true, if the app has input focus.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static bool HasInputFocus();
/**
* Returns true, if the system overlay is present.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static bool HasSystemOverlayPresent();
/**
* Returns the GPU utilization availability and value
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static void GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization);
/**
* Returns the GPU frame time on supported mobile platforms (Go for now)
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static float GetGPUFrameTime();
/**
* Returns the foveated rendering method currently being used
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static EOculusXRFoveatedRenderingMethod GetFoveatedRenderingMethod();
/**
* Set the requested foveated rendering method
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method);
/**
* Returns the current multiresolution level
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static EOculusXRFoveatedRenderingLevel GetFoveatedRenderingLevel();
/**
* Set the requested foveated rendering level for the next frame, and whether FFR's level is now dynamic or not.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic);
/**
* Returns whether eye-tracked foveated rendering is supported or not
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static bool GetEyeTrackedFoveatedRenderingSupported();
/**
* Returns the current device's name
*/
UE_DEPRECATED(4.22, "UOculusXRFunctionLibrary::GetDeviceName has been deprecated and no longer functions as before. Please use the enum-based GetDeviceType instead.")
UFUNCTION(BlueprintPure, Category = "OculusLibrary", meta = (DeprecatedFunction, DeprecationMessage = "UOculusXRFunctionLibrary::GetDeviceName has been deprecated and no longer functions as before. Please use the enum-based GetDeviceType instead."))
static FString GetDeviceName();
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static EOculusXRDeviceType GetDeviceType();
/**
* Returns the current controller's type
* @param deviceHand (in) The hand to get the position from
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static EOculusXRControllerType GetControllerType(EControllerHand deviceHand);
/**
* Returns the current available frequencies
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static TArray<float> GetAvailableDisplayFrequencies();
/**
* Returns the current display frequency
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static float GetCurrentDisplayFrequency();
/**
* Sets the requested display frequency
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetDisplayFrequency(float RequestedFrequency);
/**
* Enables/disables positional tracking on devices that support it.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void EnablePositionTracking(bool bPositionTracking);
/**
* Enables/disables orientation tracking on devices that support it.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void EnableOrientationTracking(bool bOrientationTracking);
/**
* Set the Color Scale/Offset
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers = false);
/**
* Returns true if system headset is in 3dof mode
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static bool GetSystemHmd3DofModeEnabled();
/**
* Returns the color space of the target HMD
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static EOculusXRColorSpace GetHmdColorDesc();
/**
* Sets the target HMD to do color space correction to a specific color space
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetClientColorDesc(EOculusXRColorSpace ColorSpace);
/**
* Turns on or off local dimming
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetLocalDimmingOn(bool LocalDimmingOn);
/**
* Checks if passthrough is supported
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static bool IsPassthroughSupported();
/**
* Checks if color passthrough is supported
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static bool IsColorPassthroughSupported();
/**
* Create the environment depth texture swap chain and start receiving
* depth texture every frame until stopped.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void StartEnvironmentDepth();
/**
* Destroy the environment depth texture swap chain and stop receiving
* new depth textures every frame. Call this when environment depth is
* no longer needed to free up resources.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void StopEnvironmentDepth();
/**
* Returns true if StartEnvironmentDepth() has been called and is currently running.
* If called right after calling StartEnvironmentDepth() it'll return false as it needs some time for EnvironmentDepth to start
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary")
static bool IsEnvironmentDepthStarted();
/**
* When hands removal is enabled and hand tracking is active, the region
* of the depth texture which contains the hands will be replaced with
* the best estimate for depth behind the hands.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetEnvironmentDepthHandRemoval(bool RemoveHands);
/**
* Sets the occlusions mode using environment depth. When occlusions are enabled
* virtual objects that are behind physical objects will be occluded so that
* the sense of immersion is preserved.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary", meta = (WorldContext = "WorldContextObject"))
static void SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode);
/**
* Sets the eyebuffer sharpen type. This amplifies contrast and fine details.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static void SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType);
/**
* Get a system recommendation on whether Passthrough should be active.
* When set, it is recommended for apps which optionally support an MR experience
* with Passthrough to default to that mode.
* Currently, this is determined based on whether the user has Passthrough active in the home environment.
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary")
static bool IsPassthroughRecommended();
/**
* Returns IStereoLayers interface to work with overlays.
*/
static class IStereoLayers* GetStereoLayers();
/* GUARDIAN API */
/**
* Returns true if the Guardian Outer Boundary is being displayed
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Guardian")
static bool IsGuardianDisplayed();
/* GUARDIAN API */
/**
* Returns true if the Guardian has been set up by the user, false if the user is in "seated" mode and has not set up a play space.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Guardian")
static bool IsGuardianConfigured();
/**
* Returns the list of points in UE world space of the requested Boundary Type
* @param BoundaryType (in) An enum representing the boundary type requested, either Outer Boundary (exact guardian bounds) or PlayArea (rectangle inside the Outer Boundary)
* @param UsePawnSpace (in) Boolean indicating to return the points in world space or pawn space
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Guardian")
static TArray<FVector> GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace = false);
/**
* Returns the dimensions in UE world space of the requested Boundary Type
* @param BoundaryType (in) An enum representing the boundary type requested, either Outer Boundary (exact guardian bounds) or PlayArea (rectangle inside the Outer Boundary)
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Guardian")
static FVector GetGuardianDimensions(EOculusXRBoundaryType BoundaryType);
/**
* Returns the transform of the play area rectangle, defining its position, rotation and scale to apply to a unit cube to match it with the play area.
*/
UFUNCTION(BlueprintPure, Category = "OculusLibrary|Guardian")
static FTransform GetPlayAreaTransform();
/**
* Get the intersection result between a UE4 coordinate and a guardian boundary
* @param Point (in) Point in UE space to test against guardian boundaries
* @param BoundaryType (in) An enum representing the boundary type requested, either Outer Boundary (exact guardian bounds) or PlayArea (rectangle inside the Outer Boundary)
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Guardian")
static FOculusXRGuardianTestResult GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType);
/**
* Get the intersection result between a tracked device (HMD or controllers) and a guardian boundary
* @param DeviceType (in) Tracked Device type to test against guardian boundaries
* @param BoundaryType (in) An enum representing the boundary type requested, either Outer Boundary (exact guardian bounds) or PlayArea (rectangle inside the Outer Boundary)
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Guardian")
static FOculusXRGuardianTestResult GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType);
/**
* Forces the runtime to render guardian at all times or not
* @param GuardianVisible (in) True will display guardian, False will hide it
*/
UFUNCTION(BlueprintCallable, Category = "OculusLibrary|Guardian")
static void SetGuardianVisibility(bool GuardianVisible);
/** When player triggers the Guardian boundary */
DECLARE_MULTICAST_DELEGATE_OneParam(FOculusGuardianTriggeredEvent, FOculusXRGuardianTestResult);
/** When player returns within outer bounds */
DECLARE_MULTICAST_DELEGATE(FOculusGuardianReturnedEvent);
/**
* For outer boundary only. Devs can bind delegates via something like: BoundaryComponent->OnOuterBoundaryTriggered.AddDynamic(this, &UCameraActor::PauseGameForBoundarySystem) where
* PauseGameForBoundarySystem() takes a TArray<FBoundaryTestResult> parameter.
*/
//UPROPERTY(BlueprintAssignable, Category = "Input|OculusLibrary|Guardian")
//static FOculusGuardianTriggeredEvent OnGuardianTriggered;
/** For outer boundary only. Devs can bind delegates via something like: BoundaryComponent->OnOuterBoundaryReturned.AddDynamic(this, &UCameraActor::ResumeGameForBoundarySystem) */
//UPROPERTY(BlueprintAssignable, Category = "OculusLibrary|Guardian")
//FOculusGuardianReturnedEvent OnGuardianReturned;
protected:
static class OculusXRHMD::FOculusXRHMD* GetOculusXRHMD();
};

View File

@@ -0,0 +1,212 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "OculusXRHMDTypes.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMDRuntimeSettings.generated.h"
UENUM()
enum class EOculusXRSupportedDevices : uint8
{
/** 0 was the deprecated Meta Quest */
Quest2 = 1 UMETA(DisplayName = "Meta Quest 2"),
QuestPro = 2 UMETA(DisplayName = "Meta Quest Pro"),
Quest3 = 3 UMETA(DisplayName = "Meta Quest 3"),
};
/**
* Implements the settings for the OculusVR plugin.
*/
UCLASS(config = Engine, defaultconfig)
class OCULUSXRHMD_API UOculusXRHMDRuntimeSettings : public UObject
{
GENERATED_UCLASS_BODY()
public:
/** Configure System Splash Screen background type. To configure Splash Image go to Project Settings > Platforms > Android > Launch Image. */
UPROPERTY(config, EditAnywhere, Category = "System SplashScreen", meta = (DisplayName = "System Splash Screen Background"))
ESystemSplashBackgroundType SystemSplashBackground;
/** Whether the Splash screen is enabled. */
UPROPERTY(config, EditAnywhere, Category = "Engine SplashScreen")
bool bAutoEnabled;
/** An array of splash screen descriptors listing textures to show and their positions. */
UPROPERTY(config, EditAnywhere, Category = "Engine SplashScreen")
TArray<FOculusXRSplashDesc> SplashDescs;
/**
This selects the XR API that the engine will use. If unsure, OVRPlugin OpenXR is the recommended API.
The OpenXR plugin must also be enabled to use Native OpenXR.
*/
UPROPERTY(config, EditAnywhere, Category = General, meta = (DisplayName = "XR API", ConfigRestartRequired = true))
EOculusXRXrApi XrApi;
/** The target color space */
UPROPERTY(config, EditAnywhere, Category = General)
EOculusXRColorSpace ColorSpace;
/** Whether the controller hand poses align to the Meta XR pose definitions or the OpenXR pose definitions */
UPROPERTY(config, EditAnywhere, Category = General, meta = (EditCondition = "XrApi != EOculusXRXrApi::NativeOpenXR"))
EOculusXRControllerPoseAlignment ControllerPoseAlignment;
/** Whether Dash is supported by the app, which will keep the app in foreground when the User presses the oculus button (needs the app to handle input focus loss!) */
UPROPERTY(config, EditAnywhere, Category = PC)
bool bSupportsDash;
/** Whether the app's depth buffer is shared with the Rift Compositor, for layer (including Dash) compositing, PTW, and potentially more. */
UPROPERTY(config, EditAnywhere, Category = PC)
bool bCompositesDepth;
/** Computes mipmaps for the eye buffers every frame, for a higher quality distortion */
UPROPERTY(config, EditAnywhere, Category = PC)
bool bHQDistortion;
/**
Path to Meta XR Simulator JSON file (meta_openxr_simulator.json).
*/
UPROPERTY(config, EditAnywhere, Category = PC, meta = (DisplayName = "Meta XR Simulator JSON File."))
FFilePath MetaXRJsonPath;
/** Maximum allowed pixel density. */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Dynamic Resolution", DisplayName = "Enable Dynamic Resolution")
bool bDynamicResolution;
/** Minimum allowed pixel density. */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Dynamic Resolution")
float PixelDensityMin;
/** Maximum allowed pixel density. */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Dynamic Resolution")
float PixelDensityMax;
/** Default CPU level controlling CPU frequency on the mobile device */
UPROPERTY(config, meta = (DeprecatedProperty, DeprecationMessage = "Use Blueprint Function Get/SetSuggestedCpuAndGpuPerformanceLevels instead."))
int CPULevel_DEPRECATED;
/** Default GPU level controlling GPU frequency on the mobile device */
UPROPERTY(config, meta = (DeprecatedProperty, DeprecationMessage = "Use Blueprint Function Get/SetSuggestedCpuAndGpuPerformanceLevels instead."))
int GPULevel_DEPRECATED;
/** Select supported Meta Quest Devices */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Supported Meta Quest devices"))
TArray<EOculusXRSupportedDevices> SupportedDevices;
/** Suggested CPU perf level when application starts on Oculus Quest */
UPROPERTY(config, EditAnywhere, Category = Mobile)
EOculusXRProcessorPerformanceLevel SuggestedCpuPerfLevel;
/** Suggested GPU perf level when application starts on Oculus Quest */
UPROPERTY(config, EditAnywhere, Category = Mobile)
EOculusXRProcessorPerformanceLevel SuggestedGpuPerfLevel;
/** Foveated rendering method */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Foveated Rendering", meta = (EditCondition = "XrApi == EOculusXRXrApi::OVRPluginOpenXR"))
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod;
/** Foveated rendering level */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Foveated Rendering", meta = (EditCondition = "XrApi != EOculusXRXrApi::NativeOpenXR"))
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel;
/** Whether foveated rendering levels will change dynamically based on performance headroom or not (up to the set Foveation Level) */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Foveated Rendering", meta = (EditCondition = "XrApi != EOculusXRXrApi::NativeOpenXR"))
bool bDynamicFoveatedRendering;
/** Whether eye tracked foveated rendering can be used with the app. */
UPROPERTY(config, EditAnywhere, Category = "Mobile|Foveated Rendering", meta = (EditCondition = "XrApi == EOculusXRXrApi::OVRPluginOpenXR"))
bool bSupportEyeTrackedFoveatedRendering;
/** Whether the app's depth buffer is shared with the compositor to enable depth testing against other layers.
Mobile depth composition has performance overhead both on the engine (for resolving depth) and on the compositor (for depth testing against other layers) */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Composite Depth"))
bool bCompositeDepthMobile;
/** If enabled the app will be focus aware. This will keep the app in foreground when the User presses the oculus button (needs the app to handle input focus loss!) */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (EditCondition = "false"))
bool bFocusAware;
/** [Experimental]Enable Late latching for reducing HMD and controller latency, improve tracking prediction quality, multiview and vulkan must be enabled for this feature. */
UPROPERTY(config, EditAnywhere, Category = Mobile)
bool bLateLatching;
/** If enabled the app will use the Oculus system keyboard for input fields. This requires that the app be focus aware. */
UPROPERTY(config, EditAnywhere, Category = Mobile)
bool bRequiresSystemKeyboard;
/** Whether controllers and/or hands can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile)
EOculusXRHandTrackingSupport HandTrackingSupport;
/** Note that a higher tracking frequency will reserve some performance headroom from the application's budget. */
UPROPERTY(config, EditAnywhere, Category = Mobile)
EOculusXRHandTrackingFrequency HandTrackingFrequency;
/** The version of hand tracking algorithm */
UPROPERTY(config, EditAnywhere, Category = Mobile)
EOculusXRHandTrackingVersion HandTrackingVersion;
/** Whether passthrough functionality can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Passthrough Enabled"))
bool bInsightPassthroughEnabled;
/** Whether Spatial Anchors can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Anchor Support"))
bool bAnchorSupportEnabled;
/** Whether Spatial Anchor Sharing can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Anchor Sharing"))
bool bAnchorSharingEnabled;
/** Whether Scene can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Scene Support"))
bool bSceneSupportEnabled;
/** Whether body tracking functionality can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Body Tracking Enabled", EditCondition = "XrApi == EOculusXRXrApi::OVRPluginOpenXR"))
bool bBodyTrackingEnabled;
/** Whether eye tracking functionality can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Eye Tracking Enabled", EditCondition = "XrApi == EOculusXRXrApi::OVRPluginOpenXR"))
bool bEyeTrackingEnabled;
/** Whether face tracking functionality can be used with the app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Face Tracking Enabled", EditCondition = "XrApi == EOculusXRXrApi::OVRPluginOpenXR"))
bool bFaceTrackingEnabled;
/** Select preffered Face Tracking data sources */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Face Tracking Source", EditCondition = "XrApi == EOculusXRXrApi::OVRPluginOpenXR"))
TSet<EFaceTrackingDataSourceConfig> FaceTrackingDataSource;
/** On supported Oculus mobile platforms, copy compiled .so directly to device. Allows updating compiled code without rebuilding and installing an APK. */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Deploy compiled .so directly to device"))
bool bDeploySoToDevice;
/** Whether experimental features listed below can be used with the app. */
UPROPERTY(config, EditAnywhere, Category = Experimental)
bool bSupportExperimentalFeatures;
/** If selected, will increase the frequency of one processor at the expense of decreasing the frequency of the other on supported devices. */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Processor Favor"))
EProcessorFavor ProcessorFavor;
/** Whether Tile Turn Off is enabled in app */
UPROPERTY(config, EditAnywhere, Category = Mobile, meta = (DisplayName = "Tile Turn Off", EditCondition = "false"))
bool bTileTurnOffEnabled;
private:
#if WITH_EDITOR
virtual bool CanEditChange(const FProperty* InProperty) const override;
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
virtual void PostInitProperties() override;
void LoadFromIni();
void RenameProperties();
};

View File

@@ -0,0 +1,361 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "RHI.h"
#include "RHIResources.h"
#include "Engine/Texture2D.h"
#include "UObject/SoftObjectPath.h"
#include "OculusXRHMDTypes.generated.h"
/* Tracked device types corresponding to ovrTrackedDeviceType enum*/
UENUM(BlueprintType)
enum class EOculusXRTrackedDeviceType : uint8
{
None UMETA(DisplayName = "No Devices"),
HMD UMETA(DisplayName = "HMD"),
LTouch UMETA(DisplayName = "Left Hand"),
RTouch UMETA(DisplayName = "Right Hand"),
Touch UMETA(DisplayName = "All Hands"),
DeviceObjectZero UMETA(DisplayName = "DeviceObject Zero"),
All UMETA(DisplayName = "All Devices")
};
USTRUCT(BlueprintType, meta = (DisplayName = "HMD User Profile Data Field"))
struct FOculusXRHmdUserProfileField
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
FString FieldName;
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
FString FieldValue;
FOculusXRHmdUserProfileField() {}
FOculusXRHmdUserProfileField(const FString& Name, const FString& Value)
: FieldName(Name), FieldValue(Value) {}
};
USTRUCT(BlueprintType, meta = (DisplayName = "HMD User Profile Data"))
struct FOculusXRHmdUserProfile
{
GENERATED_USTRUCT_BODY()
/** Name of the user's profile. */
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
FString Name;
/** Gender of the user ("male", "female", etc). */
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
FString Gender;
/** Height of the player, in meters */
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
float PlayerHeight;
/** Height of the player, in meters */
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
float EyeHeight;
/** Interpupillary distance of the player, in meters */
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
float IPD;
/** Neck-to-eye distance, in meters. X - horizontal, Y - vertical. */
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
FVector2D NeckToEyeDistance;
UPROPERTY(BlueprintReadWrite, Category = "Input|HeadMountedDisplay")
TArray<FOculusXRHmdUserProfileField> ExtraFields;
FOculusXRHmdUserProfile()
: PlayerHeight(0.f), EyeHeight(0.f), IPD(0.f), NeckToEyeDistance(FVector2D::ZeroVector) {}
};
UENUM(BlueprintType)
enum class EOculusXRFoveatedRenderingMethod : uint8
{
FixedFoveatedRendering = 0,
EyeTrackedFoveatedRendering = 1,
};
UENUM(BlueprintType)
enum class EOculusXRFoveatedRenderingLevel : uint8
{
Off = 0,
Low = 1,
Medium = 2,
High = 3,
// High foveation setting with more detail toward the bottom of the view and more foveation near the top
HighTop = 4
};
/* Guardian boundary types*/
UENUM(BlueprintType)
enum class EOculusXRBoundaryType : uint8
{
Boundary_Outer UMETA(DisplayName = "Outer Boundary"),
Boundary_PlayArea UMETA(DisplayName = "Play Area"),
};
UENUM(BlueprintType)
enum class EOculusXRColorSpace : uint8
{
/// The default value from GetHmdColorSpace until SetClientColorDesc is called. Only valid on PC, and will be remapped to Quest on Mobile
Unknown = 0,
/// No color correction, not recommended for production use. See documentation for more info
Unmanaged = 1,
/// Color space for standardized color across all Oculus HMDs with D65 white point
Rec_2020 = 2,
/// Rec. 709 is used on Oculus Go and shares the same primary color coordinates as sRGB
Rec_709 = 3,
/// Oculus Rift CV1 uses a unique color space, see documentation for more info
Rift_CV1 = 4 UMETA(DisplayName = "Rift CV1"),
/// Oculus Rift S uses a unique color space, see documentation for more info
Rift_S = 5,
/// Oculus Quest's native color space is slightly different than Rift CV1
Quest = 6 UMETA(DisplayName = "Quest 1"),
/// DCI-P3 color space. See documentation for more details
P3 = 7 UMETA(DisplayName = "P3 (Recommended)"),
/// Similar to sRGB but with deeper greens using D65 white point
Adobe_RGB = 8,
};
/*
* Hand tracking settings. Please check https://developer.oculus.com/documentation/unreal/unreal-hand-tracking/
* for detailed information.
*/
UENUM(BlueprintType)
enum class EOculusXRHandTrackingSupport : uint8
{
ControllersOnly,
ControllersAndHands,
HandsOnly,
};
UENUM(BlueprintType)
enum class EOculusXRHandTrackingFrequency : uint8
{
LOW,
HIGH,
MAX,
};
UENUM(BlueprintType)
enum class EOculusXRHandTrackingVersion : uint8
{
Default,
V1,
V2,
};
UENUM(BlueprintType)
enum class EOculusXRProcessorPerformanceLevel : uint8
{
PowerSavings = 0 UMETA(DisplayName = "PowerSavings", ToolTip = "Usually used in non-XR section (head-locked / static screen), during which power savings are to be prioritized"),
SustainedLow = 1 UMETA(DisplayName = "SustainedLow", ToolTip = "App enters a low and stable complexity section, during which reducing power is more important than occasional late rendering frames"),
SustainedHigh = 2 UMETA(DisplayName = "SustainedHigh", ToolTip = "Let XR Runtime to perform consistent XR compositing and frame rendering within a thermally sustainable range"),
Boost = 3 UMETA(DisplayName = "Boost(*)", ToolTip = "Allow XR Runtime to step up beyond the thermally sustainable range for short period. (Currently equivalent to SustainedHigh and not recommended to be used on Quest)")
};
UENUM(BlueprintType)
enum class EOculusXRDeviceType : uint8
{
//mobile HMDs
OculusMobile_Deprecated0 = 0,
OculusQuest_Deprecated,
OculusQuest2,
MetaQuestPro,
MetaQuest3,
//PC HMDs
Rift = 100,
Rift_S,
Quest_Link_Deprecated,
Quest2_Link,
MetaQuestProLink,
MetaQuest3Link,
//default
OculusUnknown = 200,
};
UENUM(BlueprintType)
enum class EOculusXRControllerType : uint8
{
None = 0,
MetaQuestTouch = 1,
MetaQuestTouchPro = 2,
MetaQuestTouchPlus = 3,
Unknown = 0x7f,
};
UENUM(BlueprintType)
enum class EOculusXRXrApi : uint8
{
OVRPluginOpenXR = 0 UMETA(DisplayName = "Oculus OVRPlugin + OpenXR backend (current recommended)", ToolTip = "Oculus plugin integration using OpenXR backend on both Mobile and PC. All new features will ship on backend for the forseeable future."),
NativeOpenXR = 1 UMETA(DisplayName = "Epic Native OpenXR with Oculus vendor extensions", ToolTip = "Disable Legacy Oculus in favor of the native OpenXR implementation, with Oculus vendor extensions. Must enable the OpenXR plugin. This will be where Epic focuses XR development going forward. Oculus OpenXR extensions may be moved into a separate plugin (or plugins) in the future to improve modularity. The features supported by OpenXR are listed in the OpenXR specification on khronos.org, and the features supported by a given runtime can be verified with the \"OpenXR Explorer\" application on GitHub."),
};
/*
* Information about relationships between a triggered boundary (EOculusXRBoundaryType::Boundary_Outer or
* EOculusXRBoundaryType::Boundary_PlayArea) and a device or point in the world.
* All dimensions, points, and vectors are returned in Unreal world coordinate space.
*/
USTRUCT(BlueprintType)
struct FOculusXRGuardianTestResult
{
GENERATED_BODY()
/** Is there a triggering interaction between the device/point and specified boundary? */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Boundary Test Result")
bool IsTriggering = false;
/** Device type triggering boundary (EOculusXRTrackedDeviceType::None if BoundaryTestResult corresponds to a point rather than a device) */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Boundary Test Result")
EOculusXRTrackedDeviceType DeviceType = EOculusXRTrackedDeviceType::None;
/** Distance of device/point to surface of boundary specified by BoundaryType */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Boundary Test Result")
float ClosestDistance = 0.0f;
/** Closest point on surface corresponding to specified boundary */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Boundary Test Result")
FVector ClosestPoint = FVector(0.0f);
/** Normal of closest point */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Boundary Test Result")
FVector ClosestPointNormal = FVector(0.0f, 0.0f, 1.0f);
};
UENUM()
enum class EOculusXRControllerPoseAlignment : uint8
{
Default = 0 UMETA(ToolTip = "Default pose alignment used in all versions of the Meta XR plugin. Recommended pose for compatibility with previous assets designed for the Meta XR plugin."),
Grip = 1 UMETA(ToolTip = "Grip pose alignment as defined by OpenXR. Use this for cross-plugin compatibility with assets designed for the native OpenXR grip pose."),
Aim = 2 UMETA(ToolTip = "Aim pose alignment as defined by OpenXR. Use this for cross-plugin compatibility with assets designed for the native OpenXR aim pose."),
};
USTRUCT()
struct FOculusXRSplashDesc
{
GENERATED_USTRUCT_BODY()
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (AllowedClasses = "/Script/Engine.Texture", ToolTip = "Texture to display"))
FSoftObjectPath TexturePath;
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (ToolTip = "transform of center of quad (meters)."))
FTransform TransformInMeters;
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (ToolTip = "Dimensions in meters."))
FVector2D QuadSizeInMeters;
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (ToolTip = "A delta rotation that will be added each rendering frame (half rate of full vsync)."))
FQuat DeltaRotation;
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (ToolTip = "Texture offset amount from the top left corner."))
FVector2D TextureOffset;
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (ToolTip = "Texture scale."))
FVector2D TextureScale;
UPROPERTY(config, EditAnywhere, Category = Settings, meta = (ToolTip = "Whether the splash layer uses it's alpha channel."))
bool bNoAlphaChannel;
// Runtime data
UTexture* LoadingTexture;
FTextureRHIRef LoadedTexture;
bool bIsDynamic;
FOculusXRSplashDesc()
: TransformInMeters(FVector(4.0f, 0.f, 0.f))
, QuadSizeInMeters(3.f, 3.f)
, DeltaRotation(FQuat::Identity)
, TextureOffset(0.0f, 0.0f)
, TextureScale(1.0f, 1.0f)
, bNoAlphaChannel(false)
, LoadingTexture(nullptr)
, LoadedTexture(nullptr)
, bIsDynamic(false)
{
}
bool operator==(const FOculusXRSplashDesc& d) const
{
return TexturePath == d.TexturePath && TransformInMeters.Equals(d.TransformInMeters) && QuadSizeInMeters == d.QuadSizeInMeters && DeltaRotation.Equals(d.DeltaRotation) && TextureOffset == d.TextureOffset && TextureScale == d.TextureScale && bNoAlphaChannel == d.bNoAlphaChannel && LoadingTexture == d.LoadingTexture && LoadedTexture == d.LoadedTexture && bIsDynamic == d.bIsDynamic;
}
};
UENUM(BlueprintType)
enum class EOculusXROcclusionsMode : uint8
{
/// Environment depth occlusions disabled.
Disabled = 0,
/// Hard occlusions, good performance, shows hard edges between the real and virtual world.
HardOcclusions = 1,
/// Soft occlusions, most expensive, shows soft edges between the real and virtual world.
SoftOcclusions = 2,
};
UENUM(BlueprintType)
enum class EOculusXREyeBufferSharpenType : uint8
{
/// No Sharpening
SLST_None UMETA(DisplayName = "No Sharpening"),
/// Normal Sharpening
SLST_Normal UMETA(DisplayName = "Normal Sharpening"),
/// Quality Sharpening
SLST_Quality UMETA(DisplayName = "Quality Sharpening"),
/// Auto Filtering: Runtime automatically chooses the appropriate sharpening filter
SLST_Auto UMETA(DisplayName = "Auto Filtering"),
SLST_MAX,
};
UENUM()
enum class EProcessorFavor : int8
{
FavorEqually = 0 UMETA(DisplayName = "Favor Equally"),
FavorCPU = 1 UMETA(DisplayName = "Favor CPU"),
FavorGPU = 2 UMETA(DisplayName = "Favor GPU"),
};
UENUM(BlueprintType)
enum class EOculusXRHMDBodyTrackingFidelity : uint8
{
Unset = 0 UMETA(Hidden),
Low = 1,
High = 2,
};
UENUM(BlueprintType)
enum class EOculusXRHMDBodyJointSet : uint8
{
UpperBody = 0,
FullBody = 1,
};
UENUM()
enum class EFaceTrackingDataSourceConfig : int8
{
Visual = 0 UMETA(DisplayName = "Visual"),
Audio = 1 UMETA(DisplayName = "Audio"),
MAX = 2 UMETA(Hidden),
};
UENUM()
enum class ESystemSplashBackgroundType : int8
{
Black = 0 UMETA(DisplayName = "Black"),
Contextual = 1 UMETA(DisplayName = "Passthrough (Contextual)"),
};

View File

@@ -0,0 +1,164 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "StereoLayerShapes.h"
#include "OculusXRPassthroughMesh.h"
#include "Misc/EngineVersionComparison.h"
#include "OculusXRPassthroughLayerShapes.generated.h"
UENUM()
enum EOculusXRColorMapType
{
/** None*/
ColorMapType_None = 0 UMETA(DisplayName = "None"),
/** Grayscale to color */
ColorMapType_GrayscaleToColor = 1 UMETA(DisplayName = "Grayscale To Color"),
/** Grayscale */
ColorMapType_Grayscale = 2 UMETA(DisplayName = "Grayscale"),
/** Color Adjustment */
ColorMapType_ColorAdjustment = 3 UMETA(DisplayName = "Color Adjustment"),
/** Color LUT */
ColorMapType_ColorLut = 4 UMETA(DisplayName = "Color LUT"),
/** Interpolated Color LUT */
ColorMapType_ColorLut_Interpolated = 5 UMETA(DisplayName = "Interpolated Color LUT"),
ColorMapType_MAX = 255,
};
UENUM()
enum EOculusXRPassthroughLayerOrder
{
/** Layer is rendered on top of scene */
PassthroughLayerOrder_Overlay = 0 UMETA(DisplayName = "Overlay"),
/** Layer is rendered under scene */
PassthroughLayerOrder_Underlay = 1 UMETA(DisplayName = "Underlay"),
PassthroughLayerOrder_MAX = 255,
};
struct OCULUSXRHMD_API FColorLutDesc
{
FColorLutDesc();
FColorLutDesc(const TArray<uint64>& InColorLuts, float InWeight);
float Weight;
TArray<uint64> ColorLuts;
};
struct OCULUSXRHMD_API FEdgeStyleParameters
{
public:
FEdgeStyleParameters();
FEdgeStyleParameters(
bool bEnableEdgeColor,
bool bEnableColorMap,
float TextureOpacityFactor,
float Brightness,
float Contrast,
float Posterize,
float Saturation,
FLinearColor EdgeColor,
FLinearColor ColorScale,
FLinearColor ColorOffset,
EOculusXRColorMapType InColorMapType,
const TArray<FLinearColor>& InColorMapGradient,
const FColorLutDesc& InLutDesc);
bool bEnableEdgeColor;
bool bEnableColorMap;
bool bUseColorLuts;
float TextureOpacityFactor;
float Brightness;
float Contrast;
float Posterize;
float Saturation;
FLinearColor EdgeColor;
FLinearColor ColorScale;
FLinearColor ColorOffset;
EOculusXRColorMapType ColorMapType;
TArray<uint8> ColorMapData;
FColorLutDesc ColorLutDesc;
private:
/** Generates the corresponding color map based on given color map type */
TArray<uint8> GenerateColorMapData(EOculusXRColorMapType InColorMapType, const TArray<FLinearColor>& InColorMapGradient);
/** Generates a grayscale to color color map based on given gradient --> It also applies the color scale and offset */
TArray<uint8> GenerateMonoToRGBA(const TArray<FLinearColor>& InGradient, const TArray<uint8>& InColorMapData);
/** Generates a grayscale color map with given Brightness/Contrast/Posterize settings */
TArray<uint8> GenerateMonoBrightnessContrastPosterizeMap();
/** Generates a luminance based colormap from the the Brightness/Contrast */
TArray<uint8> GenerateBrightnessContrastSaturationColorMap();
};
#if UE_VERSION_OLDER_THAN(5, 3, 0)
#define OCULUSXRHMD_API_CLASS OCULUSXRHMD_API
#define OCULUSXRHMD_API_MEMBER
#else
#define OCULUSXRHMD_API_CLASS
#define OCULUSXRHMD_API_MEMBER OCULUSXRHMD_API
#endif
class OCULUSXRHMD_API_CLASS FReconstructedLayer : public IStereoLayerShape
{
public:
OCULUSXRHMD_API_MEMBER static const FName ShapeName;
virtual FName GetShapeName() override { return ShapeName; }
virtual IStereoLayerShape* Clone() const override { return new FReconstructedLayer(*this); }
public:
FReconstructedLayer(){};
FReconstructedLayer(const FEdgeStyleParameters& EdgeStyleParameters, EOculusXRPassthroughLayerOrder PassthroughLayerOrder)
: EdgeStyleParameters(EdgeStyleParameters), PassthroughLayerOrder(PassthroughLayerOrder){};
FEdgeStyleParameters EdgeStyleParameters;
EOculusXRPassthroughLayerOrder PassthroughLayerOrder;
};
struct FUserDefinedGeometryDesc
{
FUserDefinedGeometryDesc(const FString& MeshName, OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh, const FTransform& Transform, bool bUpdateTransform)
: MeshName(MeshName)
, PassthroughMesh(PassthroughMesh)
, Transform(Transform)
, bUpdateTransform(bUpdateTransform){};
FString MeshName;
OculusXRHMD::FOculusPassthroughMeshRef PassthroughMesh;
FTransform Transform;
bool bUpdateTransform;
};
class OCULUSXRHMD_API_CLASS FUserDefinedLayer : public IStereoLayerShape
{
public:
OCULUSXRHMD_API_MEMBER static const FName ShapeName;
virtual FName GetShapeName() override { return ShapeName; }
virtual IStereoLayerShape* Clone() const override { return new FUserDefinedLayer(*this); }
public:
FUserDefinedLayer(){};
FUserDefinedLayer(TArray<FUserDefinedGeometryDesc> InUserGeometryList, const FEdgeStyleParameters& EdgeStyleParameters, EOculusXRPassthroughLayerOrder PassthroughLayerOrder)
: UserGeometryList{}
, EdgeStyleParameters(EdgeStyleParameters)
, PassthroughLayerOrder(PassthroughLayerOrder)
{
UserGeometryList = InUserGeometryList;
}
TArray<FUserDefinedGeometryDesc> UserGeometryList;
FEdgeStyleParameters EdgeStyleParameters;
EOculusXRPassthroughLayerOrder PassthroughLayerOrder;
private:
};

View File

@@ -0,0 +1,30 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "Templates/RefCounting.h"
namespace OculusXRHMD
{
class FOculusPassthroughMesh : public FRefCountedObject
{
public:
FOculusPassthroughMesh(const TArray<FVector>& InVertices, const TArray<int32>& InTriangles)
: Vertices(InVertices)
, Triangles(InTriangles)
{
}
const TArray<FVector>& GetVertices() const { return Vertices; };
const TArray<int32>& GetTriangles() const { return Triangles; };
private:
TArray<FVector> Vertices;
TArray<int32> Triangles;
};
typedef TRefCountPtr<FOculusPassthroughMesh> FOculusPassthroughMeshRef;
} // namespace OculusXRHMD

View File

@@ -0,0 +1,75 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "Misc/DateTime.h"
namespace OculusXRTelemetry
{
enum class OCULUSXRHMD_API EAction : short
{
Start = 1,
Success = 2,
Fail = 3,
Cancel = 4,
DrawComplete = 5,
OnResume = 6
};
class OCULUSXRHMD_API FTelemetryInstanceKey
{
public:
constexpr FTelemetryInstanceKey(const int InstanceKey)
: Value(InstanceKey){};
constexpr int GetValue() const { return Value; }
private:
int Value;
};
constexpr FTelemetryInstanceKey DefaultTelemetryInstance = FTelemetryInstanceKey(0);
class OCULUSXRHMD_API FTelemetryTimestamp
{
static constexpr int OneHundredNsToMs = 1000;
public:
FTelemetryTimestamp(FDateTime Timestamp)
: TotalMs(Timestamp.GetTicks() / OneHundredNsToMs){};
constexpr FTelemetryTimestamp(int64 TimestampMs)
: TotalMs(TimestampMs){};
constexpr int64 GetTimestamp() const { return TotalMs; }
private:
int64 TotalMs;
};
constexpr FTelemetryTimestamp AutoSetTimestamp = FTelemetryTimestamp(-1);
struct OCULUSXRHMD_API FEmptyBackend
{
static bool MarkerStart(int, FTelemetryInstanceKey, FTelemetryTimestamp) { return false; };
static bool MarkerEnd(int, EAction, FTelemetryInstanceKey, FTelemetryTimestamp) { return false; };
static bool MarkerPoint(int, const char*, FTelemetryInstanceKey, FTelemetryTimestamp) { return false; };
static bool MarkerPointCached(int, int, FTelemetryInstanceKey, FTelemetryTimestamp) { return false; };
static bool MarkerAnnotation(int, const char*, const char*, FTelemetryInstanceKey) { return false; };
static bool CreateMarkerHandle(const char*, int*) { return false; };
static bool DestroyMarkerHandle(int) { return false; };
static bool OnEditorShutdown() { return false; };
static constexpr bool IsNullBackend() { return true; };
};
struct OCULUSXRHMD_API FQPLBackend
{
static bool MarkerStart(int MarkerId, FTelemetryInstanceKey InstanceKey, FTelemetryTimestamp Timestamp);
static bool MarkerEnd(int MarkerId, EAction Action, FTelemetryInstanceKey InstanceKey, FTelemetryTimestamp Timestamp);
static bool MarkerPoint(int MarkerId, const char* Name, FTelemetryInstanceKey InstanceKey, FTelemetryTimestamp Timestamp);
static bool MarkerPointCached(int MarkerId, int NameHandle, FTelemetryInstanceKey InstanceKey, FTelemetryTimestamp Timestamp);
static bool MarkerAnnotation(int MarkerId, const char* AnnotationKey, const char* AnnotationValue, FTelemetryInstanceKey InstanceKey);
static bool CreateMarkerHandle(const char* Name, int* NameHandle);
static bool DestroyMarkerHandle(int NameHandle);
static bool OnEditorShutdown();
static constexpr bool IsNullBackend() { return false; };
};
} // namespace OculusXRTelemetry

View File

@@ -0,0 +1,170 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRQPL.h"
namespace OculusXRTelemetry
{
#ifndef TURN_OFF_META_TELEMETRY
using FTelemetryBackend = FQPLBackend;
#else
using FTelemetryBackend = FEmptyBackend;
#endif
OCULUSXRHMD_API bool IsActive();
OCULUSXRHMD_API void IfActiveThen(TUniqueFunction<void()> Function);
OCULUSXRHMD_API void PropagateTelemetryConsent();
template <typename Backend = FTelemetryBackend>
class OCULUSXRHMD_API TMarkerPoint : FNoncopyable
{
public:
explicit TMarkerPoint(const char* Name)
: bCreated(Backend::CreateMarkerHandle(Name, Handle)) {}
~TMarkerPoint()
{
if (bCreated)
{
Backend::DestroyMarkerHandle(Handle);
}
}
int GetHandle() const { return Handle; }
private:
const bool bCreated{ false };
const int Handle{ -1 };
};
template <int MarkerId, typename Backend = FTelemetryBackend>
class TMarker
{
public:
explicit TMarker(const FTelemetryInstanceKey InstanceKey = DefaultTelemetryInstance)
: InstanceKey(InstanceKey)
{
}
TMarker(const TMarker&& Other) noexcept
: InstanceKey(Other.GetMarkerId()) {}
const TMarker& Start(const FTelemetryTimestamp Timestamp = AutoSetTimestamp) const
{
Backend::MarkerStart(MarkerId, InstanceKey, Timestamp);
return *this;
}
const TMarker& AddAnnotation(const char* Key, const char* Value) const
{
Backend::MarkerAnnotation(MarkerId, Key, Value, InstanceKey);
return *this;
}
const TMarker& AddPoint(const char* Name, const FTelemetryTimestamp Timestamp = AutoSetTimestamp) const
{
Backend::MarkerPoint(MarkerId, Name, InstanceKey, Timestamp);
return *this;
}
const TMarker& AddPoint(const TMarkerPoint<Backend>& MarkerPoint, const FTelemetryTimestamp Timestamp = AutoSetTimestamp) const
{
Backend::MarkerPoint(MarkerId, MarkerPoint.GetHandle(), InstanceKey, Timestamp);
return *this;
}
void End(EAction Result, const FTelemetryTimestamp Timestamp = AutoSetTimestamp) const
{
Backend::MarkerEnd(MarkerId, Result, InstanceKey, Timestamp);
}
constexpr static int GetMarkerId()
{
return MarkerId;
}
private:
const FTelemetryInstanceKey InstanceKey;
};
struct FIgnoreNotEndedMarker
{
template <int MarkerId, typename Backend>
constexpr const FIgnoreNotEndedMarker& operator=(const TMarker<MarkerId, Backend>&) const noexcept
{
// do nothing
return *this;
}
};
constexpr FIgnoreNotEndedMarker NotEnd{};
enum class EScopeMode
{
StartAndEnd,
Start,
End
};
template <typename TMarker, EScopeMode TScope = EScopeMode::StartAndEnd>
class TScopedMarker : public FNoncopyable
{
TOptional<TMarker> Marker;
EAction Result{ EAction::Success };
static constexpr EScopeMode Scope{ TScope };
public:
TScopedMarker(const FTelemetryInstanceKey InstanceKey = DefaultTelemetryInstance)
{
if (IsActive())
{
Marker.Emplace(InstanceKey);
if constexpr (Scope != EScopeMode::End)
{
const auto& Self = Start();
}
}
}
~TScopedMarker()
{
if constexpr (Scope != EScopeMode::Start)
{
End();
}
}
const TScopedMarker& Start() const
{
if (Marker)
{
Marker->Start();
}
return *this;
}
const TScopedMarker& AddPoint(const char* Name) const
{
if (Marker)
{
Marker->AddPoint(Name);
}
return *this;
}
const TScopedMarker& AddAnnotation(const char* Key, const char* Value) const
{
if (Marker)
{
Marker->AddAnnotation(Key, Value);
}
return *this;
}
const TScopedMarker& SetResult(EAction InResult)
{
Result = InResult;
return *this;
}
void End() const
{
if (Marker)
{
Marker->End(Result);
}
}
};
} // namespace OculusXRTelemetry