上传YomovSDK

This commit is contained in:
Sora丶kong
2026-03-03 03:15:46 +08:00
parent 9096da7e6c
commit eb97f31065
6477 changed files with 1932208 additions and 3 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 929b3d36d7ab7b342b57089d4482c724
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d12249ab0d5646ff8f3e46fab464bd46
timeCreated: 1677813281

View File

@@ -0,0 +1,80 @@
using System;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.XR.OpenXR.Features.MetaQuestSupport;
namespace UnityEditor.XR.OpenXR.Features.MetaQuestSupport
{
internal class MetaQuestFeatureBuildHooks : OpenXRFeatureBuildHooks
{
private const string kLateLatchingSupported = "xr-latelatching-enabled";
private const string kLateLatchingDebug = "xr-latelatchingdebug-enabled";
private const string kVulkanExtensionFragmentDensityMap = "xr-vulkan-extension-fragment-density-map-enabled";
private const string kLowLatencyAudioEnabled = "xr-low-latency-audio-enabled";
private const string kRequireBackbufferTextures = "xr-require-backbuffer-textures";
private const string kKeyboardOverlayEnabled = "xr-keyboard-overlay-enabled";
private const string kPipelineCacheEnabled = "xr-pipeline-cache-enabled";
private const string kSkipB10G11R11SpecialCasing = "xr-skip-B10G11R11-special-casing";
private const string kHideMemorylessRenderTexture = "xr-hide-memoryless-render-texture";
private const string kSkipAudioBufferSizeCheck = "xr-skip-audio-buffer-size-check";
private const string kUsableCoreMaskEnabled = "xr-usable-core-mask-enabled";
private const string kMvpvvEnabled = "xr-mvpvv-enabled";
private MetaQuestFeature GetMetaQuestFeature()
{
var featureGuids = AssetDatabase.FindAssets("t:" + typeof(MetaQuestFeature).Name);
// we should only find one
if (featureGuids.Length != 1)
return null;
string path = AssetDatabase.GUIDToAssetPath(featureGuids[0]);
return AssetDatabase.LoadAssetAtPath<MetaQuestFeature>(path);
}
public override int callbackOrder => 2;
public override Type featureType => typeof(MetaQuestFeature);
protected override void OnPreprocessBuildExt(BuildReport report)
{
}
protected override void OnProcessBootConfigExt(BuildReport report, BootConfigBuilder builder)
{
if (report.summary.platform != BuildTarget.Android)
return;
var item = GetMetaQuestFeature();
if (item == null)
{
Debug.Log("Unable to locate the MetaQuestFeature Asset");
return;
}
// Update the boot config
builder.SetBootConfigBoolean(kLateLatchingSupported, item.lateLatchingMode);
builder.SetBootConfigBoolean(kLateLatchingDebug, item.lateLatchingDebug);
builder.SetBootConfigBoolean(kVulkanExtensionFragmentDensityMap, true);
builder.SetBootConfigBoolean(kLowLatencyAudioEnabled, true);
builder.SetBootConfigBoolean(kRequireBackbufferTextures, false);
builder.SetBootConfigBoolean(kKeyboardOverlayEnabled, true);
builder.SetBootConfigBoolean(kPipelineCacheEnabled, true);
builder.SetBootConfigBoolean(kSkipB10G11R11SpecialCasing, true);
builder.SetBootConfigBoolean(kHideMemorylessRenderTexture, true);
builder.SetBootConfigBoolean(kSkipAudioBufferSizeCheck, true);
builder.SetBootConfigBoolean(kUsableCoreMaskEnabled, true);
#if UNITY_6000_1_OR_NEWER
builder.SetBootConfigBoolean(kMvpvvEnabled, item.optimizeMultiviewRenderRegions);
#endif
}
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
{
}
protected override void OnPostprocessBuildExt(BuildReport report)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e6e545add294555ba87b5c10bab2467
timeCreated: 1677813385

View File

@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MetaQuestSupport;
namespace UnityEditor.XR.OpenXR.Features.MetaQuestSupport
{
[CustomEditor(typeof(MetaQuestFeature))]
internal class MetaQuestFeatureEditor : Editor
{
private bool m_ShowAndroidExperimental = false;
private bool m_LateLatchingModeEnabled;
private bool m_LateLatchingDebug;
private static GUIContent s_LateLatchingSupportedLabel = EditorGUIUtility.TrTextContent("Late Latching (Vulkan)");
private static GUIContent s_LateLatchingDebugLabel = EditorGUIUtility.TrTextContent("Late Latching Debug Mode");
private static GUIContent s_ShowAndroidExperimentalLabel = EditorGUIUtility.TrTextContent("Experimental", "Experimental settings that are under active development and should be used with caution.");
#if UNITY_6000_1_OR_NEWER
private static GUIContent s_OptimizeMultiviewRenderRegionsLabel = EditorGUIUtility.TrTextContent("Optimize Multiview Render Regions (Vulkan)", "Activates Multiview Render Regions optimizations at application start. Requires usage of Unity 6.1 or later, Vulkan as the Graphics API, Render Mode set to Multi-view and Symmetric rendering enabled.");
#endif
struct TargetDeviceProperty
{
public SerializedProperty property;
public GUIContent label;
}
private List<TargetDeviceProperty> targetDeviceProperties;
private Dictionary<string, bool> activeTargetDevices;
private SerializedProperty forceRemoveInternetPermission;
private SerializedProperty systemSplashScreen;
private SerializedProperty symmetricProjection;
private SerializedProperty m_LateLatchingModeProperty;
private SerializedProperty m_LateLatchingDebugProperty;
private SerializedProperty optimizeBufferDiscards;
#if UNITY_6000_1_OR_NEWER
private SerializedProperty optimizeMultiviewRenderRegions;
#endif
private SerializedProperty spacewarpMotionVectorTextureFormat;
void InitActiveTargetDevices()
{
activeTargetDevices = new Dictionary<string, bool>();
OpenXRSettings androidOpenXRSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
var questFeature = androidOpenXRSettings.GetFeature<MetaQuestFeature>();
if (questFeature == null)
return;
foreach (var dev in questFeature.targetDevices)
{
activeTargetDevices.Add(dev.manifestName, dev.active);
}
}
void OnEnable()
{
forceRemoveInternetPermission =
serializedObject.FindProperty("forceRemoveInternetPermission");
systemSplashScreen =
serializedObject.FindProperty("systemSplashScreen");
symmetricProjection =
serializedObject.FindProperty("symmetricProjection");
optimizeBufferDiscards =
serializedObject.FindProperty("optimizeBufferDiscards");
#if UNITY_6000_1_OR_NEWER
optimizeMultiviewRenderRegions =
serializedObject.FindProperty("optimizeMultiviewRenderRegions");
#endif
targetDeviceProperties = new List<TargetDeviceProperty>();
InitActiveTargetDevices();
if (activeTargetDevices.Count == 0)
return;
var targetDevicesProperty = serializedObject.FindProperty("targetDevices");
// mapping these to Properties so tha we can get Undo/redo functionality
m_LateLatchingDebugProperty = serializedObject.FindProperty("lateLatchingDebug");
m_LateLatchingModeProperty = serializedObject.FindProperty("lateLatchingMode");
spacewarpMotionVectorTextureFormat = serializedObject.FindProperty("spacewarpMotionVectorTextureFormat");
for (int i = 0; i < targetDevicesProperty.arraySize; ++i)
{
var targetDeviceProp = targetDevicesProperty.GetArrayElementAtIndex(i);
var propManifestName = targetDeviceProp.FindPropertyRelative("manifestName");
// don't present inactive target devices to the user
if (propManifestName == null || activeTargetDevices[propManifestName.stringValue] == false)
continue;
var propEnabled = targetDeviceProp.FindPropertyRelative("enabled");
var propName = targetDeviceProp.FindPropertyRelative("visibleName");
TargetDeviceProperty curTarget = new TargetDeviceProperty { property = propEnabled, label = EditorGUIUtility.TrTextContent(propName.stringValue) };
targetDeviceProperties.Add(curTarget);
}
}
public override void OnInspectorGUI()
{
// Update anything from the serializable object
EditorGUIUtility.labelWidth = 275.0f;
serializedObject.Update();
EditorGUILayout.LabelField("Rendering Settings", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(symmetricProjection, new GUIContent("Symmetric Projection (Vulkan)"));
EditorGUILayout.PropertyField(optimizeBufferDiscards, new GUIContent("Optimize Buffer Discards (Vulkan)"));
// OptimizeMultiviewRenderRegions (aka MVPVV) only supported on Unity 6.1 onwards
#if UNITY_6000_1_OR_NEWER
EditorGUILayout.PropertyField(optimizeMultiviewRenderRegions, s_OptimizeMultiviewRenderRegionsLabel);
#endif
EditorGUILayout.PropertyField(spacewarpMotionVectorTextureFormat, new GUIContent("Space Warp motion vector texture format"));
EditorGUILayout.Space();
EditorGUILayout.LabelField("Manifest Settings", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(forceRemoveInternetPermission);
EditorGUILayout.PropertyField(systemSplashScreen);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Target Devices", EditorStyles.boldLabel);
// Layout the Target Device properties
EditorGUI.indentLevel++;
foreach (var device in targetDeviceProperties)
{
EditorGUILayout.PropertyField(device.property, device.label);
}
EditorGUI.indentLevel--;
// Foldout for the Experimental properties
if (m_ShowAndroidExperimental = EditorGUILayout.Foldout(m_ShowAndroidExperimental, s_ShowAndroidExperimentalLabel, EditorStyles.miniBoldFont))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_LateLatchingModeProperty, s_LateLatchingSupportedLabel);
EditorGUILayout.PropertyField(m_LateLatchingDebugProperty, s_LateLatchingDebugLabel);
EditorGUI.indentLevel--;
}
EditorGUIUtility.labelWidth = 0.0f;
// update any serializable properties
serializedObject.ApplyModifiedProperties();
OpenXRSettings androidOpenXRSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
var serializedOpenXrSettings = new SerializedObject(androidOpenXRSettings);
androidOpenXRSettings.symmetricProjection = symmetricProjection.boolValue;
#if UNITY_6000_1_OR_NEWER
androidOpenXRSettings.optimizeMultiviewRenderRegions = optimizeMultiviewRenderRegions.boolValue;
#endif
androidOpenXRSettings.optimizeBufferDiscards = optimizeBufferDiscards.boolValue;
androidOpenXRSettings.spacewarpMotionVectorTextureFormat = (OpenXRSettings.SpaceWarpMotionVectorTextureFormat)spacewarpMotionVectorTextureFormat.enumValueIndex;
serializedOpenXrSettings.ApplyModifiedProperties();
EditorGUIUtility.labelWidth = 0.0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 82210fac00b17f4429ac7f1b85d6e30a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,422 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MetaQuestSupport;
using UnityEngine.XR.OpenXR.Features;
using System.Linq;
#if XR_MGMT_4_4_0_OR_NEWER
using Unity.XR.Management.AndroidManifest.Editor;
#endif
namespace UnityEditor.XR.OpenXR.Features.MetaQuestSupport
{
internal class ModifyAndroidManifestMeta : OpenXRFeatureBuildHooks
{
public override int callbackOrder => 1;
public override Type featureType => typeof(MetaQuestFeature);
protected override void OnPreprocessBuildExt(BuildReport report)
{
}
protected override void OnPostGenerateGradleAndroidProjectExt(string path)
{
ProcessSystemSplashScreen(path);
#if !XR_MGMT_4_4_0_OR_NEWER
var androidManifest = new AndroidManifest(GetManifestPath(path));
androidManifest.AddMetaData();
androidManifest.Save();
#endif
}
protected override void OnPostprocessBuildExt(BuildReport report)
{
}
#if XR_MGMT_4_4_0_OR_NEWER
protected override ManifestRequirement ProvideManifestRequirementExt()
{
var elementsToRemove = new List<ManifestElement>()
{
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "uses-permission" },
Attributes = new Dictionary<string, string>
{
{ "name", "android.permission.BLUETOOTH" }
}
}
};
if (ForceRemoveInternetPermission())
{
elementsToRemove.Add(new ManifestElement()
{
ElementPath = new List<string> { "manifest", "uses-permission" },
Attributes = new Dictionary<string, string>
{
{ "name", "android.permission.INTERNET" }
}
});
}
var elementsToAdd = new List<ManifestElement>()
{
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "uses-feature" },
Attributes = new Dictionary<string, string>
{
{ "name", "android.hardware.vr.headtracking" },
{ "required", "true" },
{ "version", "1" }
}
},
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "application", "meta-data" },
Attributes = new Dictionary<string, string>
{
{ "name", "com.oculus.supportedDevices" },
{ "value", GetMetaSupportedDevices() }
}
},
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "application", "activity", "meta-data" },
Attributes = new Dictionary<string, string>
{
{ "name", "com.oculus.vr.focusaware" },
{ "value", "true" }
}
},
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "application", "activity", "intent-filter", "category" },
Attributes = new Dictionary<string, string>
{
{ "name", "com.oculus.intent.category.VR" }
}
}
};
if (SystemSplashScreen() != null)
{
elementsToAdd.Add(new ManifestElement()
{
ElementPath = new List<string> { "manifest", "application", "meta-data" },
Attributes = new Dictionary<string, string>
{
{ "name", "com.oculus.ossplash" },
{ "value", "true" }
}
});
}
if (IsAppTargetingQuestPro())
{
elementsToAdd.Add(
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "uses-feature" },
Attributes = new Dictionary<string, string>
{
{ "name", "oculus.software.eye_tracking" },
{ "required", "true" }
}
});
elementsToAdd.Add(
new ManifestElement()
{
ElementPath = new List<string> { "manifest", "uses-permission" },
Attributes = new Dictionary<string, string>
{
{ "name", "com.oculus.permission.EYE_TRACKING" }
}
});
}
return new ManifestRequirement
{
SupportedXRLoaders = new HashSet<Type>()
{
typeof(OpenXRLoader)
},
NewElements = elementsToAdd,
RemoveElements = elementsToRemove
};
}
#endif
private static string GetMetaSupportedDevices()
{
var androidOpenXRSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(BuildTargetGroup.Android);
var questFeature = androidOpenXRSettings.GetFeature<MetaQuestFeature>();
if (questFeature != null)
{
List<string> deviceList = new List<string>();
foreach (var device in questFeature.targetDevices)
{
if (device.active && device.enabled)
deviceList.Add(device.manifestName);
}
if (deviceList.Count > 0)
{
return string.Join("|", deviceList.ToArray());
}
else
{
UnityEngine.Debug.LogWarning("No target devices selected in Meta Quest Support Feature. No devices will be listed as supported in the application Android manifest.");
return string.Empty;
}
}
return string.Empty;
}
private static bool ForceRemoveInternetPermission()
{
var questFeature = GetFeatureFromSettings<MetaQuestFeature>(BuildTargetGroup.Android);
if (questFeature == null || !questFeature.enabled)
return false; // By default the permission is retained
return questFeature.forceRemoveInternetPermission;
}
private static Texture2D SystemSplashScreen()
{
var questFeature = GetFeatureFromSettings<MetaQuestFeature>(BuildTargetGroup.Android);
if (questFeature == null || !questFeature.enabled)
return null;
return questFeature.systemSplashScreen;
}
private static bool IsAppTargetingQuestPro()
{
var questFeature = GetFeatureFromSettings<MetaQuestFeature>(BuildTargetGroup.Android);
return questFeature.targetDevices
.Where(device => string.Equals(device.manifestName, "cambria"))
.Where(device => device.enabled)
.Any();
}
private static T GetFeatureFromSettings<T>(BuildTargetGroup buildTargetGroup) where T : OpenXRFeature
{
var androidOpenXRSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup);
return androidOpenXRSettings.GetFeature<T>();
}
private static void ProcessSystemSplashScreen(string gradlePath)
{
var systemSplashScreen = SystemSplashScreen();
if (systemSplashScreen == null)
return;
string splashScreenAssetPath = AssetDatabase.GetAssetPath(systemSplashScreen);
string sourcePath = splashScreenAssetPath;
string targetFolder = Path.Combine(gradlePath, "src/main/assets");
string targetPath = targetFolder + "/vr_splash.png";
// copy the splash over into the gradle folder and make sure it's not read only
FileUtil.ReplaceFile(sourcePath, targetPath);
FileInfo targetInfo = new FileInfo(targetPath);
targetInfo.IsReadOnly = false;
}
#if !XR_MGMT_4_4_0_OR_NEWER
private string _manifestFilePath;
private string GetManifestPath(string basePath)
{
if (!string.IsNullOrEmpty(_manifestFilePath)) return _manifestFilePath;
var pathBuilder = new StringBuilder(basePath);
pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml");
_manifestFilePath = pathBuilder.ToString();
return _manifestFilePath;
}
private class AndroidXmlDocument : XmlDocument
{
private string m_Path;
protected XmlNamespaceManager nsMgr;
public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
public AndroidXmlDocument(string path)
{
m_Path = path;
using (var reader = new XmlTextReader(m_Path))
{
reader.Read();
Load(reader);
}
nsMgr = new XmlNamespaceManager(NameTable);
nsMgr.AddNamespace("android", AndroidXmlNamespace);
}
public string Save()
{
return SaveAs(m_Path);
}
public string SaveAs(string path)
{
using (var writer = new XmlTextWriter(path, new UTF8Encoding(false)))
{
writer.Formatting = Formatting.Indented;
Save(writer);
}
return path;
}
}
private class AndroidManifest : AndroidXmlDocument
{
private readonly XmlElement ApplicationElement;
private readonly XmlElement ActivityIntentFilterElement;
private readonly XmlElement ActivityElement;
private readonly XmlElement ManifestElement;
public AndroidManifest(string path) : base(path)
{
ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
ActivityIntentFilterElement = SelectSingleNode("/manifest/application/activity/intent-filter") as XmlElement;
ActivityElement = SelectSingleNode("manifest/application/activity") as XmlElement;
ManifestElement = SelectSingleNode("/manifest") as XmlElement;
}
private XmlAttribute CreateAndroidAttribute(string key, string value)
{
XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
attr.Value = value;
return attr;
}
private void UpdateOrCreateAttribute(XmlElement xmlParentElement, string tag, string name, params (string name, string value)[] attributes)
{
var xmlNodeList = xmlParentElement.SelectNodes(tag);
XmlElement targetNode = null;
// Check all XmlNodes to see if a node with matching name already exists.
foreach (XmlNode node in xmlNodeList)
{
XmlAttribute nameAttr = (XmlAttribute)node.Attributes.GetNamedItem("name", AndroidXmlNamespace);
if (nameAttr != null && nameAttr.Value.Equals(name))
{
targetNode = (XmlElement)node;
break;
}
}
// If node exists, update the attribute values if they are present or create new ones as requested. Else, create new XmlElement.
if (targetNode != null)
{
for (int i = 0; i < attributes.Length; i++)
{
XmlAttribute attr = (XmlAttribute)targetNode.Attributes.GetNamedItem(attributes[i].name, AndroidXmlNamespace);
if (attr != null)
{
attr.Value = attributes[i].value;
}
else
{
targetNode.SetAttribute(attributes[i].name, AndroidXmlNamespace, attributes[i].value);
}
}
}
else
{
XmlElement newElement = CreateElement(tag);
newElement.SetAttribute("name", AndroidXmlNamespace, name);
for (int i = 0; i < attributes.Length; i++)
newElement.SetAttribute(attributes[i].name, AndroidXmlNamespace, attributes[i].value);
xmlParentElement.AppendChild(newElement);
}
}
void RemoveNameValueElementInTag(string parentPath, string tag, string name, string value)
{
var xmlNodeList = this.SelectNodes(parentPath + "/" + tag);
foreach (XmlNode node in xmlNodeList)
{
var attributeList = ((XmlElement)node).Attributes;
foreach (XmlAttribute attrib in attributeList)
{
if (attrib.Name == name && attrib.Value == value)
{
node.ParentNode?.RemoveChild(node);
}
}
}
}
internal void AddMetaData()
{
string supportedDevices = GetMetaSupportedDevices();
UpdateOrCreateAttribute(ActivityIntentFilterElement,
"category", "com.oculus.intent.category.VR"
);
UpdateOrCreateAttribute(ActivityElement,
"meta-data", "com.oculus.vr.focusaware",
new (string name, string value)[]
{
("value", "true")
});
UpdateOrCreateAttribute(ApplicationElement,
"meta-data", "com.oculus.supportedDevices",
new (string name, string value)[]
{
("value", supportedDevices)
});
UpdateOrCreateAttribute(ManifestElement,
"uses-feature", "android.hardware.vr.headtracking",
new (string name, string value)[]
{
("required", "true"),
("version", "1")
});
if (SystemSplashScreen() != null)
{
UpdateOrCreateAttribute(ApplicationElement,
"meta-data", "com.oculus.ossplash",
new (string name, string value)[]
{
("value", "true")
});
}
// if the Microphone class is used in a project, the BLUETOOTH permission is automatically added to the manifest
// we remove it here since it will cause projects to fail Meta cert
// this shouldn't affect Bluetooth HID devices, which don't need the permission
RemoveNameValueElementInTag("/manifest", "uses-permission", "android:name", "android.permission.BLUETOOTH");
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a4b338a7faf8f4408f2320a777b654c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
{
"name": "Unity.XR.OpenXR.Features.MetaQuestSupport.Editor",
"rootNamespace": "",
"references": [
"GUID:96aa6ba065960476598f8f643e7252b6",
"GUID:4847341ff46394e83bb78fbd0652937e",
"GUID:95054a9710b0f114a85f6ced6b7f891c",
"GUID:f9fe0089ec81f4079af78eb2287a6163"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.xr.management",
"expression": "4.4.0",
"define": "XR_MGMT_4_4_0_OR_NEWER"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f68571b73246ce44e903d7128579af58
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dfa25b7a93b2fb845a29054f5367c55f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,502 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UnityEditor;
#if UNITY_EDITOR
using System.IO;
using UnityEditor.XR.OpenXR.Features;
using UnityEngine.Rendering;
using UnityEngine.XR.OpenXR.Features.Interactions;
#endif
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Features.OculusQuestSupport")]
[assembly: InternalsVisibleTo("Unity.XR.OpenXR.Features.MetaQuestSupport.Editor")]
[assembly: InternalsVisibleTo("Unity.XRTesting")]
namespace UnityEngine.XR.OpenXR.Features.MetaQuestSupport
{
/// <summary>
/// Enables the Meta mobile OpenXR Loader for Android, and modifies the AndroidManifest to be compatible with Quest.
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = "Meta Quest Support",
Desc = "Necessary to deploy a Meta Quest compatible app.",
Company = "Unity",
DocumentationLink = "https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/",
OpenxrExtensionStrings = "XR_OCULUS_android_initialize_loader",
Version = "1.0.0",
BuildTargetGroups = new[] { BuildTargetGroup.Android },
FeatureId = featureId
)]
#endif
public class MetaQuestFeature : OpenXRFeature
{
[Serializable]
internal struct TargetDevice
{
public string visibleName;
public string manifestName;
public bool enabled;
[NonSerialized] public bool active;
}
/// <summary>
/// The feature id string. This is used to give the feature a well known id for reference.
/// </summary>
public const string featureId = "com.unity.openxr.feature.metaquest";
/// <summary>
/// The name of the ambient occlusion render feature script.
/// Used for validation regarding ambient occlusion on meta quest devices.
/// </summary>
private const string ambientOcclusionScriptName = "ScreenSpaceAmbientOcclusion";
#if UNITY_EDITOR
/// <summary>
/// Adds devices to the supported devices list in the Android manifest.
/// </summary>
[SerializeField]
internal List<TargetDevice> targetDevices;
/// <summary>
/// Forces the removal of Internet permissions added to the Android Manifest.
/// </summary>
[SerializeField, Tooltip("Forces the removal of Internet permissions added to the Android Manifest.")]
internal bool forceRemoveInternetPermission = false;
[SerializeField]
internal bool symmetricProjection = false;
/// <summary>
/// Different APIs to use in the backend.
/// On Built-in Render Pipeline, only Legacy will be used.
/// On Scriptable Render Pipelines, it is highly recommended to use the SRPFoveation API. More textures will use FDM with the SRPFoveation API.
/// </summary>
[SerializeField, Tooltip("On Scriptable Render Pipelines, it is highly recommended to use the SRPFoveation API. More textures will use FDM with the SRPFoveation API.")]
internal OpenXRSettings.BackendFovationApi foveatedRenderingApi = OpenXRSettings.BackendFovationApi.Legacy;
/// <summary>
/// Uses a PNG in the Assets folder as the system splash screen image. If set, the OS will display the system splash screen as a high quality compositor layer as soon as the app is starting to launch until the app submits the first frame.
/// </summary>
[SerializeField, Tooltip("Uses a PNG in the Assets folder as the system splash screen image. If set, the OS will display the system splash screen as a high quality compositor layer as soon as the app is starting to launch until the app submits the first frame.")]
public Texture2D systemSplashScreen;
[SerializeField, Tooltip("Optimization that allows 4x MSAA textures to be memoryless on Vulkan")]
internal bool optimizeBufferDiscards = true;
/// <summary>
/// Caches validation rules for each build target group requested by <see cref="GetValidationChecks="/>.
/// </summary>
private Dictionary<BuildTargetGroup, ValidationRule[]> validationRules = new Dictionary<BuildTargetGroup, ValidationRule[]>();
/// Holding the Late Latching mode here for the editor (so we get undo/redo functionality)
/// </summary>
[SerializeField]
internal bool lateLatchingMode;
/// <summary>
/// Holding the Late Latching mode here for the editor (so we get undo/redo functionality)
/// </summary>
[SerializeField]
internal bool lateLatchingDebug;
/// <summary>
/// If enabled, the application can use Multi-View Per View Viewports functionality. This feature requires Unity 6.1 or later, and usage of the Vulkan renderer.
/// </summary>
[SerializeField]
internal bool optimizeMultiviewRenderRegions;
/// <summary>
/// Holding the Space Warp motion vector texture format
/// </summary>
[SerializeField]
internal OpenXRSettings.SpaceWarpMotionVectorTextureFormat spacewarpMotionVectorTextureFormat = OpenXRSettings.SpaceWarpMotionVectorTextureFormat.RGBA16f;
/// <summary>
/// Forces the removal of Internet permissions added to the Android Manifest.
/// </summary>
public bool ForceRemoveInternetPermission
{
get => forceRemoveInternetPermission;
set => forceRemoveInternetPermission = value;
}
public new void OnEnable()
{
// add known devices
AddTargetDevice("quest", "Quest", true);
AddTargetDevice("quest2", "Quest 2", true);
AddTargetDevice("cambria", "Quest Pro", true);
AddTargetDevice("eureka", "Quest 3", true);
AddTargetDevice("quest3s", "Quest 3S", true);
}
/// <summary>
/// Adds additional target devices to the devices list in the MetaQuestFeatureEditor. Added target devices will
/// be serialized into the settings asset and will persist across editor sessions, but will only be visible to users
/// and the manifest if they've been added in the active editor session.
/// </summary>
/// <param name="manifestName">Target device name that will be added to AndroidManifest</param>
/// <param name="visibleName">Device name that will be displayed in feature configuration UI</param>
/// <param name="enabledByDefault">Target device should be enabled by default or not</param>
public void AddTargetDevice(string manifestName, string visibleName, bool enabledByDefault)
{
if (targetDevices == null)
targetDevices = new List<TargetDevice>();
// don't add devices that already exist, but do mark them active for this session
for (int i = 0; i < targetDevices.Count; ++i)
{
var dev = targetDevices[i];
if (dev.manifestName == manifestName)
{
dev.active = true;
targetDevices[i] = dev;
return;
}
}
TargetDevice targetDevice = new TargetDevice { manifestName = manifestName, visibleName = visibleName, enabled = enabledByDefault, active = true };
targetDevices.Add(targetDevice);
}
private bool SettingsUseVulkan()
{
if (!PlayerSettings.GetUseDefaultGraphicsAPIs(BuildTarget.Android))
{
GraphicsDeviceType[] apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android);
if (apis.Length >= 1 && apis[0] == GraphicsDeviceType.Vulkan)
{
return true;
}
return false;
}
return true;
}
protected override void GetValidationChecks(List<ValidationRule> rules, BuildTargetGroup targetGroup)
{
if (!validationRules.ContainsKey(targetGroup))
validationRules.Add(targetGroup, CreateValidationRules(targetGroup));
rules.AddRange(validationRules[targetGroup]);
}
private ValidationRule[] CreateValidationRules(BuildTargetGroup targetGroup) =>
new ValidationRule[]
{
new ValidationRule(this)
{
message = "Select Oculus Touch Interaction Profile, Meta Quest Touch Pro Interaction Profile, or Meta Quest Touch Plus Interaction Profile to pair with.",
checkPredicate = () =>
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
if (null == settings)
return false;
bool touchFeatureEnabled = false;
foreach (var feature in settings.GetFeatures<OpenXRInteractionFeature>())
{
if (feature.enabled)
{
if ((feature is OculusTouchControllerProfile) || (feature is MetaQuestTouchProControllerProfile) || (feature is MetaQuestTouchPlusControllerProfile))
touchFeatureEnabled = true;
}
}
return touchFeatureEnabled;
},
error = false,
fixIt = () => { SettingsService.OpenProjectSettings("Project/XR Plug-in Management/OpenXR"); },
fixItAutomatic = false,
fixItMessage = "Open Project Settings to select Oculus Touch, Meta Quest Touch Pro, or Meta Quest Touch Plus interaction profiles or select all."
},
new ValidationRule(this)
{
message = "No Quest target devices selected.",
checkPredicate = () =>
{
foreach (var device in targetDevices)
{
if (device.enabled)
return true;
}
return false;
},
fixIt = () =>
{
var window = MetaQuestFeatureEditorWindow.Create(this);
window.ShowPopup();
},
error = true,
fixItAutomatic = false,
},
new ValidationRule(this)
{
message = "Using the Screen Space Ambient Occlusion render feature results in significant performance overhead when the application is running natively on device. Disabling or removing that render feature is recommended.",
helpText = "Only removing the Screen Space Ambient Occlusion render feature from all UniversalRenderer assets that may be used will make this warning go away, but just disabling the render feature will still prevent the performance overhead.",
checkPredicate = () =>
{
// Checks the dependencies of all configured render pipeline assets.
foreach(var renderPipeline in GraphicsSettings.allConfiguredRenderPipelines)
{
var dependencies = AssetDatabase.GetDependencies(AssetDatabase.GetAssetPath(renderPipeline));
foreach(var dependency in dependencies)
{
if (dependency.Contains(ambientOcclusionScriptName))
return false;
}
}
return true;
},
fixItAutomatic = false,
},
new ValidationRule(this)
{
message = "System Splash Screen must be a PNG texture asset.",
checkPredicate = () =>
{
if (systemSplashScreen == null)
return true;
string splashScreenAssetPath = AssetDatabase.GetAssetPath(systemSplashScreen);
if (Path.GetExtension(splashScreenAssetPath).ToLower() != ".png")
return false;
return true;
},
fixIt = () =>
{
var window = MetaQuestFeatureEditorWindow.Create(this);
window.ShowPopup();
},
error = true,
fixItAutomatic = false,
},
// OptimizeMultiviewRenderRegions (aka MVPVV) only supported on Unity 6.1 onwards
#if UNITY_6000_1_OR_NEWER
new ValidationRule(this)
{
message = "Optimize Multiview Render Regions requires symmetric projection setting turned on.",
checkPredicate = () =>
{
if (optimizeMultiviewRenderRegions)
{
return symmetricProjection;
}
return true;
},
error = true,
fixIt = () =>
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
var feature = settings.GetFeature<MetaQuestFeature>();
feature.symmetricProjection = true;
}
},
new ValidationRule(this)
{
message = "Optimize Multiview Render Regions requires Render Mode set to \"Single Pass Instanced / Multi-view\".",
checkPredicate = () =>
{
if (optimizeMultiviewRenderRegions)
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
return (settings.renderMode == OpenXRSettings.RenderMode.SinglePassInstanced);
}
return true;
},
error = true,
fixIt = () =>
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
settings.renderMode = OpenXRSettings.RenderMode.SinglePassInstanced;
}
},
new ValidationRule(this)
{
message = "Optimize Multiview Render Regions needs the Vulkan Graphics API to be the default Graphics API to work at runtime.",
helpText = "The Optimize Multiview Render Regions feature only works with the Vulkan Graphics API, which needs to be set as the first Graphics API to be loaded at application startup. Choosing other Graphics API may require to switch to Vulkan and restart the application.",
checkPredicate = () =>
{
if (optimizeMultiviewRenderRegions)
{
var graphicsApis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android);
return graphicsApis[0] == GraphicsDeviceType.Vulkan;
}
return true;
},
error = false
},
#endif
#if UNITY_ANDROID
new ValidationRule(this)
{
message = "Symmetric Projection is only supported on Vulkan graphics API",
checkPredicate = () =>
{
if (symmetricProjection && !SettingsUseVulkan())
{
return false;
}
return true;
},
fixIt = () =>
{
PlayerSettings.SetGraphicsAPIs(BuildTarget.Android, new[] { GraphicsDeviceType.Vulkan });
},
error = true,
fixItAutomatic = true,
fixItMessage = "Set Vulkan as Graphics API"
},
new ValidationRule(this)
{
message = "Symmetric Projection is only supported when using Multi-view",
checkPredicate = () =>
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
if (null == settings)
return false;
if (symmetricProjection && (settings.renderMode != OpenXRSettings.RenderMode.SinglePassInstanced))
{
return false;
}
return true;
},
fixIt = () =>
{
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
if (null != settings)
{
settings.renderMode = OpenXRSettings.RenderMode.SinglePassInstanced;
}
},
error = true,
fixItAutomatic = true,
fixItMessage = "Set Render Mode to Multi-view"
},
new ValidationRule(this)
{
message = "Only Legacy Foveated Rendering API usage is possible on Built-in Render Pipeline",
checkPredicate = () =>
{
return GraphicsSettings.defaultRenderPipeline != null || foveatedRenderingApi == OpenXRSettings.BackendFovationApi.Legacy;
},
fixIt = () =>
{
foveatedRenderingApi = OpenXRSettings.BackendFovationApi.Legacy;
},
error = true,
fixItAutomatic = true,
fixItMessage = "Set Foveated Rendering API to Legacy"
},
new ValidationRule(this)
{
message = "Symmetric Projection is only available on Quest 2 or higher",
checkPredicate = () =>
{
if (symmetricProjection)
{
foreach (var device in targetDevices)
{
if (device.enabled && device.manifestName == "quest")
{
return false;
}
}
}
return true;
},
fixIt = () =>
{
var window = MetaQuestFeatureEditorWindow.Create(this);
window.ShowPopup();
},
error = true,
fixItAutomatic = false,
},
new ValidationRule(this)
{
message = "Optimize Buffer Discards is only supported on Vulkan graphics API",
checkPredicate = () =>
{
if (optimizeBufferDiscards && !SettingsUseVulkan())
{
return false;
}
return true;
}
},
#endif
new ValidationRule(this)
{
message = "Meta Quest HMDs only support Landscape Left orientation.",
checkPredicate = () =>
{
if (PlayerSettings.defaultInterfaceOrientation == UIOrientation.AutoRotation)
{
if (!PlayerSettings.allowedAutorotateToLandscapeLeft)
return false;
}
else
{
if (PlayerSettings.defaultInterfaceOrientation != UIOrientation.LandscapeLeft)
return false;
}
return true;
},
error = true,
fixIt = () =>
{
if (PlayerSettings.defaultInterfaceOrientation == UIOrientation.AutoRotation)
{
PlayerSettings.allowedAutorotateToLandscapeLeft = true;
}
else
{
PlayerSettings.defaultInterfaceOrientation = UIOrientation.LandscapeLeft;
}
},
fixItAutomatic = true,
}
};
internal class MetaQuestFeatureEditorWindow : EditorWindow
{
private Object feature;
private Editor featureEditor;
public static EditorWindow Create(Object feature)
{
var window = EditorWindow.GetWindow<MetaQuestFeatureEditorWindow>(true, "Meta Quest Feature Configuration", true);
window.feature = feature;
window.featureEditor = Editor.CreateEditor(feature);
return window;
}
private void OnGUI()
{
featureEditor.OnInspectorGUI();
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f647cc0545697264a9878224faada6d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
{
"name": "Unity.XR.OpenXR.Features.MetaQuestSupport",
"rootNamespace": "",
"references": [
"GUID:4847341ff46394e83bb78fbd0652937e"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 95054a9710b0f114a85f6ced6b7f891c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: