From 3f894f9ba1605656dbd32458541d96613e90054b Mon Sep 17 00:00:00 2001
From: xpeng <1216772231@qq.com>
Date: Sat, 20 Aug 2022 21:35:57 +0200
Subject: [PATCH] Add Oculus MR
---
.../Oculus_MRC_XRT_Manager_Full.prefab | 578 +
Assets/MRCHelpers/Scripts/CopyTransform.cs | 30 +
.../Scripts/OVRMixedRealityCaptureTestMod.cs | 165 +
.../Scripts/RemoveMRCamerasTracking.cs | 59 +
Assets/Oculus/OculusProjectConfig.asset | 31 +
Assets/Oculus/VR/AudioClips/sfx_belt_in.wav | Bin 0 -> 13292 bytes
.../Editor/AndroidManifest.OVRSubmission.xml | 21 +
Assets/Oculus/VR/Editor/OVRADBTool.cs | 261 +
Assets/Oculus/VR/Editor/OVRBuild.cs | 880 +
Assets/Oculus/VR/Editor/OVRBundleManager.cs | 716 +
Assets/Oculus/VR/Editor/OVRBundleTool.cs | 647 +
Assets/Oculus/VR/Editor/OVRConfig.cs | 203 +
Assets/Oculus/VR/Editor/OVRDeviceSelector.cs | 68 +
Assets/Oculus/VR/Editor/OVRDirectorySyncer.cs | 242 +
.../Editor/OVREngineConfigurationUpdater.cs | 199 +
.../VR/Editor/OVRExpansionFileGenerator.cs | 116 +
.../Oculus/VR/Editor/OVRGradleGeneration.cs | 449 +
.../VR/Editor/OVRLayerAttributeEditor.cs | 31 +
Assets/Oculus/VR/Editor/OVRLint.cs | 1024 +
.../VR/Editor/OVRManifestPreprocessor.cs | 469 +
Assets/Oculus/VR/Editor/OVRPlatformTool.cs | 1373 +
.../VR/Editor/OVRPlatformToolSettings.cs | 600 +
Assets/Oculus/VR/Editor/OVRPluginUpdater.cs | 1267 +
.../Oculus/VR/Editor/OVRPluginUpdaterStub.cs | 40 +
Assets/Oculus/VR/Editor/OVRProjectConfig.cs | 209 +
.../Oculus/VR/Editor/OVRScreenshotWizard.cs | 215 +
.../VR/Editor/OVRShaderBuildProcessor.cs | 74 +
.../VR/Editor/OVRSystemProfilerPanel.cs | 735 +
.../Oculus/VR/Editor/Oculus.VR.Editor.asmdef | 47 +
Assets/Oculus/VR/Editor/PathHelper.cs | 46 +
.../VR/Editor/Scenes/OVRTransitionScene.unity | 671 +
Assets/Oculus/VR/Editor/Textures/odh_icon.png | Bin 0 -> 7984 bytes
.../Oculus/VR/Editor/network_sec_config.xml | 4 +
Assets/Oculus/VR/LICENSE.txt | 2 +
Assets/Oculus/VR/Materials/Arrow.mat | 64 +
.../Oculus/VR/Materials/BasicHandMaterial.mat | 76 +
Assets/Oculus/VR/Materials/CubeMaterial.mat | 29 +
Assets/Oculus/VR/Materials/GazePointer.mat | 43 +
.../OVRTrackedKeyboard/BillboardMask.mat | 94 +
Assets/Oculus/VR/Materials/PlainMaterial.mat | 77 +
Assets/Oculus/VR/Materials/SelectionRay.mat | 78 +
Assets/Oculus/VR/Materials/SelfieImage.mat | 77 +
Assets/Oculus/VR/Materials/SelfieMat.mat | 77 +
.../VR/Materials/cursor_timer_material.mat | 43 +
Assets/Oculus/VR/Materials/gaze_cursor.mat | 28 +
.../VR/Meshes/HandTracking/OculusHand_L.fbx | Bin 0 -> 335584 bytes
.../VR/Meshes/HandTracking/OculusHand_R.fbx | Bin 0 -> 356160 bytes
.../OculusTouchQuest2LeftAnimation.controller | 679 +
...OculusTouchQuest2RightAnimation.controller | 679 +
.../Materials/OculusTouchForQuest2_MAT.mat | 78 +
.../OculusTouchForQuest2_Left.fbx | 3998 ++
.../OculusTouchForQuest2_Right.fbx | 3785 ++
...TouchQuestAndRiftSLeftAnimation.controller | 679 +
...ouchQuestAndRiftSRightAnimation.controller | 679 +
.../OculusTouchForQuestAndRiftS_Material.mat | 76 +
.../OculusTouchForQuestAndRiftS_Left.fbx | 3612 ++
.../OculusTouchForQuestAndRiftS_Right.fbx | 3492 ++
.../OculusTouchRiftLeftAnimation.controller | 679 +
.../OculusTouchRiftRightAnimation.controller | 679 +
.../Materials/OculusTouchForRift_Material.mat | 76 +
.../left_touch_controller_model_skel.fbx | 5776 ++
.../right_touch_controller_model_skel.fbx | 4801 ++
Assets/Oculus/VR/NOTICE.txt | 93 +
Assets/Oculus/VR/Oculus.VR.asmdef | 47 +
.../VR/Plugins/1.74.0/Android/OVRPlugin.aar | Bin 0 -> 1044067 bytes
.../1.74.0/AndroidOpenXR/OVRPlugin.aar | Bin 0 -> 3967235 bytes
.../1.74.0/AndroidUniversal/OVRPlugin.aar | Bin 0 -> 2147798 bytes
.../OVRGamepad.bundle/Contents/Info.plist | 36 +
.../Contents/MacOS/OVRGamepad | Bin 0 -> 60760 bytes
Assets/Oculus/VR/Plugins/placeholder.txt | 0
Assets/Oculus/VR/Prefabs/Cursor_Timer.prefab | 70 +
Assets/Oculus/VR/Prefabs/OVRCameraRig.prefab | 625 +
.../VR/Prefabs/OVRControllerPrefab.prefab | 496 +
.../VR/Prefabs/OVRCubemapCaptureProbe.prefab | 58 +
.../VR/Prefabs/OVRCustomHandPrefab_L.prefab | 2683 +
.../VR/Prefabs/OVRCustomHandPrefab_R.prefab | 2683 +
Assets/Oculus/VR/Prefabs/OVRHandPrefab.prefab | 168 +
.../VR/Prefabs/OVRPlayerController.prefab | 656 +
.../Prefabs/OVRRuntimeControllerPrefab.prefab | 48 +
.../Oculus/VR/Prefabs/OVRSceneManager.prefab | 62 +
.../OVRTrackedKeyboard/OVRHandMask_L.prefab | 4692 ++
.../OVRTrackedKeyboard/OVRHandMask_R.prefab | 4694 ++
.../OVRTrackedKeyboard.prefab | 243 +
.../Oculus/VR/Resources/Cubemap Blit.shader | 76 +
.../VR/Resources/OVRMRCameraFrame.shader | 99 +
.../VR/Resources/OVRMRCameraFrameLit.shader | 112 +
.../Oculus/VR/Resources/OVRMRChromaKey.cginc | 103 +
Assets/Oculus/VR/Resources/OVRMRUnlit.shader | 48 +
.../VR/Resources/OVRMRUnlitTransparent.shader | 54 +
.../Oculus/VR/Resources/Texture2D Blit.shader | 72 +
.../VR/Resources/Underlay Impostor.shader | 48 +
.../Underlay Transparent Occluder.shader | 36 +
.../Resources/Unlit Transparent Color.shader | 17 +
.../Oculus/VR/Scenes/ControllerModels.unity | 7105 +++
Assets/Oculus/VR/Scenes/Cubes.unity | 47147 ++++++++++++++++
Assets/Oculus/VR/Scenes/HandTest.unity | 1292 +
Assets/Oculus/VR/Scenes/HandTest_Custom.unity | 2015 +
Assets/Oculus/VR/Scenes/MRC.unity | 7221 +++
Assets/Oculus/VR/Scenes/Room.unity | 6363 +++
Assets/Oculus/VR/Scenes/SpectatorMode.unity | 7470 +++
Assets/Oculus/VR/Scenes/TrackedKeyboard.unity | 4894 ++
Assets/Oculus/VR/Scenes/Trivial.unity | 306 +
Assets/Oculus/VR/Scenes/UI.unity | 3288 ++
.../Composition/OVRCameraComposition.cs | 338 +
.../VR/Scripts/Composition/OVRComposition.cs | 108 +
.../Scripts/Composition/OVRCompositionUtil.cs | 178 +
.../Composition/OVRDirectComposition.cs | 203 +
.../Composition/OVRExternalComposition.cs | 584 +
.../Composition/OVRSandwichComposition.cs | 24 +
.../Scripts/Editor/OVRCustomSkeletonEditor.cs | 66 +
.../Oculus/VR/Scripts/Editor/OVREditorUtil.cs | 252 +
.../VR/Scripts/Editor/OVRManagerEditor.cs | 184 +
.../Editor/OVROverlayDestRectEditor.shader | 174 +
.../VR/Scripts/Editor/OVROverlayEditor.cs | 654 +
.../Editor/OVROverlaySrcRectEditor.shader | 150 +
.../Editor/OVRPassthroughLayerEditor.cs | 116 +
.../Scripts/Editor/OVRProfilerDeprecated.cs | 41 +
.../Scripts/Editor/OVRProjectConfigEditor.cs | 238 +
.../Editor/Oculus.VR.Scripts.Editor.asmdef | 42 +
.../Scripts/Editor/OculusXRFeatureEnabler.cs | 92 +
Assets/Oculus/VR/Scripts/OVRBoundary.cs | 245 +
Assets/Oculus/VR/Scripts/OVRCameraRig.cs | 464 +
Assets/Oculus/VR/Scripts/OVRCommon.cs | 749 +
.../VR/Scripts/OVRDebugHeadController.cs | 143 +
Assets/Oculus/VR/Scripts/OVRDisplay.cs | 394 +
Assets/Oculus/VR/Scripts/OVRGLTFAccessor.cs | 442 +
Assets/Oculus/VR/Scripts/OVRGLTFLoader.cs | 663 +
Assets/Oculus/VR/Scripts/OVRHaptics.cs | 392 +
Assets/Oculus/VR/Scripts/OVRHapticsClip.cs | 168 +
.../Oculus/VR/Scripts/OVRHeadsetEmulator.cs | 188 +
Assets/Oculus/VR/Scripts/OVRInput.cs | 3188 ++
Assets/Oculus/VR/Scripts/OVRKtxTexture.cs | 77 +
Assets/Oculus/VR/Scripts/OVRLayerAttribute.cs | 27 +
Assets/Oculus/VR/Scripts/OVRManager.cs | 2641 +
Assets/Oculus/VR/Scripts/OVRMixedReality.cs | 146 +
.../VR/Scripts/OVROnCompleteListener.cs | 37 +
Assets/Oculus/VR/Scripts/OVROverlay.cs | 1204 +
.../VR/Scripts/OVROverlayMeshGenerator.cs | 443 +
.../Oculus/VR/Scripts/OVRPassthroughLayer.cs | 955 +
Assets/Oculus/VR/Scripts/OVRPlatformMenu.cs | 127 +
Assets/Oculus/VR/Scripts/OVRPlugin.cs | 8300 +++
.../Oculus/VR/Scripts/OVRPointerVisualizer.cs | 40 +
Assets/Oculus/VR/Scripts/OVRProfile.cs | 53 +
Assets/Oculus/VR/Scripts/OVRResources.cs | 66 +
.../Oculus/VR/Scripts/OVRRuntimeSettings.cs | 91 +
Assets/Oculus/VR/Scripts/OVRSceneAnchor.cs | 166 +
Assets/Oculus/VR/Scripts/OVRSceneLoader.cs | 285 +
Assets/Oculus/VR/Scripts/OVRSceneManager.cs | 699 +
.../Oculus/VR/Scripts/OVRSceneModelLoader.cs | 132 +
Assets/Oculus/VR/Scripts/OVRScenePlane.cs | 86 +
.../VR/Scripts/OVRScenePlaneMeshFilter.cs | 34 +
.../VR/Scripts/OVRScenePrefabOverride.cs | 148 +
Assets/Oculus/VR/Scripts/OVRSceneVolume.cs | 86 +
.../VR/Scripts/OVRSemanticClassification.cs | 86 +
Assets/Oculus/VR/Scripts/OVRSpace.cs | 85 +
Assets/Oculus/VR/Scripts/OVRSpatialAnchor.cs | 385 +
.../Scripts/OVRTrackedKeyboard/OVRKeyboard.cs | 94 +
.../OVRTrackedKeyboard/OVRTrackedKeyboard.cs | 1020 +
.../OVRTrackedKeyboardHands.cs | 523 +
.../OVRTrackedKeyboardSampleControls.cs | 127 +
Assets/Oculus/VR/Scripts/OVRTracker.cs | 197 +
Assets/Oculus/VR/Scripts/OVRXRSDKNative.cs | 47 +
Assets/Oculus/VR/Scripts/OculusXRFeature.cs | 194 +
.../VR/Scripts/Util/OVRAudioSourceTest.cs | 69 +
.../VR/Scripts/Util/OVRAutoDestroyInMRC.cs | 54 +
.../VR/Scripts/Util/OVRChromaticAberration.cs | 55 +
.../VR/Scripts/Util/OVRControllerHelper.cs | 221 +
.../VR/Scripts/Util/OVRControllerTest.cs | 199 +
.../VR/Scripts/Util/OVRCubemapCapture.cs | 290 +
Assets/Oculus/VR/Scripts/Util/OVRCursor.cs | 30 +
.../VR/Scripts/Util/OVRCustomSkeleton.cs | 140 +
Assets/Oculus/VR/Scripts/Util/OVRDebugInfo.cs | 429 +
.../Oculus/VR/Scripts/Util/OVRGazePointer.cs | 282 +
.../Scripts/Util/OVRGearVrControllerTest.cs | 26 +
Assets/Oculus/VR/Scripts/Util/OVRGrabbable.cs | 168 +
Assets/Oculus/VR/Scripts/Util/OVRGrabber.cs | 422 +
Assets/Oculus/VR/Scripts/Util/OVRGridCube.cs | 199 +
Assets/Oculus/VR/Scripts/Util/OVRHand.cs | 230 +
Assets/Oculus/VR/Scripts/Util/OVRHandTest.cs | 194 +
.../Oculus/VR/Scripts/Util/OVRInputModule.cs | 919 +
Assets/Oculus/VR/Scripts/Util/OVRMesh.cs | 162 +
.../VR/Scripts/Util/OVRMeshGenerator.cs | 233 +
.../Oculus/VR/Scripts/Util/OVRMeshRenderer.cs | 203 +
.../OVRMixedRealityCaptureConfiguration.cs | 101 +
.../Util/OVRMixedRealityCaptureSettings.cs | 132 +
.../Util/OVRMixedRealityCaptureTest.cs | 267 +
Assets/Oculus/VR/Scripts/Util/OVRModeParms.cs | 85 +
.../Oculus/VR/Scripts/Util/OVRMonoscopic.cs | 52 +
Assets/Oculus/VR/Scripts/Util/OVRNetwork.cs | 439 +
.../VR/Scripts/Util/OVRPhysicsRaycaster.cs | 183 +
.../VR/Scripts/Util/OVRPlayerController.cs | 644 +
.../VR/Scripts/Util/OVRPointerEventData.cs | 98 +
Assets/Oculus/VR/Scripts/Util/OVRProfiler.cs | 21 +
.../VR/Scripts/Util/OVRProgressIndicator.cs | 48 +
Assets/Oculus/VR/Scripts/Util/OVRRaycaster.cs | 332 +
Assets/Oculus/VR/Scripts/Util/OVRRecord.cs | 75 +
.../VR/Scripts/Util/OVRResetOrientation.cs | 48 +
.../VR/Scripts/Util/OVRRuntimeController.cs | 154 +
.../Scripts/Util/OVRSceneSampleController.cs | 228 +
.../Oculus/VR/Scripts/Util/OVRScreenFade.cs | 236 +
Assets/Oculus/VR/Scripts/Util/OVRSkeleton.cs | 631 +
.../VR/Scripts/Util/OVRSkeletonRenderer.cs | 377 +
.../Scripts/Util/OVRSpectatorModeDomeTest.cs | 298 +
.../VR/Scripts/Util/OVRSystemPerfMetrics.cs | 311 +
Assets/Oculus/VR/Scripts/Util/OVRVignette.cs | 452 +
.../Oculus/VR/Scripts/Util/OVRWaitCursor.cs | 37 +
.../VR/Shaders/OVRColorRampAlpha.shader | 63 +
.../BillboardTransitions.shader | 142 +
.../OVRTrackedKeyboard/HandMask.shader | 37 +
.../PunchThroughPassthrough.shader | 54 +
Assets/Oculus/VR/Shaders/OVRVignette.shader | 67 +
.../Oculus/VR/Shaders/Unlit Crosshair.shader | 79 +
Assets/Oculus/VR/Textures/Black.png | Bin 0 -> 17239 bytes
Assets/Oculus/VR/Textures/GazeRing.png | Bin 0 -> 19053 bytes
.../HandTracking/HandTracking_uvmap_2048.png | Bin 0 -> 2169310 bytes
.../Oculus/VR/Textures/Icons/OculusIcon.png | Bin 0 -> 2416 bytes
.../Oculus/VR/Textures/Logos/OculusLogo.png | Bin 0 -> 4400 bytes
.../VR/Textures/Logos/OculusLogoSplash.png | Bin 0 -> 3895 bytes
.../OculusTouchForQuest2_AlbedoRoughness.tga | Bin 0 -> 4194348 bytes
.../OculusQuestTouchControllerTexture.tga | Bin 0 -> 16777260 bytes
.../touchController_albedo.png | Bin 0 -> 361947 bytes
.../touchController_controlmap.tga | Bin 0 -> 1048620 bytes
.../VR/Textures/SelfieImage.renderTexture | 37 +
.../Oculus/VR/Textures/color_ramp_timer.tga | Bin 0 -> 262188 bytes
Assets/Oculus/VR/Textures/cube_texture.png | Bin 0 -> 179408 bytes
Assets/Oculus/VR/Textures/gaze_cursor.png | Bin 0 -> 15982 bytes
.../Oculus/VR/Textures/gaze_cursor_timer.tga | Bin 0 -> 262188 bytes
Assets/Oculus/VR/Textures/handle.png | Bin 0 -> 2293 bytes
Assets/Oculus/VR/Textures/windowgui.png | Bin 0 -> 82702 bytes
Assets/Oculus/VR/ThirdParty/SimpleJSON.cs | 1352 +
.../Oculus/VR/ThirdParty/SimpleJSONUnity.cs | 370 +
Assets/Oculus/VR/ThirdParty/openvr_api.cs | 6032 ++
.../Resources/OVRPlatformToolSettings.asset | 22 +
Assets/Resources/OculusRuntimeSettings.asset | 15 +
Assets/Scenes/MainScene.unity | 266 +-
Assets/XR/Loaders/Oculus Loader.asset | 14 +
Assets/XR/Settings/Oculus Settings.asset | 29 +
.../XR/Settings/OpenXR Package Settings.asset | 32 +
Assets/XR/XRGeneralSettings.asset | 2 +-
Packages/manifest.json | 1 +
Packages/packages-lock.json | 9 +
ProjectSettings/EditorBuildSettings.asset | 2 +
ProjectSettings/ProjectSettings.asset | 5 +-
243 files changed, 194525 insertions(+), 6 deletions(-)
create mode 100644 Assets/MRCHelpers/Prefabs/Oculus_MRC_XRT_Manager_Full.prefab
create mode 100644 Assets/MRCHelpers/Scripts/CopyTransform.cs
create mode 100644 Assets/MRCHelpers/Scripts/OVRMixedRealityCaptureTestMod.cs
create mode 100644 Assets/MRCHelpers/Scripts/RemoveMRCamerasTracking.cs
create mode 100644 Assets/Oculus/OculusProjectConfig.asset
create mode 100644 Assets/Oculus/VR/AudioClips/sfx_belt_in.wav
create mode 100644 Assets/Oculus/VR/Editor/AndroidManifest.OVRSubmission.xml
create mode 100644 Assets/Oculus/VR/Editor/OVRADBTool.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRBuild.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRBundleManager.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRBundleTool.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRConfig.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRDeviceSelector.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRDirectorySyncer.cs
create mode 100644 Assets/Oculus/VR/Editor/OVREngineConfigurationUpdater.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRExpansionFileGenerator.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRGradleGeneration.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRLayerAttributeEditor.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRLint.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRManifestPreprocessor.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRPlatformTool.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRPlatformToolSettings.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRPluginUpdater.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRPluginUpdaterStub.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRProjectConfig.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRScreenshotWizard.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRShaderBuildProcessor.cs
create mode 100644 Assets/Oculus/VR/Editor/OVRSystemProfilerPanel.cs
create mode 100644 Assets/Oculus/VR/Editor/Oculus.VR.Editor.asmdef
create mode 100644 Assets/Oculus/VR/Editor/PathHelper.cs
create mode 100644 Assets/Oculus/VR/Editor/Scenes/OVRTransitionScene.unity
create mode 100644 Assets/Oculus/VR/Editor/Textures/odh_icon.png
create mode 100644 Assets/Oculus/VR/Editor/network_sec_config.xml
create mode 100644 Assets/Oculus/VR/LICENSE.txt
create mode 100644 Assets/Oculus/VR/Materials/Arrow.mat
create mode 100644 Assets/Oculus/VR/Materials/BasicHandMaterial.mat
create mode 100644 Assets/Oculus/VR/Materials/CubeMaterial.mat
create mode 100644 Assets/Oculus/VR/Materials/GazePointer.mat
create mode 100644 Assets/Oculus/VR/Materials/OVRTrackedKeyboard/BillboardMask.mat
create mode 100644 Assets/Oculus/VR/Materials/PlainMaterial.mat
create mode 100644 Assets/Oculus/VR/Materials/SelectionRay.mat
create mode 100644 Assets/Oculus/VR/Materials/SelfieImage.mat
create mode 100644 Assets/Oculus/VR/Materials/SelfieMat.mat
create mode 100644 Assets/Oculus/VR/Materials/cursor_timer_material.mat
create mode 100644 Assets/Oculus/VR/Materials/gaze_cursor.mat
create mode 100644 Assets/Oculus/VR/Meshes/HandTracking/OculusHand_L.fbx
create mode 100644 Assets/Oculus/VR/Meshes/HandTracking/OculusHand_R.fbx
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuest2/Animation/OculusTouchQuest2LeftAnimation.controller
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuest2/Animation/OculusTouchQuest2RightAnimation.controller
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuest2/Materials/OculusTouchForQuest2_MAT.mat
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuest2/OculusTouchForQuest2_Left.fbx
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuest2/OculusTouchForQuest2_Right.fbx
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuestAndRiftS/Animation/OculusTouchQuestAndRiftSLeftAnimation.controller
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuestAndRiftS/Animation/OculusTouchQuestAndRiftSRightAnimation.controller
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuestAndRiftS/Materials/OculusTouchForQuestAndRiftS_Material.mat
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuestAndRiftS/OculusTouchForQuestAndRiftS_Left.fbx
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForQuestAndRiftS/OculusTouchForQuestAndRiftS_Right.fbx
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForRift/Animation/OculusTouchRiftLeftAnimation.controller
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForRift/Animation/OculusTouchRiftRightAnimation.controller
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForRift/Materials/OculusTouchForRift_Material.mat
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForRift/left_touch_controller_model_skel.fbx
create mode 100644 Assets/Oculus/VR/Meshes/OculusTouchForRift/right_touch_controller_model_skel.fbx
create mode 100644 Assets/Oculus/VR/NOTICE.txt
create mode 100644 Assets/Oculus/VR/Oculus.VR.asmdef
create mode 100644 Assets/Oculus/VR/Plugins/1.74.0/Android/OVRPlugin.aar
create mode 100644 Assets/Oculus/VR/Plugins/1.74.0/AndroidOpenXR/OVRPlugin.aar
create mode 100644 Assets/Oculus/VR/Plugins/1.74.0/AndroidUniversal/OVRPlugin.aar
create mode 100644 Assets/Oculus/VR/Plugins/MacOSX/OVRGamepad.bundle/Contents/Info.plist
create mode 100644 Assets/Oculus/VR/Plugins/MacOSX/OVRGamepad.bundle/Contents/MacOS/OVRGamepad
create mode 100644 Assets/Oculus/VR/Plugins/placeholder.txt
create mode 100644 Assets/Oculus/VR/Prefabs/Cursor_Timer.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRCameraRig.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRControllerPrefab.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRCubemapCaptureProbe.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRCustomHandPrefab_L.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRCustomHandPrefab_R.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRHandPrefab.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRPlayerController.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRRuntimeControllerPrefab.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRSceneManager.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRTrackedKeyboard/OVRHandMask_L.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRTrackedKeyboard/OVRHandMask_R.prefab
create mode 100644 Assets/Oculus/VR/Prefabs/OVRTrackedKeyboard/OVRTrackedKeyboard.prefab
create mode 100644 Assets/Oculus/VR/Resources/Cubemap Blit.shader
create mode 100644 Assets/Oculus/VR/Resources/OVRMRCameraFrame.shader
create mode 100644 Assets/Oculus/VR/Resources/OVRMRCameraFrameLit.shader
create mode 100644 Assets/Oculus/VR/Resources/OVRMRChromaKey.cginc
create mode 100644 Assets/Oculus/VR/Resources/OVRMRUnlit.shader
create mode 100644 Assets/Oculus/VR/Resources/OVRMRUnlitTransparent.shader
create mode 100644 Assets/Oculus/VR/Resources/Texture2D Blit.shader
create mode 100644 Assets/Oculus/VR/Resources/Underlay Impostor.shader
create mode 100644 Assets/Oculus/VR/Resources/Underlay Transparent Occluder.shader
create mode 100644 Assets/Oculus/VR/Resources/Unlit Transparent Color.shader
create mode 100644 Assets/Oculus/VR/Scenes/ControllerModels.unity
create mode 100644 Assets/Oculus/VR/Scenes/Cubes.unity
create mode 100644 Assets/Oculus/VR/Scenes/HandTest.unity
create mode 100644 Assets/Oculus/VR/Scenes/HandTest_Custom.unity
create mode 100644 Assets/Oculus/VR/Scenes/MRC.unity
create mode 100644 Assets/Oculus/VR/Scenes/Room.unity
create mode 100644 Assets/Oculus/VR/Scenes/SpectatorMode.unity
create mode 100644 Assets/Oculus/VR/Scenes/TrackedKeyboard.unity
create mode 100644 Assets/Oculus/VR/Scenes/Trivial.unity
create mode 100644 Assets/Oculus/VR/Scenes/UI.unity
create mode 100644 Assets/Oculus/VR/Scripts/Composition/OVRCameraComposition.cs
create mode 100644 Assets/Oculus/VR/Scripts/Composition/OVRComposition.cs
create mode 100644 Assets/Oculus/VR/Scripts/Composition/OVRCompositionUtil.cs
create mode 100644 Assets/Oculus/VR/Scripts/Composition/OVRDirectComposition.cs
create mode 100644 Assets/Oculus/VR/Scripts/Composition/OVRExternalComposition.cs
create mode 100644 Assets/Oculus/VR/Scripts/Composition/OVRSandwichComposition.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVRCustomSkeletonEditor.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVREditorUtil.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVRManagerEditor.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVROverlayDestRectEditor.shader
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVROverlayEditor.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVROverlaySrcRectEditor.shader
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVRPassthroughLayerEditor.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVRProfilerDeprecated.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OVRProjectConfigEditor.cs
create mode 100644 Assets/Oculus/VR/Scripts/Editor/Oculus.VR.Scripts.Editor.asmdef
create mode 100644 Assets/Oculus/VR/Scripts/Editor/OculusXRFeatureEnabler.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRBoundary.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRCameraRig.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRCommon.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRDebugHeadController.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRDisplay.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRGLTFAccessor.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRGLTFLoader.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRHaptics.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRHapticsClip.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRHeadsetEmulator.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRInput.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRKtxTexture.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRLayerAttribute.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRManager.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRMixedReality.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVROnCompleteListener.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVROverlay.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVROverlayMeshGenerator.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRPassthroughLayer.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRPlatformMenu.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRPlugin.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRPointerVisualizer.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRProfile.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRResources.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRRuntimeSettings.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSceneAnchor.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSceneLoader.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSceneManager.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSceneModelLoader.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRScenePlane.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRScenePlaneMeshFilter.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRScenePrefabOverride.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSceneVolume.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSemanticClassification.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSpace.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRSpatialAnchor.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRKeyboard.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRTrackedKeyboard.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRTrackedKeyboardHands.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRTrackedKeyboard/OVRTrackedKeyboardSampleControls.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRTracker.cs
create mode 100644 Assets/Oculus/VR/Scripts/OVRXRSDKNative.cs
create mode 100644 Assets/Oculus/VR/Scripts/OculusXRFeature.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRAudioSourceTest.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRAutoDestroyInMRC.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRChromaticAberration.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRControllerHelper.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRControllerTest.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRCubemapCapture.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRCursor.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRCustomSkeleton.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRDebugInfo.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRGazePointer.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRGearVrControllerTest.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRGrabbable.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRGrabber.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRGridCube.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRHand.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRHandTest.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRInputModule.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMesh.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMeshGenerator.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMeshRenderer.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMixedRealityCaptureConfiguration.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMixedRealityCaptureSettings.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMixedRealityCaptureTest.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRModeParms.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRMonoscopic.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRNetwork.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRPhysicsRaycaster.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRPlayerController.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRPointerEventData.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRProfiler.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRProgressIndicator.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRRaycaster.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRRecord.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRResetOrientation.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRRuntimeController.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRSceneSampleController.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRScreenFade.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRSkeleton.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRSkeletonRenderer.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRSpectatorModeDomeTest.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRSystemPerfMetrics.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRVignette.cs
create mode 100644 Assets/Oculus/VR/Scripts/Util/OVRWaitCursor.cs
create mode 100644 Assets/Oculus/VR/Shaders/OVRColorRampAlpha.shader
create mode 100644 Assets/Oculus/VR/Shaders/OVRTrackedKeyboard/BillboardTransitions.shader
create mode 100644 Assets/Oculus/VR/Shaders/OVRTrackedKeyboard/HandMask.shader
create mode 100644 Assets/Oculus/VR/Shaders/OVRTrackedKeyboard/PunchThroughPassthrough.shader
create mode 100644 Assets/Oculus/VR/Shaders/OVRVignette.shader
create mode 100644 Assets/Oculus/VR/Shaders/Unlit Crosshair.shader
create mode 100644 Assets/Oculus/VR/Textures/Black.png
create mode 100644 Assets/Oculus/VR/Textures/GazeRing.png
create mode 100644 Assets/Oculus/VR/Textures/HandTracking/HandTracking_uvmap_2048.png
create mode 100644 Assets/Oculus/VR/Textures/Icons/OculusIcon.png
create mode 100644 Assets/Oculus/VR/Textures/Logos/OculusLogo.png
create mode 100644 Assets/Oculus/VR/Textures/Logos/OculusLogoSplash.png
create mode 100644 Assets/Oculus/VR/Textures/OculusTouchForQuest2/OculusTouchForQuest2_AlbedoRoughness.tga
create mode 100644 Assets/Oculus/VR/Textures/OculusTouchForQuestAndRiftS/OculusQuestTouchControllerTexture.tga
create mode 100644 Assets/Oculus/VR/Textures/OculusTouchForRift/touchController_albedo.png
create mode 100644 Assets/Oculus/VR/Textures/OculusTouchForRift/touchController_controlmap.tga
create mode 100644 Assets/Oculus/VR/Textures/SelfieImage.renderTexture
create mode 100644 Assets/Oculus/VR/Textures/color_ramp_timer.tga
create mode 100644 Assets/Oculus/VR/Textures/cube_texture.png
create mode 100644 Assets/Oculus/VR/Textures/gaze_cursor.png
create mode 100644 Assets/Oculus/VR/Textures/gaze_cursor_timer.tga
create mode 100644 Assets/Oculus/VR/Textures/handle.png
create mode 100644 Assets/Oculus/VR/Textures/windowgui.png
create mode 100644 Assets/Oculus/VR/ThirdParty/SimpleJSON.cs
create mode 100644 Assets/Oculus/VR/ThirdParty/SimpleJSONUnity.cs
create mode 100644 Assets/Oculus/VR/ThirdParty/openvr_api.cs
create mode 100644 Assets/Resources/OVRPlatformToolSettings.asset
create mode 100644 Assets/Resources/OculusRuntimeSettings.asset
create mode 100644 Assets/XR/Loaders/Oculus Loader.asset
create mode 100644 Assets/XR/Settings/Oculus Settings.asset
diff --git a/Assets/MRCHelpers/Prefabs/Oculus_MRC_XRT_Manager_Full.prefab b/Assets/MRCHelpers/Prefabs/Oculus_MRC_XRT_Manager_Full.prefab
new file mode 100644
index 0000000..e3b9c53
--- /dev/null
+++ b/Assets/MRCHelpers/Prefabs/Oculus_MRC_XRT_Manager_Full.prefab
@@ -0,0 +1,578 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &122432412
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 122432413}
+ - component: {fileID: 122432414}
+ m_Layer: 0
+ m_Name: RightEyeAnchor
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &122432413
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 122432412}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 1654481609}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!20 &122432414
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 122432412}
+ m_Enabled: 0
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 2
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!1 &608780163
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 608780164}
+ m_Layer: 0
+ m_Name: TrackerAnchor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &608780164
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 608780163}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 1654481609}
+ m_RootOrder: 5
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1010144588
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1010144589}
+ m_Layer: 0
+ m_Name: RightControllerAnchor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1010144589
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1010144588}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 1521644926}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1103977314
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1103977315}
+ - component: {fileID: 1103977316}
+ m_Layer: 0
+ m_Name: LeftEyeAnchor
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1103977315
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1103977314}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 1654481609}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!20 &1103977316
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1103977314}
+ m_Enabled: 0
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 1
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!1 &1174096078
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1174096079}
+ m_Layer: 0
+ m_Name: LeftControllerAnchor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1174096079
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1174096078}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 1814618716}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1255382394
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1255382395}
+ - component: {fileID: 1255382396}
+ m_Layer: 0
+ m_Name: CenterEyeAnchor
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1255382395
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1255382394}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 1654481609}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!20 &1255382396
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1255382394}
+ m_Enabled: 0
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!1 &1521644925
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1521644926}
+ m_Layer: 0
+ m_Name: RightHandAnchor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1521644926
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1521644925}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 1010144589}
+ m_Father: {fileID: 1654481609}
+ m_RootOrder: 4
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1654481608
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1654481609}
+ m_Layer: 0
+ m_Name: TrackingSpace
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1654481609
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1654481608}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 1103977315}
+ - {fileID: 1255382395}
+ - {fileID: 122432413}
+ - {fileID: 1814618716}
+ - {fileID: 1521644926}
+ - {fileID: 608780164}
+ m_Father: {fileID: 7449411159797096978}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1814618715
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1814618716}
+ m_Layer: 0
+ m_Name: LeftHandAnchor
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1814618716
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1814618715}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 1174096079}
+ m_Father: {fileID: 1654481609}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &342779316052413363
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6020623325241151543}
+ m_Layer: 0
+ m_Name: Oculus_MRC_XRT_Manager_Full
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &6020623325241151543
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 342779316052413363}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 7449411159797096978}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &7449411159797096982
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7449411159797096978}
+ - component: {fileID: 7449411159797096981}
+ - component: {fileID: 7449411159797096980}
+ - component: {fileID: 7449411159797096983}
+ - component: {fileID: 1089416225}
+ m_Layer: 0
+ m_Name: OVRManager
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7449411159797096978
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7449411159797096982}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 1654481609}
+ m_Father: {fileID: 6020623325241151543}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7449411159797096981
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7449411159797096982}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 7e933e81d3c20c74ea6fdc708a67e3a5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ useRecommendedMSAALevel: 1
+ _monoscopic: 0
+ enableAdaptiveResolution: 0
+ _colorGamut: 4
+ minRenderScale: 0.7
+ maxRenderScale: 1
+ _headPoseRelativeOffsetRotation: {x: 0, y: 0, z: 0}
+ _headPoseRelativeOffsetTranslation: {x: 0, y: 0, z: 0}
+ profilerTcpPort: 32419
+ expandMixedRealityCapturePropertySheet: 0
+ enableMixedReality: 0
+ compositionMethod: 0
+ extraHiddenLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ extraVisibleLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ dynamicCullingMask: 1
+ externalCompositionBackdropColorRift: {r: 0, g: 1, b: 0, a: 1}
+ externalCompositionBackdropColorQuest: {r: 0, g: 0, b: 0, a: 0}
+ capturingCameraDevice: 0
+ flipCameraFrameHorizontally: 0
+ flipCameraFrameVertically: 0
+ handPoseStateLatency: 0
+ sandwichCompositionRenderLatency: 0
+ sandwichCompositionBufferedFrames: 8
+ chromaKeyColor: {r: 0, g: 1, b: 0, a: 1}
+ chromaKeySimilarity: 0.6
+ chromaKeySmoothRange: 0.03
+ chromaKeySpillRange: 0.06
+ useDynamicLighting: 0
+ depthQuality: 1
+ dynamicLightingSmoothFactor: 8
+ dynamicLightingDepthVariationClampingValue: 0.001
+ virtualGreenScreenType: 0
+ virtualGreenScreenTopY: 10
+ virtualGreenScreenBottomY: -10
+ virtualGreenScreenApplyDepthCulling: 0
+ virtualGreenScreenDepthTolerance: 0.2
+ mrcActivationMode: 0
+ _trackingOriginType: 2
+ usePositionTracking: 1
+ useRotationTracking: 1
+ useIPDInPositionTracking: 1
+ resetTrackerOnLoad: 0
+ AllowRecenter: 0
+ LateControllerUpdate: 1
+--- !u!114 &7449411159797096980
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7449411159797096982}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 7b7741adcaae3294c84769ebcfc8e8db, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_originalTransform: {fileID: 0}
+--- !u!114 &7449411159797096983
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7449411159797096982}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 651f9b800d6cfba42a29be5f69653f86, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!114 &1089416225
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7449411159797096982}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: df9f338034892c44ebb62d97894772f1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ usePerEyeCameras: 0
+ useFixedUpdateForTracking: 0
+ disableEyeAnchorCameras: 1
diff --git a/Assets/MRCHelpers/Scripts/CopyTransform.cs b/Assets/MRCHelpers/Scripts/CopyTransform.cs
new file mode 100644
index 0000000..5c6781d
--- /dev/null
+++ b/Assets/MRCHelpers/Scripts/CopyTransform.cs
@@ -0,0 +1,30 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace MRCHelpers
+{
+
+ ///
+ /// Copies the position of a transform every frame
+ ///
+ public class CopyTransform : MonoBehaviour
+ {
+ ///
+ /// The transform to follow
+ ///
+ [SerializeField]
+ private Transform m_originalTransform;
+
+ ///
+ /// Update
+ ///
+ void Update()
+ {
+ //copy the pose of the other transform each frame
+ transform.position = m_originalTransform.position;
+ transform.rotation = m_originalTransform.rotation;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/MRCHelpers/Scripts/OVRMixedRealityCaptureTestMod.cs b/Assets/MRCHelpers/Scripts/OVRMixedRealityCaptureTestMod.cs
new file mode 100644
index 0000000..3958ad3
--- /dev/null
+++ b/Assets/MRCHelpers/Scripts/OVRMixedRealityCaptureTestMod.cs
@@ -0,0 +1,165 @@
+#if UNITY_ANDROID && !UNITY_EDITOR
+#define OVR_ANDROID_MRC
+#endif
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace MRCHelpers
+{
+
+ ///
+ /// Triggers the Oculus MRC tracking engine if the scene uses the XR Interaction Toolkit.
+ /// The code is modified from the file OVRMixedRealityCaptureTest in Oculus plugin
+ ///
+ public class OVRMixedRealityCaptureTestMod : MonoBehaviour
+ {
+
+ bool inited = false;
+
+ public Camera defaultExternalCamera;
+ OVRPlugin.Fovf defaultFov;
+
+ // Use this for initialization
+ void Start()
+ {
+#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC
+ if (!defaultExternalCamera)
+ {
+ Debug.LogWarning("defaultExternalCamera undefined");
+ }
+
+#if !OVR_ANDROID_MRC
+ // On Quest, we enable MRC automatically through the configuration
+ if (!OVRManager.instance.enableMixedReality)
+ {
+ OVRManager.instance.enableMixedReality = true;
+ }
+#endif
+#endif
+ }
+
+ void Initialize()
+ {
+#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC
+ if (inited)
+ return;
+
+#if OVR_ANDROID_MRC
+ if (!OVRPlugin.Media.GetInitialized())
+ return;
+#else
+ if (!OVRPlugin.IsMixedRealityInitialized())
+ return;
+#endif
+
+ OVRPlugin.ResetDefaultExternalCamera();
+ Debug.LogFormat("GetExternalCameraCount before adding manual external camera {0}", OVRPlugin.GetExternalCameraCount());
+ UpdateDefaultExternalCamera();
+ Debug.LogFormat("GetExternalCameraCount after adding manual external camera {0}", OVRPlugin.GetExternalCameraCount());
+
+ // obtain default FOV
+ {
+ OVRPlugin.CameraIntrinsics cameraIntrinsics;
+ OVRPlugin.CameraExtrinsics cameraExtrinsics;
+ OVRPlugin.GetMixedRealityCameraInfo(0, out cameraExtrinsics, out cameraIntrinsics);
+ defaultFov = cameraIntrinsics.FOVPort;
+ }
+
+ inited = true;
+#endif
+ }
+
+ void UpdateDefaultExternalCamera()
+ {
+#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC
+ // always build a 1080p external camera
+ const int cameraPixelWidth = 1920;
+ const int cameraPixelHeight = 1080;
+ const float cameraAspect = (float)cameraPixelWidth / cameraPixelHeight;
+
+
+ string cameraName = "UnityExternalCamera";
+ OVRPlugin.CameraIntrinsics cameraIntrinsics = new OVRPlugin.CameraIntrinsics();
+ OVRPlugin.CameraExtrinsics cameraExtrinsics = new OVRPlugin.CameraExtrinsics();
+
+ // intrinsics
+
+ cameraIntrinsics.IsValid = OVRPlugin.Bool.True;
+ cameraIntrinsics.LastChangedTimeSeconds = Time.time;
+
+ float vFov = defaultExternalCamera.fieldOfView * Mathf.Deg2Rad;
+ float hFov = Mathf.Atan(Mathf.Tan(vFov * 0.5f) * cameraAspect) * 2.0f;
+ OVRPlugin.Fovf fov = new OVRPlugin.Fovf();
+ fov.UpTan = fov.DownTan = Mathf.Tan(vFov * 0.5f);
+ fov.LeftTan = fov.RightTan = Mathf.Tan(hFov * 0.5f);
+
+ cameraIntrinsics.FOVPort = fov;
+ cameraIntrinsics.VirtualNearPlaneDistanceMeters = defaultExternalCamera.nearClipPlane;
+ cameraIntrinsics.VirtualFarPlaneDistanceMeters = defaultExternalCamera.farClipPlane;
+ cameraIntrinsics.ImageSensorPixelResolution.w = cameraPixelWidth;
+ cameraIntrinsics.ImageSensorPixelResolution.h = cameraPixelHeight;
+
+ // extrinsics
+
+ cameraExtrinsics.IsValid = OVRPlugin.Bool.True;
+ cameraExtrinsics.LastChangedTimeSeconds = Time.time;
+ cameraExtrinsics.CameraStatusData = OVRPlugin.CameraStatus.CameraStatus_Calibrated;
+ cameraExtrinsics.AttachedToNode = OVRPlugin.Node.None;
+
+ Camera mainCamera = Camera.main;
+
+ Transform trackingSpace = Camera.main.transform.parent;
+ OVRPose trackingSpacePose = trackingSpace.ToOVRPose(false);
+ OVRPose cameraPose = defaultExternalCamera.transform.ToOVRPose(false);
+ OVRPose relativePose = trackingSpacePose.Inverse() * cameraPose;
+#if OVR_ANDROID_MRC
+ OVRPose stageToLocalPose = OVRPlugin.GetTrackingTransformRelativePose(OVRPlugin.TrackingOrigin.Stage).ToOVRPose();
+ OVRPose localToStagePose = stageToLocalPose.Inverse();
+ relativePose = localToStagePose * relativePose;
+#endif
+ cameraExtrinsics.RelativePose = relativePose.ToPosef();
+
+ if (!OVRPlugin.SetDefaultExternalCamera(cameraName, ref cameraIntrinsics, ref cameraExtrinsics))
+ {
+ Debug.LogError("SetDefaultExternalCamera() failed");
+ }
+#endif
+ }
+
+ // Update is called once per frame
+ void Update()
+ {
+#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || OVR_ANDROID_MRC
+ if (!inited)
+ {
+ Initialize();
+ return;
+ }
+
+ if (!defaultExternalCamera)
+ {
+ return;
+ }
+
+#if OVR_ANDROID_MRC
+ if (!OVRPlugin.Media.GetInitialized())
+ {
+ return;
+ }
+#else
+ if (!OVRPlugin.IsMixedRealityInitialized())
+ {
+ return;
+ }
+#endif
+
+ UpdateDefaultExternalCamera();
+ OVRPlugin.OverrideExternalCameraFov(0, false, new OVRPlugin.Fovf());
+ OVRPlugin.OverrideExternalCameraStaticPose(0, false, OVRPlugin.Posef.identity);
+#endif
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/MRCHelpers/Scripts/RemoveMRCamerasTracking.cs b/Assets/MRCHelpers/Scripts/RemoveMRCamerasTracking.cs
new file mode 100644
index 0000000..ddd2673
--- /dev/null
+++ b/Assets/MRCHelpers/Scripts/RemoveMRCamerasTracking.cs
@@ -0,0 +1,59 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.SpatialTracking;
+
+namespace MRCHelpers
+{
+
+ ///
+ /// Removes the tracked pose driver in the cameras used for mixed reality capture.
+ /// Unity automatically adds it, and this way, the cameras follow the head of the
+ /// user, while they should be fixed in the world
+ ///
+ public class RemoveMRCamerasTracking : MonoBehaviour
+ {
+ ///
+ /// Start
+ ///
+ private void Start()
+ {
+ StartCoroutine(RemoveCamerasTracking());
+ }
+
+ ///
+ /// Coroutine to remove tracking scripts from cameras
+ ///
+ private IEnumerator RemoveCamerasTracking()
+ {
+ //get the tracking space child
+ Transform trackingSpaceTransform = transform.Find("TrackingSpace");
+
+ //the names of the cameras that Oculus adds for MRC
+ string[] camerasNames = new string[] { "OculusMRC_BackgroundCamera", "OculusMRC_ForgroundCamera" };
+ Transform tr = null;
+
+ //let everything initialize
+ yield return null;
+ var waiter = new WaitForSeconds(0.5f);
+
+ //I have noticed the system may re-created the cameras in some conditions
+ //(e.g. the player disconnects and reconnects with OBS), so we must keep deleting them forever
+ while (true)
+ {
+ //for each camera
+ foreach (string cameraName in camerasNames)
+ {
+ //find the camera, and destroy its TrackedPoseDriver if there is one attached
+ if ((tr = trackingSpaceTransform.Find(cameraName)) != null)
+ if (tr.GetComponent() != null)
+ Destroy(tr.GetComponent());
+ }
+
+ //wait a bit before looking again
+ yield return waiter;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Oculus/OculusProjectConfig.asset b/Assets/Oculus/OculusProjectConfig.asset
new file mode 100644
index 0000000..8dc9f53
--- /dev/null
+++ b/Assets/Oculus/OculusProjectConfig.asset
@@ -0,0 +1,31 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 05d394ae2a81edd4cbc3c51917e766e3, type: 3}
+ m_Name: OculusProjectConfig
+ m_EditorClassIdentifier:
+ targetDeviceTypes: 0100000002000000
+ allowOptional3DofHeadTracking: 0
+ handTrackingSupport: 1
+ handTrackingFrequency: 2
+ handTrackingVersion: 2
+ anchorSupport: 0
+ renderModelSupport: 0
+ trackedKeyboardSupport: 0
+ disableBackups: 1
+ enableNSCConfig: 1
+ securityXmlPath:
+ skipUnneededShaders: 0
+ focusAware: 1
+ requiresSystemKeyboard: 0
+ experimentalFeaturesEnabled: 0
+ insightPassthroughEnabled: 1
+ systemSplashScreen: {fileID: 0}
diff --git a/Assets/Oculus/VR/AudioClips/sfx_belt_in.wav b/Assets/Oculus/VR/AudioClips/sfx_belt_in.wav
new file mode 100644
index 0000000000000000000000000000000000000000..6e1229cb7ebe1f7860a0c150bf636da643b8fe91
GIT binary patch
literal 13292
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
h3PuM9=*<6rz(@EV4mbf$gOkA~jDB
literal 0
HcmV?d00001
diff --git a/Assets/Oculus/VR/Editor/AndroidManifest.OVRSubmission.xml b/Assets/Oculus/VR/Editor/AndroidManifest.OVRSubmission.xml
new file mode 100644
index 0000000..b9e2868
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/AndroidManifest.OVRSubmission.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/Oculus/VR/Editor/OVRADBTool.cs b/Assets/Oculus/VR/Editor/OVRADBTool.cs
new file mode 100644
index 0000000..4b63f90
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRADBTool.cs
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Threading;
+using UnityEngine;
+
+using Debug = UnityEngine.Debug;
+
+public class OVRADBTool
+{
+ public bool isReady;
+
+ public string androidSdkRoot;
+ public string androidPlatformToolsPath;
+ public string adbPath;
+
+ public OVRADBTool(string androidSdkRoot)
+ {
+ if (!String.IsNullOrEmpty(androidSdkRoot))
+ {
+ this.androidSdkRoot = androidSdkRoot;
+ }
+ else
+ {
+ this.androidSdkRoot = String.Empty;
+ }
+
+ if (this.androidSdkRoot.EndsWith("\\") || this.androidSdkRoot.EndsWith("/"))
+ {
+ this.androidSdkRoot = this.androidSdkRoot.Remove(this.androidSdkRoot.Length - 1);
+ }
+ androidPlatformToolsPath = Path.Combine(this.androidSdkRoot, "platform-tools");
+ adbPath = Path.Combine(androidPlatformToolsPath, "adb.exe");
+ isReady = File.Exists(adbPath);
+ }
+
+ public static bool IsAndroidSdkRootValid(string androidSdkRoot)
+ {
+ OVRADBTool tool = new OVRADBTool(androidSdkRoot);
+ return tool.isReady;
+ }
+
+ public delegate void WaitingProcessToExitCallback();
+
+ public int StartServer(WaitingProcessToExitCallback waitingProcessToExitCallback)
+ {
+ string outputString;
+ string errorString;
+
+ int exitCode = RunCommand(new string[] { "start-server" }, waitingProcessToExitCallback, out outputString, out errorString);
+ return exitCode;
+ }
+
+ public int KillServer(WaitingProcessToExitCallback waitingProcessToExitCallback)
+ {
+ string outputString;
+ string errorString;
+
+ int exitCode = RunCommand(new string[] { "kill-server" }, waitingProcessToExitCallback, out outputString, out errorString);
+ return exitCode;
+ }
+
+ public int ForwardPort(int port, WaitingProcessToExitCallback waitingProcessToExitCallback)
+ {
+ string outputString;
+ string errorString;
+
+ string portString = string.Format("tcp:{0}", port);
+
+ int exitCode = RunCommand(new string[] { "forward", portString, portString }, waitingProcessToExitCallback, out outputString, out errorString);
+ return exitCode;
+ }
+
+ public int ReleasePort(int port, WaitingProcessToExitCallback waitingProcessToExitCallback)
+ {
+ string outputString;
+ string errorString;
+
+ string portString = string.Format("tcp:{0}", port);
+
+ int exitCode = RunCommand(new string[] { "forward", "--remove", portString }, waitingProcessToExitCallback, out outputString, out errorString);
+ return exitCode;
+ }
+
+ public List GetDevices()
+ {
+ string outputString;
+ string errorString;
+
+ RunCommand(new string[] { "devices" }, null, out outputString, out errorString);
+ string[] devices = outputString.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
+
+ List deviceList = new List(devices);
+ deviceList.RemoveAt(0);
+
+ for(int i = 0; i < deviceList.Count; i++)
+ {
+ string deviceName = deviceList[i];
+ int index = deviceName.IndexOf('\t');
+ if (index >= 0)
+ deviceList[i] = deviceName.Substring(0, index);
+ else
+ deviceList[i] = "";
+
+ }
+
+ return deviceList;
+ }
+
+ private StringBuilder outputStringBuilder = null;
+ private StringBuilder errorStringBuilder = null;
+
+ public int RunCommand(string[] arguments, WaitingProcessToExitCallback waitingProcessToExitCallback, out string outputString, out string errorString)
+ {
+ int exitCode = -1;
+
+ if (!isReady)
+ {
+ Debug.LogWarning("OVRADBTool not ready");
+ outputString = string.Empty;
+ errorString = "OVRADBTool not ready";
+ return exitCode;
+ }
+
+ string args = string.Join(" ", arguments);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo(adbPath, args);
+ startInfo.WorkingDirectory = androidSdkRoot;
+ startInfo.CreateNoWindow = true;
+ startInfo.UseShellExecute = false;
+ startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
+
+ outputStringBuilder = new StringBuilder("");
+ errorStringBuilder = new StringBuilder("");
+
+ Process process = Process.Start(startInfo);
+ process.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceivedHandler);
+ process.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataReceivedHandler);
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ try
+ {
+ do
+ {
+ if (waitingProcessToExitCallback != null)
+ {
+ waitingProcessToExitCallback();
+ }
+ } while (!process.WaitForExit(100));
+
+ process.WaitForExit();
+ }
+ catch (Exception e)
+ {
+ Debug.LogWarningFormat("[OVRADBTool.RunCommand] exception {0}", e.Message);
+ }
+
+ exitCode = process.ExitCode;
+
+ process.Close();
+
+ outputString = outputStringBuilder.ToString();
+ errorString = errorStringBuilder.ToString();
+
+ outputStringBuilder = null;
+ errorStringBuilder = null;
+
+ if (!string.IsNullOrEmpty(errorString))
+ {
+ if (errorString.Contains("Warning"))
+ {
+ UnityEngine.Debug.LogWarning("OVRADBTool " + errorString);
+ }
+ else
+ {
+ UnityEngine.Debug.LogError("OVRADBTool " + errorString);
+ }
+ }
+
+ return exitCode;
+ }
+
+ public Process RunCommandAsync(string[] arguments, DataReceivedEventHandler outputDataRecievedHandler)
+ {
+ if (!isReady)
+ {
+ Debug.LogWarning("OVRADBTool not ready");
+ return null;
+ }
+
+ string args = string.Join(" ", arguments);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo(adbPath, args);
+ startInfo.WorkingDirectory = androidSdkRoot;
+ startInfo.CreateNoWindow = true;
+ startInfo.UseShellExecute = false;
+ startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
+
+ Process process = Process.Start(startInfo);
+ if (outputDataRecievedHandler != null)
+ {
+ process.OutputDataReceived += new DataReceivedEventHandler(outputDataRecievedHandler);
+ }
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ return process;
+ }
+
+ private void OutputDataReceivedHandler(object sendingProcess, DataReceivedEventArgs args)
+ {
+ // Collect the sort command output.
+ if (!string.IsNullOrEmpty(args.Data))
+ {
+ // Add the text to the collected output.
+ outputStringBuilder.Append(args.Data);
+ outputStringBuilder.Append(Environment.NewLine);
+ }
+ }
+
+ private void ErrorDataReceivedHandler(object sendingProcess, DataReceivedEventArgs args)
+ {
+ // Collect the sort command output.
+ if (!string.IsNullOrEmpty(args.Data))
+ {
+ // Add the text to the collected output.
+ errorStringBuilder.Append(args.Data);
+ errorStringBuilder.Append(Environment.NewLine);
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRBuild.cs b/Assets/Oculus/VR/Editor/OVRBuild.cs
new file mode 100644
index 0000000..9f98383
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRBuild.cs
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR)
+#define USING_XR_SDK
+#endif
+
+#if UNITY_2020_1_OR_NEWER
+#define REQUIRES_XR_SDK
+#endif
+
+// Unit made a change that broke `BuildOptions.AcceptExternalModificationsToPlayer`
+// Thread detailing issue: https://forum.unity.com/threads/oculus-build-invalidoperationexception-the-build-target-does-not-support-build-appending.994930/
+#if UNITY_2020_1_OR_NEWER || UNITY_2019_4_OR_NEWER
+#define DONT_USE_BUILD_OPTIONS_EXTERNAL_MODIFICATIONS_FLAG
+#endif
+
+using UnityEngine;
+using UnityEditor;
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Threading;
+
+///
+/// Allows Oculus to build apps from the command line.
+///
+[InitializeOnLoadAttribute]
+partial class OculusBuildApp : EditorWindow
+{
+ static void SetPCTarget()
+ {
+ if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.StandaloneWindows)
+ {
+ EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows);
+ }
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+ UnityEditorInternal.VR.VREditor.SetVREnabledOnTargetGroup(BuildTargetGroup.Standalone, true);
+#pragma warning disable 618
+ PlayerSettings.virtualRealitySupported = true;
+#pragma warning restore 618
+#endif
+ AssetDatabase.SaveAssets();
+ }
+
+ static void SetAndroidTarget()
+ {
+ EditorUserBuildSettings.androidBuildSubtarget = MobileTextureSubtarget.ASTC;
+ EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
+
+ if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
+ {
+ EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
+ }
+
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+ UnityEditorInternal.VR.VREditor.SetVREnabledOnTargetGroup(BuildTargetGroup.Standalone, true);
+#pragma warning disable 618
+ PlayerSettings.virtualRealitySupported = true;
+#pragma warning restore 618
+#endif
+ AssetDatabase.SaveAssets();
+ }
+
+#if UNITY_EDITOR_WIN && UNITY_ANDROID
+ // Build setting constants
+ const string REMOTE_APK_PATH = "/data/local/tmp";
+ const float USB_TRANSFER_SPEED_THRES = 25.0f;
+ const float USB_3_TRANSFER_SPEED = 32.0f;
+ const float TRANSFER_SPEED_CHECK_THRESHOLD = 4.0f;
+ const int NUM_BUILD_AND_RUN_STEPS = 9;
+ const int BYTES_TO_MEGABYTES = 1048576;
+
+ // Build window variables (not saved)
+ GUIStyle windowStyle;
+ GUIStyle calloutStyle;
+ string currConnectedDevice;
+ Vector2 scrollViewPos;
+
+ // Build window variables (saved)
+ static bool saveKeystorePasswords;
+ static bool isRunOnDevice;
+ static string outputApkPath;
+
+ // Progress bar variables
+ static int totalBuildSteps;
+ static int currentStep;
+ static string progressMessage;
+
+ // Build setting varaibles
+ static string gradlePath;
+ static string jdkPath;
+ static string androidSdkPath;
+ static string applicationIdentifier;
+ static bool isDevelopmentBuild;
+ static string productName;
+ static string dataPath;
+
+ static string gradleTempExport;
+ static string gradleExport;
+ static bool showCancel;
+ static bool buildInProgress;
+
+ static DirectorySyncer.CancellationTokenSource syncCancelToken;
+ static Process gradleBuildProcess;
+ static Thread buildThread;
+
+ static bool? apkOutputSuccessful;
+
+ [MenuItem("Oculus/OVR Build/OVR Build APK... %#k", false, 20)]
+ static void Init()
+ {
+ EditorWindow.GetWindow(false, "OVR Build APK", true);
+ OnBuildComplete();
+ }
+
+ [MenuItem("Oculus/OVR Build/OVR Build APK And Run %k", false, 21)]
+ static void InitAndRun()
+ {
+ var window = EditorWindow.GetWindow(false, "OVR Build APK", true);
+ isRunOnDevice = true; // make sure the "And Run" part of the menu name is true
+ window.StartBuild();
+ }
+
+ private void OnEnable()
+ {
+ isRunOnDevice = EditorPrefs.GetBool("OVRBuild_RunOnDevice", false);
+ saveKeystorePasswords = EditorPrefs.GetBool("OVRBuild_SaveKeystorePasswords", false);
+ if (saveKeystorePasswords)
+ {
+ if (EditorPrefs.HasKey("OVRBuild_KeystorePassword"))
+ PlayerSettings.Android.keystorePass = EditorPrefs.GetString("OVRBuild_KeystorePassword");
+ if (EditorPrefs.HasKey("OVRBuild_KeyAliasPassword"))
+ PlayerSettings.Android.keyaliasPass = EditorPrefs.GetString("OVRBuild_KeyAliasPassword");
+ }
+ outputApkPath = EditorPrefs.GetString("OVRBuild_BuiltAPKPath", "");
+
+ CheckADBDevices(out currConnectedDevice);
+ }
+
+ private void OnDisable()
+ {
+ EditorPrefs.SetBool("OVRBuild_RunOnDevice", isRunOnDevice);
+ EditorPrefs.SetBool("OVRBuild_SaveKeystorePasswords", saveKeystorePasswords);
+ if (saveKeystorePasswords)
+ {
+ EditorPrefs.SetString("OVRBuild_KeystorePassword", PlayerSettings.Android.keystorePass);
+ EditorPrefs.SetString("OVRBuild_KeyAliasPassword", PlayerSettings.Android.keyaliasPass);
+ }
+ else
+ {
+ EditorPrefs.DeleteKey("OVRBuild_KeystorePassword");
+ EditorPrefs.DeleteKey("OVRBuild_KeyAliasPassword");
+ }
+ EditorPrefs.SetString("OVRBuild_BuiltAPKPath", outputApkPath);
+ }
+
+ private void OnGUI()
+ {
+ if (windowStyle == null)
+ {
+ windowStyle = new GUIStyle();
+ windowStyle.margin = new RectOffset(10, 10, 10, 10);
+ }
+
+ if (calloutStyle == null)
+ {
+ calloutStyle = new GUIStyle(EditorStyles.label);
+ calloutStyle.richText = true;
+ calloutStyle.wordWrap = true;
+ }
+
+ // Fix progress bar window size
+ minSize = new Vector2(500, 305);
+
+ float oldLabelWidth = EditorGUIUtility.labelWidth;
+ EditorGUIUtility.labelWidth = 160;
+
+ EditorGUILayout.BeginVertical(windowStyle);
+
+ GUILayout.BeginHorizontal(EditorStyles.helpBox);
+ GUILayout.BeginVertical();
+ EditorGUILayout.LabelField("Builds created in the OVR Build APK window are identical to Unity-built APKs, but use the Gradle cache to only touch changed files, resulting in shorter build times.",
+ calloutStyle);
+
+#if UNITY_2021_1_OR_NEWER
+ if (EditorGUILayout.LinkButton("Documentation"))
+#else
+ if (GUILayout.Button("Documentation", GUILayout.ExpandWidth(false)))
+#endif
+ {
+ Application.OpenURL("https://developer.oculus.com/documentation/unity/unity-build-android-tools/");
+ }
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ EditorGUILayout.Space(15f);
+
+ scrollViewPos = EditorGUILayout.BeginScrollView(scrollViewPos, GUIStyle.none, GUI.skin.verticalScrollbar);
+ using (new EditorGUI.DisabledScope(buildInProgress))
+ {
+ EditorGUILayout.BeginHorizontal();
+ outputApkPath = EditorGUILayout.TextField("Built APK Path", outputApkPath);
+ if (GUILayout.Button("Browse...", GUILayout.Width(80)))
+ DisplayAPKPathDialog();
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.BeginHorizontal();
+
+ PlayerSettings.Android.bundleVersionCode = EditorGUILayout.IntField(new GUIContent("Version Number",
+ "Builds uploaded to the Oculus storefront are required to have incrementing version numbers.\nThis value is exposed to players."),
+ PlayerSettings.Android.bundleVersionCode, GUILayout.Width(220));
+
+ EditorGUI.BeginChangeCheck();
+ bool isAutoIncrement = PlayerPrefs.GetInt(OVRGradleGeneration.prefName, 0) != 0;
+ isAutoIncrement = EditorGUILayout.ToggleLeft(new GUIContent("Auto-Increment?",
+ "If true, version number will be automatically incremented after every successful build."),
+ isAutoIncrement, EditorStyles.miniLabel, GUILayout.Width(120));
+ if (EditorGUI.EndChangeCheck())
+ OVRGradleGeneration.ToggleUtilities();
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.Space(15f);
+
+ EditorGUILayout.BeginHorizontal();
+ using (new EditorGUI.DisabledScope(string.IsNullOrEmpty(currConnectedDevice)))
+ {
+ isRunOnDevice = EditorGUILayout.Toggle("Install & Run on Device?", isRunOnDevice);
+ GUILayout.FlexibleSpace();
+ EditorGUILayout.LabelField(string.IsNullOrEmpty(currConnectedDevice) ? "No device connected" : $"Device: {currConnectedDevice}");
+ }
+ if (GUILayout.Button("Refresh", GUILayout.Width(80)))
+ CheckADBDevices(out currConnectedDevice);
+ EditorGUILayout.EndHorizontal();
+
+ EditorUserBuildSettings.development = EditorGUILayout.Toggle(new GUIContent("Development Build?",
+ "Development builds allow you to debug scripts. However, they're slightly slower, and they're not allowed on the Oculus storefront."),
+ EditorUserBuildSettings.development);
+
+ EditorGUILayout.Space(15f);
+
+ EditorGUILayout.BeginHorizontal();
+ saveKeystorePasswords = EditorGUILayout.Toggle(new GUIContent("Save Keystore Passwords?",
+ "These values are also found in Project Settings > Player > [Android] > Publishing Settings > Project Keystore.\nStoring passwords is convenient, but reduces security."),
+ saveKeystorePasswords);
+ if (GUILayout.Button("Select Keystore...", GUILayout.Width(150)))
+ SettingsService.OpenProjectSettings("Project/Player");
+ EditorGUILayout.EndHorizontal();
+
+ if (saveKeystorePasswords)
+ {
+ EditorGUI.indentLevel++;
+
+ EditorGUILayout.LabelField("Keystore Path", PlayerSettings.Android.keystoreName);
+ PlayerSettings.Android.keystorePass = EditorGUILayout.PasswordField("Keystore Password", PlayerSettings.Android.keystorePass);
+
+ EditorGUILayout.LabelField("Key Alias Name", PlayerSettings.Android.keyaliasName);
+ PlayerSettings.Android.keyaliasPass = EditorGUILayout.PasswordField("Alias Password", PlayerSettings.Android.keyaliasPass);
+
+ EditorGUI.indentLevel--;
+ }
+ }
+ EditorGUILayout.EndScrollView();
+ EditorGUILayout.Space(10);
+
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+
+ //better to perform these at end of GUI
+ bool shouldCancel = false;
+ bool shouldBuild = false;
+ if (showCancel)
+ shouldCancel = GUILayout.Button("Cancel", GUILayout.Height(30), GUILayout.Width(100));
+ else
+ {
+ using (new EditorGUI.DisabledScope(buildInProgress))
+ {
+ shouldBuild = GUILayout.Button("Build", GUILayout.Height(30), GUILayout.Width(100));
+ }
+ }
+ GUILayout.FlexibleSpace();
+ EditorGUILayout.EndHorizontal();
+ EditorGUILayout.Space(10);
+
+ // Show progress bar
+ Rect progressRect = EditorGUILayout.GetControlRect(GUILayout.Height(25));
+ float progress = currentStep / (float)totalBuildSteps;
+ EditorGUI.ProgressBar(progressRect, progress, progressMessage);
+
+ EditorGUIUtility.labelWidth = oldLabelWidth;
+ EditorGUILayout.EndVertical();
+
+ if (shouldBuild)
+ StartBuild();
+ else if (shouldCancel)
+ CancelBuild();
+ }
+
+ void Update()
+ {
+ // Force window update if not in focus to ensure progress bar still updates
+ var window = EditorWindow.focusedWindow;
+ if (window != null && window.ToString().Contains("OculusBuildApp"))
+ {
+ Repaint();
+ }
+ }
+
+ void DisplayAPKPathDialog()
+ {
+ string fileName = "build.apk";
+ string path = "";
+ if (!string.IsNullOrEmpty(outputApkPath))
+ {
+ try
+ {
+ path = Path.GetDirectoryName(outputApkPath);
+ fileName = Path.GetFileName(outputApkPath);
+ }
+ catch (Exception)
+ {
+ //do nothing, we just have a malformed apkPath and should accept defaults
+ }
+ }
+
+ outputApkPath = EditorUtility.SaveFilePanel("APK Path", path, fileName, "apk");
+ Repaint();
+ }
+
+ void CancelBuild()
+ {
+ SetProgressBarMessage("Canceling . . .");
+
+ if (syncCancelToken != null)
+ {
+ syncCancelToken.Cancel();
+ }
+
+ if (apkOutputSuccessful.HasValue && apkOutputSuccessful.Value)
+ {
+ buildThread.Abort();
+ OnBuildComplete();
+ }
+
+ if (gradleBuildProcess != null && !gradleBuildProcess.HasExited)
+ {
+ var cancelThread = new Thread(delegate ()
+ {
+ CancelGradleBuild();
+ });
+ cancelThread.Start();
+ }
+ }
+
+ void CancelGradleBuild()
+ {
+ Process cancelGradleProcess = new Process();
+ string arguments = "-Xmx1024m -classpath \"" + gradlePath +
+ "\" org.gradle.launcher.GradleMain --stop";
+ var processInfo = new System.Diagnostics.ProcessStartInfo
+ {
+ WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal,
+ FileName = jdkPath,
+ Arguments = arguments,
+ RedirectStandardInput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ };
+
+ cancelGradleProcess.StartInfo = processInfo;
+ cancelGradleProcess.EnableRaisingEvents = true;
+
+ cancelGradleProcess.OutputDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e != null && e.Data != null && e.Data.Length != 0)
+ {
+ UnityEngine.Debug.LogFormat("Gradle: {0}", e.Data);
+ }
+ }
+ );
+
+ apkOutputSuccessful = false;
+
+ cancelGradleProcess.Start();
+ cancelGradleProcess.BeginOutputReadLine();
+ cancelGradleProcess.WaitForExit();
+
+ OnBuildComplete();
+ }
+
+ public void StartBuild()
+ {
+ showCancel = false;
+ buildInProgress = true;
+
+ InitializeProgressBar(NUM_BUILD_AND_RUN_STEPS);
+ IncrementProgressBar("Exporting Unity Project . . .");
+
+ apkOutputSuccessful = null;
+ syncCancelToken = null;
+ gradleBuildProcess = null;
+
+ UnityEngine.Debug.Log("OVRBuild: Starting Unity build ...");
+
+ SetupDirectories();
+
+ // 1. Get scenes to build in Unity, and export gradle project
+ var buildResult = UnityBuildPlayer();
+
+ if (buildResult.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
+ {
+ // Set static variables so build thread has updated data
+ showCancel = true;
+ gradlePath = OVRConfig.Instance.GetGradlePath();
+ jdkPath = OVRConfig.Instance.GetJDKPath();
+ androidSdkPath = OVRConfig.Instance.GetAndroidSDKPath();
+ applicationIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
+ isDevelopmentBuild = EditorUserBuildSettings.development;
+#if UNITY_2019_3_OR_NEWER
+ productName = "launcher";
+#else
+ productName = Application.productName;
+#endif
+ dataPath = Application.dataPath;
+
+ buildThread = new Thread(delegate ()
+ {
+ OVRBuildRun();
+ });
+ buildThread.Start();
+ return;
+ }
+ else if (buildResult.summary.result == UnityEditor.Build.Reporting.BuildResult.Cancelled)
+ {
+ UnityEngine.Debug.Log("Build cancelled.");
+ }
+ else
+ {
+ UnityEngine.Debug.Log("Build failed.");
+ }
+ OnBuildComplete();
+ }
+
+ private UnityEditor.Build.Reporting.BuildReport UnityBuildPlayer()
+ {
+ // Unity introduced a possible bug with Unity 2020.1.10f1 that causes an exception
+ // when building with the option 'BuildOptions.AcceptExternalModificationsToPlayer'
+ // In order to maintain the same logic, we are using `EditorUserBuildSettings.exportAsGoogleAndroidProject`
+#if DONT_USE_BUILD_OPTIONS_EXTERNAL_MODIFICATIONS_FLAG
+ bool previousExportAsGoogleAndroidProject = EditorUserBuildSettings.exportAsGoogleAndroidProject;
+ EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
+#endif
+ try
+ {
+ var sceneList = GetScenesToBuild();
+
+ var buildOptions = BuildOptions.None;
+ if (isDevelopmentBuild)
+ buildOptions |= (BuildOptions.Development | BuildOptions.AllowDebugging);
+ if (isRunOnDevice)
+ buildOptions |= BuildOptions.AutoRunPlayer;
+#if !DONT_USE_BUILD_OPTIONS_EXTERNAL_MODIFICATIONS_FLAG
+ buildOptions |= BuildOptions.AcceptExternalModificationsToPlayer;
+#endif
+
+ var buildPlayerOptions = new BuildPlayerOptions
+ {
+ scenes = sceneList.ToArray(),
+ locationPathName = gradleTempExport,
+ target = BuildTarget.Android,
+ options = buildOptions
+ };
+
+ var buildResult = BuildPipeline.BuildPlayer(buildPlayerOptions);
+
+ UnityEngine.Debug.Log(UnityBuildPlayerSummary(buildResult));
+
+ return buildResult;
+ }
+ finally
+ {
+#if DONT_USE_BUILD_OPTIONS_EXTERNAL_MODIFICATIONS_FLAG
+ EditorUserBuildSettings.exportAsGoogleAndroidProject = previousExportAsGoogleAndroidProject;
+#endif
+ }
+ }
+
+ private static string UnityBuildPlayerSummary(UnityEditor.Build.Reporting.BuildReport report)
+ {
+ var sb = new System.Text.StringBuilder();
+
+ sb.Append($"Unity Build Player: Build {report.summary.result} ({report.summary.totalSize} bytes) in {report.summary.totalTime.TotalSeconds:0.00}s");
+
+ foreach (var step in report.steps)
+ {
+ sb.AppendLine();
+ if (step.depth > 0)
+ {
+ sb.Append(new String('-', step.depth));
+ sb.Append(' ');
+ }
+ sb.Append($"{step.name}: {step.duration:g}");
+ }
+
+ return sb.ToString();
+ }
+
+ private static void OVRBuildRun()
+ {
+ // 2. Process gradle project
+ IncrementProgressBar("Processing gradle project . . .");
+ if (ProcessGradleProject())
+ {
+ // 3. Build gradle project
+ IncrementProgressBar("Starting gradle build . . .");
+ if (BuildGradleProject())
+ {
+ CopyAPK();
+
+ // 4. Deploy and run
+ if (isRunOnDevice)
+ DeployAPK();
+ }
+ }
+
+ OnBuildComplete();
+ }
+
+ private static bool BuildGradleProject()
+ {
+ gradleBuildProcess = new Process();
+ string arguments = "-Xmx4096m -classpath \"" + gradlePath + "\" org.gradle.launcher.GradleMain --profile ";
+ if (isDevelopmentBuild)
+ arguments += "assembleDebug -x validateSigningDebug";
+ else
+ arguments += "assembleRelease -x verifyReleaseResources";
+
+#if UNITY_2019_3_OR_NEWER
+ var gradleProjectPath = gradleExport;
+#else
+ var gradleProjectPath = Path.Combine(gradleExport, productName);
+#endif
+
+ var processInfo = new System.Diagnostics.ProcessStartInfo
+ {
+ WorkingDirectory = gradleProjectPath,
+ WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal,
+ FileName = jdkPath,
+ Arguments = arguments,
+ RedirectStandardInput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ };
+
+ gradleBuildProcess.StartInfo = processInfo;
+ gradleBuildProcess.EnableRaisingEvents = true;
+
+ DateTime gradleStartTime = System.DateTime.Now;
+ DateTime gradleEndTime = System.DateTime.MinValue;
+
+ gradleBuildProcess.Exited += new System.EventHandler(
+ (s, e) =>
+ {
+ UnityEngine.Debug.Log("Gradle: Exited");
+ }
+ );
+
+ gradleBuildProcess.OutputDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e != null && e.Data != null &&
+ e.Data.Length != 0 &&
+ (e.Data.Contains("BUILD") || e.Data.StartsWith("See the profiling report at:")))
+ {
+ UnityEngine.Debug.LogFormat("Gradle: {0}", e.Data);
+ if (e.Data.Contains("SUCCESSFUL"))
+ {
+ string buildFlavor = isDevelopmentBuild ? "debug" : "release";
+ UnityEngine.Debug.LogFormat("APK Build Completed: {0}",
+ Path.Combine(gradleExport, $"build\\outputs\\apk\\{buildFlavor}", productName + $"-{buildFlavor}.apk").Replace("/", "\\"));
+ if (!apkOutputSuccessful.HasValue)
+ {
+ apkOutputSuccessful = true;
+ }
+ gradleEndTime = System.DateTime.Now;
+ }
+ else if (e.Data.Contains("FAILED"))
+ {
+ apkOutputSuccessful = false;
+ }
+ }
+ }
+ );
+
+ gradleBuildProcess.ErrorDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e != null && e.Data != null &&
+ e.Data.Length != 0)
+ {
+ UnityEngine.Debug.LogErrorFormat("Gradle: {0}", e.Data);
+ }
+ apkOutputSuccessful = false;
+ }
+ );
+
+ gradleBuildProcess.Start();
+ gradleBuildProcess.BeginOutputReadLine();
+ IncrementProgressBar("Building gradle project . . .");
+
+ gradleBuildProcess.WaitForExit();
+
+ // Add a timeout for if gradle unexpectedlly exits or errors out
+ Stopwatch timeout = new Stopwatch();
+ timeout.Start();
+ while (apkOutputSuccessful == null)
+ {
+ if (timeout.ElapsedMilliseconds > 5000)
+ {
+ UnityEngine.Debug.LogError("Gradle has exited unexpectedly.");
+ apkOutputSuccessful = false;
+ }
+ System.Threading.Thread.Sleep(100);
+ }
+
+ return apkOutputSuccessful.HasValue && apkOutputSuccessful.Value;
+ }
+
+ private static bool ProcessGradleProject()
+ {
+ DateTime syncStartTime = System.DateTime.Now;
+ DateTime syncEndTime = System.DateTime.MinValue;
+
+ try
+ {
+ var ps = System.Text.RegularExpressions.Regex.Escape("" + Path.DirectorySeparatorChar);
+ // ignore files .gradle/** build/** foo/.gradle/** and bar/build/**
+ var ignorePattern = string.Format("^([^{0}]+{0})?(\\.gradle|build){0}", ps);
+
+ var syncer = new DirectorySyncer(gradleTempExport,
+ gradleExport, ignorePattern);
+
+ syncCancelToken = new DirectorySyncer.CancellationTokenSource();
+ var syncResult = syncer.Synchronize(syncCancelToken.Token);
+ syncEndTime = System.DateTime.Now;
+ }
+ catch (Exception e)
+ {
+ UnityEngine.Debug.Log("OVRBuild: Processing gradle project failed with exception: " +
+ e.Message);
+ return false;
+ }
+
+ if (syncCancelToken.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static List GetScenesToBuild()
+ {
+ var sceneList = new List();
+ foreach (var scene in EditorBuildSettings.scenes)
+ {
+ // Enumerate scenes in project and check if scene is enabled to build
+ if (scene.enabled)
+ {
+ sceneList.Add(scene.path);
+ }
+ }
+ return sceneList;
+ }
+
+ public static bool CopyAPK()
+ {
+ string buildFlavor = isDevelopmentBuild ? "debug" : "release";
+ string apkPathLocal = Path.Combine(gradleExport, productName, $"build\\outputs\\apk\\{buildFlavor}", productName + $"-{buildFlavor}.apk");
+ if (File.Exists(apkPathLocal))
+ {
+ try
+ {
+ File.Copy(apkPathLocal, outputApkPath, true);
+ UnityEngine.Debug.Log($"OVRBuild: Output APK generated at {outputApkPath}");
+ Process.Start("explorer.exe", Path.GetDirectoryName(outputApkPath));
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public static bool DeployAPK()
+ {
+ // Create new instance of ADB Tool
+ var adbTool = new OVRADBTool(androidSdkPath);
+
+ if (adbTool.isReady)
+ {
+ string apkPathLocal;
+ string buildFlavor = isDevelopmentBuild ? "debug" : "release";
+ string gradleExportFolder = Path.Combine(gradleExport, productName, $"build\\outputs\\apk\\{buildFlavor}");
+
+ // Check to see if gradle output directory exists
+ gradleExportFolder = gradleExportFolder.Replace("/", "\\");
+ if (!Directory.Exists(gradleExportFolder))
+ {
+ UnityEngine.Debug.LogError("Could not find the gradle project at the expected path: " + gradleExportFolder);
+ return false;
+ }
+
+ // Search for output APK in gradle output directory
+ apkPathLocal = Path.Combine(gradleExportFolder, productName + $"-{buildFlavor}.apk");
+ if (!System.IO.File.Exists(apkPathLocal))
+ {
+ UnityEngine.Debug.LogError(string.Format("Could not find {0} in the gradle project.", productName + $"-{buildFlavor}.apk"));
+ return false;
+ }
+
+ string output, error;
+ DateTime timerStart;
+
+ // Ensure that the Oculus temp directory is on the device by making it
+ IncrementProgressBar("Making Temp directory on device");
+ string[] mkdirCommand = { "-d shell", "mkdir -p", REMOTE_APK_PATH };
+ if (adbTool.RunCommand(mkdirCommand, null, out output, out error) != 0) return false;
+
+ // Push APK to device, also time how long it takes
+ timerStart = System.DateTime.Now;
+ IncrementProgressBar("Pushing APK to device . . .");
+ string[] pushCommand = { "-d push", "\"" + apkPathLocal + "\"", REMOTE_APK_PATH };
+ if (adbTool.RunCommand(pushCommand, null, out output, out error) != 0) return false;
+
+ // Calculate the transfer speed and determine if user is using USB 2.0 or 3.0
+ // Only bother informing the user on non-trivial transfers, as for very short
+ // periods of time, things like process creation overhead can dwarf the actual
+ // transfer time.
+ TimeSpan pushTime = System.DateTime.Now - timerStart;
+ bool trivialPush = pushTime.TotalSeconds < TRANSFER_SPEED_CHECK_THRESHOLD;
+ long? apkSize = (trivialPush ? (long?)null : new System.IO.FileInfo(apkPathLocal).Length);
+ double? transferSpeed = (apkSize / pushTime.TotalSeconds) / BYTES_TO_MEGABYTES;
+ bool informLog = transferSpeed.HasValue && transferSpeed.Value < USB_TRANSFER_SPEED_THRES;
+ UnityEngine.Debug.Log("OVRADBTool: Push Success");
+
+ // Install the APK package on the device
+ IncrementProgressBar("Installing APK . . .");
+ string apkPath = REMOTE_APK_PATH + "/" + productName + "-debug.apk";
+ apkPath = apkPath.Replace(" ", "\\ ");
+ string[] installCommand = { "-d shell", "pm install -r", apkPath };
+
+ timerStart = System.DateTime.Now;
+ if (adbTool.RunCommand(installCommand, null, out output, out error) != 0) return false;
+ TimeSpan installTime = System.DateTime.Now - timerStart;
+ UnityEngine.Debug.Log("OVRADBTool: Install Success");
+
+ // Start the application on the device
+ IncrementProgressBar("Launching application on device . . .");
+#if UNITY_2019_3_OR_NEWER
+ string playerActivityName = "\"" + applicationIdentifier + "/com.unity3d.player.UnityPlayerActivity\"";
+#else
+ string playerActivityName = "\"" + applicationIdentifier + "/" + applicationIdentifier + ".UnityPlayerActivity\"";
+#endif
+ string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
+ if (adbTool.RunCommand(appStartCommand, null, out output, out error) != 0) return false;
+ UnityEngine.Debug.Log("OVRADBTool: Application Start Success");
+
+ IncrementProgressBar("Success!");
+
+ // If the user is using a USB 2.0 cable, inform them about improved transfer speeds and estimate time saved
+ if (informLog)
+ {
+ float usb3Time = apkSize.Value / (USB_3_TRANSFER_SPEED * BYTES_TO_MEGABYTES); // `informLog` can't be true if `apkSize` is null.
+ UnityEngine.Debug.Log(string.Format("OVRBuild has detected slow transfer speeds. A USB 3.0 cable is recommended to reduce the time it takes to deploy your project by approximatly {0:0.0} seconds", pushTime.TotalSeconds - usb3Time));
+ return true;
+ }
+ }
+ else
+ {
+ UnityEngine.Debug.LogError("Could not find the ADB executable in the specified Android SDK directory.");
+ }
+ return false;
+ }
+
+ private static void OnBuildComplete()
+ {
+ showCancel = false;
+ buildInProgress = false;
+ currentStep = 0;
+ SetProgressBarMessage("Waiting to build . . .", false);
+ }
+
+ private static bool CheckADBDevices(out string connectedDeviceName)
+ {
+ // Check if there are any ADB devices connected before starting the build process
+ var adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ connectedDeviceName = null;
+
+ if (adbTool.isReady)
+ {
+ List devices = adbTool.GetDevices();
+ if (devices.Count == 0)
+ {
+ UnityEngine.Debug.LogError("No ADB devices connected. Connect a device to this computer to run APK.");
+ return false;
+ }
+ else if (devices.Count > 1)
+ {
+ UnityEngine.Debug.LogError("Multiple ADB devices connected. Disconnect extra devices from this computer to run APK.");
+ return false;
+ }
+ else
+ {
+ connectedDeviceName = devices[0];
+ return true;
+ }
+ }
+ else
+ {
+ UnityEngine.Debug.LogError("OVR ADB Tool failed to initialize. Check the Android SDK path in [Edit -> Preferences -> External Tools]");
+ return false;
+ }
+ }
+
+ private static void SetupDirectories()
+ {
+ gradleTempExport = Path.Combine(Path.Combine(Application.dataPath, "../Temp"), "OVRGradleTempExport");
+ gradleExport = Path.Combine(Path.Combine(Application.dataPath, "../Temp"), "OVRGradleExport");
+ if (!Directory.Exists(gradleExport))
+ {
+ Directory.CreateDirectory(gradleExport);
+ }
+ }
+
+ private static void InitializeProgressBar(int stepCount)
+ {
+ currentStep = 0;
+ totalBuildSteps = stepCount;
+ }
+
+ private static void IncrementProgressBar(string message)
+ {
+ currentStep++;
+ progressMessage = message;
+ UnityEngine.Debug.Log("OVRBuild: " + message);
+ }
+
+ private static void SetProgressBarMessage(string message, bool log = true)
+ {
+ progressMessage = message;
+ if (log)
+ UnityEngine.Debug.Log("OVRBuild: " + message);
+ }
+#endif //UNITY_EDITOR_WIN && UNITY_ANDROID
+}
diff --git a/Assets/Oculus/VR/Editor/OVRBundleManager.cs b/Assets/Oculus/VR/Editor/OVRBundleManager.cs
new file mode 100644
index 0000000..0cbbdda
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRBundleManager.cs
@@ -0,0 +1,716 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if UNITY_EDITOR_WIN && UNITY_ANDROID
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System;
+using System.Linq;
+
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Build.Reporting;
+
+public class OVRBundleManager
+{
+ public const string TRANSITION_APK_VERSION_NAME = "OVRTransitionAPKVersion";
+ public const string BUNDLE_MANAGER_OUTPUT_PATH = "OVRAssetBundles";
+ private const int BUNDLE_CHUNK_SIZE = 30;
+ private const string TRANSITION_SCENE_RELATIVE_PATH = "Scenes/OVRTransitionScene.unity";
+ private const string BUNDLE_MANAGER_MASTER_BUNDLE = "OVRMasterBundle";
+
+ private const string EXTERNAL_STORAGE_PATH = "/sdcard/Android/data";
+ private const string ADB_TOOL_INITIALIZE_ERROR = "Failed to initialize ADB Tool. Please check Android SDK path in Preferences -> External Tools";
+
+ private static string externalSceneCache;
+ private static string transitionScenePath;
+
+ private static string projectDefaultAppIdentifier;
+ private static string projectDefaultVersion;
+ private static AndroidArchitecture projectAndroidArchitecture;
+ private static ScriptingImplementation projectScriptImplementation;
+ private static ManagedStrippingLevel projectManagedStrippingLevel;
+ private static bool projectStripEngineCode;
+
+ public static void BuildDeployTransitionAPK()
+ {
+ OVRBundleTool.PrintLog("Building and deploying transition APK . . .\n", true);
+
+ PrebuildProjectSettingUpdate();
+
+ var buildPlayerOptions = CalculateBundleBuildPlayerOptions();
+ if (!buildPlayerOptions.HasValue)
+ {
+ return;
+ }
+
+ DateTime apkBuildStart = DateTime.Now;
+ BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions.Value);
+
+ if (report.summary.result == BuildResult.Succeeded)
+ {
+ OVRBundleTool.PrintSuccess();
+ }
+ else if (report.summary.result == BuildResult.Failed)
+ {
+ OVRBundleTool.PrintError();
+ }
+
+ PostbuildProjectSettingUpdate();
+ }
+
+ public static void PrebuildProjectSettingUpdate()
+ {
+ // Save existing settings as some modifications can change other settings
+ projectDefaultAppIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
+ projectDefaultVersion = PlayerSettings.bundleVersion;
+ projectAndroidArchitecture = PlayerSettings.Android.targetArchitectures;
+ projectScriptImplementation = PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup);
+ projectManagedStrippingLevel = PlayerSettings.GetManagedStrippingLevel(BuildTargetGroup.Android);
+ projectStripEngineCode = PlayerSettings.stripEngineCode;
+
+ // Modify application identifier for transition APK
+ PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android,
+ projectDefaultAppIdentifier + GetTransitionApkOptionalIdentifier());
+
+ // Set VersionCode as a unique identifier for transition APK
+ PlayerSettings.bundleVersion = TRANSITION_APK_VERSION_NAME;
+
+ // Modify Android target architecture as ARM64 does not support Mono.
+ if (projectAndroidArchitecture != AndroidArchitecture.ARMv7)
+ {
+ // Show message in console to make it more clear to developers
+ OVRBundleTool.PrintLog("Build will use ARMv7 as Android architecture.");
+ PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7;
+ }
+
+ // Modify IL2CPP option as it strips script symbols that are necessary for the scenes at runtime
+ if (projectScriptImplementation != ScriptingImplementation.Mono2x)
+ {
+ // Show message in console to make it more clear to developers
+ OVRBundleTool.PrintLog("Build will use Mono as scripting backend.");
+ PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, ScriptingImplementation.Mono2x);
+ }
+
+ // Avoid stripping managed code that are necessary for the scenes at runtime
+ if (projectManagedStrippingLevel != ManagedStrippingLevel.Disabled)
+ {
+ OVRBundleTool.PrintLog("Build will set Managed Stripping Level to Disabled.");
+ PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, ManagedStrippingLevel.Disabled);
+ }
+
+ if (projectStripEngineCode)
+ {
+ OVRBundleTool.PrintLog("Build will set Strip Engine Code to Disabled.");
+ PlayerSettings.stripEngineCode = false;
+ }
+ }
+
+ public static BuildPlayerOptions? CalculateBundleBuildPlayerOptions()
+ {
+ if (!Directory.Exists(BUNDLE_MANAGER_OUTPUT_PATH))
+ {
+ Directory.CreateDirectory(BUNDLE_MANAGER_OUTPUT_PATH);
+ }
+
+ if (String.IsNullOrEmpty(transitionScenePath))
+ {
+ // Get current editor script's directory as base path
+ string[] res = System.IO.Directory.GetFiles(Application.dataPath, "OVRBundleManager.cs", SearchOption.AllDirectories);
+ if (res.Length > 1)
+ {
+ OVRBundleTool.PrintError("More than one OVRBundleManager editor script has been found, please double check your Oculus SDK import.");
+ return null;
+ }
+ else
+ {
+ // Append Transition Scene's relative path to base path
+ var OVREditorScript = Path.GetDirectoryName(res[0]);
+ transitionScenePath = Path.Combine(OVREditorScript, TRANSITION_SCENE_RELATIVE_PATH);
+ }
+ }
+
+ string[] buildScenes = new string[1] { transitionScenePath };
+ string apkOutputPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "OVRTransition.apk");
+
+ return new BuildPlayerOptions
+ {
+ scenes = buildScenes,
+ locationPathName = apkOutputPath,
+ target = BuildTarget.Android,
+ options = BuildOptions.Development |
+ BuildOptions.AutoRunPlayer
+ };
+ }
+
+ public static void PostbuildProjectSettingUpdate()
+ {
+ // Restore application identifier
+ PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android,
+ projectDefaultAppIdentifier);
+
+ // Restore version setting
+ PlayerSettings.bundleVersion = projectDefaultVersion;
+
+ // Restore scripting backend option
+ if (PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup) != projectScriptImplementation)
+ {
+ PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, projectScriptImplementation);
+ }
+
+ // Restore managed stripping level
+ if (PlayerSettings.GetManagedStrippingLevel(BuildTargetGroup.Android) != projectManagedStrippingLevel)
+ {
+ PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, projectManagedStrippingLevel);
+ }
+
+ // Restore Strip Engine Code
+ if (PlayerSettings.stripEngineCode != projectStripEngineCode)
+ {
+ PlayerSettings.stripEngineCode = projectStripEngineCode;
+ }
+
+ // Restore Android Architecture
+ if (PlayerSettings.Android.targetArchitectures != projectAndroidArchitecture)
+ {
+ PlayerSettings.Android.targetArchitectures = projectAndroidArchitecture;
+ }
+ }
+
+ // Build and deploy a list of scenes. It's suggested to only build and deploy one active scene that's being modified and
+ // its dependencies such as scenes that are loaded additively
+ public static void BuildDeployScenes(List sceneList, bool forceRestart)
+ {
+ externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
+ + GetTransitionApkOptionalIdentifier() + "/cache/scenes";
+
+ for (int i = 0; i < sceneList.Count; i++)
+ {
+ if (!sceneList[i].shouldDeploy) continue;
+ sceneList[i].buildStatus = OVRBundleTool.SceneBundleStatus.QUEUED;
+ }
+
+ BuildSceneBundles(sceneList);
+ if (DeploySceneBundles(sceneList))
+ {
+ if (forceRestart)
+ {
+ LaunchApplication();
+ return;
+ }
+ OVRBundleTool.PrintSuccess();
+ return;
+ }
+ }
+
+ // Build assets that are used by scenes from the given list.
+ // This function will automatically de-duplicated same asset file being referenced in multiple scenes.
+ // Scene bundles will be created with name pattern: _
+ private static void BuildSceneBundles(List sceneList)
+ {
+ DateTime totalStart = DateTime.Now;
+ // Keeps track of dependent assets across scenes
+ // to ensure each asset is only packaged once in one of the scene bundles.
+ // uniqueAssetInSceneBundle is a map from "asset unique identifier" to the first scene that references the asset.
+ // It supports different assets with same file name as "asset unique identifier" contain full qualified asset file path
+ Dictionary uniqueAssetInSceneBundle = new Dictionary();
+
+ // List of bundle targets for Unity's build pipeline to package
+ List assetBundleBuilds = new List();
+ // Map that contains "asset type" to list of assets that are of the asset type
+ Dictionary> extToAssetList = new Dictionary>();
+
+ // Check if assets in Resources need to be packaged
+ if (CheckForResources())
+ {
+ var resourceDirectories = Directory.GetDirectories("Assets", "Resources", SearchOption.AllDirectories).ToArray();
+ // Fetch a list of all files in resource directory
+ string[] resourceAssetPaths = AssetDatabase.FindAssets("", resourceDirectories).Select(x => AssetDatabase.GUIDToAssetPath(x)).ToArray();
+ ProcessAssets(resourceAssetPaths, "resources", ref uniqueAssetInSceneBundle, ref extToAssetList);
+
+ AssetBundleBuild resourceBundle = new AssetBundleBuild();
+ resourceBundle.assetNames = uniqueAssetInSceneBundle.Keys.ToArray();
+ resourceBundle.assetBundleName = OVRSceneLoader.resourceBundleName;
+ assetBundleBuilds.Add(resourceBundle);
+ }
+
+ // Create scene bundle output directory
+ string sceneBundleDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
+ if (!Directory.Exists(sceneBundleDirectory))
+ {
+ Directory.CreateDirectory(sceneBundleDirectory);
+ }
+
+ OVRBundleTool.PrintLog("Building scene bundles . . . ");
+ DateTime labelingStart = DateTime.Now;
+
+ for (int i = 0; i < sceneList.Count; ++i)
+ {
+ var scene = sceneList[i];
+ if (!scene.shouldDeploy) continue;
+
+ // Get all the assets that the scene depends on and sort them by type
+ DateTime getDepStart = DateTime.Now;
+ string[] assetDependencies = AssetDatabase.GetDependencies(scene.scenePath);
+ Debug.Log("[OVRBundleManager] - " + scene.sceneName + " - Calculated scene asset dependencies in: " + (DateTime.Now - getDepStart).TotalSeconds);
+ ProcessAssets(assetDependencies, scene.sceneName, ref uniqueAssetInSceneBundle, ref extToAssetList);
+
+ // Add the scene into it's own bundle
+ string[] sceneAsset = new string[1] { scene.scenePath };
+ AssetBundleBuild sceneBuild = new AssetBundleBuild();
+ sceneBuild.assetBundleName = "scene_" + scene.sceneName;
+ sceneBuild.assetNames = sceneAsset;
+ assetBundleBuilds.Add(sceneBuild);
+ }
+
+ // Chunk the asset bundles by number of assets
+ foreach (string ext in extToAssetList.Keys)
+ {
+ int assetCount = extToAssetList[ext].Count;
+ int numChunks = (assetCount + BUNDLE_CHUNK_SIZE - 1) / BUNDLE_CHUNK_SIZE;
+ //Debug.Log(ext + " has " + assetCount + " asset(s) that will be split into " + numChunks + " chunk(s)");
+ for (int i = 0; i < numChunks; i++)
+ {
+ List assetChunkList;
+ if (i == numChunks - 1)
+ {
+ int size = BUNDLE_CHUNK_SIZE - (numChunks * BUNDLE_CHUNK_SIZE - assetCount);
+ assetChunkList = extToAssetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, size);
+ }
+ else
+ {
+ assetChunkList = extToAssetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, BUNDLE_CHUNK_SIZE);
+ }
+ AssetBundleBuild build = new AssetBundleBuild();
+ build.assetBundleName = "asset_" + ext + i;
+ build.assetNames = assetChunkList.ToArray();
+ //Debug.Log("Chunk " + i + " has " + assetChunkList.Count + " asset(s)");
+ assetBundleBuilds.Add(build);
+ }
+ }
+
+ Debug.Log("[OVRBundleManager] - Created chucked scene bundles in: " + (DateTime.Now - labelingStart).TotalSeconds);
+
+ // Build asset bundles
+ BuildPipeline.BuildAssetBundles(sceneBundleDirectory, assetBundleBuilds.ToArray(),
+ BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.Android);
+
+ double bundleBuildTime = (DateTime.Now - totalStart).TotalSeconds;
+ Debug.Log("[OVRBundleManager] - Total Time: " + bundleBuildTime);
+ }
+
+ private static void ProcessAssets(string[] assetPaths,
+ string assetParent,
+ ref Dictionary uniqueAssetInSceneBundle,
+ ref Dictionary> extToAssetList)
+ {
+ foreach (string asset in assetPaths)
+ {
+ string ext = Path.GetExtension(asset);
+ if (!string.IsNullOrEmpty(ext))
+ {
+ ext = ext.Substring(1);
+ if (!ext.Equals("cs") && !ext.Equals("unity"))
+ {
+ // Only process each asset once across all resource and scene bundles
+ // Each asset is keyed by full path as a unique identifier
+ if (!uniqueAssetInSceneBundle.ContainsKey(asset))
+ {
+ var assetObject = (UnityEngine.Object)AssetDatabase.LoadAssetAtPath(asset, typeof(UnityEngine.Object));
+ if (assetObject == null || (assetObject.hideFlags & HideFlags.DontSaveInBuild) == 0)
+ {
+ uniqueAssetInSceneBundle[asset] = assetParent;
+
+ if (assetParent != "resources")
+ {
+ if (!extToAssetList.ContainsKey(ext))
+ {
+ extToAssetList[ext] = new List();
+ }
+ extToAssetList[ext].Add(asset);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static bool DeploySceneBundles(List sceneList)
+ {
+ // Create Temp directory on local machine if it does not exist
+ string tempDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "Temp");
+ if (!Directory.Exists(tempDirectory))
+ {
+ Directory.CreateDirectory(tempDirectory);
+ }
+ string absoluteTempPath = Path.Combine(Path.Combine(Application.dataPath, ".."), tempDirectory);
+
+ OVRBundleTool.PrintLog("Deploying scene bundles to device . . . ");
+
+ OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ if (adbTool.isReady)
+ {
+ DateTime transferStart = DateTime.Now;
+
+ for (int i = 0; i < sceneList.Count; ++i)
+ {
+ if (!sceneList[i].shouldDeploy) continue;
+ OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.TRANSFERRING, i);
+ }
+
+ // Transfer all scene bundles that are relavent
+ if (!TransferSceneBundles(adbTool, absoluteTempPath, externalSceneCache))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < sceneList.Count; ++i)
+ {
+ if (!sceneList[i].shouldDeploy) continue;
+ OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.DEPLOYED, i);
+ }
+
+ // Create file to tell transition scene APK which scene to load and push it to the device
+ string sceneLoadDataPath = Path.Combine(tempDirectory, OVRSceneLoader.sceneLoadDataName);
+ if (File.Exists(sceneLoadDataPath))
+ {
+ File.Delete(sceneLoadDataPath);
+ }
+
+ StreamWriter writer = new StreamWriter(sceneLoadDataPath, true);
+ // Write version and scene names
+ long unixTime = (int)(DateTimeOffset.UtcNow.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
+ writer.WriteLine(unixTime.ToString());
+ for (int i = 0; i < sceneList.Count; i++)
+ {
+ if (!sceneList[i].shouldDeploy) continue;
+ writer.WriteLine(Path.GetFileNameWithoutExtension(sceneList[i].scenePath));
+ }
+
+ writer.Close();
+
+ string absoluteSceneLoadDataPath = Path.Combine(absoluteTempPath, OVRSceneLoader.sceneLoadDataName);
+ string[] pushCommand = { "-d push", "\"" + absoluteSceneLoadDataPath + "\"", "\"" + externalSceneCache + "\"" };
+ string output, error;
+ if (adbTool.RunCommand(pushCommand, null, out output, out error) == 0)
+ {
+ Debug.Log("[OVRBundleManager] Scene Load Data Pushed to Device.");
+ return true;
+ }
+ OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
+ }
+ else
+ {
+ OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
+ }
+ return false;
+ }
+
+ private static bool TransferSceneBundles(OVRADBTool adbTool, string absoluteTempPath, string externalSceneCache)
+ {
+ List bundlesToTransfer = new List();
+ List bundlesToDelete = new List();
+ string manifestFilePath = externalSceneCache + "/" + BUNDLE_MANAGER_MASTER_BUNDLE;
+
+ string[] pullManifestCommand = { "-d pull", "\"" + manifestFilePath + "\"", "\"" + absoluteTempPath + "\"" };
+
+ string output, error;
+ if (adbTool.RunCommand(pullManifestCommand, null, out output, out error) == 0)
+ {
+ // An existing manifest file was found on device. Load hashes and upload bundles that have changed hashes.
+ Debug.Log("[OVRBundleManager] - Scene bundle manifest file found. Decoding changes . . .");
+
+ // Load hashes from remote manifest
+ AssetBundle remoteBundle = AssetBundle.LoadFromFile(Path.Combine(absoluteTempPath, BUNDLE_MANAGER_MASTER_BUNDLE));
+ if (remoteBundle == null)
+ {
+ OVRBundleTool.PrintError("Failed to load remote asset bundle manifest file.");
+ return false;
+ }
+ AssetBundleManifest remoteManifest = remoteBundle.LoadAsset("AssetBundleManifest");
+
+ Dictionary remoteBundleToHash = new Dictionary();
+ if (remoteManifest != null)
+ {
+ string[] assetBundles = remoteManifest.GetAllAssetBundles();
+ foreach (string bundleName in assetBundles)
+ {
+ remoteBundleToHash[bundleName] = remoteManifest.GetAssetBundleHash(bundleName);
+ }
+ }
+ remoteBundle.Unload(true);
+
+ // Load hashes from local manifest
+ AssetBundle localBundle = AssetBundle.LoadFromFile(BUNDLE_MANAGER_OUTPUT_PATH + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE
+ + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE);
+ if (localBundle == null)
+ {
+ OVRBundleTool.PrintError("Failed to load local asset bundle manifest file.\n");
+ return false;
+ }
+ AssetBundleManifest localManifest = localBundle.LoadAsset("AssetBundleManifest");
+
+ if (localManifest != null)
+ {
+ Hash128 zeroHash = new Hash128(0, 0, 0, 0);
+
+ // Build a list of dirty bundles that will have to be transfered
+ string relativeSceneBundlesPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
+ bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, BUNDLE_MANAGER_MASTER_BUNDLE));
+ string[] assetBundles = localManifest.GetAllAssetBundles();
+ foreach (string bundleName in assetBundles)
+ {
+ if (!remoteBundleToHash.ContainsKey(bundleName))
+ {
+ bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
+ }
+ else
+ {
+ if (remoteBundleToHash[bundleName] != localManifest.GetAssetBundleHash(bundleName))
+ {
+ bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
+ }
+ remoteBundleToHash[bundleName] = zeroHash;
+ }
+ }
+
+ OVRBundleTool.PrintLog(bundlesToTransfer.Count + " dirty bundle(s) will be transfered.\n");
+ }
+ }
+ else
+ {
+ if (output.Contains("does not exist") || output.Contains("No such file or directory"))
+ {
+ // Fresh install of asset bundles, transfer all asset bundles
+ OVRBundleTool.PrintLog("Manifest file not found. Transfering all bundles . . . ");
+
+ string[] mkdirCommand = { "-d shell", "mkdir -p", "\"" + externalSceneCache + "\"" };
+ if (adbTool.RunCommand(mkdirCommand, null, out output, out error) == 0)
+ {
+ string absoluteSceneBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."),
+ Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE));
+
+ string[] assetBundlePaths = Directory.GetFiles(absoluteSceneBundlePath);
+ if (assetBundlePaths.Length != 0)
+ {
+ foreach (string path in assetBundlePaths)
+ {
+ if (!path.Contains(".manifest"))
+ {
+ bundlesToTransfer.Add(path);
+ }
+ }
+ }
+ else
+ {
+ OVRBundleTool.PrintError("Failed to locate scene bundles to transfer.");
+ return false;
+ }
+ }
+ }
+ }
+
+ // If any adb error occured during manifest calculation, print it and return false
+ if (!string.IsNullOrEmpty(error) || output.Contains("error"))
+ {
+ OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
+ return false;
+ }
+
+ // Transfer bundles to device
+ DateTime transferStart = DateTime.Now;
+ foreach (string bundle in bundlesToTransfer)
+ {
+ string absoluteBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."), bundle);
+ string[] pushBundleCommand = { "-d push", "\"" + absoluteBundlePath + "\"", "\"" + externalSceneCache + "\"" };
+ adbTool.RunCommandAsync(pushBundleCommand, null);
+ }
+ Debug.Log("[OVRBundleManager] - Transfer took " + (DateTime.Now - transferStart).TotalSeconds + " seconds.");
+
+ // Delete stale bundles on device
+ if (bundlesToDelete.Count > 0)
+ {
+ foreach (string bundle in bundlesToDelete)
+ {
+ string bundlePath = externalSceneCache + "/" + bundle;
+ string[] deleteBundleCommand = { "-d shell", "rm", "\"" + bundlePath + "\"" };
+ adbTool.RunCommandAsync(deleteBundleCommand, null);
+ }
+ OVRBundleTool.PrintLog("Deleted " + bundlesToDelete.Count + " bundle(s) that were stale");
+ }
+
+ return true;
+ }
+
+ public static bool LaunchApplication()
+ {
+ OVRBundleTool.PrintLog("Launching Application . . . ");
+
+ OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ if (adbTool.isReady)
+ {
+ string output, error;
+ string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
+ + GetTransitionApkOptionalIdentifier();
+ string playerActivityName = "\"" + appPackagename + "/com.unity3d.player.UnityPlayerActivity\"";
+ string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
+ if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
+ {
+ OVRBundleTool.PrintSuccess();
+ OVRBundleTool.PrintLog("App package " + appPackagename + " is launched.");
+ return true;
+ }
+
+ string completeError = "Failed to launch application. Try launching it manually through the device.\n" + (string.IsNullOrEmpty(error) ? output : error);
+ OVRBundleTool.PrintError(completeError);
+ }
+ else
+ {
+ OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
+ }
+ return false;
+ }
+
+ public static bool UninstallAPK()
+ {
+ OVRBundleTool.PrintLog("Uninstalling Application . . .");
+
+ OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ if (adbTool.isReady)
+ {
+ string output, error;
+ string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
+ + GetTransitionApkOptionalIdentifier();
+ string[] appStartCommand = { "-d shell", "pm uninstall", appPackagename };
+ if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
+ {
+ OVRBundleTool.PrintSuccess();
+ OVRBundleTool.PrintLog("App package " + appPackagename + " is uninstalled.");
+ return true;
+ }
+
+ OVRBundleTool.PrintError("Failed to uninstall APK.");
+ }
+ else
+ {
+ OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
+ }
+ return false;
+ }
+
+ public static void DeleteRemoteAssetBundles()
+ {
+ OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ if (adbTool.isReady)
+ {
+ externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
+ + GetTransitionApkOptionalIdentifier() + "/cache/scenes";
+
+ bool failure = false;
+ string fileExistsError = "No such file or directory";
+ OVRBundleTool.PrintLog("Deleting device bundles . . . ");
+ string output, error;
+ string[] deleteBundleCommand = { "-d shell", "rm -r", externalSceneCache };
+ if (adbTool.RunCommand(deleteBundleCommand, null, out output, out error) != 0)
+ {
+ if (!(output.Contains(fileExistsError) || error.Contains(fileExistsError)))
+ {
+ failure = true;
+ }
+ }
+
+ if (failure)
+ {
+ OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
+ OVRBundleTool.PrintError("Failed to delete scene bundle cache directory.");
+ }
+ else
+ {
+ OVRBundleTool.PrintSuccess();
+ }
+ }
+ else
+ {
+ OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
+ }
+ }
+
+ public static string[] ListRemoteAssetBundleNames()
+ {
+ OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ if (adbTool.isReady)
+ {
+ externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
+ + GetTransitionApkOptionalIdentifier() + "/cache/scenes";
+
+ string output, error;
+ string[] listBundlesCommand = { "-d shell", "ls", externalSceneCache };
+ if (adbTool.RunCommand(listBundlesCommand, null, out output, out error) == 0)
+ {
+ return output.Split(new[]{ '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
+ }
+ }
+
+ return null;
+ }
+
+ public static void DeleteLocalAssetBundles()
+ {
+ try
+ {
+ if (Directory.Exists(BUNDLE_MANAGER_OUTPUT_PATH))
+ {
+ OVRBundleTool.PrintLog("Deleting local bundles . . . ");
+ Directory.Delete(BUNDLE_MANAGER_OUTPUT_PATH, true);
+ }
+ }
+ catch (Exception e)
+ {
+ OVRBundleTool.PrintError(e.Message);
+ }
+ OVRBundleTool.PrintSuccess();
+ }
+
+ private static bool CheckForResources()
+ {
+ string[] resourceDirectories = Directory.GetDirectories("Assets", "Resources", SearchOption.AllDirectories);
+ return resourceDirectories.Length > 0;
+ }
+
+ private static string GetTransitionApkOptionalIdentifier()
+ {
+ string transitionApkOptionalIdentifier;
+ // Check option value from editor UI
+ if (OVRBundleTool.GetUseOptionalTransitionApkPackage())
+ {
+ // Append .transition to default app package name to optionally allow both
+ // full build apk and transition apk to be installed on device
+ transitionApkOptionalIdentifier = ".transition";
+ }
+ else
+ {
+ transitionApkOptionalIdentifier = "";
+ }
+ return transitionApkOptionalIdentifier;
+ }
+}
+#endif
diff --git a/Assets/Oculus/VR/Editor/OVRBundleTool.cs b/Assets/Oculus/VR/Editor/OVRBundleTool.cs
new file mode 100644
index 0000000..fddf70e
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRBundleTool.cs
@@ -0,0 +1,647 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if UNITY_EDITOR_WIN && UNITY_ANDROID
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Reflection;
+using System.IO;
+
+using UnityEngine;
+using UnityEditor;
+
+public class OVRBundleTool : EditorWindow
+{
+ private static List buildableScenes;
+ private static Vector2 debugLogScroll = new Vector2(0, 0);
+ private static bool invalidBuildableScene;
+
+ private static string toolLog;
+ private static bool deployScenesWhenDeployingApk;
+ private static bool useOptionalTransitionApkPackage;
+ private static GUIStyle windowStyle;
+ private static GUIStyle logBoxStyle;
+ private static GUIStyle statusStyle;
+ private static Vector2 logBoxSize;
+ private static Vector2 sceneScrollViewPos;
+
+ private bool forceRestart = false;
+ private bool showBundleManagement = false;
+ private bool showOther = false;
+
+ // Needed to ensure that APK checking does happen during editor start up, but will still happen when the window is opened/updated
+ private static bool panelInitialized = false;
+
+ const float spacesPerIndent = 12;
+
+ private enum ApkStatus
+ {
+ UNKNOWN,
+ OK,
+ NOT_INSTALLED,
+ DEVICE_NOT_CONNECTED,
+ };
+
+ public enum SceneBundleStatus
+ {
+ [Description("")]
+ UNKNOWN,
+ [Description("Queued")]
+ QUEUED,
+ [Description("Building")]
+ BUILDING,
+ [Description("Done")]
+ DONE,
+ [Description("Transferring")]
+ TRANSFERRING,
+ [Description("Deployed")]
+ DEPLOYED,
+ };
+
+ public class EditorSceneInfo
+ {
+ public string scenePath;
+ public string sceneName;
+ public SceneBundleStatus buildStatus;
+ public bool shouldDeploy;
+
+ public EditorSceneInfo(string path, string name)
+ {
+ scenePath = path;
+ sceneName = name;
+ buildStatus = SceneBundleStatus.UNKNOWN;
+ shouldDeploy = true;
+ }
+ }
+
+ private enum GuiAction
+ {
+ None,
+ OpenBuildSettingsWindow,
+ BuildAndDeployScenes,
+ BuildAndDeployApp,
+ ClearDeviceBundles,
+ ClearLocalBundles,
+ LaunchApp,
+ UninstallApk,
+ ClearLog,
+ }
+
+ private GuiAction action = GuiAction.None;
+
+ private static ApkStatus currentApkStatus;
+
+ private const string deployScenesWhenDeployingApkPrefName = "OVRBundleTool_DeployScenesWithAPK";
+ private const string useOptionalTransitionApkPackagePrefName = "OVRBundleTool_UseOptionalPackageName";
+
+ [MenuItem("Oculus/OVR Build/OVR Scene Quick Preview %l", false, 10)]
+ static void Init()
+ {
+ currentApkStatus = ApkStatus.UNKNOWN;
+
+ EditorWindow.GetWindow(typeof(OVRBundleTool));
+
+ invalidBuildableScene = false;
+ InitializePanel();
+
+ OVRPlugin.SetDeveloperMode(OVRPlugin.Bool.True);
+ OVRPlugin.SendEvent("oculus_bundle_tool", "show_window");
+ }
+
+ public void OnEnable()
+ {
+ InitializePanel();
+ }
+
+ public static void InitializePanel()
+ {
+ panelInitialized = true;
+ GetScenesFromBuildSettings();
+ deployScenesWhenDeployingApk = EditorPrefs.GetBool(deployScenesWhenDeployingApkPrefName, true);
+ useOptionalTransitionApkPackage = EditorPrefs.GetBool(useOptionalTransitionApkPackagePrefName, false);
+ EditorBuildSettings.sceneListChanged += GetScenesFromBuildSettings;
+ }
+
+ private void OnGUI()
+ {
+ this.titleContent.text = "OVR Scene Quick Preview";
+
+ if (panelInitialized)
+ {
+ CheckForTransitionAPK();
+ CheckForDeployedScenes();
+ panelInitialized = false;
+ }
+
+ if (windowStyle == null)
+ {
+ windowStyle = new GUIStyle();
+ windowStyle.margin = new RectOffset(10, 10, 10, 10);
+ }
+
+ if (logBoxStyle == null)
+ {
+ logBoxStyle = new GUIStyle();
+ logBoxStyle.margin.left = 5;
+ logBoxStyle.wordWrap = true;
+ logBoxStyle.normal.textColor = logBoxStyle.focused.textColor = EditorStyles.label.normal.textColor;
+ logBoxStyle.richText = true;
+ }
+
+ if (statusStyle == null)
+ {
+ statusStyle = new GUIStyle(EditorStyles.label);
+ statusStyle.richText = true;
+ }
+
+ EditorGUILayout.BeginVertical(windowStyle);
+
+ GUILayout.BeginHorizontal(EditorStyles.helpBox);
+ GUILayout.BeginVertical();
+ EditorGUILayout.LabelField("OVR Scene Quick Preview generates a version of your app which supports hot-reloading "
+ + "content changes to individual scenes, reducing iteration time.",
+ EditorStyles.wordWrappedLabel);
+
+#if UNITY_2021_1_OR_NEWER
+ if (EditorGUILayout.LinkButton("Documentation"))
+#else
+ if (GUILayout.Button("Documentation", GUILayout.ExpandWidth(false)))
+#endif
+ {
+ Application.OpenURL("https://developer.oculus.com/documentation/unity/unity-build-android-tools/");
+ }
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(10f);
+ GUIContent transitionContent = new GUIContent("Modified APK [?]",
+ "Build and deploy an APK that can hot-reload scenes. This enables fast iteration on content changes to scenes.");
+ GUILayout.Label(transitionContent, EditorStyles.boldLabel);
+
+ EditorGUILayout.BeginHorizontal();
+ {
+ GUILayout.Label("Status: ", statusStyle, GUILayout.ExpandWidth(false));
+
+ string statusMesssage;
+ switch (currentApkStatus)
+ {
+ case ApkStatus.OK:
+ statusMesssage = "APK installed. Ready to build and deploy scenes.";
+ break;
+ case ApkStatus.NOT_INSTALLED:
+ statusMesssage = "APK not installed. Press \"Build and Deploy APK\" to install the modified APK.";
+ break;
+ case ApkStatus.DEVICE_NOT_CONNECTED:
+ statusMesssage = "Device not connected via ADB. Please connect device and allow debugging.";
+ break;
+ case ApkStatus.UNKNOWN:
+ default:
+ statusMesssage = "Failed to get APK status!";
+ break;
+ }
+ GUILayout.Label(statusMesssage, statusStyle, GUILayout.ExpandWidth(true));
+ }
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.BeginHorizontal();
+ if (GUILayout.Button("Build and Deploy APK", GUILayout.Width(200)))
+ {
+ action = GuiAction.BuildAndDeployApp;
+ }
+ EditorGUI.BeginDisabledGroup(currentApkStatus != ApkStatus.OK);
+ if (GUILayout.Button("Launch APK", GUILayout.Width(120)))
+ {
+ action = GuiAction.LaunchApp;
+ }
+ EditorGUI.EndDisabledGroup();
+ EditorGUILayout.EndHorizontal();
+
+ GUILayout.Space(10f);
+
+ GUIContent scenesContent = new GUIContent("Scenes [?]",
+ "Build and deploy individual scenes, which can be hot-reloaded at runtime by the modified APK.");
+ GUILayout.Label(scenesContent, EditorStyles.boldLabel);
+
+ GUIContent buildSettingsBtnTxt = new GUIContent("Open Build Settings");
+ GUIContent deployLabelTxt = new GUIContent("Deploy?",
+ "If true, this scene will be hot-reloaded. To reduce iteration time, only deploy scenes under active iteration.");
+ if (buildableScenes == null || buildableScenes.Count == 0)
+ {
+ string sceneErrorMessage;
+ if (invalidBuildableScene)
+ {
+ sceneErrorMessage = "Invalid scene selection. \nPlease remove OVRTransitionScene in the project's build settings.";
+ }
+ else
+ {
+ sceneErrorMessage = "No scenes detected. \nTo get started, add scenes in the project's build settings.";
+ }
+ GUILayout.Label(sceneErrorMessage);
+
+ var buildSettingBtnRt = GUILayoutUtility.GetRect(buildSettingsBtnTxt, GUI.skin.button, GUILayout.Width(150));
+ if (GUI.Button(buildSettingBtnRt, buildSettingsBtnTxt))
+ {
+ action = GuiAction.OpenBuildSettingsWindow;
+ }
+ }
+ else
+ {
+ float currWidth = EditorGUIUtility.currentViewWidth;
+ float sceneNameWidth = Math.Max(EditorGUIUtility.currentViewWidth - 170, 60);
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Scene Name", GUI.skin.box, GUILayout.Width(sceneNameWidth));
+ EditorGUILayout.LabelField("Status", GUI.skin.box, GUILayout.Width(80));
+ EditorGUILayout.LabelField(deployLabelTxt, GUI.skin.box, GUILayout.Width(60));
+ EditorGUILayout.EndHorizontal();
+
+ int scrollViewHeight = Math.Min(buildableScenes.Count * 21, 200);
+ sceneScrollViewPos = EditorGUILayout.BeginScrollView(sceneScrollViewPos, GUILayout.MaxHeight(scrollViewHeight));
+ foreach (EditorSceneInfo scene in buildableScenes)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(scene.sceneName, GUILayout.Width(sceneNameWidth + 8));
+ EditorGUILayout.LabelField(GetEnumDescription(scene.buildStatus), GUILayout.Width(80));
+ scene.shouldDeploy = EditorGUILayout.Toggle(scene.shouldDeploy, GUILayout.Width(30));
+ EditorGUILayout.EndHorizontal();
+ }
+ EditorGUILayout.EndScrollView();
+
+ EditorGUILayout.BeginHorizontal();
+ {
+ if (GUILayout.Button("Build and Deploy Scene(s)", GUILayout.Width(200)))
+ {
+ action = GuiAction.BuildAndDeployScenes;
+ }
+ GUILayout.Space(10);
+ GUIContent forceRestartLabel = new GUIContent("Force Restart [?]", "Relaunch the application after scene bundles are finished deploying.");
+ forceRestart = GUILayout.Toggle(forceRestart, forceRestartLabel, GUILayout.ExpandWidth(true));
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+
+ GUILayout.Space(10.0f);
+ GUILayout.Label("Utilities", EditorStyles.boldLabel);
+
+ showBundleManagement = EditorGUILayout.BeginFoldoutHeaderGroup(showBundleManagement, "Bundle Management", EditorStyles.foldoutHeader);
+ if (showBundleManagement)
+ {
+ EditorGUILayout.BeginHorizontal();
+ {
+ EditorGUILayout.Space(EditorGUI.indentLevel * spacesPerIndent, false); //to match indentLevel
+ GUIContent clearDeviceBundlesTxt = new GUIContent("Delete Device Bundles [?]",
+ "Asset bundles to support hot-reloading are stored in an external location on-device. Click to delete them, freeing up space on-device.");
+ if (GUILayout.Button(clearDeviceBundlesTxt, GUILayout.ExpandWidth(true)))
+ {
+ action = GuiAction.ClearDeviceBundles;
+ }
+
+ GUIContent clearLocalBundlesTxt = new GUIContent("Delete Local Bundles [?]",
+ $"Locally, asset bundles are built into the \"{OVRBundleManager.BUNDLE_MANAGER_OUTPUT_PATH}\" folder at project root. "
+ + "Click to delete them, freeing up local space.");
+ if (GUILayout.Button(clearLocalBundlesTxt, GUILayout.ExpandWidth(true)))
+ {
+ action = GuiAction.ClearLocalBundles;
+ }
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+ EditorGUILayout.EndFoldoutHeaderGroup();
+
+ GUILayout.Space(5.0f);
+ showOther = EditorGUILayout.BeginFoldoutHeaderGroup(showOther, "Other", EditorStyles.foldoutHeader);
+ if (showOther)
+ {
+ const float otherLabelsWidth = 240f;
+ EditorGUI.indentLevel++;
+ EditorGUILayout.BeginHorizontal();
+
+ GUIContent deployScenesWithApkLabel = new GUIContent("Deploy scenes with APK deploy [?]",
+ "If checked, all scenes will be built & deployed when pressing \"Build and Deploy APK\". This takes longer, but provides more expected behavior.");
+
+ EditorGUILayout.LabelField(deployScenesWithApkLabel, GUILayout.Width(otherLabelsWidth));
+ bool newToggleValue = EditorGUILayout.Toggle(deployScenesWhenDeployingApk);
+
+ if (newToggleValue != deployScenesWhenDeployingApk)
+ {
+ deployScenesWhenDeployingApk = newToggleValue;
+ EditorPrefs.SetBool(deployScenesWhenDeployingApkPrefName, deployScenesWhenDeployingApk);
+ }
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.BeginHorizontal();
+ GUIContent useOptionalTransitionPackageLabel = new GUIContent("Use optional APK package name [?]",
+ "This allows both full build APK and transition APK to be installed on device. However, platform services like Entitlement check may fail.");
+
+ EditorGUILayout.LabelField(useOptionalTransitionPackageLabel, GUILayout.Width(otherLabelsWidth));
+ newToggleValue = EditorGUILayout.Toggle(useOptionalTransitionApkPackage);
+
+ if (newToggleValue != useOptionalTransitionApkPackage)
+ {
+ useOptionalTransitionApkPackage = newToggleValue;
+ EditorPrefs.SetBool(useOptionalTransitionApkPackagePrefName, useOptionalTransitionApkPackage);
+ // New package name = new check for associated data
+ CheckForTransitionAPK();
+ CheckForDeployedScenes();
+ }
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.Space(EditorGUI.indentLevel * spacesPerIndent, false); //to match indentLevel
+ if (GUILayout.Button(buildSettingsBtnTxt, GUILayout.ExpandWidth(true)))
+ {
+ action = GuiAction.OpenBuildSettingsWindow;
+ }
+ if (GUILayout.Button("Uninstall APK", GUILayout.ExpandWidth(true)))
+ {
+ action = GuiAction.UninstallApk;
+ }
+ EditorGUILayout.EndHorizontal();
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndFoldoutHeaderGroup();
+
+ GUILayout.Space(6f);
+ GUILayout.Label("", GUI.skin.horizontalSlider);
+ GUILayout.Space(10f);
+
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Label("Log", EditorStyles.boldLabel);
+ GUILayout.FlexibleSpace();
+ if (GUILayout.Button("Clear Log", EditorStyles.miniButton))
+ {
+ action = GuiAction.ClearLog;
+ }
+ EditorGUILayout.EndHorizontal();
+
+ debugLogScroll = EditorGUILayout.BeginScrollView(debugLogScroll, EditorStyles.helpBox, GUILayout.ExpandHeight(true));
+ if (!string.IsNullOrEmpty(toolLog))
+ {
+ EditorGUILayout.SelectableLabel(toolLog, logBoxStyle, GUILayout.Height(logBoxSize.y));
+ }
+ EditorGUILayout.EndScrollView();
+
+ EditorGUILayout.EndVertical();
+ }
+
+ private void Update()
+ {
+ switch (action)
+ {
+ case GuiAction.OpenBuildSettingsWindow:
+ OpenBuildSettingsWindow();
+ break;
+ case GuiAction.BuildAndDeployScenes:
+ OVRBundleManager.BuildDeployScenes(buildableScenes, forceRestart);
+ CheckForDeployedScenes();
+ break;
+ case GuiAction.BuildAndDeployApp:
+ OVRBundleManager.BuildDeployTransitionAPK();
+ CheckForTransitionAPK();
+ if (deployScenesWhenDeployingApk)
+ {
+ buildableScenes.ForEach(x => x.shouldDeploy = true);
+ OVRBundleManager.BuildDeployScenes(buildableScenes, false);
+ CheckForDeployedScenes();
+ }
+ OVRBundleManager.LaunchApplication();
+ break;
+ case GuiAction.ClearDeviceBundles:
+ OVRBundleManager.DeleteRemoteAssetBundles();
+ CheckForDeployedScenes();
+ break;
+ case GuiAction.ClearLocalBundles:
+ OVRBundleManager.DeleteLocalAssetBundles();
+ break;
+ case GuiAction.LaunchApp:
+ OVRBundleManager.LaunchApplication();
+ break;
+ case GuiAction.UninstallApk:
+ OVRBundleManager.UninstallAPK();
+ CheckForTransitionAPK();
+ CheckForDeployedScenes();
+ break;
+ case GuiAction.ClearLog:
+ PrintLog("", true);
+ break;
+ default:
+ break;
+ }
+
+ action = GuiAction.None;
+ }
+
+ private static void OpenBuildSettingsWindow()
+ {
+ EditorWindow.GetWindow(System.Type.GetType("UnityEditor.BuildPlayerWindow,UnityEditor"));
+ }
+
+ public static void UpdateSceneBuildStatus(SceneBundleStatus status, int index = -1)
+ {
+ if (buildableScenes == null)
+ {
+ return;
+ }
+
+ if (index >= 0 && index < buildableScenes.Count)
+ {
+ buildableScenes[index].buildStatus = status;
+ }
+ else
+ {
+ // Update status for all scenes
+ for (int i = 0; i < buildableScenes.Count; i++)
+ {
+ buildableScenes[i].buildStatus = status;
+ }
+ }
+ }
+
+ private static void GetScenesFromBuildSettings()
+ {
+ invalidBuildableScene = false;
+ buildableScenes = new List();
+ for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
+ {
+ EditorBuildSettingsScene scene = EditorBuildSettings.scenes[i];
+ if (scene.enabled)
+ {
+ if (Path.GetFileNameWithoutExtension(scene.path) != "OVRTransitionScene")
+ {
+ EditorSceneInfo sceneInfo = new EditorSceneInfo(scene.path, Path.GetFileNameWithoutExtension(scene.path));
+ buildableScenes.Add(sceneInfo);
+ }
+ else
+ {
+ buildableScenes = null;
+ invalidBuildableScene = true;
+ return;
+ }
+ }
+ }
+ }
+
+ private static void CheckForTransitionAPK()
+ {
+ OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
+ if (adbTool.isReady)
+ {
+ string matchedPackageList, error;
+ var transitionPackageName = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
+ if (useOptionalTransitionApkPackage)
+ {
+ transitionPackageName += ".transition";
+ }
+ string[] packageCheckCommand = new string[] { "-d shell pm list package", transitionPackageName };
+ if (adbTool.RunCommand(packageCheckCommand, null, out matchedPackageList, out error) == 0)
+ {
+ if (string.IsNullOrEmpty(matchedPackageList))
+ {
+ currentApkStatus = ApkStatus.NOT_INSTALLED;
+ }
+ else
+ {
+ // adb "list package" command returns all package names that contains the given query package name
+ // Need to check if the transition package name is matched exactly
+ if (matchedPackageList.Contains("package:" + transitionPackageName + "\r\n"))
+ {
+ if (useOptionalTransitionApkPackage)
+ {
+ // If optional package name is used, it is deterministic that the transition apk is installed
+ currentApkStatus = ApkStatus.OK;
+ }
+ else
+ {
+ // get package info to check for TRANSITION_APK_VERSION_NAME
+ string[] dumpPackageInfoCommand = new string[] { "-d shell dumpsys package", transitionPackageName };
+ string packageInfo;
+ if (adbTool.RunCommand(dumpPackageInfoCommand, null, out packageInfo, out error) == 0 &&
+ !string.IsNullOrEmpty(packageInfo) &&
+ packageInfo.Contains(OVRBundleManager.TRANSITION_APK_VERSION_NAME))
+ {
+ // Matched package name found, and the package info contains TRANSITION_APK_VERSION_NAME
+ currentApkStatus = ApkStatus.OK;
+ }
+ else
+ {
+ currentApkStatus = ApkStatus.NOT_INSTALLED;
+ }
+ }
+ }
+ else
+ {
+ // No matached package name returned
+ currentApkStatus = ApkStatus.NOT_INSTALLED;
+ }
+ }
+ }
+ else if (error.Contains("no devices found"))
+ {
+ currentApkStatus = ApkStatus.DEVICE_NOT_CONNECTED;
+ }
+ else
+ {
+ currentApkStatus = ApkStatus.UNKNOWN;
+ }
+ }
+ }
+
+ private static void CheckForDeployedScenes()
+ {
+ if (buildableScenes == null) return;
+ UpdateSceneBuildStatus(SceneBundleStatus.UNKNOWN);
+
+ string[] deployedBundleNames = OVRBundleManager.ListRemoteAssetBundleNames();
+ if (deployedBundleNames == null) return;
+
+ for (int i = 0; i < buildableScenes.Count; ++i)
+ {
+ string sceneBundleName = "scene_" + buildableScenes[i].sceneName;
+ if (Array.FindIndex(deployedBundleNames, x => x.Equals(sceneBundleName, StringComparison.CurrentCultureIgnoreCase)) != -1)
+ {
+ UpdateSceneBuildStatus(SceneBundleStatus.DEPLOYED, i);
+ }
+ }
+ }
+
+ public static void PrintLog(string message, bool clear = false)
+ {
+ if (clear)
+ {
+ toolLog = message;
+ }
+ else
+ {
+ toolLog += message + "\n";
+ }
+
+ if (logBoxStyle != null)
+ {
+ GUIContent logContent = new GUIContent(toolLog);
+ logBoxSize = logBoxStyle.CalcSize(logContent);
+
+ debugLogScroll.y = float.MaxValue; //scroll to bottom on new data
+ }
+ }
+
+ public static void PrintError(string error = "")
+ {
+ if(!string.IsNullOrEmpty(error))
+ {
+ toolLog += "Failed!\n" + error + "\n";
+ }
+ else
+ {
+ toolLog += "Failed! Check Unity log for more details.\n";
+ }
+ }
+
+ public static void PrintWarning(string warning)
+ {
+ toolLog += "Warning!\n" + warning + "\n";
+ }
+
+ public static void PrintSuccess()
+ {
+ toolLog += "Success!\n";
+ }
+
+ public static string GetEnumDescription(Enum eEnum)
+ {
+ Type enumType = eEnum.GetType();
+ MemberInfo[] memberInfo = enumType.GetMember(eEnum.ToString());
+ if (memberInfo != null && memberInfo.Length > 0)
+ {
+ var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
+ if (attrs != null && attrs.Length > 0)
+ {
+ return ((DescriptionAttribute)attrs[0]).Description;
+ }
+ }
+ return eEnum.ToString();
+ }
+
+ public static bool GetUseOptionalTransitionApkPackage()
+ {
+ return useOptionalTransitionApkPackage;
+ }
+}
+#endif
diff --git a/Assets/Oculus/VR/Editor/OVRConfig.cs b/Assets/Oculus/VR/Editor/OVRConfig.cs
new file mode 100644
index 0000000..2d0c5cb
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRConfig.cs
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+using System;
+
+// OVRConfig inherits from ScriptableObject for legacy reasons. Conceptually,
+// it's just a static class with path fetching helper methods. However, it
+// used to serialize path data. When the serialized fields were no longer
+// needed, we kept the ScriptableObject inheritence so as to not break backwards
+// compatibility for existing projects that upgrade OVRPlugin versions.
+#if UNITY_EDITOR
+[UnityEditor.InitializeOnLoad]
+#endif
+public class OVRConfig : ScriptableObject
+{
+ private static OVRConfig instance;
+
+ public static OVRConfig Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = Resources.Load("OVRConfig");
+ if (instance == null)
+ {
+ instance = ScriptableObject.CreateInstance();
+ string resourcePath = Path.Combine(UnityEngine.Application.dataPath, "Resources");
+ if (!Directory.Exists(resourcePath))
+ {
+ UnityEditor.AssetDatabase.CreateFolder("Assets", "Resources");
+ }
+
+ string fullPath = Path.Combine(Path.Combine("Assets", "Resources"), "OVRBuildConfig.asset");
+ UnityEditor.AssetDatabase.CreateAsset(instance, fullPath);
+ }
+ }
+ return instance;
+ }
+ set
+ {
+ instance = value;
+ }
+ }
+
+ // Returns the path to the base directory of the Android SDK
+ public string GetAndroidSDKPath(bool throwError = true)
+ {
+ string androidSDKPath = "";
+#if UNITY_2019_1_OR_NEWER
+ // Check for use of embedded path or user defined
+ bool useEmbedded = EditorPrefs.GetBool("SdkUseEmbedded") || string.IsNullOrEmpty(EditorPrefs.GetString("AndroidSdkRoot"));
+ if (useEmbedded)
+ {
+ androidSDKPath = Path.Combine(BuildPipeline.GetPlaybackEngineDirectory(BuildTarget.Android, BuildOptions.None), "SDK");
+ }
+ else
+#endif
+ {
+ androidSDKPath = EditorPrefs.GetString("AndroidSdkRoot");
+ }
+
+ androidSDKPath = androidSDKPath.Replace("/", "\\");
+ // Validate sdk path and notify user if path does not exist.
+ if (!Directory.Exists(androidSDKPath))
+ {
+ androidSDKPath = Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT");
+ if (!string.IsNullOrEmpty(androidSDKPath))
+ {
+ return androidSDKPath;
+ }
+
+ if (throwError)
+ {
+ EditorUtility.DisplayDialog("Android SDK not Found",
+ "Android SDK not found. Please ensure that the path is set correctly in (Edit -> Preferences -> External Tools) or that the Untiy Android module is installed correctly.",
+ "Ok");
+ }
+ return string.Empty;
+ }
+
+ return androidSDKPath;
+ }
+
+ // Returns the path to the gradle-launcher-*.jar
+ public string GetGradlePath(bool throwError = true)
+ {
+ string gradlePath = "";
+ string libPath = "";
+#if UNITY_2019_1_OR_NEWER
+ // Check for use of embedded path or user defined
+ bool useEmbedded = EditorPrefs.GetBool("GradleUseEmbedded") || string.IsNullOrEmpty(EditorPrefs.GetString("GradlePath"));
+
+ if (useEmbedded)
+ {
+ libPath = Path.Combine(BuildPipeline.GetPlaybackEngineDirectory(BuildTarget.Android, BuildOptions.None), "Tools\\gradle\\lib");
+ }
+ else
+ {
+ libPath = Path.Combine(EditorPrefs.GetString("GradlePath"), "lib");
+ }
+#else
+ libPath = Path.Combine(EditorApplication.applicationContentsPath, "PlaybackEngines\\AndroidPlayer\\Tools\\gradle\\lib");
+#endif
+
+ libPath = libPath.Replace("/", "\\");
+ if (!string.IsNullOrEmpty(libPath) && Directory.Exists(libPath))
+ {
+ string[] gradleJar = Directory.GetFiles(libPath, "gradle-launcher-*.jar");
+ if (gradleJar.Length == 1)
+ {
+ gradlePath = gradleJar[0];
+ }
+ }
+
+ // Validate gradle path and notify user if path does not exist.
+ if (!File.Exists(gradlePath))
+ {
+ if (throwError)
+ {
+ EditorUtility.DisplayDialog("Gradle not Found",
+ "Gradle not found. Please ensure that the path is set correctly in (Edit -> Preferences -> External Tools) or that the Untiy Android module is installed correctly.",
+ "Ok");
+ }
+ return string.Empty;
+ }
+
+ return gradlePath;
+ }
+
+ // Returns path to the Java executable in the JDK
+ public string GetJDKPath(bool throwError = true)
+ {
+ string jdkPath = "";
+#if UNITY_EDITOR_WIN
+ // Check for use of embedded path or user defined
+ bool useEmbedded = EditorPrefs.GetBool("JdkUseEmbedded") || string.IsNullOrEmpty(EditorPrefs.GetString("JdkPath"));
+
+ string exePath = "";
+ if (useEmbedded)
+ {
+#if UNITY_2019_1_OR_NEWER
+ exePath = Path.Combine(BuildPipeline.GetPlaybackEngineDirectory(BuildTarget.Android, BuildOptions.None), "Tools\\OpenJDK\\Windows\\bin");
+#else
+ exePath = Path.Combine(EditorApplication.applicationContentsPath, "PlaybackEngines\\AndroidPlayer\\Tools\\OpenJDK\\Windows\\bin");
+#endif
+ }
+ else
+ {
+ exePath = Path.Combine(EditorPrefs.GetString("JdkPath"), "bin");
+ }
+
+ jdkPath = Path.Combine(exePath, "java.exe");
+ jdkPath = jdkPath.Replace("/", "\\");
+
+ // Validate gradle path and notify user if path does not exist.
+ if (!File.Exists(jdkPath))
+ {
+ // Check the enviornment variable as a backup to see if the JDK is there.
+ string javaHome = Environment.GetEnvironmentVariable("JAVA_HOME");
+ if(!string.IsNullOrEmpty(javaHome))
+ {
+ jdkPath = Path.Combine(javaHome, "bin\\java.exe");
+ if(File.Exists(jdkPath))
+ {
+ return jdkPath;
+ }
+ }
+
+ if (throwError)
+ {
+ EditorUtility.DisplayDialog("JDK not Found",
+ "JDK not found. Please ensure that the path is set correctly in (Edit -> Preferences -> External Tools) or that the Untiy Android module is installed correctly.",
+ "Ok");
+ }
+ return string.Empty;
+ }
+#endif
+ return jdkPath;
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRDeviceSelector.cs b/Assets/Oculus/VR/Editor/OVRDeviceSelector.cs
new file mode 100644
index 0000000..ba458ba
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRDeviceSelector.cs
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+#if PRIORITIZE_OCULUS_XR_SETTINGS
+using Unity.XR.Oculus;
+#endif
+
+public class OVRDeviceSelector
+{
+ public static bool isTargetDeviceQuestFamily
+ {
+ get
+ {
+ return isTargetDeviceQuest || isTargetDeviceQuest2;
+ }
+ }
+ public static bool isTargetDeviceQuest
+ {
+ get
+ {
+#if PRIORITIZE_OCULUS_XR_SETTINGS
+ OculusSettings settings;
+ UnityEditor.EditorBuildSettings.TryGetConfigObject("Unity.XR.Oculus.Settings", out settings);
+ return settings.TargetQuest;
+#else
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+ return projectConfig.targetDeviceTypes.Contains(OVRProjectConfig.DeviceType.Quest);
+#endif
+
+ }
+ }
+
+ public static bool isTargetDeviceQuest2
+ {
+ get
+ {
+#if PRIORITIZE_OCULUS_XR_SETTINGS
+ OculusSettings settings;
+ UnityEditor.EditorBuildSettings.TryGetConfigObject("Unity.XR.Oculus.Settings", out settings);
+ return settings.TargetQuest2;
+#else
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+ return projectConfig.targetDeviceTypes.Contains(OVRProjectConfig.DeviceType.Quest2);
+#endif
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRDirectorySyncer.cs b/Assets/Oculus/VR/Editor/OVRDirectorySyncer.cs
new file mode 100644
index 0000000..3072d68
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRDirectorySyncer.cs
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System;
+
+public class DirectorySyncer
+{
+ public delegate void SyncResultDelegate(SyncResult syncResult);
+
+ public readonly string Source;
+ public readonly string Target;
+ public SyncResultDelegate WillPerformOperations;
+ private readonly Regex _ignoreExpression;
+
+ // helper classes to simplify transition beyond .NET runtime 3.5
+ public abstract class CancellationToken
+ {
+ protected abstract bool _IsCancellationRequested();
+
+ public virtual bool IsCancellationRequested
+ {
+ get { return _IsCancellationRequested(); }
+ }
+
+ public void ThrowIfCancellationRequested()
+ {
+ if (IsCancellationRequested)
+ {
+ throw new Exception("Operation Cancelled");
+ }
+ }
+
+ public static readonly CancellationToken None = new CancellationTokenNone();
+
+ private class CancellationTokenNone : CancellationToken
+ {
+ protected override bool _IsCancellationRequested()
+ {
+ return false;
+ }
+ }
+ }
+
+ public class CancellationTokenSource : CancellationToken
+ {
+ private bool _isCancelled;
+
+ protected override bool _IsCancellationRequested()
+ {
+ return _isCancelled;
+ }
+
+ public void Cancel()
+ {
+ _isCancelled = true;
+ }
+
+ public CancellationToken Token
+ {
+ get { return this; }
+ }
+ }
+
+ private static string EnsureTrailingDirectorySeparator(string path)
+ {
+ return path.EndsWith("" + Path.DirectorySeparatorChar)
+ ? path
+ : path + Path.DirectorySeparatorChar;
+ }
+
+ private static string CheckedDirectory(string nameInExceptionText, string directory)
+ {
+ directory = Path.GetFullPath(directory);
+ if (!Directory.Exists(directory))
+ {
+ throw new ArgumentException(string.Format("{0} is not a valid directory for argument ${1}", directory,
+ nameInExceptionText));
+ }
+
+ return EnsureTrailingDirectorySeparator(directory);
+ }
+
+ public DirectorySyncer(string source, string target, string ignoreRegExPattern = null)
+ {
+ Source = CheckedDirectory("source", source);
+ Target = CheckedDirectory("target", target);
+ if (Source.StartsWith(Target, StringComparison.OrdinalIgnoreCase) ||
+ Target.StartsWith(Source, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ArgumentException(string.Format("Paths must not contain each other (source: {0}, target: {1}",
+ Source, Target));
+ }
+
+ ignoreRegExPattern = ignoreRegExPattern ?? "^$";
+ _ignoreExpression = new Regex(ignoreRegExPattern, RegexOptions.IgnoreCase);
+ }
+
+ public class SyncResult
+ {
+ public readonly IEnumerable Created;
+ public readonly IEnumerable Updated;
+ public readonly IEnumerable Deleted;
+
+ public SyncResult(IEnumerable created, IEnumerable updated, IEnumerable deleted)
+ {
+ Created = created;
+ Updated = updated;
+ Deleted = deleted;
+ }
+ }
+
+ public bool RelativeFilePathIsRelevant(string relativeFilename)
+ {
+ return !_ignoreExpression.IsMatch(relativeFilename);
+ }
+
+ public bool RelativeDirectoryPathIsRelevant(string relativeDirName)
+ {
+ // Since our ignore patterns look at file names, they may contain trailing path separators
+ // In order for paths to match those rules, we add a path separator here
+ return !_ignoreExpression.IsMatch(EnsureTrailingDirectorySeparator(relativeDirName));
+ }
+
+ private HashSet RelevantRelativeFilesBeneathDirectory(string path, CancellationToken cancellationToken)
+ {
+ return new HashSet(Directory.GetFiles(path, "*", SearchOption.AllDirectories)
+ .TakeWhile((s) => !cancellationToken.IsCancellationRequested)
+ .Select(p => PathHelper.MakeRelativePath(path, p)).Where(RelativeFilePathIsRelevant));
+ }
+
+ private HashSet RelevantRelativeDirectoriesBeneathDirectory(string path,
+ CancellationToken cancellationToken)
+ {
+ return new HashSet(Directory.GetDirectories(path, "*", SearchOption.AllDirectories)
+ .TakeWhile((s) => !cancellationToken.IsCancellationRequested)
+ .Select(p => PathHelper.MakeRelativePath(path, p)).Where(RelativeDirectoryPathIsRelevant));
+ }
+
+ public SyncResult Synchronize()
+ {
+ return Synchronize(CancellationToken.None);
+ }
+
+ private void DeleteOutdatedFilesFromTarget(SyncResult syncResult, CancellationToken cancellationToken)
+ {
+ var outdatedFiles = syncResult.Updated.Union(syncResult.Deleted);
+ foreach (var fileName in outdatedFiles)
+ {
+ File.Delete(Path.Combine(Target, fileName));
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+ }
+
+ [SuppressMessage("ReSharper", "ParameterTypeCanBeEnumerable.Local")]
+ private void DeleteOutdatedEmptyDirectoriesFromTarget(HashSet sourceDirs, HashSet targetDirs,
+ CancellationToken cancellationToken)
+ {
+ var deleted = targetDirs.Except(sourceDirs).OrderByDescending(s => s);
+
+ // By sorting in descending order above, we delete leaf-first,
+ // this is simpler than collapsing the list above (which would also allow us to run these ops in parallel).
+ // Assumption is that there are few empty folders to delete
+ foreach (var dir in deleted)
+ {
+ Directory.Delete(Path.Combine(Target, dir));
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+ }
+
+ [SuppressMessage("ReSharper", "ParameterTypeCanBeEnumerable.Local")]
+ private void CreateRelevantDirectoriesAtTarget(HashSet sourceDirs, HashSet targetDirs,
+ CancellationToken cancellationToken)
+ {
+ var created = sourceDirs.Except(targetDirs);
+ foreach (var dir in created)
+ {
+ Directory.CreateDirectory(Path.Combine(Target, dir));
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+ }
+
+ private void MoveRelevantFilesToTarget(SyncResult syncResult, CancellationToken cancellationToken)
+ {
+ // step 3: we move all new files to target
+ var newFiles = syncResult.Created.Union(syncResult.Updated);
+ foreach (var fileName in newFiles)
+ {
+ var sourceFileName = Path.Combine(Source, fileName);
+ var destFileName = Path.Combine(Target, fileName);
+ // target directory exists due to step CreateRelevantDirectoriesAtTarget()
+ File.Move(sourceFileName, destFileName);
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+ }
+
+ public SyncResult Synchronize(CancellationToken cancellationToken)
+ {
+ var sourceDirs = RelevantRelativeDirectoriesBeneathDirectory(Source, cancellationToken);
+ var targetDirs = RelevantRelativeDirectoriesBeneathDirectory(Target, cancellationToken);
+ var sourceFiles = RelevantRelativeFilesBeneathDirectory(Source, cancellationToken);
+ var targetFiles = RelevantRelativeFilesBeneathDirectory(Target, cancellationToken);
+
+ var created = sourceFiles.Except(targetFiles).OrderBy(s => s).ToList();
+ var updated = sourceFiles.Intersect(targetFiles).OrderBy(s => s).ToList();
+ var deleted = targetFiles.Except(sourceFiles).OrderBy(s => s).ToList();
+ var syncResult = new SyncResult(created, updated, deleted);
+
+ if (WillPerformOperations != null)
+ {
+ WillPerformOperations.Invoke(syncResult);
+ }
+
+ DeleteOutdatedFilesFromTarget(syncResult, cancellationToken);
+ DeleteOutdatedEmptyDirectoriesFromTarget(sourceDirs, targetDirs, cancellationToken);
+ CreateRelevantDirectoriesAtTarget(sourceDirs, targetDirs, cancellationToken);
+ MoveRelevantFilesToTarget(syncResult, cancellationToken);
+
+ return syncResult;
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVREngineConfigurationUpdater.cs b/Assets/Oculus/VR/Editor/OVREngineConfigurationUpdater.cs
new file mode 100644
index 0000000..2db9d8b
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVREngineConfigurationUpdater.cs
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR)
+#define USING_XR_SDK
+#endif
+
+#if UNITY_2020_1_OR_NEWER
+#define REQUIRES_XR_SDK
+#endif
+
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using System;
+using System.IO;
+
+[InitializeOnLoad]
+class OVREngineConfigurationUpdater
+{
+ private const string prefName = "OVREngineConfigurationUpdater_Enabled";
+ private const string menuItemName = "Oculus/Tools/Use Required Project Settings";
+ private const string androidAssetsPath = "Assets/Plugins/Android/assets";
+ private const string androidManifestPath = "Assets/Plugins/Android/AndroidManifest.xml";
+ static bool setPrefsForUtilities;
+
+ [MenuItem(menuItemName)]
+ static void ToggleUtilities()
+ {
+ setPrefsForUtilities = !setPrefsForUtilities;
+ Menu.SetChecked(menuItemName, setPrefsForUtilities);
+
+ int newValue = (setPrefsForUtilities) ? 1 : 0;
+ PlayerPrefs.SetInt(prefName, newValue);
+ PlayerPrefs.Save();
+
+ Debug.Log("Using required project settings: " + setPrefsForUtilities);
+ }
+
+ private static readonly string dashSupportEnableConfirmedKey = "Oculus_Utilities_OVREngineConfiguration_DashSupportEnableConfirmed_" + Application.unityVersion + OVRManager.utilitiesVersion;
+ private static bool dashSupportEnableConfirmed
+ {
+ get
+ {
+ return PlayerPrefs.GetInt(dashSupportEnableConfirmedKey, 0) == 1;
+ }
+
+ set
+ {
+ PlayerPrefs.SetInt(dashSupportEnableConfirmedKey, value ? 1 : 0);
+ }
+ }
+
+
+ static OVREngineConfigurationUpdater()
+ {
+ EditorApplication.delayCall += OnDelayCall;
+ EditorApplication.update += OnUpdate;
+ }
+
+ static void OnDelayCall()
+ {
+ setPrefsForUtilities = PlayerPrefs.GetInt(prefName, 1) != 0;
+ Menu.SetChecked(menuItemName, setPrefsForUtilities);
+
+ if (!setPrefsForUtilities)
+ return;
+
+ OVRPlugin.AddCustomMetadata("build_target", EditorUserBuildSettings.activeBuildTarget.ToString());
+ EnforceAndroidSettings();
+ }
+
+ static void OnUpdate()
+ {
+ if (!setPrefsForUtilities)
+ return;
+
+ EnforceBundleId();
+ EnforceVRSupport();
+ EnforceInstallLocation();
+ }
+
+ static void EnforceAndroidSettings()
+ {
+ if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
+ return;
+
+ if (PlayerSettings.defaultInterfaceOrientation != UIOrientation.LandscapeLeft)
+ {
+ Debug.Log("OVREngineConfigurationUpdater: Setting orientation to Landscape Left");
+ // Default screen orientation must be set to landscape left.
+ PlayerSettings.defaultInterfaceOrientation = UIOrientation.LandscapeLeft;
+ }
+
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+#pragma warning disable 618
+ if (!PlayerSettings.virtualRealitySupported)
+#pragma warning restore 618
+ {
+ // NOTE: This value should not affect the main window surface
+ // when Built-in VR support is enabled.
+
+ // NOTE: On Adreno Lollipop, it is an error to have antiAliasing set on the
+ // main window surface with front buffer rendering enabled. The view will
+ // render black.
+ // On Adreno KitKat, some tiling control modes will cause the view to render
+ // black.
+ if (QualitySettings.antiAliasing != 0 && QualitySettings.antiAliasing != 1)
+ {
+ Debug.Log("OVREngineConfigurationUpdater: Disabling antiAliasing");
+ QualitySettings.antiAliasing = 1;
+ }
+ }
+#endif
+
+ if (QualitySettings.vSyncCount != 0)
+ {
+ Debug.Log("OVREngineConfigurationUpdater: Setting vsyncCount to 0");
+ // We sync in the TimeWarp, so we don't want unity syncing elsewhere.
+ QualitySettings.vSyncCount = 0;
+ }
+ }
+
+ static void EnforceVRSupport()
+ {
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+#pragma warning disable 618
+ if (PlayerSettings.virtualRealitySupported)
+#pragma warning restore 618
+ return;
+
+ var mgrs = GameObject.FindObjectsOfType();
+ for (int i = 0; i < mgrs.Length; ++i)
+ {
+ if (mgrs [i].isActiveAndEnabled)
+ {
+ Debug.Log ("Enabling Unity VR support");
+#pragma warning disable 618
+ PlayerSettings.virtualRealitySupported = true;
+#pragma warning restore 618
+
+ bool oculusFound = false;
+ foreach (var device in UnityEngine.XR.XRSettings.supportedDevices)
+ oculusFound |= (device == "Oculus");
+
+ if (!oculusFound)
+ Debug.LogError("Please add Oculus to the list of supported devices to use the Utilities.");
+
+ return;
+ }
+ }
+#endif
+ }
+
+ private static void EnforceBundleId()
+ {
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+#pragma warning disable 618
+ if (!PlayerSettings.virtualRealitySupported)
+ {
+ return;
+ }
+#pragma warning restore 618
+#endif
+
+#if USING_XR_SDK || !REQUIRES_XR_SDK
+ if (PlayerSettings.applicationIdentifier == "" || PlayerSettings.applicationIdentifier == "com.Company.ProductName")
+ {
+ string defaultBundleId = "com.oculus.UnitySample";
+ Debug.LogWarning("\"" + PlayerSettings.applicationIdentifier + "\" is not a valid bundle identifier. Defaulting to \"" + defaultBundleId + "\".");
+ PlayerSettings.applicationIdentifier = defaultBundleId;
+ }
+#endif
+ }
+
+ private static void EnforceInstallLocation()
+ {
+ if (PlayerSettings.Android.preferredInstallLocation != AndroidPreferredInstallLocation.Auto)
+ PlayerSettings.Android.preferredInstallLocation = AndroidPreferredInstallLocation.Auto;
+ }
+}
+
diff --git a/Assets/Oculus/VR/Editor/OVRExpansionFileGenerator.cs b/Assets/Oculus/VR/Editor/OVRExpansionFileGenerator.cs
new file mode 100644
index 0000000..75a0eda
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRExpansionFileGenerator.cs
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.IO;
+using System.Xml;
+using UnityEngine;
+using UnityEditor;
+
+public class BuildAssetBundles : MonoBehaviour
+{
+ [MenuItem("Oculus/Tools/Build Mobile-Quest Expansion File", false, 100000)]
+ public static void BuildBundles()
+ {
+ // Create expansion file directory and call build asset bundles
+ string path = Application.dataPath + "/../Asset Bundles/";
+ if (!System.IO.Directory.Exists(path))
+ {
+ System.IO.Directory.CreateDirectory(path);
+ }
+ BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android);
+
+ // Rename asset bundle file to the proper obb string
+ if (File.Exists(path + "Asset Bundles"))
+ {
+ string expansionName = "main." + PlayerSettings.Android.bundleVersionCode + "." + PlayerSettings.applicationIdentifier + ".obb";
+ try
+ {
+ if (File.Exists(path + expansionName))
+ {
+ File.Delete(path + expansionName);
+ }
+ File.Move(path + "Asset Bundles", path + expansionName);
+ UnityEngine.Debug.Log("OBB expansion file " + expansionName + " has been successfully created at " + path);
+
+ UpdateAndroidManifest();
+ }
+ catch (Exception e)
+ {
+ UnityEngine.Debug.LogError(e.Message);
+ }
+ }
+ }
+
+ public static void UpdateAndroidManifest()
+ {
+ string manifestFolder = Application.dataPath + "/Plugins/Android";
+ try
+ {
+ // Load android manfiest file
+ XmlDocument doc = new XmlDocument();
+ doc.Load(manifestFolder + "/AndroidManifest.xml");
+
+ string androidNamepsaceURI;
+ XmlElement element = (XmlElement)doc.SelectSingleNode("/manifest");
+ if(element == null)
+ {
+ UnityEngine.Debug.LogError("Could not find manifest tag in android manifest.");
+ return;
+ }
+
+ // Get android namespace URI from the manifest
+ androidNamepsaceURI = element.GetAttribute("xmlns:android");
+ if (!string.IsNullOrEmpty(androidNamepsaceURI))
+ {
+ // Check if the android manifest already has the read external storage permission
+ XmlNodeList nodeList = doc.SelectNodes("/manifest/application/uses-permission");
+ foreach (XmlElement e in nodeList)
+ {
+ string attr = e.GetAttribute("name", androidNamepsaceURI);
+ if (attr == "android.permission.READ_EXTERNAL_STORAGE")
+ {
+ UnityEngine.Debug.Log("Android manifest already has the proper permissions.");
+ return;
+ }
+ }
+
+ element = (XmlElement)doc.SelectSingleNode("/manifest/application");
+ if (element != null)
+ {
+ // Insert read external storage permission
+ XmlElement newElement = doc.CreateElement("uses-permission");
+ newElement.SetAttribute("name", androidNamepsaceURI, "android.permission.READ_EXTERNAL_STORAGE");
+ element.AppendChild(newElement);
+
+ doc.Save(manifestFolder + "/AndroidManifest.xml");
+ UnityEngine.Debug.Log("Successfully modified android manifest with external storage permission.");
+ return;
+ }
+ }
+
+ UnityEngine.Debug.LogError("Could not find android naemspace URI in android manifest.");
+ }
+ catch (Exception e)
+ {
+ UnityEngine.Debug.LogError(e.Message);
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRGradleGeneration.cs b/Assets/Oculus/VR/Editor/OVRGradleGeneration.cs
new file mode 100644
index 0000000..e105a23
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRGradleGeneration.cs
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define BUILDSESSION
+
+#if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR)
+#define USING_XR_SDK
+#endif
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+using System.Diagnostics;
+using System.Threading;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Rendering;
+using UnityEditor.Build;
+using UnityEditor.Build.Reporting;
+#if UNITY_ANDROID
+using UnityEditor.Android;
+#endif
+
+#if USING_XR_SDK_OPENXR
+using UnityEngine.XR.OpenXR;
+using UnityEditor.XR.OpenXR.Features;
+#endif
+
+[InitializeOnLoad]
+public class OVRGradleGeneration
+ : IPreprocessBuildWithReport, IPostprocessBuildWithReport
+#if UNITY_ANDROID
+ , IPostGenerateGradleAndroidProject
+#endif
+{
+ public OVRADBTool adbTool;
+ public Process adbProcess;
+
+ public int callbackOrder { get { return 3; } }
+ static private System.DateTime buildStartTime;
+ static private System.Guid buildGuid;
+
+#if UNITY_ANDROID
+ public const string prefName = "OVRAutoIncrementVersionCode_Enabled";
+ private const string menuItemAutoIncVersion = "Oculus/Tools/Auto Increment Version Code";
+ static bool autoIncrementVersion = false;
+#endif
+
+ static OVRGradleGeneration()
+ {
+ EditorApplication.delayCall += OnDelayCall;
+ }
+
+ static void OnDelayCall()
+ {
+#if UNITY_ANDROID
+ autoIncrementVersion = PlayerPrefs.GetInt(prefName, 0) != 0;
+ Menu.SetChecked(menuItemAutoIncVersion, autoIncrementVersion);
+#endif
+ }
+
+#if UNITY_ANDROID
+ [MenuItem(menuItemAutoIncVersion)]
+ public static void ToggleUtilities()
+ {
+ autoIncrementVersion = !autoIncrementVersion;
+ Menu.SetChecked(menuItemAutoIncVersion, autoIncrementVersion);
+
+ int newValue = (autoIncrementVersion) ? 1 : 0;
+ PlayerPrefs.SetInt(prefName, newValue);
+ PlayerPrefs.Save();
+
+ UnityEngine.Debug.Log("Auto Increment Version Code: " + autoIncrementVersion);
+ }
+#endif
+
+ public void OnPreprocessBuild(BuildReport report)
+ {
+ bool useOpenXR = OVRPluginUpdater.IsOVRPluginOpenXRActivated();
+
+#if USING_XR_SDK_OPENXR
+ UnityEngine.Debug.LogWarning("The installation of Unity OpenXR Plugin is detected, which should NOT be used in production when developing Oculus apps for production. Please uninstall the package, and install the Oculus XR Plugin from the Package Manager.");
+
+ // OpenXR Plugin will remove all native plugins if they are not under the Feature folder. Include OVRPlugin to the build if OculusXRFeature is enabled.
+ var oculusXRFeature = FeatureHelpers.GetFeatureWithIdForBuildTarget(report.summary.platformGroup, Oculus.XR.OculusXRFeature.featureId);
+ if (oculusXRFeature.enabled)
+ {
+ if (!useOpenXR)
+ {
+ throw new BuildFailedException("OpenXR backend for Oculus Plugin is disabled, which is required to support Unity OpenXR Plugin. Please enable OpenXR backend for Oculus Plugin through the 'Oculus -> Tools -> OpenXR' menu.");
+ }
+
+ string ovrRootPath = OVRPluginUpdater.GetUtilitiesRootPath();
+ var importers = PluginImporter.GetAllImporters();
+ foreach (var importer in importers)
+ {
+ if (!importer.GetCompatibleWithPlatform(report.summary.platform))
+ continue;
+ string fullAssetPath = Path.Combine(Directory.GetCurrentDirectory(), importer.assetPath);
+#if UNITY_EDITOR_WIN
+ fullAssetPath = fullAssetPath.Replace("/", "\\");
+#endif
+ if (fullAssetPath.StartsWith(ovrRootPath) && fullAssetPath.Contains("OVRPlugin"))
+ {
+ UnityEngine.Debug.LogFormat("[Oculus] Native plugin included in build because of enabled OculusXRFeature: {0}", importer.assetPath);
+ importer.SetIncludeInBuildDelegate(path => true);
+ }
+ if (!fullAssetPath.StartsWith(ovrRootPath) && fullAssetPath.Contains("libopenxr_loader.so"))
+ {
+ UnityEngine.Debug.LogFormat("[Oculus] libopenxr_loader.so from other packages will be disabled because of enabled OculusXRFeature: {0}", importer.assetPath);
+ importer.SetIncludeInBuildDelegate(path => false);
+ }
+ }
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("OculusXRFeature is not enabled in OpenXR Settings. Oculus Integration scripts will not be functional.");
+ }
+#endif
+
+#if UNITY_ANDROID && !(USING_XR_SDK && UNITY_2019_3_OR_NEWER)
+ // Generate error when Vulkan is selected as the perferred graphics API, which is not currently supported in Unity XR
+ if (!PlayerSettings.GetUseDefaultGraphicsAPIs(BuildTarget.Android))
+ {
+ GraphicsDeviceType[] apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android);
+ if (apis.Length >= 1 && apis[0] == GraphicsDeviceType.Vulkan)
+ {
+ throw new BuildFailedException("The Vulkan Graphics API does not support XR in your configuration. To use Vulkan, you must use Unity 2019.3 or newer, and the XR Plugin Management.");
+ }
+ }
+#endif
+
+#if UNITY_ANDROID
+#if USING_XR_SDK
+ if (useOpenXR)
+ {
+ UnityEngine.Debug.LogWarning("Oculus Utilities Plugin with OpenXR is being used, which is under experimental status");
+
+ if (PlayerSettings.colorSpace != ColorSpace.Linear)
+ {
+ throw new BuildFailedException("Oculus Utilities Plugin with OpenXR only supports linear lighting. Please set 'Rendering/Color Space' to 'Linear' in Player Settings");
+ }
+ }
+#else
+ if (useOpenXR)
+ {
+ throw new BuildFailedException("Oculus Utilities Plugin with OpenXR only supports XR Plug-in Managmenent with Oculus XR Plugin.");
+ }
+#endif
+#endif
+
+#if UNITY_ANDROID && USING_XR_SDK && !USING_COMPATIBLE_OCULUS_XR_PLUGIN_VERSION
+ if (PlayerSettings.Android.targetArchitectures != AndroidArchitecture.ARM64)
+ throw new BuildFailedException("Your project is using an Oculus XR Plugin version with known issues. Please navigate to the Package Manager and upgrade the Oculus XR Plugin to the latest verified version. When performing the upgrade" +
+ ", you must first \"Remove\" the Oculus XR Plugin package, and then \"Install\" the package at the verified version. Be sure to remove, then install, not just upgrade.");
+#endif
+
+ buildStartTime = System.DateTime.Now;
+ buildGuid = System.Guid.NewGuid();
+
+#if BUILDSESSION
+ StreamWriter writer = new StreamWriter("build_session", false);
+ UnityEngine.Debug.LogFormat("Build Session: {0}", buildGuid.ToString());
+ writer.WriteLine(buildGuid.ToString());
+ writer.Close();
+#endif
+ }
+
+ public void OnPostGenerateGradleAndroidProject(string path)
+ {
+ UnityEngine.Debug.Log("OVRGradleGeneration triggered.");
+
+ var targetOculusPlatform = new List();
+ if (OVRDeviceSelector.isTargetDeviceQuestFamily)
+ {
+ targetOculusPlatform.Add("quest");
+ }
+ UnityEngine.Debug.LogFormat("QuestFamily = {0}: Quest = {1}, Quest2 = {2}",
+ OVRDeviceSelector.isTargetDeviceQuestFamily,
+ OVRDeviceSelector.isTargetDeviceQuest,
+ OVRDeviceSelector.isTargetDeviceQuest2);
+
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+ if (projectConfig != null && projectConfig.systemSplashScreen != null)
+ {
+ if (PlayerSettings.virtualRealitySplashScreen != null)
+ {
+ UnityEngine.Debug.LogWarning("Virtual Reality Splash Screen (in Player Settings) is active. It would be displayed after the system splash screen, before the first game frame be rendered.");
+ }
+ string splashScreenAssetPath = AssetDatabase.GetAssetPath(projectConfig.systemSplashScreen);
+ if (Path.GetExtension(splashScreenAssetPath).ToLower() != ".png")
+ {
+ throw new BuildFailedException("Invalid file format of System Splash Screen. It has to be a PNG file to be used by the Quest OS. The asset path: " + splashScreenAssetPath);
+ }
+ else
+ {
+ string sourcePath = splashScreenAssetPath;
+ string targetFolder = Path.Combine(path, "src/main/assets");
+ string targetPath = targetFolder + "/vr_splash.png";
+ UnityEngine.Debug.LogFormat("Copy splash screen asset from {0} to {1}", sourcePath, targetPath);
+ try
+ {
+ File.Copy(sourcePath, targetPath, true);
+ }
+ catch(Exception e)
+ {
+ throw new BuildFailedException(e.Message);
+ }
+ }
+ }
+
+ PatchAndroidManifest(path);
+ }
+
+ public void PatchAndroidManifest(string path)
+ {
+ string manifestFolder = Path.Combine(path, "src/main");
+ string file = manifestFolder + "/AndroidManifest.xml";
+
+ bool patchedSecurityConfig = false;
+ // If Enable NSC Config, copy XML file into gradle project
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+ if (projectConfig != null)
+ {
+ if (projectConfig.enableNSCConfig)
+ {
+ // If no custom xml security path is specified, look for the default location in the integrations package.
+ string securityConfigFile = projectConfig.securityXmlPath;
+ if (string.IsNullOrEmpty(securityConfigFile))
+ {
+ securityConfigFile = GetOculusProjectNetworkSecConfigPath();
+ }
+ else
+ {
+ Uri configUri = new Uri(Path.GetFullPath(securityConfigFile));
+ Uri projectUri = new Uri(Application.dataPath);
+ Uri relativeUri = projectUri.MakeRelativeUri(configUri);
+ securityConfigFile = relativeUri.ToString();
+ }
+
+ string xmlDirectory = Path.Combine(path, "src/main/res/xml");
+ try
+ {
+ if (!Directory.Exists(xmlDirectory))
+ {
+ Directory.CreateDirectory(xmlDirectory);
+ }
+ File.Copy(securityConfigFile, Path.Combine(xmlDirectory, "network_sec_config.xml"), true);
+ patchedSecurityConfig = true;
+ }
+ catch (Exception e)
+ {
+ UnityEngine.Debug.LogError(e.Message);
+ }
+ }
+ }
+
+ OVRManifestPreprocessor.PatchAndroidManifest(file, enableSecurity: patchedSecurityConfig);
+ }
+
+ private static string GetOculusProjectNetworkSecConfigPath()
+ {
+ var so = ScriptableObject.CreateInstance(typeof(OVRPluginUpdaterStub));
+ var script = MonoScript.FromScriptableObject(so);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ string editorDir = Directory.GetParent(assetPath).FullName;
+ string configAssetPath = Path.GetFullPath(Path.Combine(editorDir, "network_sec_config.xml"));
+ Uri configUri = new Uri(configAssetPath);
+ Uri projectUri = new Uri(Application.dataPath);
+ Uri relativeUri = projectUri.MakeRelativeUri(configUri);
+
+ return relativeUri.ToString();
+ }
+
+ public void OnPostprocessBuild(BuildReport report)
+ {
+#if UNITY_ANDROID
+ if(autoIncrementVersion)
+ {
+ if((report.summary.options & BuildOptions.Development) == 0)
+ {
+ PlayerSettings.Android.bundleVersionCode++;
+ UnityEngine.Debug.Log("Incrementing version code to " + PlayerSettings.Android.bundleVersionCode);
+ }
+ }
+
+ bool isExporting = true;
+ foreach (var step in report.steps)
+ {
+ if (step.name.Contains("Compile scripts")
+ || step.name.Contains("Building scenes")
+ || step.name.Contains("Writing asset files")
+ || step.name.Contains("Preparing APK resources")
+ || step.name.Contains("Creating Android manifest")
+ || step.name.Contains("Processing plugins")
+ || step.name.Contains("Exporting project")
+ || step.name.Contains("Building Gradle project"))
+ {
+#if BUILDSESSION
+ UnityEngine.Debug.LogFormat("build_step_" + step.name.ToLower().Replace(' ', '_') + ": {0}", step.duration.TotalSeconds.ToString());
+#endif
+ if(step.name.Contains("Building Gradle project"))
+ {
+ isExporting = false;
+ }
+ }
+ }
+#endif
+ if (!report.summary.outputPath.Contains("OVRGradleTempExport"))
+ {
+#if BUILDSESSION
+ UnityEngine.Debug.LogFormat("build_complete: {0}", (System.DateTime.Now - buildStartTime).TotalSeconds.ToString());
+#endif
+ }
+
+#if UNITY_ANDROID
+ if (!isExporting)
+ {
+ // Get the hosts path to Android SDK
+ if (adbTool == null)
+ {
+ adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath(false));
+ }
+
+ if (adbTool.isReady)
+ {
+ // Check to see if there are any ADB devices connected before continuing.
+ List devices = adbTool.GetDevices();
+ if(devices.Count == 0)
+ {
+ return;
+ }
+
+ // Clear current logs on device
+ Process adbClearProcess;
+ adbClearProcess = adbTool.RunCommandAsync(new string[] { "logcat --clear" }, null);
+
+ // Add a timeout if we cannot get a response from adb logcat --clear in time.
+ Stopwatch timeout = new Stopwatch();
+ timeout.Start();
+ while (!adbClearProcess.WaitForExit(100))
+ {
+ if (timeout.ElapsedMilliseconds > 2000)
+ {
+ adbClearProcess.Kill();
+ return;
+ }
+ }
+
+ // Check if existing ADB process is still running, kill if needed
+ if (adbProcess != null && !adbProcess.HasExited)
+ {
+ adbProcess.Kill();
+ }
+
+ // Begin thread to time upload and install
+ var thread = new Thread(delegate ()
+ {
+ TimeDeploy();
+ });
+ thread.Start();
+ }
+ }
+#endif
+ }
+
+#if UNITY_ANDROID
+ public bool WaitForProcess;
+ public bool TransferStarted;
+ public DateTime UploadStart;
+ public DateTime UploadEnd;
+ public DateTime InstallEnd;
+
+ public void TimeDeploy()
+ {
+ if (adbTool != null)
+ {
+ TransferStarted = false;
+ DataReceivedEventHandler outputRecieved = new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b"))
+ {
+ if (e.Data.Contains("free_cache"))
+ {
+ // Device recieved install command and is starting upload
+ UploadStart = System.DateTime.Now;
+ TransferStarted = true;
+ }
+ else if (e.Data.Contains("Running dexopt"))
+ {
+ // Upload has finished and Package Manager is starting install
+ UploadEnd = System.DateTime.Now;
+ }
+ else if (e.Data.Contains("dex2oat took"))
+ {
+ // Package Manager finished install
+ InstallEnd = System.DateTime.Now;
+ WaitForProcess = false;
+ }
+ else if (e.Data.Contains("W PackageManager"))
+ {
+ // Warning from Package Manager is a failure in the install process
+ WaitForProcess = false;
+ }
+ }
+ }
+ );
+
+ WaitForProcess = true;
+ adbProcess = adbTool.RunCommandAsync(new string[] { "logcat" }, outputRecieved);
+
+ Stopwatch transferTimeout = new Stopwatch();
+ transferTimeout.Start();
+ while (adbProcess != null && !adbProcess.WaitForExit(100))
+ {
+ if (!WaitForProcess)
+ {
+ adbProcess.Kill();
+ }
+
+ if (!TransferStarted && transferTimeout.ElapsedMilliseconds > 5000)
+ {
+ adbProcess.Kill();
+ }
+ }
+ }
+ }
+#endif
+}
diff --git a/Assets/Oculus/VR/Editor/OVRLayerAttributeEditor.cs b/Assets/Oculus/VR/Editor/OVRLayerAttributeEditor.cs
new file mode 100644
index 0000000..3cc21fa
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRLayerAttributeEditor.cs
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using UnityEditor;
+using UnityEngine;
+
+[CustomPropertyDrawer(typeof(OVRLayerAttribute))]
+class LayerAttributeEditor : PropertyDrawer {
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ // One line of oxygen free code.
+ property.intValue = EditorGUI.LayerField(position, label, property.intValue);
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRLint.cs b/Assets/Oculus/VR/Editor/OVRLint.cs
new file mode 100644
index 0000000..72d2d16
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRLint.cs
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if UNITY_EDITOR
+
+#if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR)
+#define USING_XR_SDK
+#endif
+
+#if UNITY_2020_1_OR_NEWER
+#define REQUIRES_XR_SDK
+#endif
+
+using UnityEngine;
+using UnityEditor;
+using System.Collections.Generic;
+using Assets.OVR.Scripts;
+using Assets.Oculus.VR;
+using Assets.Oculus.VR.Editor;
+
+///
+///Scans the project and warns about the following conditions:
+///Audio sources > 16
+///Using MSAA levels other than recommended level
+///Excessive pixel lights (>1 on Mobile; >3 on Rift)
+///Directional Lightmapping Modes (on Mobile; use Non-Directional)
+///Preload audio setting on individual audio clips
+///Decompressing audio clips on load
+///Disabling occlusion mesh
+///Android target API level set to 21 or higher
+///Unity skybox use (on by default, but if you can't see the skybox switching to Color is much faster on Mobile)
+///Lights marked as "baked" but that were not included in the last bake (and are therefore realtime).
+///Lack of static batching and dynamic batching settings activated.
+///Full screen image effects (Mobile)
+///Warn about large textures that are marked as uncompressed.
+///32-bit depth buffer (use 16)
+///Use of projectors (Mobile; can be used carefully but slow enough to warrant a warning)
+///Maybe in the future once quantified: Graphics jobs and IL2CPP on Mobile.
+///Real-time global illumination
+///No texture compression, or non-ASTC texture compression as a global setting (Mobile).
+///Using deferred rendering
+///Excessive texture resolution after LOD bias (>2k on Mobile; >4k on Rift)
+///Not using trilinear or aniso filtering and not generating mipmaps
+///Excessive render scale (>1.2)
+///Slow physics settings: Sleep Threshold < 0.005, Default Contact Offset < 0.01, Solver Iteration Count > 6
+///Shadows on when approaching the geometry or draw call limits
+///Non-static objects with colliders that are missing rigidbodies on themselves or in the parent chain.
+///No initialization of GPU/CPU throttling settings, or init to dangerous values (-1 or > 3) (Mobile)
+///Using inefficient effects: SSAO, motion blur, global fog, parallax mapping, etc.
+///Too many Overlay layers
+///Use of Standard shader or Standard Specular shader on Mobile. More generally, excessive use of multipass shaders (legacy specular, etc).
+///Multiple cameras with clears (on Mobile, potential for excessive fill cost)
+///Excessive shader passes (>2)
+///Material pointers that have been instanced in the editor (esp. if we could determine that the instance has no deltas from the original)
+///Excessive draw calls (>150 on Mobile; >2000 on Rift)
+///Excessive tris or verts (>100k on Mobile; >1M on Rift)
+///Large textures, lots of prefabs in startup scene (for bootstrap optimization)
+///GPU skinning: testing Android-only, as most Rift devs are GPU-bound.
+///
+[InitializeOnLoadAttribute]
+public class OVRLint : EditorWindow
+{
+ //TODO: The following require reflection or static analysis.
+ ///Use of ONSP reflections (Mobile)
+ ///Use of LoadLevelAsync / LoadLevelAdditiveAsync (on Mobile, this kills frame rate so dramatically it's probably better to just go to black and load synchronously)
+ ///Use of Linq in non-editor assemblies (common cause of GCs). Minor: use of foreach.
+ ///Use of Unity WWW (exceptionally high overhead for large file downloads, but acceptable for tiny gets).
+ ///Declared but empty Awake/Start/Update/OnCollisionEnter/OnCollisionExit/OnCollisionStay. Also OnCollision* star methods that declare the Collision argument but do not reference it (omitting it short-circuits the collision contact calculation).
+
+ public enum eRecordType
+ {
+ StaticCommon, // Applies to all Oculus hardware, can be identified without running the app
+ StaticAndroid, // Applies to Android-based Oculus hardware, can be identified without running the app
+ RuntimeCommon, // Applies to all Oculus hardware, can be identified only while running the app
+ RuntimeAndroid, // Applies to Android-based Oculus hardware, can be identified only while running the app
+ }
+
+ private static List mRecordsStaticCommon = new List();
+ private static List mRecordsStaticAndroid = new List();
+ private static List mRecordsRuntimeCommon = new List();
+ private static List mRecordsRuntimeAndroid = new List();
+
+ bool mShowRecordsStaticCommon = false;
+ bool mShowRecordsRuntimeCommon = false;
+#if UNITY_ANDROID
+ bool mShowRecordsStaticAndroid = false;
+ bool mShowRecordsRuntimeAndroid = false;
+#endif
+
+ private static List mRuntimeEditModeRequiredRecords = new List();
+
+ private Vector2 mScrollPosition;
+
+ GUIStyle mFixIncompleteStyle;
+ GUIStyle mFixCompleteStyle;
+
+ [MenuItem("Oculus/Tools/OVR Performance Lint Tool")]
+ static void Init()
+ {
+ // Get existing open window or if none, make a new one:
+ EditorWindow.GetWindow(typeof(OVRLint));
+ OVRPlugin.SendEvent("perf_lint", "activated");
+ OVRLint.RunCheck();
+ }
+
+ void OnEnable()
+ {
+ var incompleteStyleTex = new Texture2D(1, 1);
+ incompleteStyleTex.SetPixel(0, 0, new Color(0.4f, 0.4f, 0.4f, 0.2f));
+ incompleteStyleTex.Apply();
+ mFixIncompleteStyle = new GUIStyle();
+ mFixIncompleteStyle.normal.background = incompleteStyleTex;
+ mFixIncompleteStyle.padding = new RectOffset(8, 8, 2, 2);
+ mFixIncompleteStyle.margin = new RectOffset(4, 4, 4, 4);
+
+ var completeStyleTex = new Texture2D(1, 1);
+ completeStyleTex.SetPixel(0, 0, new Color(0, 0.7f, 0, 0.2f));
+ completeStyleTex.Apply();
+ mFixCompleteStyle = new GUIStyle();
+ mFixCompleteStyle.normal.background = completeStyleTex;
+ mFixCompleteStyle.padding = new RectOffset(8, 8, 2, 2);
+ mFixCompleteStyle.margin = new RectOffset(4, 4, 4, 4);
+ }
+
+ OVRLint()
+ {
+ EditorApplication.playModeStateChanged += HandlePlayModeState;
+ }
+
+ private static void HandlePlayModeState(PlayModeStateChange state)
+ {
+ if (state == PlayModeStateChange.EnteredEditMode)
+ {
+ ApplyEditModeRequiredFix();
+ }
+ }
+
+ private static void ApplyEditModeRequiredFix()
+ {
+ // Apply runtime fixes that require edit mode when applying fix
+ foreach (FixRecord record in mRuntimeEditModeRequiredRecords)
+ {
+ record.fixMethod(null, false, 0);
+ OVRPlugin.SendEvent("perf_lint_apply_fix", record.category);
+ record.complete = true;
+ }
+ mRuntimeEditModeRequiredRecords.Clear();
+ }
+
+ void OnGUI()
+ {
+ GUILayout.Label("OVR Performance Lint Tool", EditorStyles.boldLabel);
+ if (GUILayout.Button("Refresh", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
+ {
+ RunCheck();
+ }
+
+ mScrollPosition = EditorGUILayout.BeginScrollView(mScrollPosition);
+
+ mShowRecordsStaticCommon = EditorGUILayout.BeginFoldoutHeaderGroup(mShowRecordsStaticCommon, $"Common Issues ({mRecordsStaticCommon.Count})", EditorStyles.foldoutHeader);
+ if (mShowRecordsStaticCommon)
+ DisplayRecords(mRecordsStaticCommon);
+ EditorGUILayout.EndFoldoutHeaderGroup();
+
+#if UNITY_ANDROID
+ mShowRecordsStaticAndroid = EditorGUILayout.BeginFoldoutHeaderGroup(mShowRecordsStaticAndroid, $"Quest Issues ({mRecordsStaticAndroid.Count})", EditorStyles.foldoutHeader);
+ if (mShowRecordsStaticAndroid)
+ DisplayRecords(mRecordsStaticAndroid);
+ EditorGUILayout.EndFoldoutHeaderGroup();
+#else
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.BeginFoldoutHeaderGroup(false, "Quest Issues (disabled: Build Target is not Android)");
+ EditorGUILayout.EndFoldoutHeaderGroup();
+ EditorGUI.EndDisabledGroup();
+#endif
+
+ if (Application.isPlaying)
+ {
+ mShowRecordsRuntimeCommon = EditorGUILayout.BeginFoldoutHeaderGroup(mShowRecordsRuntimeCommon, $"Common Runtime Issues ({mRecordsRuntimeCommon.Count})", EditorStyles.foldoutHeader);
+ if (mShowRecordsRuntimeCommon)
+ DisplayRecords(mRecordsRuntimeCommon);
+ EditorGUILayout.EndFoldoutHeaderGroup();
+
+#if UNITY_ANDROID
+ mShowRecordsRuntimeAndroid = EditorGUILayout.BeginFoldoutHeaderGroup(mShowRecordsRuntimeAndroid, $"Quest Runtime Issues ({mRecordsRuntimeAndroid.Count})", EditorStyles.foldoutHeader);
+ if (mShowRecordsRuntimeAndroid)
+ DisplayRecords(mRecordsRuntimeAndroid);
+ EditorGUILayout.EndFoldoutHeaderGroup();
+#else
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.BeginFoldoutHeaderGroup(false, "Quest Runtime Issues (disabled: Build Target is not Android)");
+ EditorGUILayout.EndFoldoutHeaderGroup();
+ EditorGUI.EndDisabledGroup();
+#endif
+ }
+ else
+ {
+ EditorGUI.BeginDisabledGroup(true);
+
+ EditorGUILayout.BeginFoldoutHeaderGroup(false, "Common Runtime Issues (disabled: not in Play mode)");
+ EditorGUILayout.EndFoldoutHeaderGroup();
+
+#if UNITY_ANDROID
+ EditorGUILayout.BeginFoldoutHeaderGroup(false, "Quest Runtime Issues (disabled: not in Play mode)");
+ EditorGUILayout.EndFoldoutHeaderGroup();
+#else
+ EditorGUILayout.BeginFoldoutHeaderGroup(false, "Quest Runtime Issues (disabled: Build Target is not Android)");
+ EditorGUILayout.EndFoldoutHeaderGroup();
+#endif
+
+ EditorGUI.EndDisabledGroup();
+ }
+
+ EditorGUILayout.EndScrollView();
+ }
+
+ void DisplayRecords(List records)
+ {
+ for (int x = 0; x < records.Count; x++)
+ {
+ FixRecord record = records[x];
+
+ int siblingRecordCount = 0;
+ while (x + siblingRecordCount + 1 < records.Count && records[x + siblingRecordCount + 1].category.Equals(record.category))
+ ++siblingRecordCount;
+
+ EditorGUILayout.BeginHorizontal(record.complete ? mFixCompleteStyle : mFixIncompleteStyle); //2-column wrapper for record
+ EditorGUILayout.BeginVertical(GUILayout.Width(20)); //column 1: icon
+ EditorGUILayout.LabelField(EditorGUIUtility.IconContent(record.complete ? "d_Progress" : "console.warnicon"), GUILayout.Width(20));
+ EditorGUILayout.EndVertical(); //end column 1: icon
+ EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true)); //column 2: label, message, objects
+ GUILayout.Label(record.category, EditorStyles.boldLabel);
+
+ if (!string.IsNullOrEmpty(record.message))
+ GUILayout.Label(record.message, EditorStyles.wordWrappedLabel);
+
+ for (int i = 0; i <= siblingRecordCount; ++i)
+ {
+ var iterRecord = records[x + i];
+ if (iterRecord.targetObject)
+ EditorGUILayout.ObjectField(iterRecord.targetObject, iterRecord.targetObject.GetType(), true);
+ }
+ EditorGUILayout.EndVertical(); //end column 2: label, message, objects
+
+ if (record.buttonNames != null && record.buttonNames.Length > 0)
+ {
+ EditorGUILayout.BeginVertical(GUILayout.Width(200.0f)); //column 3: buttons
+ GUI.enabled = !record.complete;
+
+ for (int y = 0; y < record.buttonNames.Length; y++)
+ {
+ if (siblingRecordCount > 0)
+ GUILayout.Label("(Applies to all entries)", EditorStyles.miniLabel);
+
+ if (GUILayout.Button(record.buttonNames[y], EditorStyles.toolbarButton))
+ {
+ var undoObjects = new List();
+ for (int i = 0; i <= siblingRecordCount; ++i)
+ if (records[x + i].targetObject)
+ undoObjects.Add(records[x + i].targetObject);
+
+ if (undoObjects.Count > 0)
+ Undo.RecordObjects(undoObjects.ToArray(), record.category);
+
+ for (int i = 0; i <= siblingRecordCount; ++i)
+ {
+ FixRecord thisRecord = records[x + i];
+
+ if (thisRecord.editModeRequired)
+ {
+ // Add to the fix record list that requires edit mode
+ mRuntimeEditModeRequiredRecords.Add(record);
+ }
+ else
+ {
+ thisRecord.fixMethod(thisRecord.targetObject, (i == siblingRecordCount), y);
+ OVRPlugin.SendEvent("perf_lint_apply_fix", thisRecord.category);
+ thisRecord.complete = true;
+ }
+ }
+
+ if (mRuntimeEditModeRequiredRecords.Count != 0)
+ {
+ // Stop the scene to apply edit mode required records
+ EditorApplication.ExecuteMenuItem("Edit/Play");
+ }
+ }
+ }
+ GUI.enabled = true;
+ EditorGUILayout.EndVertical(); //end column 3: buttons
+ }
+
+ EditorGUILayout.EndHorizontal(); //end 3-column wrapper for record
+ x += siblingRecordCount;
+ }
+ }
+
+
+ public static int RunCheck()
+ {
+ mRecordsStaticCommon.Clear();
+ mRecordsStaticAndroid.Clear();
+ mRecordsRuntimeCommon.Clear();
+ mRecordsRuntimeAndroid.Clear();
+ mRuntimeEditModeRequiredRecords.Clear();
+
+ CheckStaticCommonIssues();
+#if UNITY_ANDROID
+ CheckStaticAndroidIssues();
+#endif
+
+ if (EditorApplication.isPlaying)
+ {
+ CheckRuntimeCommonIssues();
+#if UNITY_ANDROID
+ CheckRuntimeAndroidIssues();
+#endif
+ }
+
+ mRecordsStaticCommon.Sort(FixRecordSorter);
+ mRecordsStaticAndroid.Sort(FixRecordSorter);
+ mRecordsRuntimeCommon.Sort(FixRecordSorter);
+ mRecordsRuntimeAndroid.Sort(FixRecordSorter);
+
+ return mRecordsStaticCommon.Count + mRecordsStaticAndroid.Count + mRecordsRuntimeCommon.Count + mRecordsRuntimeAndroid.Count;
+ }
+
+ static int FixRecordSorter(FixRecord record1, FixRecord record2)
+ {
+ if (record1.sortOrder != record2.sortOrder)
+ return record1.sortOrder.CompareTo(record2.sortOrder);
+ else if (record1.category != record2.category)
+ return record1.category.CompareTo(record2.category);
+ else
+ return record1.complete.CompareTo(record2.complete);
+ }
+
+ static void AddFix(eRecordType recordType, string category, string message, FixMethodDelegate method, UnityEngine.Object target, bool editModeRequired, params string[] buttons)
+ {
+ AddFix(recordType, 0/*sortOrder*/, category, message, method, target, editModeRequired, buttons);
+ }
+
+ static void AddFix(eRecordType recordType, int sortOrder, string category, string message, FixMethodDelegate method, UnityEngine.Object target, bool editModeRequired, params string[] buttons)
+ {
+ OVRPlugin.SendEvent("perf_lint_add_fix", category);
+ var fixRecord = new FixRecord(sortOrder, category, message, method, target, editModeRequired, buttons);
+ switch (recordType)
+ {
+ case eRecordType.StaticCommon: mRecordsStaticCommon.Add(fixRecord); break;
+ case eRecordType.StaticAndroid: mRecordsStaticAndroid.Add(fixRecord); break;
+ case eRecordType.RuntimeCommon: mRecordsRuntimeCommon.Add(fixRecord); break;
+ case eRecordType.RuntimeAndroid: mRecordsRuntimeAndroid.Add(fixRecord); break;
+ }
+ }
+
+ static void CheckStaticCommonIssues()
+ {
+ if (OVRManager.IsUnityAlphaOrBetaVersion())
+ {
+ AddFix(eRecordType.StaticCommon, "General", OVRManager.UnityAlphaOrBetaVersionWarningMessage, null, null, false);
+ }
+
+#if USING_XR_SDK_OPENXR
+ AddFix(eRecordType.StaticCommon, -9999, "Unity OpenXR Plugin Detected", "Unity OpenXR Plugin should NOT be used in production when developing Oculus apps. Please uninstall the package, and install the Oculus XR Plugin from the Package Manager.\nWhen using the Oculus XR Plugin, you can enable OpenXR backend for Oculus Plugin through the 'Oculus -> Tools -> OVR Utilities Plugin' menu.", null, null, false);
+#endif
+
+ if (!OVRPluginUpdater.IsOVRPluginOpenXRActivated() || OVRPluginUpdater.IsOVRPluginUnityProvidedActivated())
+ {
+ AddFix(eRecordType.StaticCommon, -9999, "Set OVRPlugin to Oculus Utilities-provided (OpenXR backend)", "Oculus recommends using OpenXR plugin provided with its Oculus Utilities package.\nYou can enable OpenXR backend for Oculus through the 'Oculus -> Tools -> OVR Utilities Plugin' menu.", null, null, false);
+ }
+
+ if (QualitySettings.anisotropicFiltering != AnisotropicFiltering.Enable && QualitySettings.anisotropicFiltering != AnisotropicFiltering.ForceEnable)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Aniso", "Anisotropic filtering is recommended for optimal image sharpness and GPU performance.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ // Ideally this would be multi-option: offer Enable or ForceEnable.
+ QualitySettings.anisotropicFiltering = AnisotropicFiltering.Enable;
+ }, null, false, "Fix");
+ }
+
+#if UNITY_ANDROID
+ int recommendedPixelLightCount = 1;
+#else
+ int recommendedPixelLightCount = 3;
+#endif
+
+ if (QualitySettings.pixelLightCount > recommendedPixelLightCount)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Pixel Light Count", "For GPU performance set no more than " + recommendedPixelLightCount + " pixel lights in Quality Settings (currently " + QualitySettings.pixelLightCount + ").", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ QualitySettings.pixelLightCount = recommendedPixelLightCount;
+ }, null, false, "Fix");
+ }
+
+#if false
+ // Should we recommend this? Seems to be mutually exclusive w/ dynamic batching.
+ if (!PlayerSettings.graphicsJobs)
+ {
+ AddFix (eRecordType.StaticCommon, "Optimize Graphics Jobs", "For CPU performance, please use graphics jobs.", delegate(UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.graphicsJobs = true;
+ }, null, false, "Fix");
+ }
+#endif
+
+ if ((!PlayerSettings.MTRendering || !PlayerSettings.GetMobileMTRendering(BuildTargetGroup.Android)))
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize MT Rendering", "For CPU performance, please enable multithreaded rendering.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.SetMobileMTRendering(BuildTargetGroup.Standalone, true);
+ PlayerSettings.SetMobileMTRendering(BuildTargetGroup.Android, true);
+ }, null, false, "Fix");
+ }
+
+#if UNITY_ANDROID
+ if (!PlayerSettings.use32BitDisplayBuffer)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Display Buffer Format", "We recommend to enable use32BitDisplayBuffer.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.use32BitDisplayBuffer = true;
+ }, null, false, "Fix");
+ }
+#endif
+
+#if !UNITY_ANDROID && !USING_XR_SDK && !REQUIRES_XR_SDK
+#pragma warning disable 618
+ if (!PlayerSettings.VROculus.dashSupport)
+ {
+ AddFix(eRecordType.StaticCommon, "Enable Dash Integration", "We recommend to enable Dash Integration for better user experience.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.VROculus.dashSupport = true;
+ }, null, false, "Fix");
+ }
+
+ if (!PlayerSettings.VROculus.sharedDepthBuffer)
+ {
+ AddFix(eRecordType.StaticCommon, "Enable Depth Buffer Sharing", "We recommend to enable Depth Buffer Sharing for better user experience on Oculus Dash.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.VROculus.sharedDepthBuffer = true;
+ }, null, false, "Fix");
+ }
+#pragma warning restore 618
+#endif
+
+ BuildTargetGroup target = EditorUserBuildSettings.selectedBuildTargetGroup;
+ var tier = UnityEngine.Rendering.GraphicsTier.Tier1;
+ var tierSettings = UnityEditor.Rendering.EditorGraphicsSettings.GetTierSettings(target, tier);
+
+ if ((tierSettings.renderingPath == RenderingPath.DeferredShading ||
+ tierSettings.renderingPath == RenderingPath.DeferredLighting))
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Rendering Path", "For CPU performance, please do not use deferred shading.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ tierSettings.renderingPath = RenderingPath.Forward;
+ UnityEditor.Rendering.EditorGraphicsSettings.SetTierSettings(target, tier, tierSettings);
+ }, null, false, "Use Forward");
+ }
+
+ if (PlayerSettings.stereoRenderingPath == StereoRenderingPath.MultiPass)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Stereo Rendering", "For CPU performance, please enable single-pass or instanced stereo rendering.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.stereoRenderingPath = StereoRenderingPath.Instancing;
+ }, null, false, "Fix");
+ }
+
+ if (LightmapSettings.lightmaps.Length > 0 && LightmapSettings.lightmapsMode != LightmapsMode.NonDirectional)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Lightmap Directionality", "Switching from directional lightmaps to non-directional lightmaps can save a small amount of GPU time.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
+ }, null, false, "Switch to non-directional lightmaps");
+ }
+
+ if (Lightmapping.realtimeGI)
+ {
+ AddFix(eRecordType.StaticCommon, "Disable Realtime GI", "Disabling real-time global illumination can improve GPU performance.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Lightmapping.realtimeGI = false;
+ }, null, false, "Set Lightmapping.realtimeGI = false.");
+ }
+
+ var lights = GameObject.FindObjectsOfType();
+ for (int i = 0; i < lights.Length; ++i)
+ {
+ if (lights[i].type != LightType.Directional && !lights[i].bakingOutput.isBaked && IsLightBaked(lights[i]))
+ {
+ AddFix(eRecordType.StaticCommon, "Unbaked Lights", "The following lights in the scene are marked as Baked, but they don't have up to date lightmap data. Generate the lightmap data, or set it to auto-generate, in Window->Lighting->Settings.", null, lights[i], false, null);
+ }
+
+ if (lights[i].shadows != LightShadows.None && !IsLightBaked(lights[i]))
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Shadows", "For CPU performance, consider disabling shadows on realtime lights.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Light thisLight = (Light)obj;
+ thisLight.shadows = LightShadows.None;
+ }, lights[i], false, "Set \"Shadow Type\" to \"No Shadows\"");
+ }
+ }
+
+ var sources = GameObject.FindObjectsOfType();
+ if (sources.Length > 16)
+ {
+ List playingAudioSources = new List();
+ foreach (var audioSource in sources)
+ {
+ if (audioSource.isPlaying)
+ {
+ playingAudioSources.Add(audioSource);
+ }
+ }
+
+ if (playingAudioSources.Count > 16)
+ {
+ // Sort playing audio sources by priority
+ playingAudioSources.Sort(delegate (AudioSource x, AudioSource y)
+ {
+ return x.priority.CompareTo(y.priority);
+ });
+ for (int i = 16; i < playingAudioSources.Count; ++i)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Audio Source Count", "For CPU performance, please disable all but the top 16 AudioSources.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ AudioSource audioSource = (AudioSource)obj;
+ audioSource.enabled = false;
+ }, playingAudioSources[i], false, "Disable");
+ }
+ }
+ }
+
+ var clips = GameObject.FindObjectsOfType();
+ for (int i = 0; i < clips.Length; ++i)
+ {
+ if (clips[i].loadType == AudioClipLoadType.DecompressOnLoad)
+ {
+ AddFix(eRecordType.StaticCommon, "Audio Loading", "For fast loading, please don't use decompress on load for audio clips", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ AudioClip thisClip = (AudioClip)obj;
+ if (selected == 0)
+ {
+ SetAudioLoadType(thisClip, AudioClipLoadType.CompressedInMemory, last);
+ }
+ else
+ {
+ SetAudioLoadType(thisClip, AudioClipLoadType.Streaming, last);
+ }
+
+ }, clips[i], false, "Change to Compressed in Memory", "Change to Streaming");
+ }
+
+ if (clips[i].preloadAudioData)
+ {
+ AddFix(eRecordType.StaticCommon, "Audio Preload", "For fast loading, please don't preload data for audio clips.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ SetAudioPreload(clips[i], false, last);
+ }, clips[i], false, "Fix");
+ }
+ }
+
+ if (Physics.defaultContactOffset < 0.01f)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Contact Offset", "For CPU performance, please don't use default contact offset below 0.01.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Physics.defaultContactOffset = 0.01f;
+ }, null, false, "Fix");
+ }
+
+ if (Physics.sleepThreshold < 0.005f)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Sleep Threshold", "For CPU performance, please don't use sleep threshold below 0.005.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Physics.sleepThreshold = 0.005f;
+ }, null, false, "Fix");
+ }
+
+ if (Physics.defaultSolverIterations > 8)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Solver Iterations", "For CPU performance, please don't use excessive solver iteration counts.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Physics.defaultSolverIterations = 8;
+ }, null, false, "Fix");
+ }
+
+ var materials = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < materials.Length; ++i)
+ {
+ if (materials[i].shader.name.Contains("Parallax") || materials[i].IsKeywordEnabled("_PARALLAXMAP"))
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize Shading", "For GPU performance, please don't use parallax-mapped materials.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Material thisMaterial = (Material)obj;
+ if (thisMaterial.IsKeywordEnabled("_PARALLAXMAP"))
+ {
+ thisMaterial.DisableKeyword("_PARALLAXMAP");
+ }
+
+ if (thisMaterial.shader.name.Contains("Parallax"))
+ {
+ var newName = thisMaterial.shader.name.Replace("-ParallaxSpec", "-BumpSpec");
+ newName = newName.Replace("-Parallax", "-Bump");
+ var newShader = Shader.Find(newName);
+ if (newShader)
+ {
+ thisMaterial.shader = newShader;
+ }
+ else
+ {
+ Debug.LogWarning("Unable to find a replacement for shader " + materials[i].shader.name);
+ }
+ }
+ }, materials[i], false, "Fix");
+ }
+ }
+
+ var renderers = GameObject.FindObjectsOfType();
+ for (int i = 0; i < renderers.Length; ++i)
+ {
+ if (renderers[i].sharedMaterial == null)
+ {
+ AddFix(eRecordType.StaticCommon, "Instanced Materials", "Please avoid instanced materials on renderers.", null, renderers[i], false);
+ }
+ }
+
+ var overlays = GameObject.FindObjectsOfType();
+ if (overlays.Length > 4)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize VR Layer Count", "For GPU performance, please use 4 or fewer VR layers.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ for (int i = 4; i < OVROverlay.instances.Length; ++i)
+ {
+ OVROverlay.instances[i].enabled = false;
+ }
+ }, null, false, "Fix");
+ }
+ for (int i = 0; i < overlays.Length; i++)
+ {
+ if (overlays[i].useLegacyCubemapRotation)
+ {
+ AddFix(eRecordType.StaticCommon, "Fix Cubemap Orientation", "Legacy cubemap rotation will be deprecated in the future. Please fix the cubemap texture instead.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ OVROverlay thisOverlay = (OVROverlay)obj;
+ thisOverlay.useLegacyCubemapRotation = false;
+ }, overlays[i], false, "Remove Legacy Rotation");
+ }
+ }
+
+ var splashScreen = PlayerSettings.virtualRealitySplashScreen;
+ if (splashScreen != null)
+ {
+ if (splashScreen.filterMode != FilterMode.Trilinear)
+ {
+ AddFix(eRecordType.StaticCommon, "Optimize VR Splash Filtering", "For visual quality, please use trilinear filtering on your VR splash screen.", delegate (UnityEngine.Object obj, bool last, int EditorSelectedRenderState)
+ {
+ var assetPath = AssetDatabase.GetAssetPath(splashScreen);
+ var importer = (TextureImporter)TextureImporter.GetAtPath(assetPath);
+ importer.filterMode = FilterMode.Trilinear;
+ AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
+ }, null, false, "Fix");
+ }
+
+ if (splashScreen.mipmapCount <= 1)
+ {
+ AddFix(eRecordType.StaticCommon, "Generate VR Splash Mipmaps", "For visual quality, please use mipmaps with your VR splash screen.", delegate (UnityEngine.Object obj, bool last, int EditorSelectedRenderState)
+ {
+ var assetPath = AssetDatabase.GetAssetPath(splashScreen);
+ var importer = (TextureImporter)TextureImporter.GetAtPath(assetPath);
+ importer.mipmapEnabled = true;
+ AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
+ }, null, false, "Fix");
+ }
+ }
+ }
+
+ static void CheckRuntimeCommonIssues()
+ {
+ if (!OVRPlugin.occlusionMesh)
+ {
+ AddFix(eRecordType.RuntimeCommon, "Occlusion Mesh", "Enabling the occlusion mesh saves substantial GPU resources, generally with no visual impact. Enable unless you have an exceptional use case.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ OVRPlugin.occlusionMesh = true;
+ }, null, false, "Set OVRPlugin.occlusionMesh = true");
+ }
+
+ if (OVRManager.instance != null && !OVRManager.instance.useRecommendedMSAALevel)
+ {
+ AddFix(eRecordType.RuntimeCommon, "Optimize MSAA", "OVRManager can select the optimal antialiasing for the installed hardware at runtime. Recommend enabling this.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ var ovrManagers = GameObject.FindObjectsOfType();
+ foreach (var ovrManager in ovrManagers)
+ {
+ ovrManager.useRecommendedMSAALevel = true;
+ }
+ }, null, true, "Stop Play and Fix");
+ }
+
+ if (UnityEngine.XR.XRSettings.eyeTextureResolutionScale > 1.5)
+ {
+ AddFix(eRecordType.RuntimeCommon, "Optimize Render Scale", "Render scale above 1.5 is extremely expensive on the GPU, with little if any positive visual benefit.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ UnityEngine.XR.XRSettings.eyeTextureResolutionScale = 1.5f;
+ }, null, false, "Fix");
+ }
+ }
+
+#if UNITY_ANDROID
+ static void CheckStaticAndroidIssues()
+ {
+ if (OVRDeviceSelector.isTargetDeviceQuestFamily && PlayerSettings.Android.targetArchitectures != AndroidArchitecture.ARM64)
+ {
+ // Quest store is only accepting 64-bit apps as of November 25th 2019
+ AddFix(eRecordType.StaticAndroid, "Set Target Architecture to ARM64", "32-bit Quest apps are no longer being accepted on the Oculus Store.",
+ delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
+ }, null, false, "Fix");
+ }
+
+ // Check that the minSDKVersion meets requirement, 29 for Quest
+ AndroidSdkVersions recommendedAndroidMinSdkVersion = AndroidSdkVersions.AndroidApiLevel29;
+ if ((int)PlayerSettings.Android.minSdkVersion != (int)recommendedAndroidMinSdkVersion)
+ {
+ AddFix(eRecordType.StaticAndroid, "Set Min Android API Level", "Oculus Quest recommend setting minumum API level to " + (int)recommendedAndroidMinSdkVersion, delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.Android.minSdkVersion = recommendedAndroidMinSdkVersion;
+ }, null, false, "Fix");
+ }
+
+ // Check that compileSDKVersion meets minimal version 26 as required for Quest's headtracking feature. Recommend 29 to match the MinSdkVersion
+ // Unity Sets compileSDKVersion in Gradle as the value used in targetSdkVersion
+ AndroidSdkVersions requiredAndroidTargetSdkVersion = AndroidSdkVersions.AndroidApiLevel29;
+ if (OVRDeviceSelector.isTargetDeviceQuestFamily &&
+ (int)PlayerSettings.Android.targetSdkVersion != (int)requiredAndroidTargetSdkVersion)
+ {
+ AddFix(eRecordType.StaticAndroid, "Set Android Target SDK Level", "Oculus Quest apps recommend setting target API level to " +
+ (int)requiredAndroidTargetSdkVersion, delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.Android.targetSdkVersion = requiredAndroidTargetSdkVersion;
+ }, null, false, "Fix");
+ }
+
+ // Check that Android TV Compatibility is disabled
+ if (PlayerSettings.Android.androidTVCompatibility)
+ {
+ AddFix(eRecordType.StaticAndroid, "Disable Android TV Compatibility", "Apps with Android TV Compatibility enabled are not accepted by the Oculus Store.",
+ delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.Android.androidTVCompatibility = false;
+ }, null, false, "Fix");
+ }
+
+ if (!PlayerSettings.gpuSkinning)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize GPU Skinning", "If you are CPU-bound, consider using GPU skinning.",
+ delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.gpuSkinning = true;
+ }, null, false, "Fix");
+ }
+
+#if USING_XR_SDK
+ if (OVRPluginUpdater.IsOVRPluginOpenXRActivated() && PlayerSettings.colorSpace != ColorSpace.Linear)
+ {
+ AddFix(eRecordType.StaticAndroid, "Set Color Space to Linear", "Oculus Utilities Plugin with OpenXR only supports linear lighting.",
+ delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.colorSpace = ColorSpace.Linear;
+ }, null, false, "Fix");
+ }
+#endif
+
+ if (RenderSettings.skybox)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Clearing", "For GPU performance, please don't use Unity's built-in Skybox.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ RenderSettings.skybox = null;
+ }, null, false, "Clear Skybox");
+ }
+
+ var materials = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < materials.Length; ++i)
+ {
+ if (materials[i].IsKeywordEnabled("_SPECGLOSSMAP") || materials[i].IsKeywordEnabled("_METALLICGLOSSMAP"))
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Specular Material", "For GPU performance, please don't use specular shader on materials.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Material thisMaterial = (Material)obj;
+ thisMaterial.DisableKeyword("_SPECGLOSSMAP");
+ thisMaterial.DisableKeyword("_METALLICGLOSSMAP");
+ }, materials[i], false, "Fix");
+ }
+
+ if (materials[i].passCount > 2)
+ {
+ AddFix(eRecordType.StaticAndroid, "Material Passes", "Please use 2 or fewer passes in materials.", null, materials[i], false);
+ }
+ }
+
+ ScriptingImplementation backend = PlayerSettings.GetScriptingBackend(UnityEditor.BuildTargetGroup.Android);
+ if (backend != UnityEditor.ScriptingImplementation.IL2CPP)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Scripting Backend", "For CPU performance, please use IL2CPP.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ PlayerSettings.SetScriptingBackend(UnityEditor.BuildTargetGroup.Android, UnityEditor.ScriptingImplementation.IL2CPP);
+ }, null, false, "Fix");
+ }
+
+ var monoBehaviours = GameObject.FindObjectsOfType();
+ System.Type effectBaseType = System.Type.GetType("UnityStandardAssets.ImageEffects.PostEffectsBase");
+ if (effectBaseType != null)
+ {
+ for (int i = 0; i < monoBehaviours.Length; ++i)
+ {
+ if (monoBehaviours[i].GetType().IsSubclassOf(effectBaseType))
+ {
+ AddFix(eRecordType.StaticAndroid, "Image Effects", "Please don't use image effects.", null, monoBehaviours[i], false);
+ }
+ }
+ }
+
+ var textures = Resources.FindObjectsOfTypeAll();
+
+ int maxTextureSize = 1024 * (1 << QualitySettings.masterTextureLimit);
+ maxTextureSize = maxTextureSize * maxTextureSize;
+
+ for (int i = 0; i < textures.Length; ++i)
+ {
+ if (textures[i].filterMode == FilterMode.Trilinear && textures[i].mipmapCount == 1)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Texture Filtering", "For GPU performance, please generate mipmaps or disable trilinear filtering for textures.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Texture2D thisTexture = (Texture2D)obj;
+ if (selected == 0)
+ {
+ thisTexture.filterMode = FilterMode.Bilinear;
+ }
+ else
+ {
+ SetTextureUseMips(thisTexture, true, last);
+ }
+ }, textures[i], false, "Switch to Bilinear", "Generate Mipmaps");
+ }
+ }
+
+ var projectors = GameObject.FindObjectsOfType();
+ if (projectors.Length > 0)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Projectors", "For GPU performance, please don't use projectors.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Projector[] thisProjectors = GameObject.FindObjectsOfType();
+ for (int i = 0; i < thisProjectors.Length; ++i)
+ {
+ thisProjectors[i].enabled = false;
+ }
+ }, null, false, "Disable Projectors");
+ }
+
+ if (EditorUserBuildSettings.androidBuildSubtarget != MobileTextureSubtarget.ASTC)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Texture Compression", "For GPU performance, please use ASTC.", delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ EditorUserBuildSettings.androidBuildSubtarget = MobileTextureSubtarget.ASTC;
+ }, null, false, "Fix");
+ }
+
+ var cameras = GameObject.FindObjectsOfType();
+ int clearCount = 0;
+ for (int i = 0; i < cameras.Length; ++i)
+ {
+ if (cameras[i].clearFlags != CameraClearFlags.Nothing && cameras[i].clearFlags != CameraClearFlags.Depth)
+ ++clearCount;
+ }
+
+ if (clearCount > 2)
+ {
+ AddFix(eRecordType.StaticAndroid, "Camera Clears", "Please use 2 or fewer clears.", null, null, false);
+ }
+
+ for (int i = 0; i < cameras.Length; ++i)
+ {
+ if (cameras[i].forceIntoRenderTexture)
+ {
+ AddFix(eRecordType.StaticAndroid, "Optimize Mobile Rendering", "For GPU performance, please don't enable forceIntoRenderTexture on your camera, this might be a flag pollution created by post process stack you used before, \nif your post process had already been turned off, we strongly encourage you to disable forceIntoRenderTexture. If you still want to use post process for some reasons, \nyou can leave this one on, but be warned, enabling this flag will introduce huge GPU performance cost. To view your flag status, please turn on you inspector's debug mode",
+ delegate (UnityEngine.Object obj, bool last, int selected)
+ {
+ Camera thisCamera = (Camera)obj;
+ thisCamera.forceIntoRenderTexture = false;
+ }, cameras[i], false, "Disable forceIntoRenderTexture");
+ }
+ }
+ }
+
+ static void CheckRuntimeAndroidIssues()
+ {
+ if (UnityStats.usedTextureMemorySize + UnityStats.vboTotalBytes > 1000000)
+ {
+ AddFix(eRecordType.RuntimeAndroid, "Graphics Memory", "Please use less than 1GB of vertex and texture memory.", null, null, false);
+ }
+
+ // Remove the CPU/GPU level test, which won't work in Unity Editor
+
+ if (UnityStats.triangles > 100000 || UnityStats.vertices > 100000)
+ {
+ AddFix(eRecordType.RuntimeAndroid, "Triangles and Verts", "Please use less than 100000 triangles or vertices.", null, null, false);
+ }
+
+ // Warn for 50 if in non-VR mode?
+ if (UnityStats.drawCalls > 100)
+ {
+ AddFix(eRecordType.RuntimeAndroid, "Draw Calls", "Please use less than 100 draw calls.", null, null, false);
+ }
+ }
+
+#endif // UNITY_ANDROID
+
+
+ enum LightmapType { Realtime = 4, Baked = 2, Mixed = 1 };
+
+ static bool IsLightBaked(Light light)
+ {
+ return light.lightmapBakeType == LightmapBakeType.Baked;
+ }
+
+ static void SetAudioPreload(AudioClip clip, bool preload, bool refreshImmediately)
+ {
+ if (clip != null)
+ {
+ string assetPath = AssetDatabase.GetAssetPath(clip);
+ AudioImporter importer = AssetImporter.GetAtPath(assetPath) as AudioImporter;
+ if (importer != null)
+ {
+ if (preload != importer.preloadAudioData)
+ {
+ importer.preloadAudioData = preload;
+
+ AssetDatabase.ImportAsset(assetPath);
+ if (refreshImmediately)
+ {
+ AssetDatabase.Refresh();
+ }
+ }
+ }
+ }
+ }
+
+ static void SetAudioLoadType(AudioClip clip, AudioClipLoadType loadType, bool refreshImmediately)
+ {
+ if (clip != null)
+ {
+ string assetPath = AssetDatabase.GetAssetPath(clip);
+ AudioImporter importer = AssetImporter.GetAtPath(assetPath) as AudioImporter;
+ if (importer != null)
+ {
+ if (loadType != importer.defaultSampleSettings.loadType)
+ {
+ AudioImporterSampleSettings settings = importer.defaultSampleSettings;
+ settings.loadType = loadType;
+ importer.defaultSampleSettings = settings;
+
+ AssetDatabase.ImportAsset(assetPath);
+ if (refreshImmediately)
+ {
+ AssetDatabase.Refresh();
+ }
+ }
+ }
+ }
+ }
+
+ public static void SetTextureUseMips(Texture texture, bool useMips, bool refreshImmediately)
+ {
+ if (texture != null)
+ {
+ string assetPath = AssetDatabase.GetAssetPath(texture);
+ TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
+ if (tImporter != null && tImporter.mipmapEnabled != useMips)
+ {
+ tImporter.mipmapEnabled = useMips;
+
+ AssetDatabase.ImportAsset(assetPath);
+ if (refreshImmediately)
+ {
+ AssetDatabase.Refresh();
+ }
+ }
+ }
+ }
+
+ static T FindComponentInParents(GameObject obj) where T : Component
+ {
+ T component = null;
+ if (obj != null)
+ {
+ Transform parent = obj.transform.parent;
+ if (parent != null)
+ {
+ do
+ {
+ component = parent.GetComponent(typeof(T)) as T;
+ parent = parent.parent;
+ } while (parent != null && component == null);
+ }
+ }
+ return component;
+ }
+}
+
+#endif
diff --git a/Assets/Oculus/VR/Editor/OVRManifestPreprocessor.cs b/Assets/Oculus/VR/Editor/OVRManifestPreprocessor.cs
new file mode 100644
index 0000000..3b54473
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRManifestPreprocessor.cs
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+using System.Xml;
+
+public class OVRManifestPreprocessor
+{
+ [MenuItem("Oculus/Tools/Create store-compatible AndroidManifest.xml", false, 100000)]
+ public static void GenerateManifestForSubmission()
+ {
+ var so = ScriptableObject.CreateInstance(typeof(OVRPluginUpdaterStub));
+ var script = MonoScript.FromScriptableObject(so);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ string editorDir = Directory.GetParent(assetPath).FullName;
+ string srcFile = editorDir + "/AndroidManifest.OVRSubmission.xml";
+
+ if (!File.Exists(srcFile))
+ {
+ Debug.LogError("Cannot find Android manifest template for submission." +
+ " Please delete the OVR folder and reimport the Oculus Utilities.");
+ return;
+ }
+
+ string manifestFolder = Application.dataPath + "/Plugins/Android";
+
+ if (!Directory.Exists(manifestFolder))
+ Directory.CreateDirectory(manifestFolder);
+
+ string dstFile = manifestFolder + "/AndroidManifest.xml";
+
+ if (File.Exists(dstFile))
+ {
+ if (!EditorUtility.DisplayDialog("AndroidManifest.xml Already Exists!", "Would you like to replace the existing manifest with a new one? All modifications will be lost.", "Replace", "Cancel"))
+ {
+ return;
+ }
+ }
+
+ PatchAndroidManifest(srcFile, dstFile, false);
+
+ AssetDatabase.Refresh();
+ }
+
+ [MenuItem("Oculus/Tools/Update AndroidManifest.xml")]
+ public static void UpdateAndroidManifest()
+ {
+ string manifestFile = "Assets/Plugins/Android/AndroidManifest.xml";
+
+ if (!File.Exists(manifestFile))
+ {
+ Debug.LogError("Unable to update manifest because it does not exist! Run \"Create store-compatible AndroidManifest.xml\" first");
+ return;
+ }
+
+ if (!EditorUtility.DisplayDialog("Update AndroidManifest.xml", "This will overwrite all Oculus specific AndroidManifest Settings. Continue?", "Overwrite", "Cancel"))
+ {
+ return;
+ }
+
+ PatchAndroidManifest(manifestFile, skipExistingAttributes: false);
+ AssetDatabase.Refresh();
+ }
+
+ [MenuItem("Oculus/Tools/Remove AndroidManifest.xml")]
+ public static void RemoveAndroidManifest()
+ {
+ AssetDatabase.DeleteAsset("Assets/Plugins/Android/AndroidManifest.xml");
+ AssetDatabase.Refresh();
+ }
+
+ private static void AddOrRemoveTag(XmlDocument doc, string @namespace, string path, string elementName, string name, bool required, bool modifyIfFound, params string[] attrs) // name, value pairs
+ {
+ var nodes = doc.SelectNodes(path + "/" + elementName);
+ XmlElement element = null;
+ foreach (XmlElement e in nodes)
+ {
+ if (name == null || name == e.GetAttribute("name", @namespace))
+ {
+ element = e;
+ break;
+ }
+ }
+
+ if (required)
+ {
+ if (element == null)
+ {
+ var parent = doc.SelectSingleNode(path);
+ element = doc.CreateElement(elementName);
+ element.SetAttribute("name", @namespace, name);
+ parent.AppendChild(element);
+ }
+
+ for (int i = 0; i < attrs.Length; i += 2)
+ {
+ if (modifyIfFound || string.IsNullOrEmpty(element.GetAttribute(attrs[i], @namespace)))
+ {
+ if (attrs[i + 1] != null)
+ {
+ element.SetAttribute(attrs[i], @namespace, attrs[i + 1]);
+ }
+ else
+ {
+ element.RemoveAttribute(attrs[i], @namespace);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (element != null && modifyIfFound)
+ {
+ element.ParentNode.RemoveChild(element);
+ }
+ }
+ }
+
+ public static void PatchAndroidManifest(string sourceFile, string destinationFile = null, bool skipExistingAttributes = true, bool enableSecurity = false)
+ {
+ if (destinationFile == null)
+ {
+ destinationFile = sourceFile;
+ }
+
+ bool modifyIfFound = !skipExistingAttributes;
+
+ try
+ {
+ // Load android manfiest file
+ XmlDocument doc = new XmlDocument();
+ doc.Load(sourceFile);
+
+ string androidNamespaceURI;
+ XmlElement element = (XmlElement)doc.SelectSingleNode("/manifest");
+ if (element == null)
+ {
+ UnityEngine.Debug.LogError("Could not find manifest tag in android manifest.");
+ return;
+ }
+
+ // Get android namespace URI from the manifest
+ androidNamespaceURI = element.GetAttribute("xmlns:android");
+ if (string.IsNullOrEmpty(androidNamespaceURI))
+ {
+ UnityEngine.Debug.LogError("Could not find Android Namespace in manifest.");
+ return;
+ }
+
+ ApplyRequiredManfiestTags(doc, androidNamespaceURI, modifyIfFound, enableSecurity);
+ ApplyFeatureManfiestTags(doc, androidNamespaceURI, modifyIfFound);
+
+ // The following manifest entries are all handled through Oculus XR SDK Plugin
+#if !PRIORITIZE_OCULUS_XR_SETTINGS
+ ApplyOculusXRManifestTags(doc, androidNamespaceURI, modifyIfFound);
+#endif
+
+ doc.Save(destinationFile);
+ }
+ catch (System.Exception e)
+ {
+ UnityEngine.Debug.LogException(e);
+ }
+ }
+
+ private static void ApplyRequiredManfiestTags(XmlDocument doc, string androidNamespaceURI, bool modifyIfFound, bool enableSecurity)
+ {
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application/activity/intent-filter",
+ "category",
+ "android.intent.category.LEANBACK_LAUNCHER",
+ required: false,
+ modifyIfFound: true); // always remove leanback launcher
+
+ // First add or remove headtracking flag if targeting Quest
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "android.hardware.vr.headtracking",
+ OVRDeviceSelector.isTargetDeviceQuestFamily,
+ true,
+ "version", "1",
+ "required", OVRProjectConfig.GetProjectConfig().allowOptional3DofHeadTracking ? "false" : "true");
+
+ // make sure android label and icon are set in the manifest
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "application",
+ null,
+ true,
+ modifyIfFound,
+ "label", "@string/app_name",
+ "icon", "@mipmap/app_icon",
+ // Disable allowBackup in manifest and add Android NSC XML file
+ "allowBackup", projectConfig.disableBackups ? "false" : "true",
+ "networkSecurityConfig", projectConfig.enableNSCConfig && enableSecurity ? "@xml/network_sec_config" : null
+ );
+ }
+
+ private static void ApplyFeatureManfiestTags(XmlDocument doc, string androidNamespaceURI, bool modifyIfFound)
+ {
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+ OVRRuntimeSettings runtimeSettings = OVRRuntimeSettings.GetRuntimeSettings();
+
+ //============================================================================
+ // Hand Tracking
+ // If Quest is the target device, add the handtracking manifest tags if needed
+ // Mapping of project setting to manifest setting:
+ // OVRProjectConfig.HandTrackingSupport.ControllersOnly => manifest entry not present
+ // OVRProjectConfig.HandTrackingSupport.ControllersAndHands => manifest entry present and required=false
+ // OVRProjectConfig.HandTrackingSupport.HandsOnly => manifest entry present and required=true
+ OVRProjectConfig.HandTrackingSupport targetHandTrackingSupport = OVRProjectConfig.GetProjectConfig().handTrackingSupport;
+ OVRProjectConfig.HandTrackingVersion targetHandTrackingVersion = OVRProjectConfig.GetProjectConfig().handTrackingVersion;
+ bool handTrackingEntryNeeded = OVRDeviceSelector.isTargetDeviceQuestFamily && (targetHandTrackingSupport != OVRProjectConfig.HandTrackingSupport.ControllersOnly);
+ bool handTrackingVersionEntryNeeded = handTrackingEntryNeeded && (targetHandTrackingVersion != OVRProjectConfig.HandTrackingVersion.Default);
+ string handTrackingVersionValue = (targetHandTrackingVersion == OVRProjectConfig.HandTrackingVersion.V2) ? "V2.0" : "V1.0";
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "oculus.software.handtracking",
+ handTrackingEntryNeeded,
+ modifyIfFound,
+ "required", (targetHandTrackingSupport == OVRProjectConfig.HandTrackingSupport.HandsOnly) ? "true" : "false");
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-permission",
+ "com.oculus.permission.HAND_TRACKING",
+ handTrackingEntryNeeded,
+ modifyIfFound);
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application",
+ "meta-data",
+ "com.oculus.handtracking.frequency",
+ handTrackingEntryNeeded,
+ modifyIfFound,
+ "value", projectConfig.handTrackingFrequency.ToString());
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application",
+ "meta-data",
+ "com.oculus.handtracking.version",
+ handTrackingVersionEntryNeeded,
+ modifyIfFound,
+ "value", handTrackingVersionValue);
+
+ //============================================================================
+ // System Keyboard
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "oculus.software.overlay_keyboard",
+ projectConfig.requiresSystemKeyboard,
+ modifyIfFound,
+ "required", "false");
+
+ //============================================================================
+ // Experimental Features
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "com.oculus.experimental.enabled",
+ projectConfig.experimentalFeaturesEnabled,
+ modifyIfFound,
+ "required", "true");
+
+ //============================================================================
+ // Anchor
+ OVRProjectConfig.AnchorSupport targetAnchorSupport = OVRProjectConfig.GetProjectConfig().anchorSupport;
+ bool anchorEntryNeeded = OVRDeviceSelector.isTargetDeviceQuestFamily && (targetAnchorSupport == OVRProjectConfig.AnchorSupport.Enabled);
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-permission",
+ "com.oculus.permission.USE_ANCHOR_API",
+ anchorEntryNeeded,
+ modifyIfFound);
+
+ //============================================================================
+ // Passthrough
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "com.oculus.feature.PASSTHROUGH",
+ projectConfig.insightPassthroughEnabled,
+ modifyIfFound,
+ "required", "true");
+
+ //============================================================================
+ // System Splash Screen
+ if (projectConfig.systemSplashScreen != null)
+ {
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application",
+ "meta-data",
+ "com.oculus.ossplash",
+ true,
+ modifyIfFound,
+ "value", "true");
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application",
+ "meta-data",
+ "com.oculus.ossplash.colorspace",
+ true,
+ modifyIfFound,
+ "value", ColorSpaceToManifestTag(runtimeSettings.colorSpace));
+ }
+
+ //============================================================================
+ // Render Model
+ OVRProjectConfig.RenderModelSupport renderModelSupport = OVRProjectConfig.GetProjectConfig().renderModelSupport;
+ bool renderModelEntryNeeded = OVRDeviceSelector.isTargetDeviceQuestFamily && (renderModelSupport == OVRProjectConfig.RenderModelSupport.Enabled);
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "com.oculus.feature.RENDER_MODEL",
+ renderModelEntryNeeded,
+ modifyIfFound);
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-permission",
+ "com.oculus.permission.RENDER_MODEL",
+ renderModelEntryNeeded,
+ modifyIfFound);
+
+ //============================================================================
+ // Tracked Keyboard
+ // If Quest is the target device, add the tracked keyboard manifest tags if needed
+ // Mapping of project setting to manifest setting:
+ // OVRProjectConfig.TrackedKeyboardSupport.None => manifest entry not present
+ // OVRProjectConfig.TrackedKeyboardSupport.Supported => manifest entry present and required=false
+ // OVRProjectConfig.TrackedKeyboardSupport.Required => manifest entry present and required=true
+ OVRProjectConfig.TrackedKeyboardSupport targetTrackedKeyboardSupport = OVRProjectConfig.GetProjectConfig().trackedKeyboardSupport;
+ bool trackedKeyboardEntryNeeded = OVRDeviceSelector.isTargetDeviceQuestFamily && (targetTrackedKeyboardSupport != OVRProjectConfig.TrackedKeyboardSupport.None);
+
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-feature",
+ "oculus.software.trackedkeyboard",
+ trackedKeyboardEntryNeeded,
+ modifyIfFound,
+ "required", (targetTrackedKeyboardSupport == OVRProjectConfig.TrackedKeyboardSupport.Required) ? "true" : "false");
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest",
+ "uses-permission",
+ "com.oculus.permission.TRACKED_KEYBOARD",
+ trackedKeyboardEntryNeeded,
+ modifyIfFound);
+ }
+
+
+ private static void ApplyOculusXRManifestTags(XmlDocument doc, string androidNamespaceURI, bool modifyIfFound)
+ {
+ // Add focus aware tag if this app is targeting Quest Family
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application/activity",
+ "meta-data",
+ "com.oculus.vr.focusaware",
+ OVRDeviceSelector.isTargetDeviceQuestFamily,
+ modifyIfFound,
+ "value", "true");
+
+ // Add support devices manifest according to the target devices
+ if (OVRDeviceSelector.isTargetDeviceQuestFamily)
+ {
+ string targetDeviceValue = "quest";
+ if (OVRDeviceSelector.isTargetDeviceQuest && OVRDeviceSelector.isTargetDeviceQuest2)
+ {
+ targetDeviceValue = "quest|quest2";
+ }
+ else if (OVRDeviceSelector.isTargetDeviceQuest2)
+ {
+ targetDeviceValue = "quest2";
+ }
+ else if (OVRDeviceSelector.isTargetDeviceQuest)
+ {
+ targetDeviceValue = "quest";
+ }
+ else
+ {
+ Debug.LogError("Unexpected target devices");
+ }
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application",
+ "meta-data",
+ "com.oculus.supportedDevices",
+ true,
+ modifyIfFound,
+ "value", targetDeviceValue);
+ }
+
+ // Add VR intent filter tag in the manifest
+ AddOrRemoveTag(doc,
+ androidNamespaceURI,
+ "/manifest/application/activity/intent-filter",
+ "category",
+ "com.oculus.intent.category.VR",
+ required: true,
+ modifyIfFound: true);
+ }
+
+ private static string ColorSpaceToManifestTag(OVRManager.ColorSpace colorSpace)
+ {
+ switch (colorSpace)
+ {
+ case OVRManager.ColorSpace.Unmanaged:
+ return "!Unmanaged";
+ case OVRManager.ColorSpace.Rec_2020:
+ return "Rec.2020";
+ case OVRManager.ColorSpace.Rec_709:
+ return "Rec.709";
+ case OVRManager.ColorSpace.Rift_CV1:
+ return "!RiftCV1";
+ case OVRManager.ColorSpace.Rift_S:
+ return "!RiftS";
+ case OVRManager.ColorSpace.Quest:
+ return "!Quest";
+ case OVRManager.ColorSpace.P3:
+ return "P3";
+ case OVRManager.ColorSpace.Adobe_RGB:
+ return "Adobe";
+ default:
+ return "";
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRPlatformTool.cs b/Assets/Oculus/VR/Editor/OVRPlatformTool.cs
new file mode 100644
index 0000000..c8580fe
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRPlatformTool.cs
@@ -0,0 +1,1373 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Threading;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace Assets.Oculus.VR.Editor
+{
+ public class OVRPlatformTool : EditorWindow
+ {
+ public enum TargetPlatform
+ {
+ Rift = 0,
+ //OculusGoGearVR = 1, // DEPRECATED
+ Quest = 2,
+ None = 3,
+ };
+
+ public enum GamepadType
+ {
+ OFF,
+ TWINSTICK,
+ RIGHT_D_PAD,
+ LEFT_D_PAD,
+ };
+
+ const string urlPlatformUtil =
+ "https://www.oculus.com/download_app/?id=1076686279105243";
+
+ static private Process ovrPlatUtilProcess;
+ Vector2 commandMenuScroll;
+ Vector2 debugLogScroll;
+
+ static public string log;
+
+ private static bool activeProcess = false;
+ private static bool ranSelfUpdate = false;
+ private static int retryCount = 0;
+ private static string appToken;
+
+ private const float buttonPadding = 5.0f;
+
+ private bool showOptionalCommands = false;
+ private bool show2DCommands = false;
+ private bool showExpansionFileCommands = false;
+ private bool showRedistCommands = false;
+ private bool showUploadDebugSymbols = false;
+
+ private const float INDENT_SPACING = 15f;
+ private const float SINGLE_LINE_SPACING = 18f;
+ private const float ASSET_CONFIG_BACKGROUND_PADDING = 10f;
+ private const float DEFAULT_LABEL_WIDTH = 180f;
+ private const int MAX_DOWNLOAD_RETRY_COUNT = 2;
+
+ private static GUIStyle boldFoldoutStyle;
+ private static GUIStyle odhCalloutStyle;
+
+ [MenuItem("Oculus/Tools/Oculus Platform Tool")]
+ static void Init()
+ {
+ OVRPlatformTool.log = string.Empty;
+ // Get existing open window or if none, make a new one:
+ EditorWindow.GetWindow(typeof(OVRPlatformTool));
+
+ // Populate initial target platform value based on OVRDeviceSelector
+#if UNITY_ANDROID
+ if (OVRDeviceSelector.isTargetDeviceQuestFamily)
+ {
+ OVRPlatformToolSettings.TargetPlatform = TargetPlatform.Quest;
+ }
+#else
+ OVRPlatformToolSettings.TargetPlatform = TargetPlatform.Rift;
+#endif
+
+ // Load redist packages by calling list-redists in the CLI
+ string dataPath = Application.dataPath;
+ var thread = new Thread(delegate () {
+ retryCount = 0;
+ LoadRedistPackages(dataPath);
+ });
+ thread.Start();
+
+ OVRPlugin.SendEvent("oculus_platform_tool", "show_window");
+ }
+
+ void OnGUI()
+ {
+ if (boldFoldoutStyle == null)
+ {
+ boldFoldoutStyle = new GUIStyle(EditorStyles.foldout);
+ boldFoldoutStyle.fontStyle = FontStyle.Bold;
+ }
+
+ if (odhCalloutStyle == null)
+ {
+ odhCalloutStyle = new GUIStyle(EditorStyles.label);
+ odhCalloutStyle.richText = true;
+ odhCalloutStyle.wordWrap = true;
+ }
+
+ EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH;
+
+ GUILayout.Label("OVR Platform Tool", EditorStyles.boldLabel);
+ this.titleContent.text = "OVR Platform Tool";
+
+ GUIContent TargetPlatformLabel = new GUIContent("Target Oculus Platform");
+ OVRPlatformToolSettings.TargetPlatform = (TargetPlatform)MakeEnumPopup(TargetPlatformLabel, OVRPlatformToolSettings.TargetPlatform);
+
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.None)
+ return;
+
+ SetOVRProjectConfig(OVRPlatformToolSettings.TargetPlatform);
+ SetDirtyOnGUIChange();
+
+ commandMenuScroll = EditorGUILayout.BeginScrollView(commandMenuScroll, GUILayout.Height(Screen.height / 2));
+ {
+ // Add the UI Form
+ EditorGUI.BeginChangeCheck();
+ GUILayout.Space(15.0f);
+
+ // App ID
+ GUIContent AppIDLabel = new GUIContent("Oculus Application ID [?]: ",
+ "This AppID will be used when uploading the build.");
+ OVRPlatformToolSettings.AppID = MakeTextBox(AppIDLabel, OVRPlatformToolSettings.AppID);
+
+ // App Token
+ GUIContent AppTokenLabel = new GUIContent("Oculus App Token [?]: ",
+ "You can get your app token from your app's Oculus API Dashboard.");
+ appToken = MakePasswordBox(AppTokenLabel, appToken);
+
+ // Release Channel
+ GUIContent ReleaseChannelLabel = new GUIContent("Release Channel [?]: ",
+ "Specify the releaes channel of the new build, you can reassign to other channels after upload.");
+ OVRPlatformToolSettings.ReleaseChannel = MakeTextBox(ReleaseChannelLabel, OVRPlatformToolSettings.ReleaseChannel);
+
+ // Releaes Note
+ GUIContent ReleaseNoteLabel = new GUIContent("Release Note: ");
+ OVRPlatformToolSettings.ReleaseNote = MakeTextBox(ReleaseNoteLabel, OVRPlatformToolSettings.ReleaseNote);
+
+ // Platform specific fields
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Rift)
+ {
+ GUIContent BuildDirLabel = new GUIContent("Rift Build Directory [?]: ",
+ "The full path to the directory containing your Rift build files.");
+ OVRPlatformToolSettings.RiftBuildDirectory = MakeFileDirectoryField(BuildDirLabel, OVRPlatformToolSettings.RiftBuildDirectory,
+ "Choose Rifle Build Directory");
+
+ GUIContent BuildVersionLabel = new GUIContent("Build Version [?]: ",
+ "The version number shown to users.");
+ OVRPlatformToolSettings.RiftBuildVersion = MakeTextBox(BuildVersionLabel, OVRPlatformToolSettings.RiftBuildVersion);
+
+ GUIContent LaunchFileLabel = new GUIContent("Launch File Path [?]: ",
+ "The full path to the executable that launches your app.");
+ OVRPlatformToolSettings.RiftLaunchFile = MakeFileDirectoryField(LaunchFileLabel, OVRPlatformToolSettings.RiftLaunchFile,
+ "Choose Launch File", true, "exe");
+ }
+ else
+ {
+ GUIContent ApkPathLabel = new GUIContent("Build APK File Path [?]: ",
+ "The full path to the APK file.");
+ OVRPlatformToolSettings.ApkBuildPath = MakeFileDirectoryField(ApkPathLabel, OVRPlatformToolSettings.ApkBuildPath,
+ "Choose APK File", true, "apk");
+
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
+ {
+ // Quest specific fields
+ }
+ }
+
+ GUIContent uploadDebugSymbolsLabel = new GUIContent("Upload Debug Symbols [?]: ",
+ "Check this box to enable uploading of debug symbols.");
+ OVRPlatformToolSettings.UploadDebugSymbols = MakeToggleBox(uploadDebugSymbolsLabel, OVRPlatformToolSettings.UploadDebugSymbols);
+ if (OVRPlatformToolSettings.UploadDebugSymbols)
+ {
+ IncrementIndent();
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
+ {
+ if (showUploadDebugSymbols != OVRPlatformToolSettings.UploadDebugSymbols)
+ {
+ // If no debug symbol directory is set, default to the expected place based of scripting backend
+ if (string.IsNullOrEmpty(OVRPlatformToolSettings.DebugSymbolsDirectory))
+ {
+ ScriptingImplementation scriptingBackend = PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup);
+ if (scriptingBackend == ScriptingImplementation.IL2CPP)
+ {
+ OVRPlatformToolSettings.DebugSymbolsDirectory = Path.Combine(Application.dataPath, "../Temp/StagingArea/symbols");
+ }
+ else
+ {
+ OVRPlatformToolSettings.DebugSymbolsDirectory = Path.Combine(EditorApplication.applicationContentsPath,
+ "PlaybackEngines/AndroidPlayer/Variations/mono/Release/Symbols/armeabi-v7a");
+ }
+ }
+ EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
+ }
+
+ GUIContent DebugSymbolLabel = new GUIContent("Debug Symbols Directory [?]: ",
+ "The full path to the directory containing the app symbols (libil2cpp.sym.so)");
+ OVRPlatformToolSettings.DebugSymbolsDirectory = MakeFileDirectoryField(DebugSymbolLabel, OVRPlatformToolSettings.DebugSymbolsDirectory,
+ "Choose Debug Symbols Directory", false);
+
+ GUIContent uploadSymbolsOnlyLabel = new GUIContent("Upload Debug Symbols Only [?]: ",
+ "Check this box to upload debug symbols to an existing build. Will require a Build ID along with the App ID, App Token, and Symbols Directory.");
+ OVRPlatformToolSettings.UploadDebugSymbolsOnly = MakeToggleBox(uploadSymbolsOnlyLabel, OVRPlatformToolSettings.UploadDebugSymbolsOnly);
+
+ if (OVRPlatformToolSettings.UploadDebugSymbolsOnly)
+ {
+ GUIContent BuildIDLabel = new GUIContent("Build ID [?]: ",
+ "This BuildID will be used for uploading debug symbols to a specific build.");
+ OVRPlatformToolSettings.BuildID = MakeTextBox(BuildIDLabel, OVRPlatformToolSettings.BuildID);
+ }
+ }
+ DecrementIndent();
+ }
+ showUploadDebugSymbols = OVRPlatformToolSettings.UploadDebugSymbols;
+
+ showOptionalCommands = EditorGUILayout.Foldout(showOptionalCommands, "Optional Commands", boldFoldoutStyle);
+ if (showOptionalCommands)
+ {
+ IncrementIndent();
+
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Rift)
+ {
+ // Launch Parameters
+ GUIContent LaunchParamLabel = new GUIContent("Launch Parameters [?]: ",
+ "Specifies any arguments passed to the launcher.");
+ OVRPlatformToolSettings.RiftLaunchParams = MakeTextBox(LaunchParamLabel, OVRPlatformToolSettings.RiftLaunchParams);
+
+ GUIContent FirewallExceptionLabel = new GUIContent("Firewall Exception [?]: ",
+ "Specifies if a Windows Firewall exception is required.");
+ OVRPlatformToolSettings.RiftFirewallException = MakeToggleBox(FirewallExceptionLabel, OVRPlatformToolSettings.RiftFirewallException);
+
+ GUIContent GamepadEmulationLabel = new GUIContent("Gamepad Emulation [?]: ",
+ "Specifies the type of gamepad emulation used by the Oculus Touch controllers.");
+ OVRPlatformToolSettings.RiftGamepadEmulation = (GamepadType)MakeEnumPopup(GamepadEmulationLabel, OVRPlatformToolSettings.RiftGamepadEmulation);
+
+ show2DCommands = EditorGUILayout.Foldout(show2DCommands, "2D", boldFoldoutStyle);
+ if (show2DCommands)
+ {
+ IncrementIndent();
+
+ // 2D Launch File
+ GUIContent LaunchFile2DLabel = new GUIContent("2D Launch File [?]: ",
+ "The full path to the executable that launches your app in 2D mode.");
+ OVRPlatformToolSettings.Rift2DLaunchFile = MakeFileDirectoryField(LaunchFile2DLabel, OVRPlatformToolSettings.Rift2DLaunchFile,
+ "Choose 2D Launch File", true, "exe");
+
+ // 2D Launch Parameters
+ GUIContent LaunchParam2DLabel = new GUIContent("2D Launch Parameters [?]: ",
+ "Specifies any arguments passed to the launcher in 2D mode.");
+ OVRPlatformToolSettings.Rift2DLaunchParams = MakeTextBox(LaunchParam2DLabel, OVRPlatformToolSettings.Rift2DLaunchParams);
+
+ DecrementIndent();
+ }
+
+ showRedistCommands = EditorGUILayout.Foldout(showRedistCommands, "Redistributable Packages", boldFoldoutStyle);
+ if (showRedistCommands)
+ {
+ IncrementIndent();
+
+ for (int i = 0; i < OVRPlatformToolSettings.RiftRedistPackages.Count; i++)
+ {
+ GUIContent RedistPackageLabel = new GUIContent(OVRPlatformToolSettings.RiftRedistPackages[i].name);
+ OVRPlatformToolSettings.RiftRedistPackages[i].include = MakeToggleBox(RedistPackageLabel, OVRPlatformToolSettings.RiftRedistPackages[i].include);
+ }
+
+ DecrementIndent();
+ }
+
+ showExpansionFileCommands = EditorGUILayout.Foldout(showExpansionFileCommands, "Expansion Files", boldFoldoutStyle);
+ if (showExpansionFileCommands)
+ {
+ IncrementIndent();
+
+ // Language Pack Directory
+ GUIContent LanguagePackLabel = new GUIContent("Language Pack Directory [?]: ",
+ "The full path to the directory containing the language packs");
+ OVRPlatformToolSettings.LanguagePackDirectory = MakeFileDirectoryField(LanguagePackLabel, OVRPlatformToolSettings.LanguagePackDirectory,
+ "Choose Language Pack Directory");
+ }
+ }
+ else
+ {
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
+ {
+ // Quest specific optional fields
+ }
+
+ showExpansionFileCommands = EditorGUILayout.Foldout(showExpansionFileCommands, "Expansion Files", boldFoldoutStyle);
+ if (showExpansionFileCommands)
+ {
+ IncrementIndent();
+
+ // OBB File Path
+ GUIContent ObbPathLabel = new GUIContent("OBB File Path [?]: ",
+ "The full path to the OBB file.");
+ OVRPlatformToolSettings.ObbFilePath = MakeFileDirectoryField(ObbPathLabel, OVRPlatformToolSettings.ObbFilePath,
+ "Choose OBB File", true, "obb");
+ }
+ }
+
+ if (showExpansionFileCommands)
+ {
+ // Assets Directory
+ GUIContent AssetsDirLabel = new GUIContent("Assets Directory [?]: ",
+ "The full path to the directory with DLCs for this build.");
+ string assetsDirectory = MakeFileDirectoryField(AssetsDirLabel, OVRPlatformToolSettings.AssetsDirectory,
+ "Choose Assets Directory");
+
+ if (assetsDirectory != OVRPlatformToolSettings.AssetsDirectory)
+ {
+ OVRPlatformToolSettings.AssetsDirectory = assetsDirectory;
+ OVRPlatformToolSettings.AssetConfigs.Clear();
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.AssetsDirectory))
+ {
+ DirectoryInfo dirInfo = new DirectoryInfo(OVRPlatformToolSettings.AssetsDirectory);
+ FileInfo[] assetFiles = dirInfo.GetFiles();
+ foreach (FileInfo f in assetFiles)
+ {
+ OVRPlatformToolSettings.AssetConfigs.Add(new AssetConfig(f.Name));
+ }
+ }
+ EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
+ }
+
+ // Display bordered asset configuration list
+ GUILayout.Space(3f);
+ Rect rect = GUILayoutUtility.GetRect(0, GetAssetConfigElementHeight() + (ASSET_CONFIG_BACKGROUND_PADDING * 2),
+ GUILayout.ExpandWidth(true));
+ rect.x += (EditorGUI.indentLevel * INDENT_SPACING + 5);
+ rect.width -= (EditorGUI.indentLevel * INDENT_SPACING + 10);
+ DrawAssetConfigList(rect);
+
+ DecrementIndent();
+ }
+
+ EditorGUI.indentLevel--;
+ }
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
+ }
+ }
+ EditorGUILayout.EndScrollView();
+
+ GUILayout.Space(SINGLE_LINE_SPACING);
+
+ GUILayout.FlexibleSpace();
+
+ // Run OVR Lint Option
+ EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH;
+ GUIContent RunOvrLintLabel = new GUIContent("Run OVR Lint (Recommended) [?]: ",
+ "Run OVR Lint tool to ensure project is optimized for performance and meets Oculus packaging requirement for publishing.");
+ OVRPlatformToolSettings.RunOvrLint = MakeToggleBox(RunOvrLintLabel, OVRPlatformToolSettings.RunOvrLint);
+
+ // Add an Upload button
+ GUI.enabled = !activeProcess;
+ GUIContent btnTxt = new GUIContent("Upload");
+ var rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.ExpandWidth(false));
+ var btnYPos = rt.center.y;
+ rt.center = new Vector2(EditorGUIUtility.currentViewWidth / 2 - rt.width / 2 - buttonPadding, btnYPos);
+ if (GUI.Button(rt, btnTxt, GUI.skin.button))
+ {
+ OVRPlugin.SendEvent("oculus_platform_tool", "upload");
+ OVRPlatformTool.log = string.Empty;
+ OnUpload(OVRPlatformToolSettings.TargetPlatform);
+ }
+
+ // Add a cancel button
+ GUI.enabled = activeProcess;
+ btnTxt = new GUIContent("Cancel");
+ rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.ExpandWidth(false));
+ rt.center = new Vector2(EditorGUIUtility.currentViewWidth / 2 + rt.width / 2 + buttonPadding, btnYPos);
+ if (GUI.Button(rt, btnTxt, GUI.skin.button))
+ {
+ if (EditorUtility.DisplayDialog("Cancel Upload Process", "Are you sure you want to cancel the upload process?", "Yes", "No"))
+ {
+ if (ovrPlatUtilProcess != null)
+ {
+ ovrPlatUtilProcess.Kill();
+ OVRPlatformTool.log += "Upload process was canceled\n";
+ }
+ }
+ }
+
+ GUI.enabled = true;
+ GUILayout.FlexibleSpace();
+
+ GUILayout.Space(SINGLE_LINE_SPACING);
+
+ debugLogScroll = EditorGUILayout.BeginScrollView(debugLogScroll);
+ GUIStyle logBoxStyle = new GUIStyle();
+ logBoxStyle.margin.left = 5;
+ logBoxStyle.wordWrap = true;
+ logBoxStyle.normal.textColor = logBoxStyle.focused.textColor = EditorStyles.label.normal.textColor;
+ EditorGUILayout.SelectableLabel(OVRPlatformTool.log, logBoxStyle, GUILayout.Height(position.height - 30));
+ EditorGUILayout.EndScrollView();
+
+ // ODH Callout Section
+ GUILayout.BeginHorizontal(EditorStyles.helpBox);
+ var script = MonoScript.FromScriptableObject(this);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ string editorPath = Path.GetDirectoryName(assetPath);
+ string odhIconPath = Path.Combine(editorPath, "Textures\\odh_icon.png");
+ Texture ODHIcon = (Texture)EditorGUIUtility.Load(odhIconPath);
+ GUILayout.Box(ODHIcon, GUILayout.Width(60.0f), GUILayout.Height(60.0f));
+
+ GUILayout.BeginVertical();
+
+ EditorGUILayout.LabelField("Oculus Developer Hub is a desktop companion tool that can upload builds, manage apps and reduce friction in daily Quest development.",
+ odhCalloutStyle);
+ GUIContent ODHLabel = new GUIContent("Download Oculus Developer Hub");
+#if UNITY_2021_1_OR_NEWER
+ if (EditorGUILayout.LinkButton(ODHLabel))
+#else
+ if (GUILayout.Button(ODHLabel, GUILayout.ExpandWidth(false)))
+#endif
+ {
+#if UNITY_EDITOR_WIN
+ Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-win/?source=unity");
+#elif UNITY_EDITOR_OSX
+ Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-mac/?source=unity");
+#endif
+ }
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ }
+
+ private void SetOVRProjectConfig(TargetPlatform targetPlatform)
+ {
+#if UNITY_ANDROID
+ var targetDeviceTypes = new List();
+
+ if (targetPlatform == TargetPlatform.Quest && !OVRDeviceSelector.isTargetDeviceQuestFamily)
+ {
+ targetDeviceTypes.Add(OVRProjectConfig.DeviceType.Quest);
+ targetDeviceTypes.Add(OVRProjectConfig.DeviceType.Quest2);
+ }
+
+ if (targetDeviceTypes.Count != 0)
+ {
+ OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
+ projectConfig.targetDeviceTypes = targetDeviceTypes;
+ OVRProjectConfig.CommitProjectConfig(projectConfig);
+ }
+#endif
+ }
+
+ private void IncrementIndent()
+ {
+ EditorGUI.indentLevel++;
+ EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH - (EditorGUI.indentLevel * INDENT_SPACING);
+ }
+
+ private void DecrementIndent()
+ {
+ EditorGUI.indentLevel--;
+ EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH - (EditorGUI.indentLevel * INDENT_SPACING);
+ }
+
+ private void OnUpload(TargetPlatform targetPlatform)
+ {
+ OVRPlatformTool.log = string.Empty;
+ SetDirtyOnGUIChange();
+ var lintCount = 0;
+ if (OVRPlatformToolSettings.RunOvrLint)
+ {
+ lintCount = OVRLint.RunCheck();
+ }
+ if (lintCount != 0)
+ {
+ OVRPlatformTool.log += lintCount.ToString() + " lint suggestions are found. \n" +
+ "Please run Oculus\\Tools\\OVR Performance Lint Tool to review and fix lint errors. \n" +
+ "You can uncheck Run OVR Lint to bypass lint errors. \n";
+ OVRPlugin.SendEvent("oculus_platform_tool_lint", lintCount.ToString());
+ }
+ else
+ {
+ // Continue uploading process
+ ExecuteCommand(targetPlatform);
+ }
+ }
+
+ static void ExecuteCommand(TargetPlatform targetPlatform)
+ {
+ string dataPath = Application.dataPath;
+
+ // If we already have a copy of the platform util, check if it needs to be updated
+ if (!ranSelfUpdate && File.Exists(dataPath + "/Oculus/VR/Editor/Tools/ovr-platform-util.exe"))
+ {
+ ranSelfUpdate = true;
+ activeProcess = true;
+ var updateThread = new Thread(delegate () {
+ retryCount = 0;
+ CheckForUpdate(dataPath);
+ });
+ updateThread.Start();
+ }
+
+ string uploadCommand;
+ if (genUploadCommand(targetPlatform, out uploadCommand))
+ {
+ var thread = new Thread(delegate ()
+ {
+ // Wait for update process to finish before starting upload process
+ while (activeProcess)
+ {
+ Thread.Sleep(100);
+ }
+ retryCount = 0;
+ Command(targetPlatform, dataPath, uploadCommand);
+ });
+ thread.Start();
+ }
+ else
+ {
+ UnityEngine.Debug.LogError("Failed to generated upload command.");
+ }
+ }
+
+ private static string CheckForPlatformUtil(string dataPath)
+ {
+ string toolDataPath = dataPath + "/Oculus/VR/Editor/Tools";
+ if (!Directory.Exists(toolDataPath))
+ {
+ Directory.CreateDirectory(toolDataPath);
+ }
+
+ string platformUtil = toolDataPath + "/ovr-platform-util.exe";
+ if (!System.IO.File.Exists(platformUtil))
+ {
+ OVRPlugin.SendEvent("oculus_platform_tool", "provision_util");
+ EditorCoroutine downloadCoroutine = EditorCoroutine.Start(ProvisionPlatformUtil(platformUtil));
+ while (!downloadCoroutine.GetCompleted()) { }
+ }
+
+ return platformUtil;
+ }
+
+ private static void InitializePlatformUtilProcess(string path, string args)
+ {
+ ovrPlatUtilProcess = new Process();
+ var processInfo = new ProcessStartInfo(path, args);
+
+ processInfo.CreateNoWindow = true;
+ processInfo.UseShellExecute = false;
+ processInfo.RedirectStandardError = true;
+ processInfo.RedirectStandardOutput = true;
+
+ ovrPlatUtilProcess.StartInfo = processInfo;
+ ovrPlatUtilProcess.EnableRaisingEvents = true;
+ }
+
+ static void CheckForUpdate(string dataPath)
+ {
+ string platformUtilPath = CheckForPlatformUtil(dataPath);
+ InitializePlatformUtilProcess(platformUtilPath, "self-update");
+
+ OVRPlatformTool.log += "Checking for update...\n";
+
+ ovrPlatUtilProcess.Exited += new EventHandler(
+ (s, e) =>
+ {
+ if (File.Exists(dataPath + ".ovr-platform-util.exe"))
+ {
+ OVRPlatformTool.log += "Cleaning up...\n";
+ while (File.Exists(dataPath + ".ovr-platform-util.exe")) { }
+ OVRPlatformTool.log += "Finished updating platform utility.\n";
+ }
+ activeProcess = false;
+ }
+ );
+
+ ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b"))
+ {
+ OVRPlatformTool.log += e.Data + "\n";
+ }
+ }
+ );
+
+ try
+ {
+ ovrPlatUtilProcess.Start();
+ ovrPlatUtilProcess.BeginOutputReadLine();
+ }
+ catch
+ {
+ if (ThrowPlatformUtilStartupError(platformUtilPath))
+ {
+ CheckForUpdate(dataPath);
+ }
+ }
+ }
+
+ static void LoadRedistPackages(string dataPath)
+ {
+ // Check / Download the platform util and call list-redists on it
+ activeProcess = true;
+ string platformUtilPath = CheckForPlatformUtil(dataPath);
+ InitializePlatformUtilProcess(platformUtilPath, "list-redists");
+
+ OVRPlatformTool.log += "Loading redistributable packages...\n";
+
+ List redistPacks = new List();
+
+ ovrPlatUtilProcess.Exited += new EventHandler(
+ (s, e) =>
+ {
+ activeProcess = false;
+ }
+ );
+
+ ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b") && !e.Data.Contains("ID"))
+ {
+ // Get the name / ID pair from the CLI and create a redist package instance
+ string[] terms = e.Data.Split('|');
+ if (terms.Length == 2)
+ {
+ RedistPackage redistPack = new RedistPackage(terms[1], terms[0]);
+ redistPacks.Add(redistPack);
+ }
+ }
+ }
+ );
+
+ try
+ {
+ ovrPlatUtilProcess.Start();
+ ovrPlatUtilProcess.BeginOutputReadLine();
+
+ ovrPlatUtilProcess.WaitForExit();
+
+ if (redistPacks.Count != OVRPlatformToolSettings.RiftRedistPackages.Count)
+ {
+ OVRPlatformTool.log += "Successfully updated redistributable packages.\n";
+ OVRPlatformToolSettings.RiftRedistPackages = redistPacks;
+ }
+ else
+ {
+ OVRPlatformTool.log += "Redistributable packages up to date.\n";
+ }
+ }
+ catch
+ {
+ if (ThrowPlatformUtilStartupError(platformUtilPath))
+ {
+ LoadRedistPackages(dataPath);
+ }
+ }
+ }
+
+ static void Command(TargetPlatform targetPlatform, string dataPath, string uploadCommand)
+ {
+ string platformUtilPath = CheckForPlatformUtil(dataPath);
+
+ activeProcess = true;
+ InitializePlatformUtilProcess(platformUtilPath, uploadCommand);
+
+ ovrPlatUtilProcess.Exited += new EventHandler(
+ (s, e) =>
+ {
+ activeProcess = false;
+ }
+ );
+
+ ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ if (e.Data != null && e.Data.Length != 0)
+ {
+ int index = e.Data.IndexOf("\u001b");
+ if (index >= 0)
+ {
+ OVRPlatformTool.log += e.Data.Substring(0, index) + "\n";
+ }
+ else
+ {
+ OVRPlatformTool.log += e.Data + "\n";
+ }
+ }
+ }
+ );
+ ovrPlatUtilProcess.ErrorDataReceived += new DataReceivedEventHandler(
+ (s, e) =>
+ {
+ OVRPlatformTool.log += e.Data + "\n";
+ }
+ );
+
+ try
+ {
+ ovrPlatUtilProcess.Start();
+ ovrPlatUtilProcess.BeginOutputReadLine();
+ ovrPlatUtilProcess.BeginErrorReadLine();
+ }
+ catch
+ {
+ if (ThrowPlatformUtilStartupError(platformUtilPath))
+ {
+ Command(targetPlatform, dataPath, uploadCommand);
+ }
+ }
+ }
+
+ private static bool genUploadCommand(TargetPlatform targetPlatform, out string command)
+ {
+ bool success = true;
+ command = "";
+
+ if (OVRPlatformToolSettings.UploadDebugSymbols && OVRPlatformToolSettings.UploadDebugSymbolsOnly)
+ {
+ return genDebugSymbolsCommand(out command);
+ }
+ else
+ {
+ switch (targetPlatform)
+ {
+ case TargetPlatform.Rift:
+ command = "upload-rift-build";
+ break;
+ case TargetPlatform.Quest:
+ command = "upload-quest-build";
+ break;
+ default:
+ OVRPlatformTool.log += "ERROR: Invalid target platform selected";
+ success = false;
+ break;
+ }
+ }
+
+ // Add App ID
+ ValidateTextField(IDFieldValidator, OVRPlatformToolSettings.AppID, "App ID", ref success);
+ command += " --app-id \"" + OVRPlatformToolSettings.AppID + "\"";
+
+ // Add App Token
+ ValidateTextField(GenericFieldValidator, appToken, "App Token", ref success);
+ command += " --app-secret \"" + appToken + "\"";
+
+ // Add Platform specific fields
+ if (targetPlatform == TargetPlatform.Rift)
+ {
+ // Add Rift Build Directory
+ ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.RiftBuildDirectory, "Rift Build Directory", ref success);
+ command += " --build-dir \"" + OVRPlatformToolSettings.RiftBuildDirectory + "\"";
+
+ // Add Rift Launch File
+ ValidateTextField(FileValidator, OVRPlatformToolSettings.RiftLaunchFile, "Rift Launch File Path", ref success);
+ command += " --launch-file \"" + OVRPlatformToolSettings.RiftLaunchFile + "\"";
+
+ // Add Rift Build Version
+ ValidateTextField(GenericFieldValidator, OVRPlatformToolSettings.RiftBuildVersion, "Build Version", ref success);
+ command += " --version \"" + OVRPlatformToolSettings.RiftBuildVersion + "\"";
+
+ // Add Rift Launch Parameters
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.RiftLaunchParams))
+ {
+ ValidateTextField(LaunchParameterValidator, OVRPlatformToolSettings.RiftLaunchParams, "Launch Parameters", ref success);
+ command += " --launch_params \"" + OVRPlatformToolSettings.RiftLaunchParams + "\"";
+ }
+
+ // Add 2D Launch File
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.Rift2DLaunchFile))
+ {
+ ValidateTextField(FileValidator, OVRPlatformToolSettings.Rift2DLaunchFile, "2D Launch File", ref success);
+ command += " --launch_file_2d \"" + OVRPlatformToolSettings.Rift2DLaunchFile + "\"";
+
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.Rift2DLaunchParams))
+ {
+ ValidateTextField(LaunchParameterValidator, OVRPlatformToolSettings.Rift2DLaunchParams, "2D Launch Parameters", ref success);
+ command += " --launch_params_2d \"" + OVRPlatformToolSettings.Rift2DLaunchParams + "\"";
+ }
+ }
+
+ // Add Firewall Exception
+ if (OVRPlatformToolSettings.RiftFirewallException)
+ {
+ command += " --firewall_exceptions true";
+ }
+
+ // Add Redistributable Packages
+ List redistCommandIds = new List();
+ for (int i = 0; i < OVRPlatformToolSettings.RiftRedistPackages.Count; i++)
+ {
+ if (OVRPlatformToolSettings.RiftRedistPackages[i].include)
+ {
+ redistCommandIds.Add(OVRPlatformToolSettings.RiftRedistPackages[i].id);
+ }
+ }
+ if (redistCommandIds.Count > 0)
+ {
+ command += " --redistributables \"" + string.Join(",", redistCommandIds.ToArray()) + "\"";
+ }
+
+ // Add Gamepad Emulation
+ if (OVRPlatformToolSettings.RiftGamepadEmulation > GamepadType.OFF &&
+ OVRPlatformToolSettings.RiftGamepadEmulation <= GamepadType.LEFT_D_PAD)
+ {
+ command += " --gamepad-emulation ";
+ switch (OVRPlatformToolSettings.RiftGamepadEmulation)
+ {
+ case GamepadType.TWINSTICK: command += "TWINSTICK"; break;
+ case GamepadType.RIGHT_D_PAD: command += "RIGHT_D_PAD"; break;
+ case GamepadType.LEFT_D_PAD: command += "LEFT_D_PAD"; break;
+ default: command += "OFF"; break;
+ }
+ }
+
+ // Add Rift Language Pack Directory
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.LanguagePackDirectory))
+ {
+ ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.LanguagePackDirectory, "Language Pack Directory", ref success);
+ command += " --language_packs_dir \"" + OVRPlatformToolSettings.LanguagePackDirectory + "\"";
+ }
+ }
+ else
+ {
+ // Add APK Build Path
+ ValidateTextField(FileValidator, OVRPlatformToolSettings.ApkBuildPath, "APK Build Path", ref success);
+ command += " --apk \"" + OVRPlatformToolSettings.ApkBuildPath + "\"";
+
+ // Add OBB File Path
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.ObbFilePath))
+ {
+ ValidateTextField(FileValidator, OVRPlatformToolSettings.ObbFilePath, "OBB File Path", ref success);
+ command += " --obb \"" + OVRPlatformToolSettings.ObbFilePath + "\"";
+ }
+
+ // Add Debug Symbols Path
+ if (OVRPlatformToolSettings.UploadDebugSymbols)
+ {
+ ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.DebugSymbolsDirectory, "Debug Symbols Directory", ref success);
+ command += " --debug-symbols-dir \"" + OVRPlatformToolSettings.DebugSymbolsDirectory + "\"";
+ command += " --debug-symbols-pattern \"*.sym.so\"";
+ }
+
+ if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
+ {
+ // Quest specific commands
+ }
+ }
+
+ // Add Assets Directory
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.AssetsDirectory))
+ {
+ ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.AssetsDirectory, "Assets Directory", ref success);
+ command += " --assets-dir \"" + OVRPlatformToolSettings.AssetsDirectory + "\"";
+
+ // Add Asset Configurations
+ if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
+ {
+ List assetConfigs = new List();
+ for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
+ {
+ List configParameters = new List();
+ AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
+
+ if (config.required)
+ {
+ configParameters.Add("\\\"required\\\":true");
+ }
+ if (config.type > AssetConfig.AssetType.DEFAULT)
+ {
+ string typeCommand = "\\\"type\\\":";
+ switch (config.type)
+ {
+ case AssetConfig.AssetType.LANGUAGE_PACK:
+ configParameters.Add(typeCommand + "\\\"LANGUAGE_PACK\\\"");
+ break;
+ case AssetConfig.AssetType.STORE:
+ configParameters.Add(typeCommand + "\\\"STORE\\\"");
+ break;
+ default:
+ configParameters.Add(typeCommand + "\\\"DEFAULT\\\"");
+ break;
+ }
+ }
+ if (!string.IsNullOrEmpty(config.sku))
+ {
+ configParameters.Add("\\\"sku\\\":\\\"" + config.sku + "\\\"");
+ }
+
+ if (configParameters.Count > 0)
+ {
+ string configString = "\\\"" + config.name + "\\\":{" + string.Join(",", configParameters.ToArray()) + "}";
+ assetConfigs.Add(configString);
+ }
+ }
+
+ if (assetConfigs.Count > 0)
+ {
+ command += " --asset_files_config {" + string.Join(",", assetConfigs.ToArray()) + "}";
+ }
+ }
+ }
+
+ // Add Release Channel
+ ValidateTextField(GenericFieldValidator, OVRPlatformToolSettings.ReleaseChannel, "Release Channel", ref success);
+ command += " --channel \"" + OVRPlatformToolSettings.ReleaseChannel + "\"";
+
+ // Add Notes
+ if (!string.IsNullOrEmpty(OVRPlatformToolSettings.ReleaseNote))
+ {
+ string sanatizedReleaseNote = OVRPlatformToolSettings.ReleaseNote;
+ sanatizedReleaseNote = sanatizedReleaseNote.Replace("\"", "\"\"");
+ command += " --notes \"" + sanatizedReleaseNote + "\"";
+ }
+
+ return success;
+ }
+
+ private static bool genDebugSymbolsCommand(out string command)
+ {
+ bool success = true;
+ command = "upload-debug-symbols";
+
+ // Add Build ID
+ ValidateTextField(IDFieldValidator, OVRPlatformToolSettings.BuildID, "Build ID", ref success);
+ command += " --parent \"" + OVRPlatformToolSettings.BuildID + "\"";
+
+ // Add App ID
+ ValidateTextField(IDFieldValidator, OVRPlatformToolSettings.AppID, "App ID", ref success);
+ command += " --app-id \"" + OVRPlatformToolSettings.AppID + "\"";
+
+ // Add App Token
+ ValidateTextField(GenericFieldValidator, appToken, "App Token", ref success);
+ command += " --app-secret \"" + appToken + "\"";
+
+ ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.DebugSymbolsDirectory, "Debug Symbols Directory", ref success);
+ command += " --debug-symbols-dir \"" + OVRPlatformToolSettings.DebugSymbolsDirectory + "\"";
+ command += " --debug-symbols-pattern \"*.sym.so\"";
+
+ return success;
+ }
+
+ // Private delegate for text field validation functions
+ private delegate TSuccess FieldValidatorDelegate(TText text, ref TError error);
+
+ // Validate the text using a given field validator function. An error message will be printed if validation fails. Success will ONLY be modified to false if validation fails.
+ static void ValidateTextField(FieldValidatorDelegate fieldValidator, string fieldText, string fieldName, ref bool success)
+ {
+ string error = "";
+ if (!fieldValidator(fieldText, ref error))
+ {
+ OVRPlatformTool.log += "ERROR: Please verify that the " + fieldName + " is correct. ";
+ OVRPlatformTool.log += string.IsNullOrEmpty(error) ? "\n" : error + "\n";
+ success = false;
+ }
+ }
+
+ // Checks if the text is null or empty
+ static bool GenericFieldValidator(string fieldText, ref string error)
+ {
+ if (string.IsNullOrEmpty(fieldText))
+ {
+ error = "The field is empty.";
+ return false;
+ }
+ return true;
+ }
+
+ // Checks if the App ID contains only numbers
+ static bool IDFieldValidator(string fieldText, ref string error)
+ {
+ if (string.IsNullOrEmpty(fieldText))
+ {
+ error = "The field is empty.";
+ return false;
+ }
+ else if (!Regex.IsMatch(fieldText, "^[0-9]+$"))
+ {
+ error = "The field contains invalid characters.";
+ return false;
+ }
+ return true;
+ }
+
+ // Check that the directory exists
+ static bool DirectoryValidator(string path, ref string error)
+ {
+ if (!Directory.Exists(path))
+ {
+ error = "The directory does not exist.";
+ return false;
+ }
+ return true;
+ }
+
+ // Check that the file exists
+ static bool FileValidator(string path, ref string error)
+ {
+ if (!File.Exists(path))
+ {
+ error = "The file does not exist.";
+ return false;
+ }
+ return true;
+ }
+
+ // Check if the launch parameter string contains illegal characters
+ static bool LaunchParameterValidator(string fieldText, ref string error)
+ {
+ if (fieldText.Contains("\""))
+ {
+ error = "The field contains illegal characters.";
+ return false;
+ }
+ return true;
+ }
+
+ void OnInspectorUpdate()
+ {
+ Repaint();
+ }
+
+ private static bool ThrowPlatformUtilStartupError(string utilPath)
+ {
+ if (retryCount < MAX_DOWNLOAD_RETRY_COUNT)
+ {
+ retryCount++;
+ OVRPlatformTool.log += "There was a problem starting Oculus Platform Util. Restarting provision process...\n";
+ File.Delete(utilPath);
+ return true;
+ }
+ else
+ {
+ OVRPlatformTool.log += "OVR Platform Tool had a problem with downloading a valid executable after several trys. Please reopen the tool to try again.\n";
+ return false;
+ }
+ }
+
+ private string MakeTextBox(GUIContent label, string variable)
+ {
+ string result = string.Empty;
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
+ result = EditorGUILayout.TextField(variable);
+ EditorGUILayout.EndHorizontal();
+
+ return result;
+ }
+
+ private string MakePasswordBox(GUIContent label, string variable)
+ {
+ string result = string.Empty;
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
+ result = EditorGUILayout.PasswordField(variable);
+ EditorGUILayout.EndHorizontal();
+
+ return result;
+ }
+
+ private bool MakeToggleBox(GUIContent label, bool variable)
+ {
+ bool result = false;
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
+ result = EditorGUILayout.Toggle(variable);
+ EditorGUILayout.EndHorizontal();
+
+ return result;
+ }
+
+ private Enum MakeEnumPopup(GUIContent label, Enum selected)
+ {
+ Enum result;
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
+ result = EditorGUILayout.EnumPopup(selected);
+ EditorGUILayout.EndHorizontal();
+
+ return result;
+ }
+
+ private string MakeFileDirectoryField(GUIContent label, string variable, string title, bool isFile = false, string extension = "")
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
+
+ Rect rect = GUILayoutUtility.GetRect(0, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(true));
+ EditorGUI.SelectableLabel(rect, variable);
+
+ // Create X button if there is a valid path in the field
+ string result = variable;
+ if (!string.IsNullOrEmpty(variable))
+ {
+ Color defaultColor = GUI.backgroundColor;
+ GUI.backgroundColor = new Color(.9f, 0.5f, 0.5f);
+ rect = GUILayoutUtility.GetRect(SINGLE_LINE_SPACING, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(false));
+ if (GUI.Button(rect, "X"))
+ {
+ result = string.Empty;
+ }
+ GUI.backgroundColor = defaultColor;
+ }
+
+ // Create the Choose button to initiate the file explorer
+ rect = GUILayoutUtility.GetRect(75f, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(false));
+ if (GUI.Button(rect, "Choose ..."))
+ {
+ string newPath = string.Empty;
+ string path = string.IsNullOrEmpty(variable) ? Application.dataPath : variable;
+ if (isFile)
+ {
+ newPath = EditorUtility.OpenFilePanel(title, path, extension);
+ }
+ else
+ {
+ newPath = EditorUtility.OpenFolderPanel(title, path, string.Empty);
+ }
+ if (newPath.Length > 0)
+ {
+ result = newPath;
+ }
+ }
+ GUILayout.Space(5f);
+ EditorGUILayout.EndHorizontal();
+
+ // If the path has changed, deselect the selectable field so that it can update.
+ if (result != variable)
+ {
+ GUIUtility.hotControl = 0;
+ GUIUtility.keyboardControl = 0;
+ }
+
+ return result;
+ }
+
+ private static void SetDirtyOnGUIChange()
+ {
+ if (GUI.changed)
+ {
+ EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
+ GUI.changed = false;
+ }
+ }
+
+ private static IEnumerator ProvisionPlatformUtil(string dataPath)
+ {
+ UnityEngine.Debug.Log("Started Provisioning Oculus Platform Util");
+ var webRequest = new UnityWebRequest(urlPlatformUtil, UnityWebRequest.kHttpVerbGET);
+ string path = dataPath;
+ webRequest.downloadHandler = new DownloadHandlerFile(path);
+ // WWW request timeout in seconds
+ webRequest.timeout = 60;
+ UnityWebRequestAsyncOperation webOp = webRequest.SendWebRequest();
+ while (!webOp.isDone) { }
+#if UNITY_2020_1_OR_NEWER
+ if (webRequest.result == UnityWebRequest.Result.ConnectionError ||
+ webRequest.result == UnityWebRequest.Result.ProtocolError)
+#else
+ if (webRequest.isNetworkError || webRequest.isHttpError)
+#endif
+ {
+ var networkErrorMsg = "Failed to provision Oculus Platform Util\n";
+ UnityEngine.Debug.LogError(networkErrorMsg);
+ OVRPlatformTool.log += networkErrorMsg;
+ }
+ else
+ {
+ OVRPlatformTool.log += "Completed Provisioning Oculus Platform Util\n";
+ }
+ SetDirtyOnGUIChange();
+ yield return webOp;
+ }
+
+ private static void DrawAssetConfigList(Rect rect)
+ {
+ DrawAssetConfigHeader(rect);
+ DrawAssetConfigBackground(rect);
+ DrawAssetConfigElement(rect);
+ }
+
+ private static void DrawAssetConfigElement(Rect rect)
+ {
+ Rect elementRect = new Rect(rect.x, rect.y + SINGLE_LINE_SPACING + ASSET_CONFIG_BACKGROUND_PADDING / 2,
+ rect.width, SINGLE_LINE_SPACING);
+ if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
+ {
+ for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
+ {
+ AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
+ GUIContent fieldLabel;
+
+ config.SetFoldoutState(EditorGUI.Foldout(elementRect, config.GetFoldoutState(), config.name, boldFoldoutStyle));
+ if (config.GetFoldoutState())
+ {
+ Rect attributeRect = new Rect(elementRect.x + INDENT_SPACING, elementRect.y + SINGLE_LINE_SPACING,
+ elementRect.width - INDENT_SPACING - 3f, SINGLE_LINE_SPACING);
+ // Extra asset config params are disabled for now until CLI supports them
+#if !DISABLE_EXTRA_ASSET_CONFIG
+ fieldLabel = new GUIContent("Required Asset [?]", "Whether or not this asset file is required for the app to run.");
+ config.required = EditorGUI.Toggle(attributeRect, fieldLabel, config.required);
+
+ attributeRect.y += SINGLE_LINE_SPACING;
+ fieldLabel = new GUIContent("Asset Type [?]", "The asset file type.");
+ config.type = (AssetConfig.AssetType)EditorGUI.EnumPopup(attributeRect, fieldLabel, config.type);
+
+ attributeRect.y += SINGLE_LINE_SPACING;
+#endif
+ fieldLabel = new GUIContent("Asset SKU [?]", "The Oculus store SKU for this asset file.");
+ config.sku = EditorGUI.TextField(attributeRect, fieldLabel, config.sku);
+
+ elementRect.y = attributeRect.y;
+ }
+ elementRect.y += SINGLE_LINE_SPACING;
+ }
+ }
+ else
+ {
+ EditorGUI.LabelField(elementRect, "No asset files found. Choose a valid assets directory.");
+ }
+ }
+
+ private static float GetAssetConfigElementHeight()
+ {
+ float totalHeight = 0f;
+ if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
+ {
+ for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
+ {
+ AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
+#if !DISABLE_EXTRA_ASSET_CONFIG
+ totalHeight += config.GetFoldoutState() ? SINGLE_LINE_SPACING * 4 : SINGLE_LINE_SPACING;
+#else
+ totalHeight += config.GetFoldoutState() ? SINGLE_LINE_SPACING * 2 : SINGLE_LINE_SPACING;
+#endif
+ }
+ }
+ else
+ {
+ totalHeight += SINGLE_LINE_SPACING;
+ }
+ return totalHeight + ASSET_CONFIG_BACKGROUND_PADDING;
+ }
+
+ private static void DrawAssetConfigHeader(Rect rect)
+ {
+ Rect headerRect = new Rect(rect.x, rect.y, rect.width, SINGLE_LINE_SPACING);
+ EditorGUI.DrawRect(headerRect, EditorGUIUtility.isProSkin ? new Color(0.37f, 0.37f, 0.37f) : new Color(0.55f, 0.55f, 0.55f));
+ EditorGUI.LabelField(rect, "Asset File Configuration");
+ }
+
+ private static void DrawAssetConfigBackground(Rect rect)
+ {
+ Rect backgroundRect = new Rect(rect.x, rect.y + SINGLE_LINE_SPACING, rect.width, GetAssetConfigElementHeight());
+ EditorGUI.DrawRect(backgroundRect, EditorGUIUtility.isProSkin ? new Color(0.3f, 0.3f, 0.3f) : new Color(0.63f, 0.63f, 0.63f));
+ }
+
+ class GUIHelper
+ {
+ public delegate void Worker();
+
+ static void InOut(Worker begin, Worker body, Worker end)
+ {
+ try
+ {
+ begin();
+ body();
+ }
+ finally
+ {
+ end();
+ }
+ }
+
+ public static void HInset(int pixels, Worker worker)
+ {
+ InOut(
+ () => {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(pixels);
+ GUILayout.BeginVertical();
+ },
+ worker,
+ () => {
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ }
+ );
+ }
+
+ public delegate T ControlWorker();
+ public static T MakeControlWithLabel(GUIContent label, ControlWorker worker)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label);
+
+ var result = worker();
+
+ EditorGUILayout.EndHorizontal();
+ return result;
+ }
+ }
+
+ public class EditorCoroutine
+ {
+ public static EditorCoroutine Start(IEnumerator routine)
+ {
+ EditorCoroutine coroutine = new EditorCoroutine(routine);
+ coroutine.Start();
+ return coroutine;
+ }
+
+ readonly IEnumerator routine;
+ bool completed;
+ EditorCoroutine(IEnumerator _routine)
+ {
+ routine = _routine;
+ completed = false;
+ }
+
+ void Start()
+ {
+ EditorApplication.update += Update;
+ }
+ public void Stop()
+ {
+ EditorApplication.update -= Update;
+ completed = true;
+ }
+
+ public bool GetCompleted()
+ {
+ return completed;
+ }
+
+ void Update()
+ {
+ if (!routine.MoveNext())
+ {
+ Stop();
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRPlatformToolSettings.cs b/Assets/Oculus/VR/Editor/OVRPlatformToolSettings.cs
new file mode 100644
index 0000000..0be7420
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRPlatformToolSettings.cs
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace Assets.Oculus.VR.Editor
+{
+#if UNITY_EDITOR
+ [UnityEditor.InitializeOnLoad]
+#endif
+ public sealed class OVRPlatformToolSettings : ScriptableObject
+ {
+ private const string DEFAULT_RELEASE_CHANNEL = "Alpha";
+
+ static OVRPlatformToolSettings()
+ {
+ // BuildPipeline.isBuildingPlayer cannot be called in a static constructor
+ // Run Update once to call TryInitialize then remove delegate
+ EditorApplication.update += Update;
+ }
+
+ static void Update()
+ {
+ // Initialize the instance only if a build is not currently running.
+ TryInitialize();
+ // Stop running Update
+ EditorApplication.update -= Update;
+ }
+
+ public static string AppID
+ {
+ get
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None &&
+ EditorPrefs.HasKey("OVRPlatformToolSettings_AppID" + (int)Instance.targetPlatform))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_AppID" + (int)Instance.targetPlatform);
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_AppID" + (int)Instance.targetPlatform, value);
+ }
+ }
+ }
+
+ public static string ReleaseNote
+ {
+ get
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None &&
+ EditorPrefs.HasKey("OVRPlatformToolSettings_ReleaseNote" + (int)Instance.targetPlatform))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_ReleaseNote" + (int)Instance.targetPlatform);
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_ReleaseNote" + (int)Instance.targetPlatform, value);
+ }
+ }
+ }
+
+ public static string ReleaseChannel
+ {
+ get
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None &&
+ EditorPrefs.HasKey("OVRPlatformToolSettings_ReleaseChannel" + (int)Instance.targetPlatform))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_ReleaseChannel" + (int)Instance.targetPlatform);
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_ReleaseChannel" + (int)Instance.targetPlatform, value);
+ }
+ }
+ }
+
+ public static string ApkBuildPath
+ {
+ get
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None &&
+ EditorPrefs.HasKey("OVRPlatformToolSettings_ApkBuildPath" + (int)Instance.targetPlatform))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_ApkBuildPath" + (int)Instance.targetPlatform);
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_ApkBuildPath" + (int)Instance.targetPlatform, value);
+ }
+ }
+ }
+
+ public static string ObbFilePath
+ {
+ get
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None &&
+ EditorPrefs.HasKey("OVRPlatformToolSettings_ObbFilePath" + (int)Instance.targetPlatform))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_ObbFilePath" + (int)Instance.targetPlatform);
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_ObbFilePath" + (int)Instance.targetPlatform, value);
+ }
+ }
+ }
+
+ public static string AssetsDirectory
+ {
+ get
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None &&
+ EditorPrefs.HasKey("OVRPlatformToolSettings_AssetsDirectory" + (int)Instance.targetPlatform))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_AssetsDirectory" + (int)Instance.targetPlatform);
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_AssetsDirectory" + (int)Instance.targetPlatform, value);
+ }
+ }
+ }
+
+ public static string RiftBuildDirectory
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_RiftBuildDirectory"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_RiftBuildDirectory");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_RiftBuildDirectory", value);
+ }
+ }
+
+ public static string RiftBuildVersion
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_RiftBuildVersion"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_RiftBuildVersion");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_RiftBuildVersion", value);
+ }
+ }
+
+ public static string RiftLaunchFile
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_RiftLaunchFile"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_RiftLaunchFile");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_RiftLaunchFile", value);
+ }
+ }
+
+ public static string RiftLaunchParams
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_RiftLaunchParams"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_RiftLaunchParams");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_RiftLaunchParams", value);
+ }
+ }
+
+ public static string Rift2DLaunchFile
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_Rift2DLaunchFile"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_Rift2DLaunchFile");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_Rift2DLaunchFile", value);
+ }
+ }
+
+ public static string Rift2DLaunchParams
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_Rift2DLaunchParams"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_Rift2DLaunchParams");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_Rift2DLaunchParams", value);
+ }
+ }
+
+ public static bool RiftFirewallException
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_RiftFirewallException"))
+ {
+ return EditorPrefs.GetBool("OVRPlatformToolSettings_RiftFirewallException");
+ }
+ else
+ {
+ return false;
+ }
+ }
+ set
+ {
+ EditorPrefs.SetBool("OVRPlatformToolSettings_RiftFirewallException", value);
+ }
+ }
+
+ public static OVRPlatformTool.GamepadType RiftGamepadEmulation
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_RiftGamepadEmulation"))
+ {
+ return (OVRPlatformTool.GamepadType)EditorPrefs.GetInt("OVRPlatformToolSettings_RiftGamepadEmulation");
+ }
+ else
+ {
+ return OVRPlatformTool.GamepadType.OFF;
+ }
+ }
+ set
+ {
+ EditorPrefs.SetInt("OVRPlatformToolSettings_RiftGamepadEmulation", (int)value);
+ }
+ }
+
+ public static List RiftRedistPackages
+ {
+ get { return Instance.riftRedistPackages; }
+ set { Instance.riftRedistPackages = value; }
+ }
+
+ public static string LanguagePackDirectory
+ {
+ get { return Instance.languagePackDirectory; }
+ set { Instance.languagePackDirectory = value; }
+ }
+
+ public static List AssetConfigs
+ {
+ get
+ {
+ return Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None ? Instance.assetConfigs[(int)Instance.targetPlatform].configList : new List();
+ }
+ set
+ {
+ if (Instance.targetPlatform < OVRPlatformTool.TargetPlatform.None)
+ {
+ Instance.assetConfigs[(int)Instance.targetPlatform].configList = value;
+ }
+ }
+ }
+
+ public static OVRPlatformTool.TargetPlatform TargetPlatform
+ {
+ get { return Instance.targetPlatform; }
+ set { Instance.targetPlatform = value; }
+ }
+
+ public static bool RunOvrLint
+ {
+ get { return Instance.runOvrLint; }
+ set { Instance.runOvrLint = value; }
+ }
+
+ public static bool UploadDebugSymbols
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_UploadDebugSymbols"))
+ {
+ return EditorPrefs.GetBool("OVRPlatformToolSettings_UploadDebugSymbols");
+ }
+ else
+ {
+ return true;
+ }
+ }
+ set
+ {
+ EditorPrefs.SetBool("OVRPlatformToolSettings_UploadDebugSymbols", value);
+ }
+ }
+
+ public static string DebugSymbolsDirectory
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_DebugSymbolsDirectory"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_DebugSymbolsDirectory");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_DebugSymbolsDirectory", value);
+ }
+ }
+
+ public static bool UploadDebugSymbolsOnly
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_UploadDebugSymbolsOnly"))
+ {
+ return EditorPrefs.GetBool("OVRPlatformToolSettings_UploadDebugSymbolsOnly");
+ }
+ else
+ {
+ return false;
+ }
+ }
+ set
+ {
+ EditorPrefs.SetBool("OVRPlatformToolSettings_UploadDebugSymbolsOnly", value);
+ }
+ }
+
+ public static string BuildID
+ {
+ get
+ {
+ if (EditorPrefs.HasKey("OVRPlatformToolSettings_BuildID"))
+ {
+ return EditorPrefs.GetString("OVRPlatformToolSettings_BuildID");
+ }
+ else
+ {
+ return "";
+ }
+ }
+ set
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_BuildID", value);
+ }
+ }
+
+ [SerializeField]
+ private List riftRedistPackages;
+
+ [SerializeField]
+ private string languagePackDirectory = "";
+
+ [SerializeField]
+ private AssetConfigList[] assetConfigs = new AssetConfigList[(int)OVRPlatformTool.TargetPlatform.None];
+
+ [SerializeField]
+ private OVRPlatformTool.TargetPlatform targetPlatform = OVRPlatformTool.TargetPlatform.None;
+
+ [SerializeField]
+ private bool runOvrLint = true;
+
+ public static bool TryInitialize()
+ {
+ // If not initialized and Build Player is current running, UnityEditor.AssetDatabase.CreateAsset
+ // is unsafe to call and will cause a crash. Only load the resource if it already exists.
+ if (instance == null && BuildPipeline.isBuildingPlayer)
+ {
+ instance = Resources.Load("OVRPlatformToolSettings");
+ return instance != null;
+ }
+ // Otherwise create/load the resource instance normally.
+ return Instance != null;
+ }
+
+ private static OVRPlatformToolSettings instance;
+ public static OVRPlatformToolSettings Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ instance = Resources.Load("OVRPlatformToolSettings");
+
+ if (instance == null)
+ {
+ if (BuildPipeline.isBuildingPlayer)
+ {
+ // UnityEditor.AssetDatabase.CreateAsset is unsafe to call during a build and
+ // may cause a crash.
+ // This should be rare as the asset is created in the static constructor and should
+ // usually exist.
+ throw new UnityEditor.Build.BuildFailedException(
+ "Cannot create OVRPlatformToolSettings asset while building.");
+ }
+ instance = ScriptableObject.CreateInstance();
+
+ string properPath = System.IO.Path.Combine(UnityEngine.Application.dataPath, "Resources");
+ if (!System.IO.Directory.Exists(properPath))
+ {
+ UnityEditor.AssetDatabase.CreateFolder("Assets", "Resources");
+ }
+
+ string fullPath = System.IO.Path.Combine(
+ System.IO.Path.Combine("Assets", "Resources"),
+ "OVRPlatformToolSettings.asset"
+ );
+ UnityEditor.AssetDatabase.CreateAsset(instance, fullPath);
+
+ // Initialize cross platform default values for the new instance of OVRPlatformToolSettings here
+ if (instance != null)
+ {
+ for (int i = 0; i < (int)OVRPlatformTool.TargetPlatform.None; i++)
+ {
+ EditorPrefs.SetString("OVRPlatformToolSettings_ReleaseChannel" + i, DEFAULT_RELEASE_CHANNEL);
+ instance.assetConfigs[i] = new AssetConfigList();
+ }
+
+ instance.riftRedistPackages = new List();
+ }
+ }
+ }
+ return instance;
+ }
+ set
+ {
+ instance = value;
+ }
+ }
+ }
+
+ // Wrapper for asset config list so that it can be serialized properly
+ [System.Serializable]
+ public class AssetConfigList
+ {
+ public List configList;
+
+ public AssetConfigList()
+ {
+ configList = new List();
+ }
+ }
+
+ [System.Serializable]
+ public class AssetConfig
+ {
+ public enum AssetType
+ {
+ DEFAULT,
+ STORE,
+ LANGUAGE_PACK,
+ };
+
+ public string name;
+ public bool required;
+ public AssetType type;
+ public string sku;
+
+ private bool foldout;
+
+ public AssetConfig(string assetName)
+ {
+ name = assetName;
+ }
+
+ public bool GetFoldoutState()
+ {
+ return foldout;
+ }
+
+ public void SetFoldoutState(bool state)
+ {
+ foldout = state;
+ }
+ }
+
+ [System.Serializable]
+ public class RedistPackage
+ {
+ public bool include = false;
+ public string name;
+ public string id;
+
+ public RedistPackage(string pkgName, string pkgId)
+ {
+ name = pkgName;
+ id = pkgId;
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRPluginUpdater.cs b/Assets/Oculus/VR/Editor/OVRPluginUpdater.cs
new file mode 100644
index 0000000..4a6abfb
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRPluginUpdater.cs
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if USING_XR_MANAGEMENT && (USING_XR_SDK_OCULUS || USING_XR_SDK_OPENXR)
+#define USING_XR_SDK
+#endif
+
+#if UNITY_2020_1_OR_NEWER
+#define REQUIRES_XR_SDK
+#endif
+
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.IO;
+using System.Diagnostics;
+
+[InitializeOnLoad]
+public class OVRPluginUpdater
+{
+ enum PluginPlatform
+ {
+ Android,
+ AndroidUniversal,
+ AndroidOpenXR,
+ OSXUniversal,
+ Win,
+ Win64,
+ Win64OpenXR,
+ }
+ class PluginPackage
+ {
+ public string RootPath;
+ public System.Version Version;
+ public Dictionary Plugins = new Dictionary();
+
+ public bool IsBundledPluginPackage()
+ {
+ return (RootPath == GetBundledPluginRootPath());
+ }
+
+ public bool IsEnabled()
+ {
+ foreach (PluginPlatform platform in Enum.GetValues(typeof(PluginPlatform)))
+ {
+ string path = "";
+ if (Plugins.TryGetValue(platform, out path) && File.Exists(path))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsWin64Enabled()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.Win64, out path))
+ {
+ if (File.Exists(path))
+ {
+ string basePath = GetCurrentProjectPath();
+ string relPath = path.Substring(basePath.Length + 1);
+
+ PluginImporter pi = PluginImporter.GetAtPath(relPath) as PluginImporter;
+ if (pi != null)
+ {
+ return pi.GetCompatibleWithPlatform(BuildTarget.StandaloneWindows64) && pi.GetCompatibleWithEditor();
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsWin64Present()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.Win64, out path))
+ {
+ string disabledPath = path + GetDisabledPluginSuffix();
+
+ if (File.Exists(path) || File.Exists(disabledPath))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsWin64OpenXREnabled()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.Win64OpenXR, out path))
+ {
+ if (File.Exists(path))
+ {
+ string basePath = GetCurrentProjectPath();
+ string relPath = path.Substring(basePath.Length + 1);
+
+ PluginImporter pi = PluginImporter.GetAtPath(relPath) as PluginImporter;
+ if (pi != null)
+ {
+ return pi.GetCompatibleWithPlatform(BuildTarget.StandaloneWindows64) && pi.GetCompatibleWithEditor();
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsWin64OpenXRPresent()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.Win64OpenXR, out path))
+ {
+ string disabledPath = path + GetDisabledPluginSuffix();
+
+ if (File.Exists(path) || File.Exists(disabledPath))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ public bool IsAndroidUniversalEnabled()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.AndroidUniversal, out path))
+ {
+ if (File.Exists(path))
+ {
+ string basePath = GetCurrentProjectPath();
+ string relPath = path.Substring(basePath.Length + 1);
+
+ PluginImporter pi = PluginImporter.GetAtPath(relPath) as PluginImporter;
+ if (pi != null)
+ {
+ return pi.GetCompatibleWithPlatform(BuildTarget.Android);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsAndroidUniversalPresent()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.AndroidUniversal, out path))
+ {
+ string disabledPath = path + GetDisabledPluginSuffix();
+
+ if (File.Exists(path) || File.Exists(disabledPath))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsAndroidOpenXREnabled()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.AndroidOpenXR, out path))
+ {
+ if (File.Exists(path))
+ {
+ string basePath = GetCurrentProjectPath();
+ string relPath = path.Substring(basePath.Length + 1);
+
+ PluginImporter pi = PluginImporter.GetAtPath(relPath) as PluginImporter;
+ if (pi != null)
+ {
+ return pi.GetCompatibleWithPlatform(BuildTarget.Android);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsAndroidOpenXRPresent()
+ {
+ string path = "";
+ if (Plugins.TryGetValue(PluginPlatform.AndroidOpenXR, out path))
+ {
+ string disabledPath = path + GetDisabledPluginSuffix();
+
+ if (File.Exists(path) || File.Exists(disabledPath))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ private static bool restartPending = false;
+ private static bool unityRunningInBatchmode = false;
+ private static bool unityVersionSupportsAndroidUniversal = true;
+ private static bool enableAndroidUniversalSupport = true;
+
+ private static System.Version invalidVersion = new System.Version("0.0.0");
+ private static System.Version minimalProductionVersionForOpenXR = new Version(1, 63, 0);
+
+
+ static OVRPluginUpdater()
+ {
+ EditorApplication.delayCall += OnDelayCall;
+ }
+
+ static void OnDelayCall()
+ {
+ if (System.Environment.CommandLine.Contains("-batchmode"))
+ {
+ unityRunningInBatchmode = true;
+ }
+
+ if (enableAndroidUniversalSupport)
+ {
+ unityVersionSupportsAndroidUniversal = true;
+ }
+
+ if (ShouldAttemptPluginUpdate())
+ {
+ AttemptPluginUpdate(true);
+ }
+ }
+
+ private static PluginPackage GetPluginPackage(string rootPath)
+ {
+ return new PluginPackage()
+ {
+ RootPath = rootPath,
+ Version = GetPluginVersion(rootPath),
+ Plugins = new Dictionary()
+ {
+ { PluginPlatform.Android, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.Android) },
+ { PluginPlatform.AndroidUniversal, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.AndroidUniversal) },
+ { PluginPlatform.AndroidOpenXR, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.AndroidOpenXR) },
+ { PluginPlatform.OSXUniversal, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.OSXUniversal) },
+ { PluginPlatform.Win, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.Win) },
+ { PluginPlatform.Win64, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.Win64) },
+ { PluginPlatform.Win64OpenXR, rootPath + GetPluginBuildTargetSubPath(PluginPlatform.Win64OpenXR) },
+ }
+ };
+ }
+
+ private static PluginPackage GetBundledPluginPackage()
+ {
+ return GetPluginPackage(GetBundledPluginRootPath());
+ }
+
+ private static List GetAllUtilitiesPluginPackages()
+ {
+ string pluginRootPath = GetUtilitiesPluginRootPath();
+ List packages = new List();
+
+ if (Directory.Exists(pluginRootPath))
+ {
+ var dirs = Directory.GetDirectories(pluginRootPath);
+
+ foreach(string dir in dirs)
+ {
+ packages.Add(GetPluginPackage(dir));
+ }
+ }
+
+ return packages;
+ }
+
+ private static string GetCurrentProjectPath()
+ {
+ return Directory.GetParent(Application.dataPath).FullName;
+ }
+
+ private static string GetUtilitiesPluginRootPath()
+ {
+ return GetUtilitiesRootPath() + @"/Plugins";
+ }
+
+ public static string GetUtilitiesRootPath()
+ {
+ var so = ScriptableObject.CreateInstance(typeof(OVRPluginUpdaterStub));
+ var script = MonoScript.FromScriptableObject(so);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ string editorDir = Directory.GetParent(assetPath).FullName;
+ string ovrDir = Directory.GetParent(editorDir).FullName;
+
+ return ovrDir;
+ }
+
+ private static string GetBundledPluginRootPath()
+ {
+ string basePath = EditorApplication.applicationContentsPath;
+ string pluginPath = @"/UnityExtensions/Unity/VR";
+
+ return basePath + pluginPath;
+ }
+
+ private static string GetPluginBuildTargetSubPath(PluginPlatform target)
+ {
+ string path = string.Empty;
+
+ switch (target)
+ {
+ case PluginPlatform.Android:
+ path = @"/Android/OVRPlugin.aar";
+ break;
+ case PluginPlatform.AndroidUniversal:
+ path = @"/AndroidUniversal/OVRPlugin.aar";
+ break;
+ case PluginPlatform.AndroidOpenXR:
+ path = @"/AndroidOpenXR/OVRPlugin.aar";
+ break;
+ case PluginPlatform.OSXUniversal:
+ path = @"/OSXUniversal/OVRPlugin.bundle";
+ break;
+ case PluginPlatform.Win:
+ path = @"/Win/OVRPlugin.dll";
+ break;
+ case PluginPlatform.Win64:
+ path = @"/Win64/OVRPlugin.dll";
+ break;
+ case PluginPlatform.Win64OpenXR:
+ path = @"/Win64OpenXR/OVRPlugin.dll";
+ break;
+ default:
+ throw new ArgumentException("Attempted GetPluginBuildTargetSubPath() for unsupported BuildTarget: " + target);
+ }
+
+ return path;
+ }
+
+ private static string GetDisabledPluginSuffix()
+ {
+ return @".disabled";
+ }
+
+ private static System.Version GetPluginVersion(string path)
+ {
+ System.Version pluginVersion = invalidVersion;
+
+ try
+ {
+ pluginVersion = new System.Version(Path.GetFileName(path));
+ }
+ catch
+ {
+ pluginVersion = invalidVersion;
+ }
+
+ if (pluginVersion == invalidVersion)
+ {
+ //Unable to determine version from path, fallback to Win64 DLL meta data
+ path += GetPluginBuildTargetSubPath(PluginPlatform.Win64);
+ if (!File.Exists(path))
+ {
+ path += GetDisabledPluginSuffix();
+ if (!File.Exists(path))
+ {
+ return invalidVersion;
+ }
+ }
+
+ FileVersionInfo pluginVersionInfo = FileVersionInfo.GetVersionInfo(path);
+ if (pluginVersionInfo == null || pluginVersionInfo.ProductVersion == null || pluginVersionInfo.ProductVersion == "")
+ {
+ return invalidVersion;
+ }
+
+ pluginVersion = new System.Version(pluginVersionInfo.ProductVersion);
+ }
+
+ return pluginVersion;
+ }
+
+ public static string GetVersionDescription(System.Version version)
+ {
+ bool isVersionValid = (version != invalidVersion);
+ return isVersionValid ? version.ToString() : "(Unknown)";
+ }
+
+ private static bool ShouldAttemptPluginUpdate()
+ {
+ if (unityRunningInBatchmode || OVRPluginUpdaterStub.IsInsidePackageDistribution())
+ {
+ return false;
+ }
+ else
+ {
+ return !UnitySupportsEnabledAndroidPlugin() || (autoUpdateEnabled && !restartPending && !Application.isPlaying);
+ }
+ }
+
+ private static void DisableAllUtilitiesPluginPackages()
+ {
+ List allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
+
+ foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
+ {
+ foreach(string path in pluginPkg.Plugins.Values)
+ {
+ if ((Directory.Exists(path)) || (File.Exists(path)))
+ {
+ string basePath = GetCurrentProjectPath();
+ string relPath = path.Substring(basePath.Length + 1);
+ string relDisabledPath = relPath + GetDisabledPluginSuffix();
+
+ AssetDatabase.MoveAsset(relPath, relDisabledPath);
+ AssetDatabase.ImportAsset(relDisabledPath, ImportAssetOptions.ForceUpdate);
+ }
+ }
+ }
+
+ AssetDatabase.Refresh();
+ AssetDatabase.SaveAssets();
+ }
+
+ private static void EnablePluginPackage(PluginPackage pluginPkg)
+ {
+#if UNITY_2020_1_OR_NEWER
+ bool activateOpenXRPlugin = pluginPkg.Version >= minimalProductionVersionForOpenXR;
+ if (activateOpenXRPlugin && !unityRunningInBatchmode)
+ {
+ while(true)
+ {
+ // display a dialog to prompt developer to confirm if they want to proceed with OpenXR backend
+ int result = EditorUtility.DisplayDialogComplex("OpenXR Backend",
+ "OpenXR is now fully supported by Oculus. However, some of the functionalities are not supported in the baseline OpenXR spec, which would be provided in our future releases.\n\nIf you depend on the following features in your project, please click Cancel to continue using the legacy backend:\n\n * Mixed Reality Capture on Rift\n\nNew features, such as Passthrough API, are only supported through the OpenXR backend.\n\nPlease check our release notes for more details.\n\nReminder: you can switch the legacy and OpenXR backends at any time from Oculus > Tools > OpenXR menu options.", "Use OpenXR", "Cancel", "Release Notes");
+ if (result == 0)
+ break;
+ else if (result == 1)
+ {
+ activateOpenXRPlugin = false;
+ break;
+ }
+ else if (result == 2)
+ {
+ Application.OpenURL("https://developer.oculus.com/downloads/package/unity-integration/");
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarningFormat("Unrecognized result from DisplayDialogComplex: {0}", result);
+ break;
+ }
+ }
+ }
+#else
+ bool activateOpenXRPlugin = false;
+#endif
+ if (activateOpenXRPlugin)
+ {
+ UnityEngine.Debug.Log("OVRPlugin with OpenXR backend is activated by default");
+ if (!unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("OVRPlugin", "OVRPlugin with OpenXR backend will be activated by default", "Ok");
+ }
+ }
+ else
+ {
+ UnityEngine.Debug.Log("OVRPlugin with LibOVR/VRAPI backend is activated by default");
+ if (!unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("OVRPlugin", "OVRPlugin with LibOVR/VRAPI backend will be activated by default", "Ok");
+ }
+ }
+
+ foreach (var kvp in pluginPkg.Plugins)
+ {
+ PluginPlatform platform = kvp.Key;
+ string path = kvp.Value;
+
+ if ((Directory.Exists(path + GetDisabledPluginSuffix())) || (File.Exists(path + GetDisabledPluginSuffix())))
+ {
+ string basePath = GetCurrentProjectPath();
+ string relPath = path.Substring(basePath.Length + 1);
+ string relDisabledPath = relPath + GetDisabledPluginSuffix();
+
+ AssetDatabase.MoveAsset(relDisabledPath, relPath);
+ AssetDatabase.ImportAsset(relPath, ImportAssetOptions.ForceUpdate);
+
+ PluginImporter pi = PluginImporter.GetAtPath(relPath) as PluginImporter;
+ if (pi == null)
+ {
+ continue;
+ }
+
+ // Disable support for all platforms, then conditionally enable desired support below
+ pi.SetCompatibleWithEditor(false);
+ pi.SetCompatibleWithAnyPlatform(false);
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, false);
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false);
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false);
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, false);
+
+ switch (platform)
+ {
+ case PluginPlatform.Android:
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, !unityVersionSupportsAndroidUniversal);
+ if (!unityVersionSupportsAndroidUniversal)
+ {
+ pi.SetPlatformData(BuildTarget.Android, "CPU", "ARMv7");
+ }
+ break;
+ case PluginPlatform.AndroidUniversal:
+ if (!activateOpenXRPlugin)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, unityVersionSupportsAndroidUniversal);
+ }
+ break;
+ case PluginPlatform.AndroidOpenXR:
+ if (activateOpenXRPlugin)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, unityVersionSupportsAndroidUniversal);
+ }
+ break;
+ case PluginPlatform.OSXUniversal:
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, true);
+ pi.SetCompatibleWithEditor(true);
+ pi.SetEditorData("CPU", "AnyCPU");
+ pi.SetEditorData("OS", "OSX");
+ pi.SetPlatformData("Editor", "CPU", "AnyCPU");
+ pi.SetPlatformData("Editor", "OS", "OSX");
+ break;
+ case PluginPlatform.Win:
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, true);
+ pi.SetCompatibleWithEditor(true);
+ pi.SetEditorData("CPU", "X86");
+ pi.SetEditorData("OS", "Windows");
+ pi.SetPlatformData("Editor", "CPU", "X86");
+ pi.SetPlatformData("Editor", "OS", "Windows");
+ break;
+ case PluginPlatform.Win64:
+ if (!activateOpenXRPlugin)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, true);
+ pi.SetCompatibleWithEditor(true);
+ pi.SetEditorData("CPU", "X86_64");
+ pi.SetEditorData("OS", "Windows");
+ pi.SetPlatformData("Editor", "CPU", "X86_64");
+ pi.SetPlatformData("Editor", "OS", "Windows");
+ }
+ break;
+ case PluginPlatform.Win64OpenXR:
+ if (activateOpenXRPlugin)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, true);
+ pi.SetCompatibleWithEditor(true);
+ pi.SetEditorData("CPU", "X86_64");
+ pi.SetEditorData("OS", "Windows");
+ pi.SetPlatformData("Editor", "CPU", "X86_64");
+ pi.SetPlatformData("Editor", "OS", "Windows");
+ }
+ break;
+ default:
+ throw new ArgumentException("Attempted EnablePluginPackage() for unsupported BuildTarget: " + platform);
+ }
+
+ AssetDatabase.ImportAsset(relPath, ImportAssetOptions.ForceUpdate);
+ }
+ }
+
+ AssetDatabase.Refresh();
+ AssetDatabase.SaveAssets();
+ }
+
+ private static readonly string autoUpdateEnabledKey = "Oculus_Utilities_OVRPluginUpdater_AutoUpdate_" + OVRManager.utilitiesVersion;
+ private static bool autoUpdateEnabled
+ {
+ get {
+ return PlayerPrefs.GetInt(autoUpdateEnabledKey, 1) == 1;
+ }
+
+ set {
+ PlayerPrefs.SetInt(autoUpdateEnabledKey, value ? 1 : 0);
+ }
+ }
+
+
+ private static PluginPackage GetEnabledUtilsPluginPkg()
+ {
+ List allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
+
+ PluginPackage enabledUtilsPluginPkg = null;
+
+ foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
+ {
+ if (pluginPkg.IsEnabled())
+ {
+ if ((enabledUtilsPluginPkg == null) || (pluginPkg.Version > enabledUtilsPluginPkg.Version))
+ {
+ enabledUtilsPluginPkg = pluginPkg;
+ }
+ }
+ }
+
+ return enabledUtilsPluginPkg;
+ }
+
+ const string k_disablePluginMenuStr = "Oculus/Tools/OVR Utilities Plugin/Set OVRPlugin to Package Manager-provided (Disable OVR Utilities Plugin version)";
+ [MenuItem(k_disablePluginMenuStr, true, 102)]
+ private static bool IsDisableOVRPluginMenuEnabled()
+ {
+ //This section controls whether we draw a checkmark next to this menu item (it's currently active...)
+ Menu.SetChecked(k_disablePluginMenuStr, GetEnabledUtilsPluginPkg() == null);
+
+ //And this section controls whether the menu item is enabled (you're allowed to toggle it)
+ return true;
+ }
+
+ [MenuItem(k_disablePluginMenuStr, false, 102)]
+ private static void AttemptPluginDisable()
+ {
+ if (OVRPluginUpdaterStub.IsInsidePackageDistribution())
+ {
+ UnityEngine.Debug.LogError("Unable to change plugin when using package distribution");
+ return;
+ }
+
+ PluginPackage enabledUtilsPluginPkg = GetEnabledUtilsPluginPkg();
+
+ if (enabledUtilsPluginPkg == null)
+ {
+ if (unityRunningInBatchmode
+ || EditorUtility.DisplayDialog("Disable Oculus Utilities Plugin",
+ "The OVRPlugin included with Oculus Utilities is already disabled."
+ + " The OVRPlugin installed through the Package Manager will continue to be used.\n",
+ "Ok",
+ ""))
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (unityRunningInBatchmode
+ || EditorUtility.DisplayDialog("Disable Oculus Utilities Plugin",
+ "Do you want to disable the OVRPlugin included with Oculus Utilities and revert to the OVRPlugin installed through the Package Manager?\n\n"
+ + "Current version: " + GetVersionDescription(enabledUtilsPluginPkg.Version),
+ "Yes",
+ "No"))
+ {
+ DisableAllUtilitiesPluginPackages();
+
+ if (unityRunningInBatchmode
+ || EditorUtility.DisplayDialog("Restart Unity",
+ "Now you will be using the OVRPlugin installed through Package Manager."
+ + "\n\nPlease restart the Unity Editor to complete the update process.",
+ "Restart",
+ "Not Now"))
+ {
+ RestartUnityEditor();
+ }
+ }
+ }
+ }
+
+ [MenuItem("Oculus/Tools/OVR Utilities Plugin/Manual Update OVRPlugin (to OVR Utilities version)", false, 0)]
+ private static void RunPluginUpdate()
+ {
+ if (OVRPluginUpdaterStub.IsInsidePackageDistribution())
+ {
+ UnityEngine.Debug.LogError("Unable to change plugin when using package distribution");
+ return;
+ }
+
+ autoUpdateEnabled = true;
+ AttemptPluginUpdate(false);
+ }
+
+ private static void BatchmodeActivateOVRPluginOpenXR()
+ {
+ OnDelayCall(); // manually invoke when running editor in batchmode
+ ActivateOVRPluginOpenXR();
+ }
+
+ const string k_setToOpenXRPluginMenuStr = "Oculus/Tools/OVR Utilities Plugin/Set OVRPlugin to OpenXR";
+ [MenuItem(k_setToOpenXRPluginMenuStr, true, 100)]
+ private static bool IsActivateOVRPluginOpenXRMenuEnabled()
+ {
+ //This section controls whether we draw a checkmark next to this menu item (it's currently active...)
+ Menu.SetChecked(k_setToOpenXRPluginMenuStr, IsOVRPluginOpenXRActivated());
+
+ //And this section controls whether the menu item is enabled (you're allowed to toggle it)
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ [MenuItem(k_setToOpenXRPluginMenuStr, false, 100)]
+ private static void ActivateOVRPluginOpenXR()
+ {
+ if (!unityVersionSupportsAndroidUniversal)
+ {
+ UnityEngine.Debug.LogError("Unexpected error: Unity must support AndroidUniversal version of Oculus Utilities Plugin for accessing OpenXR");
+ return;
+ }
+
+ if (OVRPluginUpdaterStub.IsInsidePackageDistribution())
+ {
+ UnityEngine.Debug.LogError("Unable to change plugin when using package distribution");
+ return;
+ }
+
+#if !USING_XR_SDK && !REQUIRES_XR_SDK
+ UnityEngine.Debug.LogError("Oculus Utilities Plugin with OpenXR only supports XR Plug-in Management with Oculus XR Plugin");
+ return;
+#else
+
+ List allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
+
+ PluginPackage enabledUtilsPluginPkg = null;
+
+ foreach (PluginPackage pluginPkg in allUtilsPluginPkgs)
+ {
+ if (pluginPkg.IsEnabled())
+ {
+ enabledUtilsPluginPkg = pluginPkg;
+ break;
+ }
+ }
+
+ if (enabledUtilsPluginPkg == null)
+ {
+ UnityEngine.Debug.LogError("Unable to Activate OVRPlugin with OpenXR: Oculus Utilities Plugin package not activated");
+ return;
+ }
+
+ if (!enabledUtilsPluginPkg.IsAndroidOpenXRPresent() && !enabledUtilsPluginPkg.IsWin64OpenXRPresent())
+ {
+ UnityEngine.Debug.LogError("Unable to Activate OVRPlugin with OpenXR: Both AndroidOpenXR/OVRPlugin.aar or Win64OpenXR/OVRPlugin.dll does not exist");
+ return;
+ }
+
+ if (enabledUtilsPluginPkg.IsAndroidOpenXREnabled() && enabledUtilsPluginPkg.IsWin64OpenXREnabled())
+ {
+ if (!unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("Unable to Activate OVRPlugin with OpenXR", "Both AndroidOpenXR/OVRPlugin.aar and Win64OpenXR/OVRPlugin.dll already enabled", "Ok");
+ }
+ return;
+ }
+
+ if (enabledUtilsPluginPkg.Version < minimalProductionVersionForOpenXR)
+ {
+ if (!unityRunningInBatchmode)
+ {
+ bool accepted = EditorUtility.DisplayDialog("Warning",
+ "OVRPlugin with OpenXR backend is experimental before v31. You may expect to encounter stability issues and/or missing functionalities, " +
+ "including but not limited to, fixed foveated rendering / composition layer / display refresh rates / etc." +
+ "\n\n" +
+ "Also, OVRPlugin with OpenXR backend only supports XR Plug-in Management with Oculus XR Plugin on Quest",
+ "Continue", "Cancel");
+
+ if (!accepted)
+ {
+ return;
+ }
+ }
+ }
+
+ if (enabledUtilsPluginPkg.IsAndroidOpenXRPresent() && !enabledUtilsPluginPkg.IsAndroidOpenXREnabled())
+ {
+ if (enabledUtilsPluginPkg.IsAndroidUniversalEnabled())
+ {
+ string androidUniveralPluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.AndroidUniversal];
+ string androidUniveralPluginBasePath = GetCurrentProjectPath();
+ string androidUniveralPluginRelPath = androidUniveralPluginPath.Substring(androidUniveralPluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(androidUniveralPluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, false);
+ AssetDatabase.ImportAsset(androidUniveralPluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + androidUniveralPluginRelPath);
+ }
+ }
+
+ {
+ string androidOpenXRPluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.AndroidOpenXR];
+ string androidOpenXRPluginBasePath = GetCurrentProjectPath();
+ string androidOpenXRPluginRelPath = androidOpenXRPluginPath.Substring(androidOpenXRPluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(androidOpenXRPluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, true);
+ AssetDatabase.ImportAsset(androidOpenXRPluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + androidOpenXRPluginRelPath);
+ }
+ }
+ }
+
+
+ bool win64PluginUpdated = false;
+
+ if (enabledUtilsPluginPkg.IsWin64OpenXRPresent() && !enabledUtilsPluginPkg.IsWin64OpenXREnabled())
+ {
+ if (enabledUtilsPluginPkg.IsWin64Enabled())
+ {
+ string win64PluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.Win64];
+ string win64PluginBasePath = GetCurrentProjectPath();
+ string win64PluginRelPath = win64PluginPath.Substring(win64PluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(win64PluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.ClearSettings();
+ pi.SetCompatibleWithEditor(false);
+ pi.SetCompatibleWithAnyPlatform(false);
+ AssetDatabase.ImportAsset(win64PluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + win64PluginRelPath);
+ }
+ }
+
+ {
+ string win64OpenXRPluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.Win64OpenXR];
+ string win64OpenXRPluginBasePath = GetCurrentProjectPath();
+ string win64OpenXRPluginRelPath = win64OpenXRPluginPath.Substring(win64OpenXRPluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(win64OpenXRPluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, true);
+ pi.SetCompatibleWithEditor(true);
+ pi.SetEditorData("CPU", "X86_64");
+ pi.SetEditorData("OS", "Windows");
+ pi.SetPlatformData("Editor", "CPU", "X86_64");
+ pi.SetPlatformData("Editor", "OS", "Windows");
+ AssetDatabase.ImportAsset(win64OpenXRPluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + win64OpenXRPluginRelPath);
+ }
+ }
+
+ win64PluginUpdated = true;
+ }
+
+ AssetDatabase.Refresh();
+ AssetDatabase.SaveAssets();
+
+ if (!unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("Activate OVRPlugin with OpenXR", "Oculus Utilities Plugin with OpenXR has been enabled on Android", "Ok");
+ if (win64PluginUpdated && EditorUtility.DisplayDialog("Restart Unity",
+ "Win64 plugin updated. Do you want to restart Unity editor?",
+ "Restart",
+ "Not Now"))
+ {
+ RestartUnityEditor();
+ }
+ }
+#endif // !USING_XR_SDK
+ }
+
+ const string k_setToLegacyPluginMenuStr = "Oculus/Tools/OVR Utilities Plugin/Set OVRPlugin to Legacy LibOVR+VRAPI";
+ [MenuItem(k_setToLegacyPluginMenuStr, true, 101)]
+ private static bool IsRestoreStandardOVRPluginMenuEnabled()
+ {
+ //This section controls whether we draw a checkmark next to this menu item (it's currently active...)
+ Menu.SetChecked(k_setToLegacyPluginMenuStr, IsOVRPluginLegacyAPIActivated());
+
+ //And this section controls whether the menu item is enabled (you're allowed to toggle it)
+ return true;
+ }
+
+ [MenuItem(k_setToLegacyPluginMenuStr, false, 101)]
+ private static void RestoreStandardOVRPlugin()
+ {
+ if (!unityVersionSupportsAndroidUniversal) // sanity check
+ {
+ UnityEngine.Debug.LogError("Unexpected error: Unity must support AndroidUniversal version of Oculus Utilities Plugin for accessing OpenXR");
+ return;
+ }
+
+ if (OVRPluginUpdaterStub.IsInsidePackageDistribution())
+ {
+ UnityEngine.Debug.LogError("Unable to change plugin when using package distribution");
+ return;
+ }
+
+ List allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
+
+ PluginPackage enabledUtilsPluginPkg = null;
+
+ foreach (PluginPackage pluginPkg in allUtilsPluginPkgs)
+ {
+ if (pluginPkg.IsEnabled())
+ {
+ enabledUtilsPluginPkg = pluginPkg;
+ break;
+ }
+ }
+
+ if (enabledUtilsPluginPkg == null)
+ {
+ UnityEngine.Debug.LogError("Unable to Restore Standard Oculus Utilities Plugin: Oculus Utilities Plugin package not activated");
+ return;
+ }
+
+ if (!enabledUtilsPluginPkg.IsAndroidUniversalPresent() && !enabledUtilsPluginPkg.IsWin64Present())
+ {
+ UnityEngine.Debug.LogError("Unable to Restore Standard Oculus Utilities Plugin: Both AndroidOpenXR/OVRPlugin.aar and Win64/OVRPlugin.dll does not exist");
+ return;
+ }
+
+ if (enabledUtilsPluginPkg.IsAndroidUniversalEnabled() && enabledUtilsPluginPkg.IsWin64Enabled())
+ {
+ if (!unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("Unable to Restore Standard Oculus Utilities Plugin", "Both AndroidUniversal/OVRPlugin.aar and Win64/OVRPlugin.dll already enabled", "Ok");
+ }
+ return;
+ }
+
+ if (enabledUtilsPluginPkg.IsAndroidUniversalPresent() && !enabledUtilsPluginPkg.IsAndroidUniversalEnabled())
+ {
+ if (enabledUtilsPluginPkg.IsAndroidOpenXREnabled())
+ {
+ string androidOpenXRPluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.AndroidOpenXR];
+ string androidOpenXRPluginBasePath = GetCurrentProjectPath();
+ string androidOpenXRPluginRelPath = androidOpenXRPluginPath.Substring(androidOpenXRPluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(androidOpenXRPluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, false);
+ AssetDatabase.ImportAsset(androidOpenXRPluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + androidOpenXRPluginRelPath);
+ }
+ }
+
+ {
+ string androidUniveralPluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.AndroidUniversal];
+ string androidUniveralPluginBasePath = GetCurrentProjectPath();
+ string androidUniveralPluginRelPath = androidUniveralPluginPath.Substring(androidUniveralPluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(androidUniveralPluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.Android, true);
+ AssetDatabase.ImportAsset(androidUniveralPluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + androidUniveralPluginRelPath);
+ }
+ }
+
+ }
+
+ bool win64PluginUpdated = false;
+ if (enabledUtilsPluginPkg.IsWin64Present() && !enabledUtilsPluginPkg.IsWin64Enabled())
+ {
+ if (enabledUtilsPluginPkg.IsWin64OpenXREnabled())
+ {
+ string win64OpenXRPluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.Win64OpenXR];
+ string win64OpenXRPluginBasePath = GetCurrentProjectPath();
+ string win64OpenXRPluginRelPath = win64OpenXRPluginPath.Substring(win64OpenXRPluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(win64OpenXRPluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.ClearSettings();
+ pi.SetCompatibleWithEditor(false);
+ pi.SetCompatibleWithAnyPlatform(false);
+ AssetDatabase.ImportAsset(win64OpenXRPluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + win64OpenXRPluginRelPath);
+ }
+ }
+
+ {
+ string win64PluginPath = enabledUtilsPluginPkg.Plugins[PluginPlatform.Win64];
+ string win64PluginBasePath = GetCurrentProjectPath();
+ string win64PluginRelPath = win64PluginPath.Substring(win64PluginBasePath.Length + 1);
+ PluginImporter pi = PluginImporter.GetAtPath(win64PluginRelPath) as PluginImporter;
+ if (pi != null)
+ {
+ pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, true);
+ pi.SetCompatibleWithEditor(true);
+ pi.SetEditorData("CPU", "X86_64");
+ pi.SetEditorData("OS", "Windows");
+ pi.SetPlatformData("Editor", "CPU", "X86_64");
+ pi.SetPlatformData("Editor", "OS", "Windows");
+ AssetDatabase.ImportAsset(win64PluginRelPath, ImportAssetOptions.ForceUpdate);
+ }
+ else
+ {
+ UnityEngine.Debug.LogWarning("Unable to find PluginImporter: " + win64PluginRelPath);
+ }
+ }
+
+ win64PluginUpdated = true;
+ }
+
+ AssetDatabase.Refresh();
+ AssetDatabase.SaveAssets();
+
+ if (!unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("Restore Standard OVRPlugin", "Standard version of Oculus Utilities Plugin has been enabled on Android", "Ok");
+ if (win64PluginUpdated && EditorUtility.DisplayDialog("Restart Unity",
+ "Win64 plugin updated. Do you want to restart Unity editor?",
+ "Restart",
+ "Not Now"))
+ {
+ RestartUnityEditor();
+ }
+ }
+ }
+
+ // Test if the OVRPlugin/OpenXR plugin is currently activated, used by other editor utilities
+ public static bool IsOVRPluginOpenXRActivated()
+ {
+ if (!unityVersionSupportsAndroidUniversal) // sanity check
+ {
+ return false;
+ }
+
+ PluginPackage enabledUtilsPluginPkg = GetEnabledUtilsPluginPkg();
+
+ if (enabledUtilsPluginPkg == null)
+ {
+ return false;
+ }
+
+ return enabledUtilsPluginPkg.IsAndroidOpenXREnabled();
+ }
+
+ public static bool IsOVRPluginLegacyAPIActivated()
+ {
+ PluginPackage enabledUtilsPluginPkg = GetEnabledUtilsPluginPkg();
+
+ if (enabledUtilsPluginPkg == null)
+ {
+ return false;
+ }
+
+ return enabledUtilsPluginPkg.IsAndroidUniversalEnabled();
+ }
+
+ public static bool IsOVRPluginUnityProvidedActivated()
+ {
+ PluginPackage enabledUtilsPluginPkg = GetEnabledUtilsPluginPkg();
+ return enabledUtilsPluginPkg != null && enabledUtilsPluginPkg.IsBundledPluginPackage();
+ }
+
+ // Separate entry point needed since "-executeMethod" does not support parameters or default parameter values
+ private static void BatchmodePluginUpdate()
+ {
+ OnDelayCall(); // manually invoke when running editor in batchmode
+ AttemptPluginUpdate(false);
+ }
+
+ private static void AttemptPluginUpdate(bool triggeredByAutoUpdate)
+ {
+ OVRPlugin.SendEvent("attempt_plugin_update_auto", triggeredByAutoUpdate.ToString());
+
+ PluginPackage bundledPluginPkg = GetBundledPluginPackage();
+ List allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
+
+ PluginPackage enabledUtilsPluginPkg = null;
+ PluginPackage newestUtilsPluginPkg = null;
+
+ foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
+ {
+ if ((newestUtilsPluginPkg == null) || (pluginPkg.Version > newestUtilsPluginPkg.Version))
+ {
+ newestUtilsPluginPkg = pluginPkg;
+ }
+
+ if (pluginPkg.IsEnabled())
+ {
+ if ((enabledUtilsPluginPkg == null) || (pluginPkg.Version > enabledUtilsPluginPkg.Version))
+ {
+ enabledUtilsPluginPkg = pluginPkg;
+ }
+ }
+ }
+
+ bool reenableCurrentPluginPkg = false;
+ PluginPackage targetPluginPkg = null;
+
+ if ((newestUtilsPluginPkg != null) && (newestUtilsPluginPkg.Version > bundledPluginPkg.Version))
+ {
+ if ((enabledUtilsPluginPkg == null) || (enabledUtilsPluginPkg.Version != newestUtilsPluginPkg.Version))
+ {
+ targetPluginPkg = newestUtilsPluginPkg;
+ }
+ }
+ else if ((enabledUtilsPluginPkg != null) && (enabledUtilsPluginPkg.Version < bundledPluginPkg.Version))
+ {
+ targetPluginPkg = bundledPluginPkg;
+ }
+
+ PluginPackage currentPluginPkg = (enabledUtilsPluginPkg != null) ? enabledUtilsPluginPkg : bundledPluginPkg;
+
+ if ((targetPluginPkg == null) && !UnitySupportsEnabledAndroidPlugin())
+ {
+ // Force reenabling the current package to configure the correct android plugin for this unity version.
+ reenableCurrentPluginPkg = true;
+ targetPluginPkg = currentPluginPkg;
+ }
+
+ if (targetPluginPkg == null)
+ {
+ if (!triggeredByAutoUpdate && !unityRunningInBatchmode)
+ {
+ EditorUtility.DisplayDialog("Update Oculus Utilities Plugin",
+ "OVRPlugin is already up to date.\n\nCurrent version: "
+ + GetVersionDescription(currentPluginPkg.Version),
+ "Ok",
+ "");
+ }
+
+ return; // No update necessary.
+ }
+
+ System.Version targetVersion = targetPluginPkg.Version;
+
+ bool userAcceptsUpdate = false;
+
+ if (unityRunningInBatchmode)
+ {
+ userAcceptsUpdate = true;
+ }
+ else
+ {
+ string dialogBody = "Oculus Utilities has detected that a newer OVRPlugin is available."
+ + " Using the newest version is recommended. Do you want to enable it?\n\n"
+ + "Current version: "
+ + GetVersionDescription(currentPluginPkg.Version)
+ + "\nAvailable version: "
+ + targetVersion;
+
+ if (reenableCurrentPluginPkg)
+ {
+ dialogBody = "Oculus Utilities has detected a configuration change that requires re-enabling the current OVRPlugin."
+ + " Do you want to proceed?\n\nCurrent version: "
+ + GetVersionDescription(currentPluginPkg.Version);
+ }
+
+ int dialogResult = EditorUtility.DisplayDialogComplex("Update Oculus Utilities Plugin", dialogBody, "Yes", "No, Don't Ask Again", "No");
+
+ switch (dialogResult)
+ {
+ case 0: // "Yes"
+ userAcceptsUpdate = true;
+ break;
+ case 1: // "No, Don't Ask Again"
+ autoUpdateEnabled = false;
+
+ EditorUtility.DisplayDialog("Oculus Utilities OVRPlugin",
+ "To manually update in the future, use the following menu option:\n\n"
+ + "[Oculus -> Tools -> Update OVR Utilities Plugin]",
+ "Ok",
+ "");
+ return;
+ case 2: // "No"
+ return;
+ }
+ }
+
+ if (userAcceptsUpdate)
+ {
+ DisableAllUtilitiesPluginPackages();
+
+ if (!targetPluginPkg.IsBundledPluginPackage())
+ {
+ EnablePluginPackage(targetPluginPkg);
+ }
+
+ if (unityRunningInBatchmode
+ || EditorUtility.DisplayDialog("Restart Unity",
+ "OVRPlugin has been updated to "
+ + GetVersionDescription(targetPluginPkg.Version)
+ + ".\n\nPlease restart the Unity Editor to complete the update process."
+ ,
+ "Restart",
+ "Not Now"))
+ {
+ RestartUnityEditor();
+ }
+ }
+ }
+
+ private static bool UnitySupportsEnabledAndroidPlugin()
+ {
+ List allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
+
+ foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
+ {
+ if (pluginPkg.IsEnabled())
+ {
+ if ((pluginPkg.IsAndroidUniversalEnabled() || pluginPkg.IsAndroidOpenXREnabled()) && !unityVersionSupportsAndroidUniversal)
+ {
+ // Android Universal should only be enabled on supported Unity versions since it can prevent app launch.
+ return false;
+ }
+ else if (!pluginPkg.IsAndroidUniversalEnabled() && pluginPkg.IsAndroidUniversalPresent() &&
+ !pluginPkg.IsAndroidOpenXREnabled() && pluginPkg.IsAndroidOpenXRPresent() &&
+ unityVersionSupportsAndroidUniversal)
+ {
+ // Android Universal is present and should be enabled on supported Unity versions since ARM64 config will fail otherwise.
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static void RestartUnityEditor()
+ {
+ if (unityRunningInBatchmode)
+ {
+ EditorApplication.Exit(0);
+ }
+ else
+ {
+ restartPending = true;
+ EditorApplication.OpenProject(GetCurrentProjectPath());
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRPluginUpdaterStub.cs b/Assets/Oculus/VR/Editor/OVRPluginUpdaterStub.cs
new file mode 100644
index 0000000..706274e
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRPluginUpdaterStub.cs
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using UnityEngine;
+using UnityEditor;
+using System;
+
+public class OVRPluginUpdaterStub : ScriptableObject
+{
+ // Stub helper class to locate OVR Utilities Path through Unity Editor API.
+ // Required to be a standalone class in a separate file or else MonoScript.FromScriptableObject() returns an empty string path.
+
+ public static bool IsInsidePackageDistribution()
+ {
+ var so = ScriptableObject.CreateInstance(typeof(OVRPluginUpdaterStub));
+ var script = MonoScript.FromScriptableObject(so);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ if (assetPath.StartsWith("Packages\\", StringComparison.InvariantCultureIgnoreCase) ||
+ assetPath.StartsWith("Packages/", StringComparison.InvariantCultureIgnoreCase))
+ return true;
+ return false;
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRProjectConfig.cs b/Assets/Oculus/VR/Editor/OVRProjectConfig.cs
new file mode 100644
index 0000000..ce8a36a
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRProjectConfig.cs
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+using System;
+using UnityEngine.Serialization;
+
+[System.Serializable]
+#if UNITY_EDITOR
+[UnityEditor.InitializeOnLoad]
+#endif
+public class OVRProjectConfig : ScriptableObject
+{
+ public enum DeviceType
+ {
+ //GearVrOrGo = 0, // DEPRECATED
+ Quest = 1,
+ Quest2 = 2
+ }
+
+ public enum HandTrackingSupport
+ {
+ ControllersOnly = 0,
+ ControllersAndHands = 1,
+ HandsOnly = 2
+ }
+
+ public enum HandTrackingFrequency
+ {
+ LOW = 0,
+ HIGH = 1,
+ MAX = 2
+ }
+
+ public enum HandTrackingVersion
+ {
+ Default = 0,
+ V1 = 1,
+ V2 = 2
+ }
+
+ public enum AnchorSupport
+ {
+ Disabled = 0,
+ Enabled = 1,
+ }
+
+ public enum RenderModelSupport
+ {
+ Disabled = 0,
+ Enabled = 1,
+ }
+
+ public enum TrackedKeyboardSupport
+ {
+ None = 0,
+ Supported = 1,
+ Required = 2
+ }
+
+
+ public List targetDeviceTypes = new List {DeviceType.Quest, DeviceType.Quest2};
+ public bool allowOptional3DofHeadTracking = false;
+ public HandTrackingSupport handTrackingSupport = HandTrackingSupport.ControllersOnly;
+ public HandTrackingFrequency handTrackingFrequency = HandTrackingFrequency.LOW;
+ public HandTrackingVersion handTrackingVersion = HandTrackingVersion.Default;
+ [FormerlySerializedAs("spatialAnchorsSupport")]
+ public AnchorSupport anchorSupport = AnchorSupport.Disabled;
+ public RenderModelSupport renderModelSupport = RenderModelSupport.Disabled;
+ public TrackedKeyboardSupport trackedKeyboardSupport = TrackedKeyboardSupport.None;
+
+ public bool disableBackups = true;
+ public bool enableNSCConfig = true;
+ public string securityXmlPath;
+
+ public bool skipUnneededShaders = false;
+
+ [System.Obsolete("Focus awareness is now required. The option will be deprecated.", false)]
+ public bool focusAware = true;
+
+ public bool requiresSystemKeyboard = false;
+ public bool experimentalFeaturesEnabled = false;
+ public bool insightPassthroughEnabled = false;
+ public Texture2D systemSplashScreen;
+
+ //public const string OculusProjectConfigAssetPath = "Assets/Oculus/OculusProjectConfig.asset";
+
+ static OVRProjectConfig()
+ {
+ // BuildPipeline.isBuildingPlayer cannot be called in a static constructor
+ // Run Update once to call GetProjectConfig then remove delegate
+ EditorApplication.update += Update;
+ }
+
+ static void Update()
+ {
+ // Initialize the asset if it doesn't exist
+ GetProjectConfig();
+ // Stop running Update
+ EditorApplication.update -= Update;
+ }
+
+ private static string GetOculusProjectConfigAssetPath()
+ {
+ var so = ScriptableObject.CreateInstance(typeof(OVRPluginUpdaterStub));
+ var script = MonoScript.FromScriptableObject(so);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ string editorDir = Directory.GetParent(assetPath).FullName;
+ string ovrDir = Directory.GetParent(editorDir).FullName;
+ string oculusDir = Directory.GetParent(ovrDir).FullName;
+
+ if (OVRPluginUpdaterStub.IsInsidePackageDistribution())
+ {
+ oculusDir = Path.GetFullPath(Path.Combine(Application.dataPath, "Oculus"));
+ if (!Directory.Exists(oculusDir))
+ {
+ Directory.CreateDirectory(oculusDir);
+ }
+ }
+
+ string configAssetPath = Path.GetFullPath(Path.Combine(oculusDir, "OculusProjectConfig.asset"));
+ Uri configUri = new Uri(configAssetPath);
+ Uri projectUri = new Uri(Application.dataPath);
+ Uri relativeUri = projectUri.MakeRelativeUri(configUri);
+
+ return relativeUri.ToString();
+ }
+
+ public static OVRProjectConfig GetProjectConfig()
+ {
+ OVRProjectConfig projectConfig = null;
+ string oculusProjectConfigAssetPath = GetOculusProjectConfigAssetPath();
+ try
+ {
+ projectConfig = AssetDatabase.LoadAssetAtPath(oculusProjectConfigAssetPath, typeof(OVRProjectConfig)) as OVRProjectConfig;
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogWarningFormat("Unable to load ProjectConfig from {0}, error {1}", oculusProjectConfigAssetPath, e.Message);
+ }
+ // Initialize the asset only if a build is not currently running.
+ if (projectConfig == null && !BuildPipeline.isBuildingPlayer)
+ {
+ projectConfig = ScriptableObject.CreateInstance();
+ projectConfig.targetDeviceTypes = new List();
+ projectConfig.targetDeviceTypes.Add(DeviceType.Quest);
+ projectConfig.targetDeviceTypes.Add(DeviceType.Quest2);
+ projectConfig.allowOptional3DofHeadTracking = false;
+ projectConfig.handTrackingSupport = HandTrackingSupport.ControllersOnly;
+ projectConfig.handTrackingFrequency = HandTrackingFrequency.LOW;
+ projectConfig.handTrackingVersion = HandTrackingVersion.Default;
+ projectConfig.anchorSupport = AnchorSupport.Disabled;
+ projectConfig.trackedKeyboardSupport = TrackedKeyboardSupport.None;
+ projectConfig.renderModelSupport = RenderModelSupport.Disabled;
+ projectConfig.disableBackups = true;
+ projectConfig.enableNSCConfig = true;
+ projectConfig.skipUnneededShaders = false;
+ projectConfig.requiresSystemKeyboard = false;
+ projectConfig.experimentalFeaturesEnabled = false;
+ projectConfig.insightPassthroughEnabled = false;
+ AssetDatabase.CreateAsset(projectConfig, oculusProjectConfigAssetPath);
+ }
+ // Force migration to Quest device if still on legacy GearVR/Go device type
+ if (projectConfig.targetDeviceTypes.Contains((DeviceType)0)) // deprecated GearVR/Go device
+ {
+ projectConfig.targetDeviceTypes.Remove((DeviceType)0); // deprecated GearVR/Go device
+ if (!projectConfig.targetDeviceTypes.Contains(DeviceType.Quest))
+ {
+ projectConfig.targetDeviceTypes.Add(DeviceType.Quest);
+ }
+ if (!projectConfig.targetDeviceTypes.Contains(DeviceType.Quest2))
+ {
+ projectConfig.targetDeviceTypes.Add(DeviceType.Quest2);
+ }
+ }
+ return projectConfig;
+ }
+
+ public static void CommitProjectConfig(OVRProjectConfig projectConfig)
+ {
+ string oculusProjectConfigAssetPath = GetOculusProjectConfigAssetPath();
+ if (AssetDatabase.GetAssetPath(projectConfig) != oculusProjectConfigAssetPath)
+ {
+ Debug.LogWarningFormat("The asset path of ProjectConfig is wrong. Expect {0}, get {1}", oculusProjectConfigAssetPath, AssetDatabase.GetAssetPath(projectConfig));
+ }
+ EditorUtility.SetDirty(projectConfig);
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRScreenshotWizard.cs b/Assets/Oculus/VR/Editor/OVRScreenshotWizard.cs
new file mode 100644
index 0000000..edfb2c5
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRScreenshotWizard.cs
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+
+///
+/// From the selected transform, takes a cubemap screenshot that can be submitted with the application
+/// as a screenshot (or additionally used for reflection shaders).
+///
+class OVRScreenshotWizard : ScriptableWizard
+{
+ public enum TexFormat
+ {
+ JPEG, // 512kb at 1k x 1k resolution vs
+ PNG, // 5.3mb
+ }
+
+ public enum SaveMode {
+ SaveCubemapScreenshot,
+ SaveUnityCubemap,
+ SaveBoth,
+ }
+
+ public GameObject renderFrom = null;
+ public int size = 2048;
+ public SaveMode saveMode = SaveMode.SaveUnityCubemap;
+ public string cubeMapFolder = "Assets/Textures/Cubemaps";
+ public TexFormat textureFormat = TexFormat.PNG;
+
+ ///
+ /// Validates the user's input
+ ///
+ void OnWizardUpdate()
+ {
+ helpString = "Select a game object positioned in the place where\nyou want to render the cubemap screenshot from: ";
+ isValid = (renderFrom != null);
+ }
+
+ ///
+ /// Create the asset path if it is not available.
+ /// Assuming the newFolderPath is stated with "Assets", which is a requirement.
+ ///
+ static bool CreateAssetPath( string newFolderPath )
+ {
+ const int maxFoldersCount = 32;
+ string currentPath;
+ string[] pathFolders;
+
+ pathFolders = newFolderPath.Split (new char[]{ '/' }, maxFoldersCount);
+
+ if (!string.Equals ("Assets", pathFolders [0], System.StringComparison.OrdinalIgnoreCase))
+ {
+ Debug.LogError( "Folder path has to be started with \" Assets \" " );
+ return false;
+ }
+
+ currentPath = "Assets";
+ for (int i = 1; i < pathFolders.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(pathFolders[i]))
+ {
+ string newPath = currentPath + "/" + pathFolders[i];
+ if (!AssetDatabase.IsValidFolder(newPath))
+ AssetDatabase.CreateFolder(currentPath, pathFolders[i]);
+ currentPath = newPath;
+ }
+ }
+
+ Debug.Log( "Created path: " + currentPath );
+ return true;
+ }
+
+ ///
+ /// Renders the cubemap
+ ///
+ void OnWizardCreate()
+ {
+ if ( !AssetDatabase.IsValidFolder( cubeMapFolder ) )
+ {
+ if (!CreateAssetPath(cubeMapFolder))
+ {
+ Debug.LogError( "Created path failed: " + cubeMapFolder );
+ return;
+ }
+ }
+
+ bool existingCamera = true;
+ bool existingCameraStateSave = true;
+ Camera camera = renderFrom.GetComponent();
+ if (camera == null)
+ {
+ camera = renderFrom.AddComponent();
+ camera.farClipPlane = 10000f;
+ existingCamera = false;
+ }
+ else
+ {
+ existingCameraStateSave = camera.enabled;
+ camera.enabled = true;
+ }
+ // find the last screenshot saved
+ if (cubeMapFolder[cubeMapFolder.Length-1] != '/')
+ {
+ cubeMapFolder += "/";
+ }
+ int idx = 0;
+ string[] fileNames = Directory.GetFiles(cubeMapFolder);
+ foreach(string fileName in fileNames)
+ {
+ if (!fileName.ToLower().EndsWith(".cubemap"))
+ {
+ continue;
+ }
+ string temp = fileName.Replace(cubeMapFolder + "vr_screenshot_", string.Empty);
+ temp = temp.Replace(".cubemap", string.Empty);
+ int tempIdx = 0;
+ if (int.TryParse( temp, out tempIdx ))
+ {
+ if (tempIdx > idx)
+ {
+ idx = tempIdx;
+ }
+ }
+ }
+ string pathName = string.Format("{0}vr_screenshot_{1}.cubemap", cubeMapFolder, (++idx).ToString("d2"));
+ Cubemap cubemap = new Cubemap(size, TextureFormat.RGB24, false);
+
+ // render into cubemap
+ if ((camera != null) && (cubemap != null))
+ {
+ // set up cubemap defaults
+ OVRCubemapCapture.RenderIntoCubemap(camera, cubemap);
+ if (existingCamera)
+ {
+ camera.enabled = existingCameraStateSave;
+ }
+ else
+ {
+ DestroyImmediate(camera);
+ }
+ // generate a regular texture as well?
+ if ( ( saveMode == SaveMode.SaveCubemapScreenshot ) || ( saveMode == SaveMode.SaveBoth ) )
+ {
+ GenerateTexture(cubemap, pathName);
+ }
+
+ if ( ( saveMode == SaveMode.SaveUnityCubemap ) || ( saveMode == SaveMode.SaveBoth ) )
+ {
+ Debug.Log( "Saving: " + pathName );
+ // by default the unity cubemap isn't saved
+ AssetDatabase.CreateAsset( cubemap, pathName );
+ // reimport as necessary
+ AssetDatabase.SaveAssets();
+ // select it in the project tree so developers can find it
+ EditorGUIUtility.PingObject( cubemap );
+ Selection.activeObject = cubemap;
+ }
+ AssetDatabase.Refresh();
+ }
+ }
+
+ ///
+ /// Generates a NPOT 6x1 cubemap in the following format PX NX PY NY PZ NZ
+ ///
+ void GenerateTexture(Cubemap cubemap, string pathName)
+ {
+ // Encode the texture and save it to disk
+ pathName = pathName.Replace(".cubemap", (textureFormat == TexFormat.PNG) ? ".png" : ".jpg" ).ToLower();
+ pathName = pathName.Replace( cubeMapFolder.ToLower(), "" );
+ string format = textureFormat.ToString();
+ string fullPath = EditorUtility.SaveFilePanel( string.Format( "Save Cubemap Screenshot as {0}", format ), "", pathName, format.ToLower() );
+ if ( !string.IsNullOrEmpty( fullPath ) )
+ {
+ Debug.Log( "Saving: " + fullPath );
+ OVRCubemapCapture.SaveCubemapCapture(cubemap, fullPath);
+ }
+ }
+
+ ///
+ /// Unity Editor menu option to take a screenshot
+ ///
+ [MenuItem("Oculus/Tools/OVR Screenshot Wizard", false, 100000)]
+ static void TakeOVRScreenshot()
+ {
+ OVRScreenshotWizard wizard = ScriptableWizard.DisplayWizard("OVR Screenshot Wizard", "Render Cubemap");
+ if (wizard != null)
+ {
+ if (Selection.activeGameObject != null)
+ wizard.renderFrom = Selection.activeGameObject;
+ else
+ wizard.renderFrom = Camera.main.gameObject;
+
+ wizard.isValid = (wizard.renderFrom != null);
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRShaderBuildProcessor.cs b/Assets/Oculus/VR/Editor/OVRShaderBuildProcessor.cs
new file mode 100644
index 0000000..47944b8
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRShaderBuildProcessor.cs
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEditor.Build;
+using UnityEditor.Rendering;
+using UnityEngine;
+using UnityEngine.Rendering;
+
+public class OVRShaderBuildProcessor : IPreprocessShaders
+{
+ public int callbackOrder { get { return 0; } }
+
+ public void OnProcessShader(
+ Shader shader, ShaderSnippetData snippet, IList shaderCompilerData)
+ {
+ var projectConfig = OVRProjectConfig.GetProjectConfig();
+ if (projectConfig == null)
+ {
+ return;
+ }
+
+ if (!projectConfig.skipUnneededShaders)
+ {
+ return;
+ }
+
+ if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
+ {
+ return;
+ }
+
+ var strippedGraphicsTiers = new HashSet();
+
+ // Unity only uses shader Tier2 on Quest and Go (regardless of graphics API)
+ if (projectConfig.targetDeviceTypes.Contains(OVRProjectConfig.DeviceType.Quest) ||
+ projectConfig.targetDeviceTypes.Contains(OVRProjectConfig.DeviceType.Quest2))
+ {
+ strippedGraphicsTiers.Add(GraphicsTier.Tier1);
+ strippedGraphicsTiers.Add(GraphicsTier.Tier3);
+ }
+
+ if (strippedGraphicsTiers.Count == 0)
+ {
+ return;
+ }
+
+ for (int i = shaderCompilerData.Count - 1; i >= 0; --i)
+ {
+ if (strippedGraphicsTiers.Contains(shaderCompilerData[i].graphicsTier))
+ {
+ shaderCompilerData.RemoveAt(i);
+ }
+ }
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/OVRSystemProfilerPanel.cs b/Assets/Oculus/VR/Editor/OVRSystemProfilerPanel.cs
new file mode 100644
index 0000000..7452148
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/OVRSystemProfilerPanel.cs
@@ -0,0 +1,735 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Collections.Generic;
+using System;
+using System.Reflection;
+using UnityEngine;
+using UnityEditor;
+using System.Text;
+using System.IO;
+
+public class OVRSystemProfilerPanel : EditorWindow {
+ [MenuItem("Oculus/Tools/(Deprecated) Oculus Profiler Panel", false, 200000)]
+ public static void ShowWindow()
+ {
+ EditorWindow.GetWindow(typeof(OVRSystemProfilerPanel), false, "Oculus Profiler");
+ OVRPlugin.SendEvent("oculus_profiler_panel", "show_window");
+ }
+
+ bool showAndroidOptions = false;
+ OVRNetwork.OVRNetworkTcpClient tcpClient = new OVRNetwork.OVRNetworkTcpClient();
+ int remoteListeningPort = OVRSystemPerfMetrics.TcpListeningPort;
+
+ //OVRSystemPerfMetrics.PerfMetrics lastReceivedMetrics;
+
+ const int maxMetricsFrames = 120;
+ const float metricsHistoryDuration = 1.0f;
+
+ List receivedMetricsList = new List();
+ bool pauseReceiveMetrics = false;
+ bool repaintRequested = false;
+
+ const float labelWidth = 140.0f;
+ const float panelInnerRightPad = 6.0f;
+ const float progressBarPad = 5.0f;
+ const float progressBarHeight = 18.0f;
+
+ string androidSdkRootPath;
+ OVRADBTool adbTool;
+
+ private static GUIStyle odhCalloutBackgroundStyle;
+ private static GUIStyle odhCalloutTextStyle;
+
+ // The actual window code goes here
+ void OnGUI()
+ {
+ if (odhCalloutBackgroundStyle == null)
+ {
+ odhCalloutBackgroundStyle = new GUIStyle(EditorStyles.helpBox);
+ var odhCalloutBackgroundStyleTex = new Texture2D(1, 1);
+ odhCalloutBackgroundStyleTex.SetPixel(0, 0, new Color(0.9f, 0.8f, 0.2f, 0.2f));
+ odhCalloutBackgroundStyleTex.Apply();
+ odhCalloutBackgroundStyle.normal.background = odhCalloutBackgroundStyleTex;
+ }
+
+ if (odhCalloutTextStyle == null)
+ {
+ odhCalloutTextStyle = new GUIStyle(EditorStyles.label);
+ odhCalloutTextStyle.richText = true;
+ odhCalloutTextStyle.wordWrap = true;
+ }
+
+ // ODH Callout Section
+ GUILayout.BeginHorizontal(odhCalloutBackgroundStyle);
+ var script = MonoScript.FromScriptableObject(this);
+ string assetPath = AssetDatabase.GetAssetPath(script);
+ string editorPath = Path.GetDirectoryName(assetPath);
+ string odhIconPath = Path.Combine(editorPath, "Textures\\odh_icon.png");
+ Texture ODHIcon = (Texture)EditorGUIUtility.Load(odhIconPath);
+ GUILayout.Box(ODHIcon, GUILayout.Width(60.0f), GUILayout.Height(60.0f));
+
+ GUILayout.BeginVertical();
+
+ EditorGUILayout.LabelField("This tool is deprecated. Oculus recommends profiling builds through the Metrics section of "
+ + "Oculus Developer Hub, a desktop companion tool that streamlines the Quest development workflow.",
+ odhCalloutTextStyle);
+ GUIContent ODHLabel = new GUIContent("Download Oculus Developer Hub");
+#if UNITY_2021_1_OR_NEWER
+ if (EditorGUILayout.LinkButton(ODHLabel))
+#else
+ if (GUILayout.Button(ODHLabel, GUILayout.ExpandWidth(false)))
+#endif
+ {
+#if UNITY_EDITOR_WIN
+ Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-win/?source=unity");
+#elif UNITY_EDITOR_OSX
+ Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-mac/?source=unity");
+#endif
+ }
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ GUILayout.Space(15.0f);
+
+ showAndroidOptions = EditorGUILayout.Foldout(showAndroidOptions, "Android Tools");
+
+ if (showAndroidOptions)
+ {
+ GUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Android SDK root path: ", androidSdkRootPath);
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("Start Server"))
+ {
+ if (adbTool == null)
+ {
+ adbTool = new OVRADBTool(androidSdkRootPath);
+ }
+ if (adbTool.isReady)
+ {
+ int exitCode = adbTool.StartServer(null);
+ EditorUtility.DisplayDialog("ADB StartServer", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
+ }
+ else
+ {
+ EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
+ }
+ }
+ if (GUILayout.Button("Kill Server"))
+ {
+ if (adbTool == null)
+ {
+ adbTool = new OVRADBTool(androidSdkRootPath);
+ }
+ if (adbTool.isReady)
+ {
+ int exitCode = adbTool.KillServer(null);
+ EditorUtility.DisplayDialog("ADB KillServer", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
+ }
+ else
+ {
+ EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
+ }
+ }
+ if (GUILayout.Button("Forward Port"))
+ {
+ if (adbTool == null)
+ {
+ adbTool = new OVRADBTool(androidSdkRootPath);
+ }
+ if (adbTool.isReady)
+ {
+ int exitCode = adbTool.ForwardPort(remoteListeningPort, null);
+ EditorUtility.DisplayDialog("ADB ForwardPort", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
+ OVRPlugin.SendEvent("device_metrics_profiler", (exitCode == 0 ? "adb_forward_success" : "adb_forward_failure"));
+ }
+ else
+ {
+ EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
+ }
+ }
+ if (GUILayout.Button("Release Port"))
+ {
+ if (adbTool == null)
+ {
+ adbTool = new OVRADBTool(androidSdkRootPath);
+ }
+ if (adbTool.isReady)
+ {
+ int exitCode = adbTool.ReleasePort(remoteListeningPort, null);
+ EditorUtility.DisplayDialog("ADB ReleasePort", (exitCode == 0 ? "Success" : "Failure. ExitCode = " + exitCode.ToString()), "Ok");
+ }
+ else
+ {
+ EditorUtility.DisplayDialog("Can't locate ADBTool", adbTool.adbPath, "Ok");
+ }
+ }
+ GUILayout.EndHorizontal();
+ }
+
+ EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
+
+ GUILayout.BeginHorizontal();
+ remoteListeningPort = EditorGUILayout.DelayedIntField("Remote Port", remoteListeningPort);
+
+ if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Disconnected)
+ {
+ if (GUILayout.Button("Connect"))
+ {
+ ConnectPerfMetricsTcpServer();
+ pauseReceiveMetrics = false;
+ OVRPlugin.SendEvent("device_metrics_profiler", "connect");
+ }
+ }
+ else
+ {
+ if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Connecting)
+ {
+ if (GUILayout.Button("Connecting ... Click again to Cancel"))
+ {
+ DisconnectPerfMetricsTcpServer();
+ OVRPlugin.SendEvent("device_metrics_profiler", "cancel");
+ }
+ }
+ else
+ {
+ if (GUILayout.Button("Disconnect"))
+ {
+ DisconnectPerfMetricsTcpServer();
+ OVRPlugin.SendEvent("device_metrics_profiler", "disconnect");
+ }
+
+ if (GUILayout.Button(pauseReceiveMetrics ? "Continue" : "Pause"))
+ {
+ pauseReceiveMetrics = !pauseReceiveMetrics;
+ }
+ }
+ }
+ GUILayout.EndHorizontal();
+
+ EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
+
+ lock (receivedMetricsList)
+ {
+ PresentIntProperty("Frame Count", "frameCount");
+ PresentIntProperty("Dropped Frame Count", "compositorDroppedFrameCount");
+
+ float? avgFrameTime = GetAveragePerfValueFloat("deltaFrameTime");
+ if (avgFrameTime.HasValue)
+ {
+ float fps = 1.0f / avgFrameTime.Value;
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("FPS", GUILayout.Width(labelWidth));
+ EditorGUILayout.LabelField(string.Format("{0:F1}", fps));
+ EditorGUILayout.EndHorizontal();
+ }
+
+ int? deviceCpuClockLevel = GetLatestPerfValueInt("deviceCpuClockLevel");
+ int? deviceGpuClockLevel = GetLatestPerfValueInt("deviceGpuClockLevel");
+ float? deviceCpuClockFrequencyInMHz = GetLatestPerfValueFloat("deviceCpuClockFrequencyInMHz");
+ float? deviceGpuClockFrequencyInMHz = GetLatestPerfValueFloat("deviceGpuClockFrequencyInMHz");
+
+ if (deviceCpuClockLevel.HasValue || deviceCpuClockFrequencyInMHz.HasValue)
+ {
+ string cpuLabel;
+ string cpuText;
+ if (deviceCpuClockLevel.HasValue && deviceCpuClockFrequencyInMHz.HasValue)
+ {
+ cpuLabel = "CPU Level (Freq)";
+ cpuText = string.Format("{0} ({1:F0} MHz)", deviceCpuClockLevel, deviceCpuClockFrequencyInMHz);
+ }
+ else if (deviceCpuClockLevel.HasValue)
+ {
+ cpuLabel = "CPU Level";
+ cpuText = string.Format("{0}", deviceCpuClockLevel);
+ }
+ else
+ {
+ cpuLabel = "CPU Frequency";
+ cpuText = string.Format("{0:F0} MHz", deviceCpuClockFrequencyInMHz);
+ }
+ PresentText(cpuLabel, cpuText);
+ }
+
+ if (deviceGpuClockLevel.HasValue || deviceGpuClockFrequencyInMHz.HasValue)
+ {
+ string cpuLabel;
+ string cpuText;
+ if (deviceGpuClockLevel.HasValue && deviceGpuClockFrequencyInMHz.HasValue)
+ {
+ cpuLabel = "GPU Level (Freq)";
+ cpuText = string.Format("{0} ({1:F0} MHz)", deviceGpuClockLevel, deviceGpuClockFrequencyInMHz);
+ }
+ else if (deviceGpuClockLevel.HasValue)
+ {
+ cpuLabel = "GPU Level";
+ cpuText = string.Format("{0}", deviceGpuClockLevel);
+ }
+ else
+ {
+ cpuLabel = "GPU Frequency";
+ cpuText = string.Format("{0:F0} MHz", deviceGpuClockFrequencyInMHz);
+ }
+ PresentText(cpuLabel, cpuText);
+ }
+
+ PresentColumnTitles("Current", "Average", "Peak");
+
+ PresentFloatTimeInMs("Frame Time", "deltaFrameTime", 0.020f, true, true);
+ PresentFloatTimeInMs("App CPU Time", "appCpuTime", 0.020f, true, true);
+ PresentFloatTimeInMs("App GPU Time", "appGpuTime", 0.020f, true, true);
+ PresentFloatTimeInMs("Compositor CPU Time", "compositorCpuTime", 0.020f, true, true);
+ PresentFloatTimeInMs("Compositor GPU Time", "compositorGpuTime", 0.020f, true, true);
+ PresentFloatPercentage("CPU Util (Average)", "systemCpuUtilAveragePercentage", false, false);
+ PresentFloatPercentage("CPU Util (Worst Core)", "systemCpuUtilWorstPercentage", false, false);
+ PresentFloatPercentage("GPU Util", "systemGpuUtilPercentage", false, false);
+ }
+ }
+
+ void GetMetricsField(string propertyName, out FieldInfo baseFieldInfo, out FieldInfo validalityFieldInfo)
+ {
+ baseFieldInfo = typeof(OVRSystemPerfMetrics.PerfMetrics).GetField(propertyName);
+ validalityFieldInfo = typeof(OVRSystemPerfMetrics.PerfMetrics).GetField(propertyName + "_IsValid");
+ }
+
+ bool HasValidPerfMetrics(string propertyName)
+ {
+ FieldInfo baseFieldInfo, validalityFieldInfo;
+ GetMetricsField(propertyName, out baseFieldInfo, out validalityFieldInfo);
+
+ if (baseFieldInfo == null || (validalityFieldInfo != null && validalityFieldInfo.FieldType != typeof(bool)))
+ {
+ Debug.LogWarning("[OVRSystemProfilerPanel] Unable to find property " + propertyName);
+ return false;
+ }
+
+ if (validalityFieldInfo == null)
+ {
+ return true;
+ }
+
+ for (int i = receivedMetricsList.Count - 1; i >= 0; --i)
+ {
+ var metrics = receivedMetricsList[i];
+ if (validalityFieldInfo != null && (bool)validalityFieldInfo.GetValue(metrics))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ int? GetLatestPerfValueInt(string propertyName)
+ {
+ FieldInfo baseFieldInfo, validalityFieldInfo;
+ GetMetricsField(propertyName, out baseFieldInfo, out validalityFieldInfo);
+
+ if (baseFieldInfo == null || baseFieldInfo.FieldType != typeof(int) ||
+ (validalityFieldInfo != null && validalityFieldInfo.FieldType != typeof(bool)))
+ {
+ Debug.LogWarning("[OVRSystemProfilerPanel] GetLatestPerfValueInt(): Type mismatch");
+ return null;
+ }
+
+ if (receivedMetricsList.Count == 0)
+ {
+ return null;
+ }
+
+ for (int i = receivedMetricsList.Count - 1; i >= 0; --i)
+ {
+ var metrics = receivedMetricsList[i];
+ if (validalityFieldInfo == null || (validalityFieldInfo != null && (bool)validalityFieldInfo.GetValue(metrics)))
+ {
+ return (int)baseFieldInfo.GetValue(metrics);
+ }
+ }
+
+ return null;
+ }
+
+ float? GetLatestPerfValueFloat(string propertyName)
+ {
+ FieldInfo baseFieldInfo, validalityFieldInfo;
+ GetMetricsField(propertyName, out baseFieldInfo, out validalityFieldInfo);
+
+ if (baseFieldInfo == null || baseFieldInfo.FieldType != typeof(float) ||
+ (validalityFieldInfo != null && validalityFieldInfo.FieldType != typeof(bool)))
+ {
+ Debug.LogWarning("[OVRSystemProfilerPanel] GetLatestPerfValueFloat(): Type mismatch");
+ return null;
+ }
+
+ if (receivedMetricsList.Count == 0)
+ {
+ return null;
+ }
+
+ for (int i = receivedMetricsList.Count - 1; i >= 0; --i)
+ {
+ var metrics = receivedMetricsList[i];
+ if (validalityFieldInfo == null || (validalityFieldInfo != null && (bool)validalityFieldInfo.GetValue(metrics)))
+ {
+ return (float)baseFieldInfo.GetValue(metrics);
+ }
+ }
+
+ return null;
+ }
+
+ float? GetAveragePerfValueFloat(string propertyName)
+ {
+ FieldInfo baseFieldInfo, validalityFieldInfo;
+ GetMetricsField(propertyName, out baseFieldInfo, out validalityFieldInfo);
+
+ if (baseFieldInfo == null || baseFieldInfo.FieldType != typeof(float) ||
+ (validalityFieldInfo != null && validalityFieldInfo.FieldType != typeof(bool)))
+ {
+ Debug.LogWarning("[OVRSystemProfilerPanel] GetAveragePerfValueFloat(): Type mismatch");
+ return null;
+ }
+
+ int count = 0;
+ float sum = 0;
+
+ OVRSystemPerfMetrics.PerfMetrics lastMetrics = null;
+ int metricsIndex;
+ for (metricsIndex = receivedMetricsList.Count - 1; metricsIndex >= 0; --metricsIndex)
+ {
+ var metrics = receivedMetricsList[metricsIndex];
+ if (validalityFieldInfo != null && !(bool)validalityFieldInfo.GetValue(metrics))
+ {
+ continue;
+ }
+ lastMetrics = metrics;
+ break;
+ }
+
+ if (lastMetrics == null)
+ {
+ return null;
+ }
+
+ for (; metricsIndex >=0; -- metricsIndex)
+ {
+ var metrics = receivedMetricsList[metricsIndex];
+ if (metrics.frameTime < lastMetrics.frameTime - metricsHistoryDuration)
+ {
+ break;
+ }
+ if (validalityFieldInfo != null && !(bool)validalityFieldInfo.GetValue(metrics))
+ {
+ continue;
+ }
+ sum += (float)baseFieldInfo.GetValue(metrics);
+ count++;
+ }
+
+ if (count == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return sum / count;
+ }
+ }
+
+ float? GetMaxPerfValueFloat(string propertyName)
+ {
+ FieldInfo baseFieldInfo, validalityFieldInfo;
+ GetMetricsField(propertyName, out baseFieldInfo, out validalityFieldInfo);
+
+ if (baseFieldInfo == null || baseFieldInfo.FieldType != typeof(float) ||
+ (validalityFieldInfo != null && validalityFieldInfo.FieldType != typeof(bool)))
+ {
+ Debug.LogWarning("[OVRSystemProfilerPanel] GetMaxPerfValueFloat(): Type mismatch");
+ return null;
+ }
+
+ OVRSystemPerfMetrics.PerfMetrics lastMetrics = null;
+ int metricsIndex;
+ for (metricsIndex = receivedMetricsList.Count - 1; metricsIndex >= 0; --metricsIndex)
+ {
+ var metrics = receivedMetricsList[metricsIndex];
+ if (validalityFieldInfo != null && !(bool)validalityFieldInfo.GetValue(metrics))
+ {
+ continue;
+ }
+ lastMetrics = metrics;
+ break;
+ }
+
+ if (lastMetrics == null)
+ {
+ return null;
+ }
+
+ float? result = null;
+
+ for (; metricsIndex >= 0; --metricsIndex)
+ {
+ var metrics = receivedMetricsList[metricsIndex];
+ if (metrics.frameTime < lastMetrics.frameTime - metricsHistoryDuration)
+ {
+ break;
+ }
+ if (validalityFieldInfo != null && !(bool)validalityFieldInfo.GetValue(metrics))
+ {
+ continue;
+ }
+ else
+ {
+ float value = (float)baseFieldInfo.GetValue(metrics);
+ if (!result.HasValue || result.Value < value)
+ {
+ result = value;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ void PresentFloatPercentage(string label, string propertyName, bool displayAverage, bool displayMaximum)
+ {
+ float? lastValue = GetLatestPerfValueFloat(propertyName);
+ if (!lastValue.HasValue)
+ {
+ return;
+ }
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.Width(labelWidth));
+
+ Rect r = EditorGUILayout.BeginVertical();
+
+ float barWidth = (r.width - panelInnerRightPad - progressBarPad * 2) / 3.0f;
+ EditorGUI.ProgressBar(new Rect(r.x, r.y, barWidth, r.height), lastValue.Value, string.Format("{0:F1}%", lastValue.Value * 100.0f));
+
+ if (displayAverage)
+ {
+ float? averageValue = GetAveragePerfValueFloat(propertyName);
+ if (averageValue.HasValue)
+ {
+ EditorGUI.ProgressBar(new Rect(r.x + barWidth + progressBarPad, r.y, barWidth, r.height), averageValue.Value, string.Format("{0:F1}%", averageValue.Value * 100.0f));
+ }
+ }
+
+ if (displayMaximum)
+ {
+ float? maxValue = GetMaxPerfValueFloat(propertyName);
+ if (maxValue.HasValue)
+ {
+ EditorGUI.ProgressBar(new Rect(r.x + (barWidth + progressBarPad) * 2, r.y, barWidth, r.height), maxValue.Value, string.Format("{0:F1}%", maxValue.Value * 100.0f));
+ }
+ }
+
+ GUILayout.Space(progressBarHeight);
+
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.EndHorizontal();
+ }
+
+ void PresentFloatTimeInMs(string label, string propertyName, float maxScale, bool displayAverage, bool displayMaximum)
+ {
+ float? lastValue = GetLatestPerfValueFloat(propertyName);
+ if (!lastValue.HasValue)
+ {
+ return;
+ }
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.Width(labelWidth));
+
+ Rect r = EditorGUILayout.BeginVertical();
+
+ float barWidth = (r.width - panelInnerRightPad - progressBarPad * 2) / 3.0f;
+ EditorGUI.ProgressBar(new Rect(r.x, r.y, barWidth, r.height), lastValue.Value/maxScale, string.Format("{0:F1} ms", lastValue.Value * 1000.0f));
+
+ if (displayAverage)
+ {
+ float? averageValue = GetAveragePerfValueFloat(propertyName);
+ if (averageValue.HasValue)
+ {
+ EditorGUI.ProgressBar(new Rect(r.x + barWidth + progressBarPad, r.y, barWidth, r.height), averageValue.Value / maxScale, string.Format("{0:F1} ms", averageValue.Value * 1000.0f));
+ }
+ }
+
+ if (displayMaximum)
+ {
+ float? maxValue = GetMaxPerfValueFloat(propertyName);
+ if (maxValue.HasValue)
+ {
+ EditorGUI.ProgressBar(new Rect(r.x + (barWidth + progressBarPad) * 2, r.y, barWidth, r.height), maxValue.Value / maxScale, string.Format("{0:F1} ms", maxValue.Value * 1000.0f));
+ }
+ }
+
+ GUILayout.Space(progressBarHeight);
+
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.EndHorizontal();
+ }
+
+ void PresentIntProperty(string label, string propertyName)
+ {
+ int? lastValue = GetLatestPerfValueInt(propertyName);
+ if (!lastValue.HasValue)
+ {
+ return;
+ }
+
+ PresentText(label, lastValue.Value.ToString());
+ }
+
+ void PresentText(string label, string text)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(label, GUILayout.Width(labelWidth));
+ EditorGUILayout.LabelField(text);
+ EditorGUILayout.EndHorizontal();
+ }
+
+ void PresentColumnTitles(string title0, string title1, string title2)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("", GUILayout.Width(labelWidth));
+
+ float windowWidth = position.width;
+ float barWidth = (windowWidth - labelWidth - panelInnerRightPad * 3 ) / 3.0f;
+ EditorGUILayout.LabelField(title0, GUILayout.Width(barWidth));
+ EditorGUILayout.LabelField(title1, GUILayout.Width(barWidth));
+ EditorGUILayout.LabelField(title2, GUILayout.Width(barWidth));
+
+ EditorGUILayout.EndHorizontal();
+ }
+
+ // Called as the new window is opened.
+ private void Awake()
+ {
+ InitializeAndroidSdkPath();
+ minSize = new Vector2(400, 300);
+ }
+
+ void InitializeAndroidSdkPath()
+ {
+ androidSdkRootPath = OVRConfig.Instance.GetAndroidSDKPath();
+ }
+
+ // OnDestroy is called to close the EditorWindow window.
+ private void OnDestroy()
+ {
+ DisconnectPerfMetricsTcpServer();
+ }
+
+ // Called multiple times per second on all visible windows.
+ private void Update()
+ {
+ if (tcpClient != null && tcpClient.Connected)
+ {
+ tcpClient.Tick();
+ }
+
+ if (repaintRequested)
+ {
+ Repaint();
+ repaintRequested = false;
+ }
+ }
+
+ void OnConnectionStateChanged()
+ {
+ repaintRequested = true;
+
+ if (tcpClient.connectionState == OVRNetwork.OVRNetworkTcpClient.ConnectionState.Disconnected)
+ {
+ tcpClient.connectionStateChangedCallback -= OnConnectionStateChanged;
+ tcpClient.payloadReceivedCallback -= OnPayloadReceived;
+ }
+ }
+
+ void OnPayloadReceived(int payloadType, byte[] buffer, int start, int length)
+ {
+ if (payloadType == OVRSystemPerfMetrics.PayloadTypeMetrics)
+ {
+ string message = Encoding.UTF8.GetString(buffer, start, length);
+ OnMessageReceived(message);
+ }
+ else
+ {
+ Debug.LogWarningFormat("[OVRSystemProfilerPanel] unrecongized payload type {0}", payloadType);
+ }
+ }
+
+ void OnMessageReceived(string message)
+ {
+ if (pauseReceiveMetrics)
+ {
+ return;
+ }
+
+ var metrics = new OVRSystemPerfMetrics.PerfMetrics();
+ if (!metrics.LoadFromJSON(message))
+ {
+ Debug.LogWarning("Cannot analyze metrics: " + message);
+ return;
+ }
+ lock(receivedMetricsList)
+ {
+ if (receivedMetricsList.Count >= maxMetricsFrames)
+ {
+ receivedMetricsList.RemoveAt(0);
+ }
+ receivedMetricsList.Add(metrics);
+ }
+ repaintRequested = true;
+ }
+
+ void ConnectPerfMetricsTcpServer()
+ {
+ tcpClient.connectionStateChangedCallback += OnConnectionStateChanged;
+ tcpClient.payloadReceivedCallback += OnPayloadReceived;
+
+ tcpClient.Connect(remoteListeningPort);
+
+ EditorApplication.playModeStateChanged += OnApplicationPlayModeStateChanged;
+ }
+
+ void DisconnectPerfMetricsTcpServer()
+ {
+ EditorApplication.playModeStateChanged -= OnApplicationPlayModeStateChanged;
+
+ tcpClient.Disconnect();
+ }
+
+ void OnApplicationPlayModeStateChanged(PlayModeStateChange change)
+ {
+ Debug.LogFormat("[OVRSystemPerfMetricsWindow] OnApplicationPlayModeStateChanged {0}", change.ToString());
+ if (change == PlayModeStateChange.ExitingPlayMode)
+ {
+ tcpClient.Disconnect();
+ }
+ }
+
+}
diff --git a/Assets/Oculus/VR/Editor/Oculus.VR.Editor.asmdef b/Assets/Oculus/VR/Editor/Oculus.VR.Editor.asmdef
new file mode 100644
index 0000000..7fe7d49
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/Oculus.VR.Editor.asmdef
@@ -0,0 +1,47 @@
+{
+ "name": "Oculus.VR.Editor",
+ "rootNamespace": "",
+ "references": [
+ "Oculus.VR",
+ "Unity.XR.Oculus",
+ "Unity.XR.OpenXR",
+ "Unity.XR.OpenXR.Editor"
+ ],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [
+ {
+ "name": "com.unity.xr.management",
+ "expression": "",
+ "define": "USING_XR_MANAGEMENT"
+ },
+ {
+ "name": "com.unity.xr.oculus",
+ "expression": "",
+ "define": "USING_XR_SDK_OCULUS"
+ },
+ {
+ "name": "com.unity.xr.openxr",
+ "expression": "",
+ "define": "USING_XR_SDK_OPENXR"
+ },
+ {
+ "name": "com.unity.xr.oculus",
+ "expression": "1.4.0",
+ "define": "USING_COMPATIBLE_OCULUS_XR_PLUGIN_VERSION"
+ },
+ {
+ "name": "com.unity.xr.oculus",
+ "expression": "1.7.0",
+ "define": "PRIORITIZE_OCULUS_XR_SETTINGS"
+ }
+ ],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Assets/Oculus/VR/Editor/PathHelper.cs b/Assets/Oculus/VR/Editor/PathHelper.cs
new file mode 100644
index 0000000..82571c2
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/PathHelper.cs
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * Licensed under the Oculus SDK License Agreement (the "License");
+ * you may not use the Oculus SDK except in compliance with the License,
+ * which is provided at the time of installation or download, or which
+ * otherwise accompanies this software in either electronic or hard copy form.
+ *
+ * You may obtain a copy of the License at
+ *
+ * https://developer.oculus.com/licenses/oculussdk/
+ *
+ * Unless required by applicable law or agreed to in writing, the Oculus SDK
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.IO;
+
+public static class PathHelper
+{
+ public static string MakeRelativePath(string fromPath, string toPath)
+ {
+ var fromUri = new Uri(Path.GetFullPath(fromPath));
+ var toUri = new Uri(Path.GetFullPath(toPath));
+
+ if (fromUri.Scheme != toUri.Scheme)
+ {
+ return toPath;
+ }
+
+ var relativeUri = fromUri.MakeRelativeUri(toUri);
+ var relativePath = Uri.UnescapeDataString(relativeUri.ToString());
+
+ if (toUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase))
+ {
+ relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+ }
+
+ return relativePath;
+ }
+}
diff --git a/Assets/Oculus/VR/Editor/Scenes/OVRTransitionScene.unity b/Assets/Oculus/VR/Editor/Scenes/OVRTransitionScene.unity
new file mode 100644
index 0000000..82bf5e6
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/Scenes/OVRTransitionScene.unity
@@ -0,0 +1,671 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 8
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 0
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 11
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_TemporalCoherenceThreshold: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 1
+ m_LightmapEditorSettings:
+ serializedVersion: 9
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_TextureWidth: 1024
+ m_TextureHeight: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_FinalGather: 0
+ m_FinalGatherFiltering: 1
+ m_FinalGatherRayCount: 256
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 1
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 512
+ m_PVRBounces: 2
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVRFilteringMode: 1
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 5
+ m_PVRFilteringGaussRadiusAO: 2
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ShowResolutionOverlay: 1
+ m_LightingDataAsset: {fileID: 0}
+ m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &25651912
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 25651914}
+ - component: {fileID: 25651913}
+ m_Layer: 0
+ m_Name: Directional Light
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!108 &25651913
+Light:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 25651912}
+ m_Enabled: 1
+ serializedVersion: 8
+ m_Type: 1
+ m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+ m_Intensity: 1
+ m_Range: 10
+ m_SpotAngle: 30
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 2
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_Lightmapping: 4
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 0
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
+--- !u!4 &25651914
+Transform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 25651912}
+ m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
+ m_LocalPosition: {x: 0, y: 3, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1 &1500274086
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 1500274089}
+ - component: {fileID: 1500274088}
+ - component: {fileID: 1500274087}
+ m_Layer: 0
+ m_Name: EventSystem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1500274087
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1500274086}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1077351063, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_HorizontalAxis: Horizontal
+ m_VerticalAxis: Vertical
+ m_SubmitButton: Submit
+ m_CancelButton: Cancel
+ m_InputActionsPerSecond: 10
+ m_RepeatDelay: 0.5
+ m_ForceModuleActive: 0
+--- !u!114 &1500274088
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1500274086}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -619905303, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_FirstSelected: {fileID: 0}
+ m_sendNavigationEvents: 1
+ m_DragThreshold: 5
+--- !u!4 &1500274089
+Transform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1500274086}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1802766121
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 1802766122}
+ - component: {fileID: 1802766124}
+ - component: {fileID: 1802766123}
+ m_Layer: 5
+ m_Name: Title
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1802766122
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1802766121}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 2026874869}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 1}
+ m_AnchorMax: {x: 0.5, y: 1}
+ m_AnchoredPosition: {x: 0, y: -15}
+ m_SizeDelta: {x: 290, y: 30}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1802766123
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1802766121}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
+ Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 24
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 2
+ m_MaxSize: 40
+ m_Alignment: 3
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: OVR Transition Scene
+--- !u!222 &1802766124
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1802766121}
+--- !u!1 &1955265764
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 1955265765}
+ - component: {fileID: 1955265767}
+ - component: {fileID: 1955265766}
+ m_Layer: 5
+ m_Name: Log
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1955265765
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1955265764}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 2026874869}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0.5}
+ m_AnchorMax: {x: 0.5, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: -15}
+ m_SizeDelta: {x: 290, y: 270}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1955265766
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1955265764}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
+ Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 14
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 10
+ m_MaxSize: 40
+ m_Alignment: 0
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 1
+ m_LineSpacing: 1
+ m_Text:
+--- !u!222 &1955265767
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 1955265764}
+--- !u!1 &2026874868
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 2026874869}
+ - component: {fileID: 2026874871}
+ - component: {fileID: 2026874870}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2026874869
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2026874868}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 1802766122}
+ - {fileID: 1955265765}
+ m_Father: {fileID: 2063971652}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &2026874870
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2026874868}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
+ Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+ m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+--- !u!222 &2026874871
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2026874868}
+--- !u!1 &2060762002
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 2060762004}
+ - component: {fileID: 2060762003}
+ m_Layer: 0
+ m_Name: OVRSceneLoader
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &2060762003
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2060762002}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a6d444f79f5ee4646b26c6d746385e80, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ sceneCheckIntervalSeconds: 1
+ mainCanvas: {fileID: 2063971651}
+ logTextBox: {fileID: 1955265766}
+--- !u!4 &2060762004
+Transform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2060762002}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 2063971652}
+ m_Father: {fileID: 0}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &2063971648
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 2063971652}
+ - component: {fileID: 2063971651}
+ - component: {fileID: 2063971650}
+ - component: {fileID: 2063971649}
+ m_Layer: 5
+ m_Name: Canvas
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &2063971649
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2063971648}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1301386320, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!114 &2063971650
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2063971648}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1980459831, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+--- !u!223 &2063971651
+Canvas:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2063971648}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 2
+ m_Camera: {fileID: 2122420806}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 0
+ m_TargetDisplay: 0
+--- !u!224 &2063971652
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2063971648}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 1}
+ m_LocalScale: {x: 0.003, y: 0.003, z: 0.003}
+ m_Children:
+ - {fileID: 2026874869}
+ m_Father: {fileID: 2060762004}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 300, y: 300}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &2122420804
+GameObject:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 5
+ m_Component:
+ - component: {fileID: 2122420807}
+ - component: {fileID: 2122420806}
+ - component: {fileID: 2122420805}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &2122420805
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2122420804}
+ m_Enabled: 1
+--- !u!20 &2122420806
+Camera:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2122420804}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 2
+ m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &2122420807
+Transform:
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 2122420804}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Oculus/VR/Editor/Textures/odh_icon.png b/Assets/Oculus/VR/Editor/Textures/odh_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..f11ce20928b5e94d057572b7ad563b3c09cff3b6
GIT binary patch
literal 7984
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
aRk{e1B*t?d^#2EN1GLn2)qbehMEnQP2s#n~
literal 0
HcmV?d00001
diff --git a/Assets/Oculus/VR/Editor/network_sec_config.xml b/Assets/Oculus/VR/Editor/network_sec_config.xml
new file mode 100644
index 0000000..e8413b0
--- /dev/null
+++ b/Assets/Oculus/VR/Editor/network_sec_config.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/Assets/Oculus/VR/LICENSE.txt b/Assets/Oculus/VR/LICENSE.txt
new file mode 100644
index 0000000..56e0eec
--- /dev/null
+++ b/Assets/Oculus/VR/LICENSE.txt
@@ -0,0 +1,2 @@
+Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at https://developer.oculus.com/licenses/oculussdk/
diff --git a/Assets/Oculus/VR/Materials/Arrow.mat b/Assets/Oculus/VR/Materials/Arrow.mat
new file mode 100644
index 0000000..4424aeb
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/Arrow.mat
@@ -0,0 +1,64 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: Arrow
+ m_Shader: {fileID: 10101, guid: 0000000000000000e000000000000000, type: 0}
+ m_ShaderKeywords: []
+ m_CustomRenderQueue: -1
+ m_SavedProperties:
+ serializedVersion: 2
+ m_TexEnvs:
+ data:
+ first:
+ name: _MainTex
+ second:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ data:
+ first:
+ name: _Shininess
+ second: .699999988
+ data:
+ first:
+ name: _Stencil
+ second: 0
+ data:
+ first:
+ name: _StencilReadMask
+ second: 255
+ data:
+ first:
+ name: _StencilWriteMask
+ second: 255
+ data:
+ first:
+ name: _StencilComp
+ second: 8
+ data:
+ first:
+ name: _StencilOp
+ second: 1
+ data:
+ first:
+ name: _ColorMask
+ second: 15
+ m_Colors:
+ data:
+ first:
+ name: _Color
+ second: {r: 1, g: 0, b: 0, a: 1}
+ data:
+ first:
+ name: _Emission
+ second: {r: 0, g: 0, b: 0, a: 0}
+ data:
+ first:
+ name: _SpecColor
+ second: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/VR/Materials/BasicHandMaterial.mat b/Assets/Oculus/VR/Materials/BasicHandMaterial.mat
new file mode 100644
index 0000000..e928554
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/BasicHandMaterial.mat
@@ -0,0 +1,76 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: BasicHandMaterial
+ m_Shader: {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 2800000, guid: c00bf1ce0c68d5646ad7ea3d26b3486d, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 0.508
+ - _Glossiness: 0.389
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/VR/Materials/CubeMaterial.mat b/Assets/Oculus/VR/Materials/CubeMaterial.mat
new file mode 100644
index 0000000..358deb1
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/CubeMaterial.mat
@@ -0,0 +1,29 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 5
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: CubeMaterial
+ m_Shader: {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 5
+ m_CustomRenderQueue: -1
+ m_SavedProperties:
+ serializedVersion: 2
+ m_TexEnvs:
+ data:
+ first:
+ name: _MainTex
+ second:
+ m_Texture: {fileID: 2800000, guid: 020d1a102a7f2a14ebf6cefe7b977303, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats: {}
+ m_Colors:
+ data:
+ first:
+ name: _Color
+ second: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/VR/Materials/GazePointer.mat b/Assets/Oculus/VR/Materials/GazePointer.mat
new file mode 100644
index 0000000..b9a77e7
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/GazePointer.mat
@@ -0,0 +1,43 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: GazePointer
+ m_Shader: {fileID: 4800000, guid: 38ad33c152e32ee46a9bbbb0e656f7e1, type: 3}
+ m_ShaderKeywords:
+ m_LightmapFlags: 5
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _AlphaTex:
+ m_Texture: {fileID: 2800000, guid: 8b000a1e9077a124f9ad4e81392fccba, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _Illum:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 2800000, guid: 82026cb669304dc4897d2c11d3753141, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _AlphaCutoff: 0
+ - _Cutoff: 0.5
+ - _EmissionLM: 0
+ - _EndRadius: 0.8
+ - _Fill: 0.5
+ - _InvFade: 1.11
+ - _StartRadius: 0.2
+ m_Colors:
+ - _Color: {r: 0, g: 1, b: 0.006896496, a: 1}
+ - _TintColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
diff --git a/Assets/Oculus/VR/Materials/OVRTrackedKeyboard/BillboardMask.mat b/Assets/Oculus/VR/Materials/OVRTrackedKeyboard/BillboardMask.mat
new file mode 100644
index 0000000..5983d0b
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/OVRTrackedKeyboard/BillboardMask.mat
@@ -0,0 +1,94 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: BillboardMask
+ m_Shader: {fileID: 4800000, guid: 488b60f10f2b48e4f9ff49f81068b166, type: 3}
+ m_ShaderKeywords: _EDGEFADING_ON _VIGNETTE_ON
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 1
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _ColorMultiply: 2
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _DstBlendAlphaMode: 1
+ - _DstBlendMode: 3
+ - _EdgeFading: 1
+ - _FadingFalloff: 0.05
+ - _Falloff: 4
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Intensity: 0.75
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _Power: 14
+ - _Scale: 0.055
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _SrcBlendAlphaMode: 5
+ - _SrcBlendMode: 0
+ - _UVSec: 0
+ - _Vignette: 1
+ - _YOffset: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ - _Distort: {r: 1, g: 1, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+ - _KeyboardPosition: {r: 0, g: 0, b: 0, a: 0}
+ - _KeyboardRotation: {r: 0, g: 0, b: 0, a: 0}
+ - _KeyboardScale: {r: 1, g: 0.1, b: 1, a: 1}
diff --git a/Assets/Oculus/VR/Materials/PlainMaterial.mat b/Assets/Oculus/VR/Materials/PlainMaterial.mat
new file mode 100644
index 0000000..b43379d
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/PlainMaterial.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: PlainMaterial
+ m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/VR/Materials/SelectionRay.mat b/Assets/Oculus/VR/Materials/SelectionRay.mat
new file mode 100644
index 0000000..f95d3f7
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/SelectionRay.mat
@@ -0,0 +1,78 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: SelectionRay
+ m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 0, g: 0, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+ m_BuildTextureStacks: []
diff --git a/Assets/Oculus/VR/Materials/SelfieImage.mat b/Assets/Oculus/VR/Materials/SelfieImage.mat
new file mode 100644
index 0000000..8d87b61
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/SelfieImage.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: SelfieImage
+ m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 8400000, guid: 57e76b1057d5b458badbc9979747256e, type: 2}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/VR/Materials/SelfieMat.mat b/Assets/Oculus/VR/Materials/SelfieMat.mat
new file mode 100644
index 0000000..2acae71
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/SelfieMat.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 6
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: SelfieMat
+ m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+ m_ShaderKeywords:
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap: {}
+ disabledShaderPasses: []
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 8400000, guid: 3a9b7034483494d4c9ab590cbe537560, type: 2}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ - _BumpScale: 1
+ - _Cutoff: 0.5
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _GlossMapScale: 1
+ - _Glossiness: 0.5
+ - _GlossyReflections: 1
+ - _Metallic: 0
+ - _Mode: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.02
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _UVSec: 0
+ - _ZWrite: 1
+ m_Colors:
+ - _Color: {r: 1, g: 1, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/VR/Materials/cursor_timer_material.mat b/Assets/Oculus/VR/Materials/cursor_timer_material.mat
new file mode 100644
index 0000000..3a700ee
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/cursor_timer_material.mat
@@ -0,0 +1,43 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: cursor_timer_material
+ m_Shader: {fileID: 4800000, guid: b95caf64e2cc3614892026a94bb2be84, type: 3}
+ m_ShaderKeywords: []
+ m_CustomRenderQueue: -1
+ m_SavedProperties:
+ serializedVersion: 2
+ m_TexEnvs:
+ data:
+ first:
+ name: _MainTex
+ second:
+ m_Texture: {fileID: 2800000, guid: 79a33e7a7166c6142ad50f46a9a23d3e, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ data:
+ first:
+ name: _ColorRamp
+ second:
+ m_Texture: {fileID: 2800000, guid: 8929c8bc5148a624b8c9d6df0ee6f0ca, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats:
+ data:
+ first:
+ name: _Cutoff
+ second: 1
+ data:
+ first:
+ name: _ColorRampOffset
+ second: 0
+ m_Colors:
+ data:
+ first:
+ name: _Color
+ second: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/VR/Materials/gaze_cursor.mat b/Assets/Oculus/VR/Materials/gaze_cursor.mat
new file mode 100644
index 0000000..9f41982
--- /dev/null
+++ b/Assets/Oculus/VR/Materials/gaze_cursor.mat
@@ -0,0 +1,28 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_PrefabParentObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_Name: gaze_cursor
+ m_Shader: {fileID: 4800000, guid: 05b53b473302943b58b8e33c93a38dac, type: 3}
+ m_ShaderKeywords: []
+ m_CustomRenderQueue: -1
+ m_SavedProperties:
+ serializedVersion: 2
+ m_TexEnvs:
+ data:
+ first:
+ name: _MainTex
+ second:
+ m_Texture: {fileID: 2800000, guid: 077028084dceb584798dade6c58d1978, type: 3}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Floats: {}
+ m_Colors:
+ data:
+ first:
+ name: _Color
+ second: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/VR/Meshes/HandTracking/OculusHand_L.fbx b/Assets/Oculus/VR/Meshes/HandTracking/OculusHand_L.fbx
new file mode 100644
index 0000000000000000000000000000000000000000..5421e8014f259e295a139348c45f1802afc4ddd2
GIT binary patch
literal 335584
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