initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,41 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
namespace UnityEditor.XR.Management
{
internal static class BuildHelpers
{
internal static void CleanOldSettings<T>()
{
UnityEngine.Object[] preloadedAssets = PlayerSettings.GetPreloadedAssets();
if (preloadedAssets == null)
return;
var oldSettings = from s in preloadedAssets
where s != null && s.GetType() == typeof(T)
select s;
if (oldSettings != null && oldSettings.Any())
{
var assets = preloadedAssets.ToList();
foreach (var s in oldSettings)
{
assets.Remove(s);
}
PlayerSettings.SetPreloadedAssets(assets.ToArray());
}
}
}
}

View file

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

View file

@ -0,0 +1,113 @@
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using UnityEngine;
namespace UnityEditor.XR.Management
{
internal static class EditorUtilities
{
internal static readonly string[] s_DefaultGeneralSettingsPath = {"XR"};
internal static readonly string[] s_DefaultLoaderPath = {"XR","Loaders"};
internal static readonly string[] s_DefaultSettingsPath = {"XR","Settings"};
internal static bool AssetDatabaseHasInstanceOfType(string type)
{
var assets = AssetDatabase.FindAssets(String.Format("t:{0}", type));
return assets.Any();
}
internal static T GetInstanceOfTypeFromAssetDatabase<T>() where T : class
{
var assets = AssetDatabase.FindAssets(String.Format("t:{0}", typeof(T).Name));
if (assets.Any())
{
string assetPath = AssetDatabase.GUIDToAssetPath(assets[0]);
var asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(T));
return asset as T;
}
return null;
}
internal static ScriptableObject GetInstanceOfTypeWithNameFromAssetDatabase(string typeName)
{
var assets = AssetDatabase.FindAssets(String.Format("t:{0}", typeName));
if (assets.Any())
{
string assetPath = AssetDatabase.GUIDToAssetPath(assets[0]);
var asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(ScriptableObject));
return asset as ScriptableObject;
}
return null;
}
internal static string GetAssetPathForComponents(string[] pathComponents, string root = "Assets")
{
if (pathComponents.Length <= 0)
return null;
string path = root;
foreach( var pc in pathComponents)
{
string subFolder = Path.Combine(path, pc);
bool shouldCreate = true;
foreach (var f in AssetDatabase.GetSubFolders(path))
{
if (String.Compare(Path.GetFullPath(f), Path.GetFullPath(subFolder), true) == 0)
{
shouldCreate = false;
break;
}
}
if (shouldCreate)
AssetDatabase.CreateFolder(path, pc);
path = subFolder;
}
return path;
}
internal static string TypeNameToString(Type type)
{
return type == null ? "" : TypeNameToString(type.FullName);
}
internal static string TypeNameToString(string type)
{
string[] typeParts = type.Split(new char[] { '.' });
if (!typeParts.Any())
return String.Empty;
string[] words = Regex.Matches(typeParts.Last(), "(^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+)")
.OfType<Match>()
.Select(m => m.Value)
.ToArray();
return string.Join(" ", words);
}
internal static ScriptableObject CreateScriptableObjectInstance(string typeName, string path)
{
ScriptableObject obj = ScriptableObject.CreateInstance(typeName) as ScriptableObject;
if (obj != null)
{
if (!string.IsNullOrEmpty(path))
{
string fileName = String.Format("{0}.asset", EditorUtilities.TypeNameToString(typeName));
string targetPath = Path.Combine(path, fileName);
AssetDatabase.CreateAsset(obj, targetPath);
AssetDatabase.SaveAssets();
return obj;
}
}
Debug.LogError($"We were unable to create an instance of the requested type {typeName}. Please make sure that all packages are updated to support this version of XR Plug-In Management. See the Unity documentation for XR Plug-In Management for information on resolving this issue.");
return null;
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEditor.XR.Management;
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management.Legacy
{
[InitializeOnLoad]
class XRLegacyUninstaller
{
private static List<string> k_PackagesToBeRemoved = new List<string>(){
"com.unity.xr.googlevr.android",
"com.unity.xr.googlevr.ios",
"com.unity.xr.oculus.android",
"com.unity.xr.oculus.standalone",
"com.unity.xr.openvr.standalone",
"com.unity.xr.windowsmr.metro",
};
[Serializable]
struct PackageRemovalRequest
{
[SerializeField]
public string packageId;
[SerializeField]
public bool shouldCheckForInstallation;
}
[Serializable]
struct PackageRemovalOperation
{
[SerializeField]
public string packageId;
[SerializeField]
public RemoveRequest packageRemoveRequest;
[SerializeField]
public bool shouldCheckForInstallation;
}
[Serializable]
struct LegacyPackageListRequest
{
[SerializeField]
public ListRequest packageListRequest;
}
private static readonly string k_PackageToRemoveQueue = "Remove Package Queue";
private static EditorWorkQueue<PackageRemovalOperation> s_PackageRemovelQueue => EditorWorkQueue<PackageRemovalOperation>.Instance;
private static bool hasPackageBeingRemoved => s_PackageRemovelQueue.HasWorkItems;
private static readonly string k_WaitingToRemoveQueue = "Waiting Remove Queue";
private static EditorWorkQueue<PackageRemovalRequest> s_PackagesToRemoveQueue => EditorWorkQueue<PackageRemovalRequest>.Instance;
private static bool hasPackagesToRemove => s_PackagesToRemoveQueue.HasWorkItems;
private static readonly string k_LocalPackageListingQueryQueue = "Local Package List";
private static EditorWorkQueue<LegacyPackageListRequest> s_PackageListQueue => EditorWorkQueue<LegacyPackageListRequest>.Instance;
private static bool hasActivePackageListQuery => EditorWorkQueueBase.SessionStateHasStoredData(k_LocalPackageListingQueryQueue);
internal static bool IsPackageInstalled(string package)
{
return File.Exists($"Packages/{package}/package.json");
}
static XRLegacyUninstaller()
{
s_PackageRemovelQueue.QueueName = k_PackageToRemoveQueue;
s_PackageRemovelQueue.ProcessItemCallback += (PackageRemovalOperation prr) =>
{
if (prr.packageRemoveRequest == null || String.IsNullOrEmpty(prr.packageRemoveRequest.PackageIdOrName))
{
return;
}
if (prr.packageRemoveRequest.Status == StatusCode.Failure)
{
return;
}
if (!prr.packageRemoveRequest.IsCompleted || (prr.shouldCheckForInstallation && IsPackageInstalled(prr.packageId)))
{
s_PackageRemovelQueue.QueueWorkItem(prr);
return;
}
};
if (s_PackageRemovelQueue.HasWorkItems) s_PackageRemovelQueue.StartQueue();
s_PackagesToRemoveQueue.QueueName = k_WaitingToRemoveQueue;
s_PackagesToRemoveQueue.ProcessItemCallback += (PackageRemovalRequest prr) =>
{
if (s_PackageRemovelQueue.HasWorkItems)
{
s_PackagesToRemoveQueue.QueueWorkItem(prr);
return;
}
if (!String.IsNullOrEmpty(prr.packageId) && IsPackageInstalled(prr.packageId))
{
PackageRemovalOperation pro = new PackageRemovalOperation { packageId = prr.packageId, packageRemoveRequest = PackageManager.Client.Remove(prr.packageId), shouldCheckForInstallation=prr.shouldCheckForInstallation };
s_PackageRemovelQueue.QueueWorkItem(pro);
}
};
if (s_PackagesToRemoveQueue.HasWorkItems) s_PackagesToRemoveQueue.StartQueue();
var packageSettings = XRPackageInitializationSettings.Instance;
if (packageSettings.HasSettings("ShouldQueryLegacyPackageRemoval"))
{
return;
}
if (!packageSettings.HasSettings("RemoveLegacyInputHelpersForReload"))
{
PackageRemovalRequest lihremreq = new PackageRemovalRequest();
lihremreq.packageId = "com.unity.xr.legacyinputhelpers";
lihremreq.shouldCheckForInstallation = false;
s_PackagesToRemoveQueue.QueueWorkItem(lihremreq);
packageSettings.AddSettings("RemoveLegacyInputHelpersForReload");
packageSettings.SaveSettings();
}
s_PackageListQueue.QueueName = k_LocalPackageListingQueryQueue;
s_PackageListQueue.ProcessItemCallback += (LegacyPackageListRequest plr) => {
if (plr.packageListRequest == null)
return;
if (!plr.packageListRequest.IsCompleted)
{
s_PackageListQueue.QueueWorkItem(plr);
}
else
{
if (plr.packageListRequest.Status == PackageManager.StatusCode.Success)
{
List<string> packageIdsToRemove = new List<string>();
string removeRequestText = $"{XRConstants.k_XRPluginManagement} has detected that this project is using built in VR. Built in VR is incompatible with the new XR Plug-in system and any built in packages should be removed.\nDo you want {XRConstants.k_XRPluginManagement} to remove the following packages for you?\n\n";
foreach (var package in plr.packageListRequest.Result)
{
if (k_PackagesToBeRemoved.Contains(package.name))
{
packageIdsToRemove.Add(package.name);
removeRequestText += $"{package.displayName}\n";
}
}
removeRequestText += "\nChoosing to cancel will require manual removal of built-in integration packages through the Package Manger window.";
if (packageIdsToRemove.Count > 0)
{
if (EditorUtility.DisplayDialog("Built in VR Detected", removeRequestText, "Ok", "Cancel"))
{
foreach (string packageId in packageIdsToRemove)
{
PackageRemovalRequest remreq = new PackageRemovalRequest();
remreq.packageId = packageId;
remreq.shouldCheckForInstallation = true;
s_PackagesToRemoveQueue.QueueWorkItem(remreq);
}
}
var packSettings = XRPackageInitializationSettings.Instance;
packSettings.AddSettings("ShouldQueryLegacyPackageRemoval");
packSettings.SaveSettings();
}
#if !UNITY_2020_2_OR_NEWER
bool virtualRealityEnabled = false;
foreach (var buildTargetGroup in (BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup)))
{
#pragma warning disable CS0618
virtualRealityEnabled |= PlayerSettings.GetVirtualRealitySupported(buildTargetGroup);
#pragma warning restore CS0618
}
if (virtualRealityEnabled)
{
if (EditorUtility.DisplayDialog("Built in VR Enabled", $"{XRConstants.k_XRPluginManagement} has detected that Built-In VR is enabled. We must disable Built-In VR prior to using {XRConstants.k_XRPluginManagement}.", "Ok"))
{
foreach (var buildTargetGroup in (BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup)))
{
#pragma warning disable CS0618
if (PlayerSettings.GetVirtualRealitySupported(buildTargetGroup))
{
PlayerSettings.SetVirtualRealitySupported(buildTargetGroup, false);
UnityEditorInternal.VR.VREditor.SetVREnabledDevicesOnTargetGroup(buildTargetGroup, new string[]{});
}
#pragma warning restore CS0618
}
}
}
#endif //!UNITY_2020_2_OR_NEWER
}
}
};
LegacyPackageListRequest req = new LegacyPackageListRequest();
req.packageListRequest = PackageManager.Client.List(true, true);
s_PackageListQueue.QueueWorkItem(req);
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UnityEditor.XR.Management.Metadata
{
internal class KnownPackages
{
internal static string k_KnownPackageMockHMDLoader = "Unity.XR.MockHMD.MockHMDLoader";
class KnownLoaderMetadata : IXRLoaderMetadata
{
public string loaderName { get; set; }
public string loaderType { get; set; }
public List<BuildTargetGroup> supportedBuildTargets { get; set; }
}
class KnownPackageMetadata : IXRPackageMetadata
{
public string packageName { get; set; }
public string packageId { get; set; }
public string settingsType { get; set; }
public List<IXRLoaderMetadata> loaderMetadata { get; set; }
}
class KnownPackage : IXRPackage
{
public IXRPackageMetadata metadata { get; set; }
public bool PopulateNewSettingsInstance(ScriptableObject obj) { return true; }
}
private static Lazy<List<IXRPackage>> s_KnownPackages = new Lazy<List<IXRPackage>>(InitKnownPackages);
internal static List<IXRPackage> Packages => s_KnownPackages.Value;
static List<IXRPackage> InitKnownPackages()
{
List<IXRPackage> packages = new List<IXRPackage>();
#if UNITY_2020_3_OR_NEWER
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata(){
packageName = "Open XR Plugin",
packageId = "com.unity.xr.openxr",
settingsType = "UnityEditor.XR.OpenXR.OpenXRPackageSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "Open XR",
loaderType = "UnityEngine.XR.OpenXR.OpenXRLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Standalone,
BuildTargetGroup.WSA
}
},
}
}
});
#endif
#if !UNITY_2021_2_OR_NEWER
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata(){
packageName = "Windows XR Plugin",
packageId = "com.unity.xr.windowsmr",
settingsType = "UnityEditor.XR.WindowsMR.WindowsMRPackageSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "Windows Mixed Reality",
loaderType = "UnityEngine.XR.WindowsMR.WindowsMRLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Standalone,
BuildTargetGroup.WSA
}
},
}
}
});
#endif
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata(){
packageName = "Oculus XR Plugin",
packageId = "com.unity.xr.oculus",
settingsType = "Unity.XR.Oculus.OculusSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "Oculus",
loaderType = "Unity.XR.Oculus.OculusLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Standalone,
BuildTargetGroup.Android,
}
},
}
}
});
// This section will get replaced with the RELISH package in the near future.
#if !UNITY_2021_2_OR_NEWER
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata(){
packageName = "Magic Leap XR Plugin",
packageId = "com.unity.xr.magicleap",
settingsType = "UnityEngine.XR.MagicLeap.MagicLeapSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "Magic Leap Zero Iteration",
loaderType = "UnityEngine.XR.MagicLeap.MagicLeapLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Standalone,
}
},
new KnownLoaderMetadata() {
loaderName = "Magic Leap",
loaderType = "UnityEngine.XR.MagicLeap.MagicLeapLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Lumin
}
},
}
}
});
#endif // !UNITY_2021_2_OR_NEWER
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata(){
packageName = "ARCore XR Plugin",
packageId = "com.unity.xr.arcore",
settingsType = "UnityEditor.XR.ARCore.ARCoreSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "ARCore",
loaderType = "UnityEngine.XR.ARCore.ARCoreLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Android,
}
},
}
}
});
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata(){
packageName = "ARKit XR Plugin",
packageId = "com.unity.xr.arkit",
settingsType = "UnityEditor.XR.ARKit.ARKitSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "ARKit",
loaderType = "UnityEngine.XR.ARKit.ARKitLoader",
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.iOS,
}
},
}
}
});
packages.Add(new KnownPackage() {
metadata = new KnownPackageMetadata() {
packageName = "MockHMD XR Plugin",
packageId = "com.unity.xr.mock-hmd",
settingsType = "Unity.XR.MockHMD.MockHMDBuildSettings",
loaderMetadata = new List<IXRLoaderMetadata>() {
new KnownLoaderMetadata() {
loaderName = "Unity Mock HMD",
loaderType = k_KnownPackageMockHMDLoader,
supportedBuildTargets = new List<BuildTargetGroup>() {
BuildTargetGroup.Standalone,
BuildTargetGroup.Android
}
},
}
}
});
return packages;
}
}
}

View file

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

View file

@ -0,0 +1,31 @@
using UnityEngine;
namespace UnityEditor.XR.Management.Metadata
{
/// <summary>
/// Implement this interface to provide package level information and actions.
///
/// XR Plug-in Management will reflect on all types in the project to find implementers
/// of this interface. These instances are used to get information required to integrate
/// your package with the XR Plug-in Management system.
/// </summary>
public interface IXRPackage
{
/// <summary>
/// Returns an instance of <see cref="IXRPackageMetadata"/>. Information will be used
/// to allow the XR Plug-in Management to provide settings and loaders through the settings UI.
/// </summary>
IXRPackageMetadata metadata { get; }
/// <summary>
/// Allows the package to configure new settings and/or port old settings to the instance passed
/// in.
///
/// </summary>
/// <param name="obj">ScriptableObject instance representing an instance of the settings
/// type provided by <see cref="IXRPackageMetadata.settingsType"/>.</param>
/// <returns>True if the operation succeeded, false if not. If implementation is empty, just return true.</returns>
bool PopulateNewSettingsInstance(ScriptableObject obj);
}
}

View file

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

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management.Metadata
{
/// <summary>
/// Provides an interface for describing specific loader metadata. Package authors should implement
/// this interface for each loader they provide in their package.
/// </summary>
public interface IXRLoaderMetadata
{
/// <summary>
/// The user facing name for this loader. Will be used to populate the
/// list in the XR Plug-in Management UI.
/// </summary>
string loaderName { get; }
/// <summary>
/// The full type name for this loader. This is used to allow management to find and
/// create instances of supported loaders for your package.
///
/// When your package is first installed, the XR Plug-in Management system will
/// use this information to create instances of your loaders in Assets/XR/Loaders.
/// </summary>
string loaderType { get; }
/// <summary>
/// The full list of supported buildtargets for this loader. This allows the UI to only show the
/// loaders appropriate for a specific build target.
///
/// Returning an empty list or a list containing just <a href="https://docs.unity3d.com/ScriptReference/BuildTargetGroup.Unknown.html">BuildTargetGroup.Unknown</a>. will make this
/// loader invisible in the ui.
/// </summary>
List<BuildTargetGroup> supportedBuildTargets { get; }
}
/// <summary>
/// Top level package metadata interface. Create an instance oif this interface to
/// provide metadata information for your package.
/// </summary>
public interface IXRPackageMetadata
{
/// <summary>
/// User facing package name. Should be the same as the value for the
/// displayName keyword in the package.json file.
/// </summary>
string packageName { get; }
/// <summary>
/// The package id used to track and install the package. Must be the same value
/// as the name keyword in the package.json file, otherwise installation will
/// not be possible.
/// </summary>
string packageId { get; }
/// <summary>
/// This is the full type name for the settings type for your package.
///
/// When your package is first installed, the XR Plug-in Management system will
/// use this information to create an instance of your settings in Assets/XR/Settings.
/// </summary>
string settingsType { get; }
/// <summary>
/// List of <see cref="IXRLoaderMetadata"/> instances describing the data about the loaders
/// your package supports.
/// </summary>
List<IXRLoaderMetadata> loaderMetadata { get; }
}
}

View file

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

View file

@ -0,0 +1,974 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management.Metadata
{
/// <summary>
/// Provide access to the metadata store. Currently only usable as a way to assign and remove loaders
/// to/from an <see cref="XRManagerSettings"/> instance.
/// </summary>
[InitializeOnLoad]
public class XRPackageMetadataStore
{
const string k_WaitingPackmanQuery = "XRMGT Waiting Packman Query.";
const string k_RebuildCache = "XRMGT Rebuilding Cache.";
const string k_InstallingPackage = "XRMGT Installing XR Package.";
const string k_AssigningPackage = "XRMGT Assigning XR Package.";
const string k_UninstallingPackage = "XRMGT Uninstalling XR Package.";
const string k_CachedMDStoreKey = "XR Metadata Store";
static float k_TimeOutDelta = 30f;
[Serializable]
struct KnownPackageInfo
{
public string packageId;
public string verifiedVersion;
}
[Serializable]
struct CachedMDStoreInformation
{
public bool hasAlreadyRequestedData;
public KnownPackageInfo[] knownPackageInfos;
public string[] installedPackages;
public string[] installablePackages;
}
static CachedMDStoreInformation s_CachedMDStoreInformation = new CachedMDStoreInformation()
{
hasAlreadyRequestedData = false,
knownPackageInfos = { },
installedPackages = { },
installablePackages = { },
};
static void LoadCachedMDStoreInformation()
{
string data = SessionState.GetString(k_CachedMDStoreKey, "{}");
s_CachedMDStoreInformation = JsonUtility.FromJson<CachedMDStoreInformation>(data);
}
static void StoreCachedMDStoreInformation()
{
SessionState.EraseString(k_CachedMDStoreKey);
string data = JsonUtility.ToJson(s_CachedMDStoreInformation, true);
SessionState.SetString(k_CachedMDStoreKey, data);
}
enum InstallationState
{
New,
RebuildInstalledCache,
StartInstallation,
Installing,
Assigning,
Complete,
Uninstalling,
Log
}
enum LogLevel
{
Info,
Warning,
Error
}
[Serializable]
struct LoaderAssignmentRequest
{
[SerializeField]
public string packageId;
[SerializeField]
public string loaderType;
[SerializeField]
public BuildTargetGroup buildTargetGroup;
[SerializeField]
public bool needsAddRequest;
[SerializeField]
public ListRequest packageListRequest;
[SerializeField]
public AddRequest packageAddRequest;
[SerializeField]
#pragma warning disable CS0649
public RemoveRequest packageRemoveRequest;
#pragma warning disable CS0649
[SerializeField]
public float timeOut;
[SerializeField]
public InstallationState installationState;
[SerializeField]
public string logMessage;
[SerializeField]
public LogLevel logLevel;
}
[Serializable]
struct LoaderAssignmentRequests
{
[SerializeField]
public List<LoaderAssignmentRequest> activeRequests;
}
static Dictionary<string, IXRPackage> s_Packages = null;
static SearchRequest s_SearchRequest = null;
static Dictionary<string, IXRPackage> packages
{
get
{
if(s_Packages == null)
{
s_Packages = new Dictionary<string, IXRPackage>();
InitKnownPluginPackages();
XRPackageInitializationBootstrap.BeginPackageInitialization();
}
return s_Packages;
}
}
const string k_DefaultSessionStateString = "DEADBEEF";
static bool SessionStateHasStoredData(string queueName)
{
return SessionState.GetString(queueName, k_DefaultSessionStateString) != XRPackageMetadataStore.k_DefaultSessionStateString;
}
internal static bool isCheckingInstallationRequirements => XRPackageMetadataStore.SessionStateHasStoredData(k_WaitingPackmanQuery);
internal static bool isRebuildingCache => XRPackageMetadataStore.SessionStateHasStoredData(k_RebuildCache);
internal static bool isInstallingPackages => XRPackageMetadataStore.SessionStateHasStoredData(k_InstallingPackage);
internal static bool isUninstallingPackages => XRPackageMetadataStore.SessionStateHasStoredData(k_UninstallingPackage);
internal static bool isAssigningLoaders => XRPackageMetadataStore.SessionStateHasStoredData(k_AssigningPackage);
internal static bool isDoingQueueProcessing
{
get
{
return isCheckingInstallationRequirements || isRebuildingCache || isInstallingPackages || isUninstallingPackages || isAssigningLoaders;
}
}
internal struct LoaderBuildTargetQueryResult
{
public string packageName;
public string packageId;
public string loaderName;
public string loaderType;
}
internal static void MoveMockInListToEnd(List<LoaderBuildTargetQueryResult> loaderList)
{
int index = loaderList.FindIndex((x) => { return String.Compare(x.loaderType, KnownPackages.k_KnownPackageMockHMDLoader) == 0; });
if (index >= 0)
{
var mock = loaderList[index];
loaderList.RemoveAt(index);
loaderList.Add(mock);
}
}
internal static List<LoaderBuildTargetQueryResult> GetAllLoadersForBuildTarget(BuildTargetGroup buildTarget)
{
var ret = from pm in (from p in packages.Values select p.metadata)
from lm in pm.loaderMetadata
where lm.supportedBuildTargets.Contains(buildTarget)
orderby lm.loaderName
select new LoaderBuildTargetQueryResult() { packageName = pm.packageName, packageId = pm.packageId, loaderName = lm.loaderName, loaderType = lm.loaderType };
var retList = ret.Distinct().ToList<LoaderBuildTargetQueryResult>();
MoveMockInListToEnd(retList);
return retList;
}
internal static List<LoaderBuildTargetQueryResult> GetLoadersForBuildTarget(BuildTargetGroup buildTargetGroup)
{
var ret = from pm in (from p in packages.Values select p.metadata)
from lm in pm.loaderMetadata
where lm.supportedBuildTargets.Contains(buildTargetGroup)
orderby lm.loaderName
select new LoaderBuildTargetQueryResult() { packageName = pm.packageName, packageId = pm.packageId, loaderName = lm.loaderName, loaderType = lm.loaderType };
var retList = ret.ToList<LoaderBuildTargetQueryResult>();
MoveMockInListToEnd(retList);
return retList;
}
/// <summary>
/// Return a read only list of all package metadata information currently known.
/// </summary>
/// <returns>Read only list of <see cref="IXRPackage" />.</returns>
public static IReadOnlyList<IXRPackage> GetAllPackageMetadata()
{
return packages.Values.ToList().AsReadOnly();
}
/// <summary>
/// Return a read only list of all package metadata information currently known that has loaders that support the given build.
/// </summary>
/// <returns>Read only list of <see cref="IXRPackage" />.</returns>
public static IReadOnlyList<IXRPackage> GetAllPackageMetadataForBuildTarget(BuildTargetGroup buildTargetGroup)
{
HashSet<IXRPackage> ret = new HashSet<IXRPackage>();
foreach (var p in packages.Values)
{
foreach (var lm in p.metadata.loaderMetadata)
{
if (lm.supportedBuildTargets.Contains(buildTargetGroup))
{
ret.Add(p);
break;
}
}
}
return ret.ToList().AsReadOnly();
}
/// <summary>
/// Given a package id, return the metadata for that package.
/// </summary>
/// <param name="packageId">The package id to check for.</param>
/// <returns>An instance of <see cref="IXRPackageMetadata" /> if the package has metadata or null.</returns>
public static IXRPackageMetadata GetMetadataForPackage(string packageId)
{
return packages.Values.
Select(x => x.metadata).
FirstOrDefault(xmd => String.Compare(xmd.packageId, packageId) == 0);
}
internal static bool HasInstallablePackageData()
{
return s_CachedMDStoreInformation.installablePackages?.Any() ?? false;
}
internal static bool IsPackageInstalled(string package)
{
return (s_CachedMDStoreInformation.installedPackages?.Contains(package) ?? false)
&& File.Exists($"Packages/{package}/package.json");
}
internal static bool IsPackageInstallable(string package)
{
return s_CachedMDStoreInformation.installablePackages?.Contains(package) ?? false;
}
/// <summary>
/// Given a loader type and a build target group will return whether or not that loader
/// is currently assigned to be active for that build target.
/// </summary>
/// <param name="loaderTypeName">Loader type to check.</param>
/// <param name="buildTargetGroup">Build target group to check for assignment in.</param>
/// <returns></returns>
public static bool IsLoaderAssigned(string loaderTypeName, BuildTargetGroup buildTargetGroup)
{
var settings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup);
if (settings == null)
return false;
foreach (var loader in settings.AssignedSettings.activeLoaders)
{
if (loader != null && String.Compare(loader.GetType().FullName, loaderTypeName) == 0)
return true;
}
return false;
}
internal static bool IsLoaderAssigned(XRManagerSettings settings, string loaderTypeName)
{
if (settings == null)
return false;
foreach (var l in settings.activeLoaders)
{
if (l != null && String.Compare(l.GetType().FullName, loaderTypeName) == 0)
return true;
}
return false;
}
internal static void InstallPackageAndAssignLoaderForBuildTarget(string package, string loaderType, BuildTargetGroup buildTargetGroup)
{
var req = new LoaderAssignmentRequest();
req.packageId = package;
req.loaderType = loaderType;
req.buildTargetGroup = buildTargetGroup;
req.installationState = InstallationState.New;
QueueLoaderRequest(req);
}
/// <summary>
/// Assigns a loader of type loaderTypeName to the settings instance. Will instantiate an
/// instance if one can't be found in the users project folder before assigning it.
/// </summary>
/// <param name="settings">An instance of <see cref="XRManagerSettings"/> to add the loader to.</param>
/// <param name="loaderTypeName">The full type name for the loader instance to assign to settings.</param>
/// <param name="buildTargetGroup">The build target group being assigned to.</param>
/// <returns>True if assignment succeeds, false if not.</returns>
public static bool AssignLoader(XRManagerSettings settings, string loaderTypeName, BuildTargetGroup buildTargetGroup)
{
if (EditorApplication.isPlaying || EditorApplication.isPaused)
{
Debug.LogError($"Attempt to add {loaderTypeName} for {buildTargetGroup} while in Play mode. XR Plug-in Management can not make changes to the loader list when running.");
return false;
}
var instance = EditorUtilities.GetInstanceOfTypeWithNameFromAssetDatabase(loaderTypeName);
if (instance == null || !(instance is XRLoader))
{
instance = EditorUtilities.CreateScriptableObjectInstance(loaderTypeName,
EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultLoaderPath));
if (instance == null)
return false;
}
XRLoader newLoader = instance as XRLoader;
if (settings.TryAddLoader(newLoader))
{
var assignedLoaders = settings.activeLoaders;
var orderedLoaders = new List<XRLoader>();
var allLoaders = GetAllLoadersForBuildTarget(buildTargetGroup);
foreach (var ldr in allLoaders)
{
var newInstance = EditorUtilities.GetInstanceOfTypeWithNameFromAssetDatabase(ldr.loaderType) as XRLoader;
if (newInstance != null && assignedLoaders.Contains(newInstance))
{
orderedLoaders.Add(newInstance);
#if UNITY_EDITOR
var loaderHelper = newLoader as XRLoaderHelper;
loaderHelper?.WasAssignedToBuildTarget(buildTargetGroup);
#endif
}
}
if (!settings.TrySetLoaders(orderedLoaders))
return false;
EditorUtility.SetDirty(settings);
AssetDatabase.SaveAssets();
}
return true;
}
/// <summary>
/// Remove a previously assigned loader from settings. If the loader type is unknown or
/// an instance of the loader can't be found in the project folder no action is taken.
///
/// Removal will not delete the instance from the project folder.
/// </summary>
/// <param name="settings">An instance of <see cref="XRManagerSettings"/> to add the loader to.</param>
/// <param name="loaderTypeName">The full type name for the loader instance to remove from settings.</param>
/// <param name="buildTargetGroup">The build target group being removed from.</param>
/// <returns>True if removal succeeds, false if not.</returns>
public static bool RemoveLoader(XRManagerSettings settings, string loaderTypeName, BuildTargetGroup buildTargetGroup)
{
if (EditorApplication.isPlaying || EditorApplication.isPaused)
{
Debug.LogError($"Attempt to remove {loaderTypeName} for {buildTargetGroup} while in Play mode. XR Plug-in Management can not make changes to the loader list when running.");
return false;
}
var instance = EditorUtilities.GetInstanceOfTypeWithNameFromAssetDatabase(loaderTypeName);
if (instance == null || !(instance is XRLoader))
return false;
XRLoader loader = instance as XRLoader;
if (settings.TryRemoveLoader(loader))
{
EditorUtility.SetDirty(settings);
AssetDatabase.SaveAssets();
#if UNITY_EDITOR
var loaderHelper = loader as XRLoaderHelper;
loaderHelper?.WasUnassignedFromBuildTarget(buildTargetGroup);
#endif
return true;
}
return false;
}
internal static IXRPackage GetPackageForSettingsTypeNamed(string settingsTypeName)
{
var ret = packages.Values.
Where((p => String.Compare(p.metadata.settingsType, settingsTypeName, true) == 0)).
Select((p) => p);
return ret.Any() ? ret.First() : null;
}
internal static string GetCurrentStatusDisplayText()
{
if (XRPackageMetadataStore.isCheckingInstallationRequirements)
{
return "Checking installation requirements for packages...";
}
else if (XRPackageMetadataStore.isRebuildingCache)
{
return "Querying Package Manager for currently installed packages...";
}
else if (XRPackageMetadataStore.isInstallingPackages)
{
return "Installing packages...";
}
else if (XRPackageMetadataStore.isUninstallingPackages)
{
return "Uninstalling packages...";
}
else if (XRPackageMetadataStore.isAssigningLoaders)
{
return "Assigning all requested loaders...";
}
return "";
}
internal static void AddPluginPackage(IXRPackage package)
{
if (s_CachedMDStoreInformation.installedPackages != null && !s_CachedMDStoreInformation.installedPackages.Contains(package.metadata.packageId))
{
List<string> installedPackages = s_CachedMDStoreInformation.installedPackages.ToList<string>();
installedPackages.Add(package.metadata.packageId);
s_CachedMDStoreInformation.installedPackages = installedPackages.ToArray();
StoreCachedMDStoreInformation();
}
InternalAddPluginPackage(package);
}
static void InternalAddPluginPackage(IXRPackage package)
{
packages[package.metadata.packageId] = package;
}
internal static void InitKnownPluginPackages()
{
foreach (var knownPackage in KnownPackages.Packages)
{
InternalAddPluginPackage(knownPackage);
}
}
static XRPackageMetadataStore()
{
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
EditorApplication.playModeStateChanged += PlayModeStateChanged;
if (IsEditorInPlayMode())
return;
AssemblyReloadEvents.afterAssemblyReload += AssemblyReloadEvents_afterAssemblyReload;
}
static void AssemblyReloadEvents_afterAssemblyReload()
{
LoadCachedMDStoreInformation();
if (!IsEditorInPlayMode())
{
if (!s_CachedMDStoreInformation.hasAlreadyRequestedData)
{
s_SearchRequest = Client.SearchAll(true);
}
RebuildInstalledCache();
StartAllQueues();
}
}
static bool IsEditorInPlayMode()
{
return EditorApplication.isPlayingOrWillChangePlaymode ||
EditorApplication.isPlaying ||
EditorApplication.isPaused;
}
static void PlayModeStateChanged(PlayModeStateChange state)
{
switch (state)
{
case PlayModeStateChange.ExitingEditMode:
StopAllQueues();
StoreCachedMDStoreInformation();
break;
case PlayModeStateChange.EnteredPlayMode:
break;
case PlayModeStateChange.EnteredEditMode:
LoadCachedMDStoreInformation();
StartAllQueues();
break;
}
}
static void StopAllQueues()
{
EditorApplication.update -= UpdateInstallablePackages;
EditorApplication.update -= WaitingOnSearchQuery;
EditorApplication.update -= MonitorPackageInstallation;
EditorApplication.update -= MonitorPackageUninstall;
EditorApplication.update -= AssignAnyRequestedLoadersUpdate;
EditorApplication.update -= RebuildCache;
}
static void StartAllQueues()
{
EditorApplication.update += UpdateInstallablePackages;
EditorApplication.update += WaitingOnSearchQuery;
EditorApplication.update += MonitorPackageInstallation;
EditorApplication.update += MonitorPackageUninstall;
EditorApplication.update += AssignAnyRequestedLoadersUpdate;
EditorApplication.update += RebuildCache;
}
static void UpdateInstallablePackages()
{
EditorApplication.update -= UpdateInstallablePackages;
if (s_SearchRequest == null || IsEditorInPlayMode() || s_CachedMDStoreInformation.hasAlreadyRequestedData)
{
return;
}
if (s_SearchRequest.IsCompleted && s_SearchRequest.Error != null)
{
Debug.LogError($"Error retrieving package information from Package Manager: {s_SearchRequest.Error.message}.");
s_SearchRequest = null;
return;
}
if (!s_SearchRequest.IsCompleted || s_SearchRequest.Result == null)
{
EditorApplication.update += UpdateInstallablePackages;
return;
}
var installablePackages = new List<string>();
var knownPackageInfos = new List<KnownPackageInfo>();
foreach (var package in s_SearchRequest.Result)
{
if (packages.ContainsKey(package.name))
{
var kpi = new KnownPackageInfo();
kpi.packageId = package.name;
kpi.verifiedVersion = package.versions.verified;
if (string.IsNullOrEmpty(kpi.verifiedVersion))
kpi.verifiedVersion = package.versions.latestCompatible;
knownPackageInfos.Add(kpi);
installablePackages.Add(package.name);
}
}
s_CachedMDStoreInformation.knownPackageInfos = knownPackageInfos.ToArray();
s_CachedMDStoreInformation.installablePackages = installablePackages.ToArray();
s_CachedMDStoreInformation.hasAlreadyRequestedData = true;
s_SearchRequest = null;
StoreCachedMDStoreInformation();
}
static void AddRequestToQueue(LoaderAssignmentRequest request, string queueName)
{
LoaderAssignmentRequests reqs;
if (XRPackageMetadataStore.SessionStateHasStoredData(queueName))
{
string fromJson = SessionState.GetString(queueName, k_DefaultSessionStateString);
reqs = JsonUtility.FromJson<LoaderAssignmentRequests>(fromJson);
}
else
{
reqs = new LoaderAssignmentRequests();
reqs.activeRequests = new List<LoaderAssignmentRequest>();
}
reqs.activeRequests.Add(request);
string json = JsonUtility.ToJson(reqs);
SessionState.SetString(queueName, json);
}
static void SetRequestsInQueue(LoaderAssignmentRequests reqs, string queueName)
{
string json = JsonUtility.ToJson(reqs);
SessionState.SetString(queueName, json);
}
static LoaderAssignmentRequests GetAllRequestsInQueue(string queueName)
{
var reqs = new LoaderAssignmentRequests();
reqs.activeRequests = new List<LoaderAssignmentRequest>();
if (XRPackageMetadataStore.SessionStateHasStoredData(queueName))
{
string fromJson = SessionState.GetString(queueName, k_DefaultSessionStateString);
reqs = JsonUtility.FromJson<LoaderAssignmentRequests>(fromJson);
SessionState.EraseString(queueName);
}
return reqs;
}
internal static void RebuildInstalledCache()
{
if (isRebuildingCache)
return;
var req = new LoaderAssignmentRequest();
req.packageListRequest = Client.List(true, false);
req.installationState = InstallationState.RebuildInstalledCache;
req.timeOut = Time.realtimeSinceStartup + k_TimeOutDelta;
QueueLoaderRequest(req);
}
static void RebuildCache()
{
EditorApplication.update -= RebuildCache;
if (IsEditorInPlayMode())
{
return; // Use the cached data that should have been passed in the play state change.
}
LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_RebuildCache);
if (reqs.activeRequests == null || reqs.activeRequests.Count == 0)
{
return;
}
var req = reqs.activeRequests[0];
reqs.activeRequests.Remove(req);
if (req.timeOut < Time.realtimeSinceStartup)
{
req.logMessage = $"Timeout trying to get package list after {k_TimeOutDelta}s.";
req.logLevel = LogLevel.Warning;
req.installationState = InstallationState.Log;
QueueLoaderRequest(req);
}
else if (req.packageListRequest.IsCompleted)
{
if (req.packageListRequest.Status == StatusCode.Success)
{
var installedPackages = new List<string>();
foreach (var packageInfo in req.packageListRequest.Result)
{
installedPackages.Add(packageInfo.name);
}
var packageIds = packages.Values.
Where((p) => installedPackages.Contains(p.metadata.packageId)).
Select((p) => p.metadata.packageId);
s_CachedMDStoreInformation.installedPackages = packageIds.ToArray();
}
StoreCachedMDStoreInformation();
}
else if (!req.packageListRequest.IsCompleted)
{
QueueLoaderRequest(req);
}
else
{
req.logMessage = $"Unable to rebuild installed package cache. Some state may be missing or incorrect.";
req.logLevel = LogLevel.Warning;
req.installationState = InstallationState.Log;
QueueLoaderRequest(req);
}
if (reqs.activeRequests.Count > 0)
{
SetRequestsInQueue(reqs, k_RebuildCache);
EditorApplication.update += RebuildCache;
}
}
static void ResetManagerUiIfAvailable()
{
if (XRSettingsManager.Instance != null) XRSettingsManager.Instance.ResetUi = true;
}
static void AssignAnyRequestedLoadersUpdate()
{
EditorApplication.update -= AssignAnyRequestedLoadersUpdate;
LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_AssigningPackage);
if (reqs.activeRequests == null || reqs.activeRequests.Count == 0)
return;
while (reqs.activeRequests.Count > 0)
{
var req = reqs.activeRequests[0];
reqs.activeRequests.RemoveAt(0);
var settings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(req.buildTargetGroup);
if (settings == null)
continue;
if (settings.AssignedSettings == null)
{
var assignedSettings = ScriptableObject.CreateInstance<XRManagerSettings>() as XRManagerSettings;
settings.AssignedSettings = assignedSettings;
EditorUtility.SetDirty(settings);
}
if (!XRPackageMetadataStore.AssignLoader(settings.AssignedSettings, req.loaderType, req.buildTargetGroup))
{
req.installationState = InstallationState.Log;
req.logMessage = $"Unable to assign {req.packageId} for build target {req.buildTargetGroup}.";
req.logLevel = LogLevel.Error;
QueueLoaderRequest(req);
}
}
ResetManagerUiIfAvailable();
}
internal static void AssignAnyRequestedLoaders()
{
EditorApplication.update += AssignAnyRequestedLoadersUpdate;
}
static void MonitorPackageInstallation()
{
EditorApplication.update -= MonitorPackageInstallation;
LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_InstallingPackage);
if (reqs.activeRequests.Count > 0)
{
var request = reqs.activeRequests[0];
reqs.activeRequests.RemoveAt(0);
if (request.needsAddRequest)
{
if (s_CachedMDStoreInformation.knownPackageInfos == null)
{
request.logMessage = $"No package information to query against. Unable to load package {request.packageId}.";
request.logLevel = LogLevel.Error;
request.installationState = InstallationState.Log;
QueueLoaderRequest(request);
}
else
{
var versionToInstallQ = s_CachedMDStoreInformation.knownPackageInfos.
Where((kpi) => String.Compare(request.packageId, kpi.packageId) == 0).
Select((kpi) => kpi.verifiedVersion);
var versionToInstall = versionToInstallQ.FirstOrDefault();
var packageToInstall = String.IsNullOrEmpty(versionToInstall) ?
request.packageId :
$"{request.packageId}@{versionToInstall}";
request.packageAddRequest = Client.Add(packageToInstall);
request.needsAddRequest = false;
request.installationState = InstallationState.Installing;
s_CachedMDStoreInformation.hasAlreadyRequestedData = true;
StoreCachedMDStoreInformation();
QueueLoaderRequest(request);
}
}
else if (request.packageAddRequest.IsCompleted && File.Exists($"Packages/{request.packageId}/package.json"))
{
if (request.packageAddRequest.Status == StatusCode.Success)
{
if (!String.IsNullOrEmpty(request.loaderType))
{
request.packageAddRequest = null;
request.installationState = InstallationState.Assigning;
QueueLoaderRequest(request);
}
else
{
request.logMessage = $"Missing loader type. Unable to assign loader.";
request.logLevel = LogLevel.Error;
request.installationState = InstallationState.Log;
QueueLoaderRequest(request);
}
}
}
else if (request.packageAddRequest.IsCompleted && request.packageAddRequest.Status != StatusCode.Success)
{
if (String.IsNullOrEmpty(request.packageId))
{
request.logMessage = $"Error installing package with no package id.";
}
else
{
request.logMessage = $"Error Message: {request.packageAddRequest?.Error?.message ?? "UNKNOWN" }.\nError installing package {request.packageId ?? "UNKNOWN PACKAGE ID" }.";
}
request.logLevel = LogLevel.Error;
request.installationState = InstallationState.Log;
QueueLoaderRequest(request);
}
else if (request.timeOut < Time.realtimeSinceStartup)
{
if (String.IsNullOrEmpty(request.packageId))
{
request.logMessage = $"Time out while installing pacakge with no package id.";
}
else
{
request.logMessage = $"Error installing package {request.packageId}. Package installation timed out. Check Package Manager UI to see if the package is installed and/or retry your operation.";
}
request.logLevel = LogLevel.Error;
if (request.packageAddRequest.IsCompleted)
{
request.logMessage += $" Error message: {request.packageAddRequest.Error.message}";
}
request.installationState = InstallationState.Log;
QueueLoaderRequest(request);
}
else
{
QueueLoaderRequest(request);
}
}
}
static void WaitingOnSearchQuery()
{
EditorApplication.update -= WaitingOnSearchQuery;
if (s_SearchRequest != null)
{
if (s_SearchRequest.IsCompleted)
EditorApplication.update += UpdateInstallablePackages;
else
EditorApplication.update += WaitingOnSearchQuery;
return;
}
LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_WaitingPackmanQuery);
if (reqs.activeRequests.Count > 0)
{
for (int i = 0; i < reqs.activeRequests.Count; i++)
{
var req = reqs.activeRequests[i];
req.installationState = IsPackageInstalled(req.packageId) ? InstallationState.Assigning : InstallationState.StartInstallation;
req.timeOut = Time.realtimeSinceStartup + k_TimeOutDelta;
QueueLoaderRequest(req);
}
}
}
static void MonitorPackageUninstall()
{
EditorApplication.update -= MonitorPackageUninstall;
LoaderAssignmentRequests reqs = GetAllRequestsInQueue(k_UninstallingPackage);
if (reqs.activeRequests.Count > 0)
{
for (int i = 0; i < reqs.activeRequests.Count; i++)
{
var req = reqs.activeRequests[i];
if (!req.packageRemoveRequest.IsCompleted)
QueueLoaderRequest(req);
if (req.packageRemoveRequest.Status == StatusCode.Failure)
{
req.installationState = InstallationState.Log;
req.logMessage = req.packageRemoveRequest.Error.message;
req.logLevel = LogLevel.Warning;
QueueLoaderRequest(req);
}
}
}
}
static void QueueLoaderRequest(LoaderAssignmentRequest req)
{
switch (req.installationState)
{
case InstallationState.New:
if (!s_CachedMDStoreInformation.hasAlreadyRequestedData && !HasInstallablePackageData() && s_SearchRequest == null)
{
s_SearchRequest = Client.SearchAll(false);
EditorApplication.update += UpdateInstallablePackages;
}
AddRequestToQueue(req, k_WaitingPackmanQuery);
EditorApplication.update += WaitingOnSearchQuery;
break;
case InstallationState.RebuildInstalledCache:
AddRequestToQueue(req, k_RebuildCache);
EditorApplication.update += RebuildCache;
break;
case InstallationState.StartInstallation:
req.needsAddRequest = true;
req.packageAddRequest = null;
req.timeOut = Time.realtimeSinceStartup + k_TimeOutDelta;
AddRequestToQueue(req, k_InstallingPackage);
EditorApplication.update += MonitorPackageInstallation;
break;
case InstallationState.Installing:
AddRequestToQueue(req, k_InstallingPackage);
EditorApplication.update += MonitorPackageInstallation;
break;
case InstallationState.Assigning:
AddRequestToQueue(req, k_AssigningPackage);
EditorApplication.update += AssignAnyRequestedLoadersUpdate;
break;
case InstallationState.Uninstalling:
AddRequestToQueue(req, k_UninstallingPackage);
EditorApplication.update += MonitorPackageUninstall;
break;
case InstallationState.Log:
const string header = "XR Plug-in Management";
switch(req.logLevel)
{
case LogLevel.Info:
Debug.Log($"{header}: {req.logMessage}");
break;
case LogLevel.Warning:
Debug.LogWarning($"{header} Warning: {req.logMessage}");
break;
case LogLevel.Error:
Debug.LogError($"{header} error. Failure reason: {req.logMessage}.\n Check if there are any other errors in the console and make sure they are corrected before trying again.");
break;
}
ResetManagerUiIfAvailable();
break;
}
}
}
}

View file

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

View file

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.XR.Management.Metadata;
using UnityEngine;
using Styles = UnityEditor.XR.Management.XRSettingsManager.Styles;
namespace UnityEditor.XR.Management
{
/// <summary>
/// This class holds information that should be displayed in an Editor tooltip for a given package.
/// </summary>
public class PackageNotificationInfo
{
private PackageNotificationInfo() {}
/// <summary>
/// Constructs a container for package notification information that displays in the XR Plug-in Management window.
/// </summary>
/// <param name="userInterfaceIcon">
/// The <c>GUIContent</c> icon to display in the XR Plug-in Management window. If the tooltip of this
/// icon is empty, null, only whitespace, or otherwise invalid, the constructor will throw an exception.
/// </param>
/// <param name="additionalInfoUri">
/// Used to surface a URI that points to additional information about the notification. For example, clicking the
/// icon directly could send the user to the package documentation website.
/// </param>
/// <exception cref="ArgumentException">
/// Thrown if either <see cref="userInterfaceIcon"/> does not contain a valid tooltip or if
/// <see cref="additionalInfoUri"/> is not empty and isn't a valid URI string.
/// </exception>
public PackageNotificationInfo(GUIContent userInterfaceIcon, string tooltip, string additionalInfoUri = default)
{
if (string.IsNullOrWhiteSpace(tooltip) || tooltip.Length == 0)
throw new ArgumentException("The package warning tooltip must contain a displayable message!");
if (additionalInfoUri != default)
{
if (!(Uri.TryCreate(additionalInfoUri, UriKind.Absolute, out var uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)))
throw new ArgumentException($"The supplied information URI {additionalInfoUri} must be a well formatted URI string!");
userInterfaceIcon.tooltip = $"{tooltip}\n\nClick the icon for additional information.";
}
else
userInterfaceIcon.tooltip = tooltip;
this.additionalInfoUri = additionalInfoUri;
this.userInterfaceIcon = userInterfaceIcon;
}
/// <summary>
/// A read-only string that contains a link to additional information about the warning.
/// </summary>
/// <remarks>
/// If this is null or empty, the window will not redirect the user.
/// </remarks>
public readonly string additionalInfoUri;
/// <summary>
/// The GUI icon and tooltip that will be drawn for this <c>PackageNotificationInfo</c>.
/// </summary>
public readonly GUIContent userInterfaceIcon;
}
/// <summary>
/// Static utility class for managing package notifications for packages.
/// </summary>
public static class PackageNotificationUtils
{
static Dictionary<string, PackageNotificationInfo> s_RegisteredPackagesWithNotifications = new Dictionary<string, PackageNotificationInfo>();
/// <summary>
/// Dictionary of packages that have notification to report. When a package is added to the project,
/// that package will register itself with this container if it requires access to notification functionality.
/// </summary>
/// <remarks>
/// This is a read-only dictionary and cannot be modified. To modify the dictionary, use the
/// <see cref="RegisterPackageNotificationInformation"/> method.
/// </remarks>
public static IReadOnlyDictionary<string, PackageNotificationInfo> registeredPackagesWithNotifications =>
s_RegisteredPackagesWithNotifications.ToDictionary(pair => pair.Key, pair => pair.Value);
/// <summary>
/// Registers a given package ID as having a notification and supplies that notification.
/// </summary>
/// <param name="packageId">
/// The metadata identifier for a given package <see cref="IXRPackageMetadata.packageId"/>
/// </param>
/// <param name="notificationInfo">
/// The <see cref="PackageNotificationInfo"/> for the package that corresponds to <see cref="packageId"/>.
/// </param>
public static void RegisterPackageNotificationInformation(string packageId, PackageNotificationInfo notificationInfo)
{
if (s_RegisteredPackagesWithNotifications.ContainsKey(packageId))
s_RegisteredPackagesWithNotifications[packageId] = notificationInfo;
else
s_RegisteredPackagesWithNotifications.Add(packageId, notificationInfo);
}
const int k_RectPixelOffsetWidth = 5;
internal static void DrawNotificationIconUI(PackageNotificationInfo notificationInfo, Rect guiRect, int pixelOffset = k_RectPixelOffsetWidth)
{
var position = new Vector2(guiRect.xMax - (notificationInfo.userInterfaceIcon.image.width + pixelOffset), guiRect.y);
var size = new Vector2(notificationInfo.userInterfaceIcon.image.width, guiRect.height);
var toolTipRect = new Rect(position, size);
var labelStyle = EditorGUIUtility.isProSkin ? Styles.k_UrlLabelProfessional : Styles.k_UrlLabelPersonal;
if (GUI.Button(toolTipRect, notificationInfo.userInterfaceIcon, labelStyle))
LaunchLink(notificationInfo);
}
static void LaunchLink(PackageNotificationInfo info)
{
if (info.additionalInfoUri.Length > 0)
Application.OpenURL(info.additionalInfoUri);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 06177c8680ea415c88d4b29d40a79cec
timeCreated: 1610066910

View file

@ -0,0 +1,36 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management
{
internal static class TypeLoaderExtensions
{
public static TypeCache.TypeCollection GetTypesWithInterface<T>(this Assembly asm)
{
return TypeCache.GetTypesDerivedFrom(typeof(T));
}
public static TypeCache.TypeCollection GetAllTypesWithInterface<T>()
{
return TypeCache.GetTypesDerivedFrom(typeof(T));
}
public static TypeCache.TypeCollection GetTypesWithAttribute<T>(this Assembly asm)
{
return TypeCache.GetTypesWithAttribute(typeof(T));
}
public static TypeCache.TypeCollection GetAllTypesWithAttribute<T>()
{
return TypeCache.GetTypesWithAttribute(typeof(T));
}
}
}

View file

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

View file

@ -0,0 +1,16 @@
{
"name": "Unity.XR.Management.Editor",
"references": [
"Unity.XR.Management"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View file

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

View file

@ -0,0 +1,89 @@
using System;
using System.Linq;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
namespace UnityEditor.XR.Management
{
/// <summary>
/// Base abstract class that provides some common functionality for plugins wishing to integrate with management assisted build.
/// </summary>
/// <typeparam name="T">The type parameter that will be used as the base type of the settings.</typeparam>
public abstract class XRBuildHelper<T> : IPreprocessBuildWithReport, IPostprocessBuildWithReport where T : UnityEngine.Object
{
/// <summary>Override of base IXxxprocessBuildWithReport</summary>
/// <returns>The callback order.</returns>
public virtual int callbackOrder { get { return 0; } }
/// <summary>Override of base IXxxprocessBuildWithReport</summary>
/// <returns>A string specifying the key to be used to set/get settigns in EditorBuildSettings.</returns>
public abstract string BuildSettingsKey { get; }
/// <summary>Helper functin to return current settings for a specific build target.</summary>
///
/// <param name="buildTargetGroup">An enum specifying which platform group this build is for.</param>
/// <returns>A unity object representing the settings instance data for that build target, or null if not found.</returns>
public virtual UnityEngine.Object SettingsForBuildTargetGroup(BuildTargetGroup buildTargetGroup)
{
UnityEngine.Object settingsObj = null;
EditorBuildSettings.TryGetConfigObject(BuildSettingsKey, out settingsObj);
if (settingsObj == null || !(settingsObj is T))
return null;
return settingsObj;
}
void CleanOldSettings()
{
BuildHelpers.CleanOldSettings<T>();
}
void SetSettingsForRuntime(UnityEngine.Object settingsObj)
{
// Always remember to cleanup preloaded assets after build to make sure we don't
// dirty later builds with assets that may not be needed or are out of date.
CleanOldSettings();
if (settingsObj == null)
return;
if (!(settingsObj is T))
{
Type typeOfT = typeof(T);
Debug.LogErrorFormat("Settings object is not of type {0}. No settings will be copied to runtime.", typeOfT.Name);
return;
}
UnityEngine.Object[] preloadedAssets = PlayerSettings.GetPreloadedAssets();
if (!preloadedAssets.Contains(settingsObj))
{
var assets = preloadedAssets.ToList();
assets.Add(settingsObj);
PlayerSettings.SetPreloadedAssets(assets.ToArray());
}
}
/// <summary>Override of base IPreprocessBuildWithReport</summary>
///
/// <param name="report">BuildReport instance passed in from build pipeline.</param>
public virtual void OnPreprocessBuild(BuildReport report)
{
SetSettingsForRuntime(SettingsForBuildTargetGroup(report.summary.platformGroup));
}
/// <summary>Override of base IPostprocessBuildWithReport</summary>
///
/// <param name="report">BuildReport instance passed in from build pipeline.</param>
public virtual void OnPostprocessBuild(BuildReport report)
{
// Always remember to cleanup preloaded assets after build to make sure we don't
// dirty later builds with assets that may not be needed or are out of date.
CleanOldSettings();
}
}
}

View file

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

View file

@ -0,0 +1,110 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.XR.Management.Metadata;
namespace UnityEditor.XR.Management
{
internal class XRConfigurationProvider : SettingsProvider
{
static readonly GUIContent s_WarningToCreateSettings = EditorGUIUtility.TrTextContent("You must create a serialized instance of the settings data in order to modify the settings in this UI. Until then only default settings set by the provider will be available.");
Type m_BuildDataType = null;
string m_BuildSettingsKey;
Editor m_CachedEditor;
SerializedObject m_SettingsWrapper;
public XRConfigurationProvider(string path, string buildSettingsKey, Type buildDataType, SettingsScope scopes = SettingsScope.Project) : base(path, scopes)
{
m_BuildDataType = buildDataType;
m_BuildSettingsKey = buildSettingsKey;
if (currentSettings == null)
{
Create();
}
}
ScriptableObject currentSettings
{
get
{
ScriptableObject settings = null;
EditorBuildSettings.TryGetConfigObject(m_BuildSettingsKey, out settings);
if (settings == null)
{
string searchText = String.Format("t:{0}", m_BuildDataType.Name);
string[] assets = AssetDatabase.FindAssets(searchText);
if (assets.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(assets[0]);
settings = AssetDatabase.LoadAssetAtPath(path, m_BuildDataType) as ScriptableObject;
EditorBuildSettings.AddConfigObject(m_BuildSettingsKey, settings, true);
}
}
return settings;
}
}
void InitEditorData(ScriptableObject settings)
{
if (settings != null)
{
m_SettingsWrapper = new SerializedObject(settings);
Editor.CreateCachedEditor(settings, null, ref m_CachedEditor);
}
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
InitEditorData(currentSettings);
}
public override void OnDeactivate()
{
if(m_CachedEditor != null)
UnityEngine.Object.DestroyImmediate(m_CachedEditor);
m_CachedEditor = null;
m_SettingsWrapper = null;
}
public override void OnGUI(string searchContext)
{
if (m_SettingsWrapper == null || m_SettingsWrapper.targetObject == null)
{
ScriptableObject settings = (currentSettings != null) ? currentSettings : Create();
InitEditorData(settings);
}
if (m_SettingsWrapper != null && m_SettingsWrapper.targetObject != null && m_CachedEditor != null)
{
m_SettingsWrapper.Update();
m_CachedEditor.OnInspectorGUI();
m_SettingsWrapper.ApplyModifiedProperties();
}
}
ScriptableObject Create()
{
ScriptableObject settings = ScriptableObject.CreateInstance(m_BuildDataType) as ScriptableObject;
if (settings != null)
{
var package = XRPackageMetadataStore.GetPackageForSettingsTypeNamed(m_BuildDataType.FullName);
package?.PopulateNewSettingsInstance(settings);
string newAssetName = String.Format("{0}.asset", EditorUtilities.TypeNameToString(m_BuildDataType));
string assetPath = EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultSettingsPath);
if (!string.IsNullOrEmpty(assetPath))
{
assetPath = Path.Combine(assetPath, newAssetName);
AssetDatabase.CreateAsset(settings, assetPath);
EditorBuildSettings.AddConfigObject(m_BuildSettingsKey, settings, true);
return settings;
}
}
return null;
}
}
}

View file

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

View file

@ -0,0 +1,9 @@
namespace UnityEditor.XR.Management
{
internal static class XRConstants
{
public static readonly string k_XRPluginManagement = "XR Plug-in Management";
}
}

View file

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

View file

@ -0,0 +1,95 @@
using System;
using UnityEditor;
using UnityEngine;
namespace UnityEditor.XR.Management
{
/// <summary>
/// Custom attribute that indicates a class supports the <see cref="IXRCustomLoaderUI"/> for a
/// specific loader and build target group. Any class marked with this attribute will
/// have <see cref="IXRCustomLoaderUI"/> methods called on it while the XR Plug-in Management UI is displaying
/// the supported loader for the supported build target.
///
/// Note that there can only be one custom loader for each (Loader Type, BuildTargetGroup) combination. If more than one loader exists,
/// the system will fall back to built-in supported rendering and log a warning in the Console window.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class XRCustomLoaderUIAttribute : Attribute
{
/// <summary>
/// Supported build target group.
/// </summary>
/// <value>Supported build target group. </value>
public BuildTargetGroup buildTargetGroup { get; set; }
/// <summary>
/// Supported loader type.
/// </summary>
/// <value>Supported loader type.</value>
public string loaderTypeName { get; set; }
private XRCustomLoaderUIAttribute() {}
/// <summary>
/// Constructor for this attribute.
/// </summary>
/// <param name="loaderTypeName">Loader type name.</param>
/// <param name="buildTargetGroup">Build Target Group.</param>
public XRCustomLoaderUIAttribute(string loaderTypeName, BuildTargetGroup buildTargetGroup)
{
this.loaderTypeName = loaderTypeName;
this.buildTargetGroup = buildTargetGroup;
}
}
/// <summary>
/// Custom interface provided by the package if the package uses
/// its own UI in the XR Plug-in Management loader selection
/// window.
///
/// Any class that implements this interface must be tagged with the <see cref="XRCustomLoaderUIAttribute"/> attribute.
/// </summary>
public interface IXRCustomLoaderUI
{
/// <summary>
/// Current enabled state of this loader.
/// </summary>
/// <value>True if the loader has been enabled, false otherwise.</value>
bool IsLoaderEnabled { get; set; }
/// <summary>
/// Array of type names that are incompatible with the loader when it's enabled. Non-compatible loaders will be grayed out
/// in the UI.
/// </summary>
/// <value>Array of type names to disable.</value>
string[] IncompatibleLoaders { get; }
/// <summary>
/// The height of the area within the UI that this renderer will need to render its UI. This height will be the height of the rect passed into the <see cref="OnGUI"/> call.
/// This should return a a non-zero value that's a multiple of the line height set in <see cref="SetRenderedLineHeight"/>.
/// </summary>
/// <value>Non-zero multiple of the line height set in <see cref="SetRenderedLineHeight"/>. If this is 0, the rect passed to <see cref="OnGUI"/> will be the default line height.</value>
float RequiredRenderHeight { get; }
/// <summary>
/// The Rendering component passes the expected line height to the custom renderer. This allows the component to
/// calculate the necessary area height required to render the custom UI into the component space. The calculated value should
/// be returned from the <see cref="RequiredRenderHeight"/>.
/// </summary>
/// <param name="height"></param>
void SetRenderedLineHeight(float height);
/// <summary>
/// Allows XR Plug-in Management to tell the UI which build target group it's being used with.
/// </summary>
/// <value>Build target that this instance handles.</value>
BuildTargetGroup ActiveBuildTargetGroup { get; set; }
/// <summary>
/// Call to render the UI for this custom loader.
/// </summary>
/// <param name="rect">The rect within which the UI should render into.</param>
void OnGUI(Rect rect);
}
}

View file

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

View file

@ -0,0 +1,39 @@
using System;
using UnityEngine;
namespace UnityEditor.XR.Management
{
internal class XRCustomLoaderUIManager
{
public static IXRCustomLoaderUI GetCustomLoaderUI(string loaderTypeName, BuildTargetGroup buildTargetGroup)
{
IXRCustomLoaderUI ret = null;
var customLoaderTypes = TypeCache.GetTypesDerivedFrom(typeof(IXRCustomLoaderUI));
foreach (var customLoader in customLoaderTypes)
{
var attribs = customLoader.GetCustomAttributes(typeof(XRCustomLoaderUIAttribute), true);
foreach (var attrib in attribs)
{
if (attrib is XRCustomLoaderUIAttribute)
{
var customUiAttrib = attrib as XRCustomLoaderUIAttribute;
if (String.Compare(loaderTypeName, customUiAttrib.loaderTypeName, true) == 0 &&
buildTargetGroup == customUiAttrib.buildTargetGroup)
{
if (ret != null)
{
Debug.Log($"Multiple custom ui renderers found for ({loaderTypeName}, {buildTargetGroup}). Defaulting to built-in rendering instead.");
return null;
}
ret = Activator.CreateInstance(customLoader) as IXRCustomLoaderUI;
}
}
}
}
return ret;
}
}
}

View file

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

View file

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace UnityEditor.XR.Management
{
internal class EditorWorkQueueBase
{
const string k_DefaultSessionStateString = "0BADF00D";
internal static bool SessionStateHasStoredData(string queueName)
{
return SessionState.GetString(queueName, k_DefaultSessionStateString) != EditorWorkQueueBase.k_DefaultSessionStateString;
}
}
internal class EditorWorkQueue<T> : EditorWorkQueueBase
{
[Serializable]
struct Queue
{
[SerializeField]
public List<T> workItems;
}
public string QueueName { get; set; }
private static Lazy<EditorWorkQueue<T>> s_Instance = new Lazy<EditorWorkQueue<T>>();
public static EditorWorkQueue<T> Instance => s_Instance.Value;
public bool HasWorkItems => EditorWorkQueueBase.SessionStateHasStoredData(QueueName);
public Action<T> ProcessItemCallback { get; set; }
public void StartQueue()
{
EditorApplication.update += ProcessWorkQueue;
}
public void QueueWorkItem(T workItem)
{
Queue queue = new Queue();
queue.workItems = new List<T>();
if (EditorWorkQueueBase.SessionStateHasStoredData(QueueName))
{
string fromJson = SessionState.GetString(QueueName, "{}");
JsonUtility.FromJsonOverwrite(fromJson, queue);
}
if (queue.workItems == null)
{
queue.workItems = new List<T>();
}
queue.workItems.Add(workItem);
string json = JsonUtility.ToJson(queue);
SessionState.SetString(QueueName, json);
StartQueue();
}
private static void ProcessWorkQueue()
{
EditorApplication.update -= ProcessWorkQueue;
if (!Instance.HasWorkItems)
return;
T workItem = GetNextWorkItem();
if (Instance.ProcessItemCallback != null)
Instance.ProcessItemCallback(workItem);
if (Instance.HasWorkItems)
EditorApplication.update += ProcessWorkQueue;
}
private static T GetNextWorkItem()
{
T ret = default(T);
if (!Instance.HasWorkItems)
{
return ret;
}
string fromJson = SessionState.GetString(Instance.QueueName, "{}");
SessionState.EraseString(Instance.QueueName);
Queue queue = JsonUtility.FromJson<Queue>(fromJson);
if (queue.workItems.Count <= 0)
{
return ret;
}
ret = queue.workItems[0];
queue.workItems.Remove(ret);
if (queue.workItems.Count > 0)
{
string json = JsonUtility.ToJson(queue);
SessionState.SetString(Instance.QueueName, json);
}
return ret;
}
}
}

View file

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

View file

@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.XR.Management;
[assembly: InternalsVisibleTo("Unity.XR.Management.EditorTests")]
namespace UnityEditor.XR.Management
{
/// <summary>
/// Small utility class for reading, updating and writing boot config.
/// </summary>
internal class BootConfig
{
static readonly string kXrBootSettingsKey = "xr-boot-settings";
Dictionary<string, string> bootConfigSettings;
BuildReport buildReport;
string bootConfigPath;
internal BootConfig(BuildReport report)
{
buildReport = report;
}
internal void ReadBootConfig()
{
bootConfigSettings = new Dictionary<string, string>();
string buildTargetName = BuildPipeline.GetBuildTargetName(buildReport.summary.platform);
string xrBootSettings = UnityEditor.EditorUserBuildSettings.GetPlatformSettings(buildTargetName, kXrBootSettingsKey);
if (!String.IsNullOrEmpty(xrBootSettings))
{
// boot settings string format
// <boot setting>:<value>[;<boot setting>:<value>]*
var bootSettings = xrBootSettings.Split(';');
foreach (var bootSetting in bootSettings)
{
var setting = bootSetting.Split(':');
if (setting.Length == 2 && !String.IsNullOrEmpty(setting[0]) && !String.IsNullOrEmpty(setting[1]))
{
bootConfigSettings.Add(setting[0], setting[1]);
}
}
}
}
internal void SetValueForKey(string key, string value, bool replace = false)
{
if (bootConfigSettings.ContainsKey(key))
{
bootConfigSettings[key] = value;
}
else
{
bootConfigSettings.Add(key, value);
}
}
internal void WriteBootConfig()
{
// boot settings string format
// <boot setting>:<value>[;<boot setting>:<value>]*
bool firstEntry = true;
var sb = new System.Text.StringBuilder();
foreach (var kvp in bootConfigSettings)
{
if (!firstEntry)
{
sb.Append(";");
}
sb.Append($"{kvp.Key}:{kvp.Value}");
firstEntry = false;
}
string buildTargetName = BuildPipeline.GetBuildTargetName(buildReport.summary.platform);
EditorUserBuildSettings.SetPlatformSettings(buildTargetName, kXrBootSettingsKey, sb.ToString());
}
}
class XRGeneralBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
static readonly string kPreInitLibraryKey = "xrsdk-pre-init-library";
class PreInitInfo
{
public PreInitInfo(IXRLoaderPreInit loader, BuildTarget buildTarget, BuildTargetGroup buildTargetGroup)
{
this.loader = loader;
this.buildTarget = buildTarget;
this.buildTargetGroup = buildTargetGroup;
}
public IXRLoaderPreInit loader;
public BuildTarget buildTarget;
public BuildTargetGroup buildTargetGroup;
}
public int callbackOrder
{
get { return 0; }
}
void CleanOldSettings()
{
BuildHelpers.CleanOldSettings<XRGeneralSettings>();
}
public void OnPreprocessBuild(BuildReport report)
{
// Always remember to cleanup preloaded assets after build to make sure we don't
// dirty later builds with assets that may not be needed or are out of date.
CleanOldSettings();
XRGeneralSettingsPerBuildTarget buildTargetSettings = XRGeneralSettingsPerBuildTarget.GetOrCreate();
XRGeneralSettings settings = buildTargetSettings.SettingsForBuildTarget(report.summary.platformGroup);
if (settings == null)
return;
XRManagerSettings loaderManager = settings.AssignedSettings;
if (loaderManager != null)
{
var loaders = loaderManager.activeLoaders;
// If there are no loaders present in the current manager instance, then the settings will not be included in the current build.
if (loaders.Count > 0)
{
var summary = report.summary;
XRManagementAnalytics.SendBuildEvent(summary.guid, summary.platform, summary.platformGroup, loaders);
// chances are that our devices won't fall back to graphics device types later in the list so it's better to assume the device will be created with the first gfx api in the list.
// furthermore, we have no way to influence falling back to other graphics API types unless we automatically change settings underneath the user which is no good!
GraphicsDeviceType[] deviceTypes = PlayerSettings.GetGraphicsAPIs(report.summary.platform);
if (deviceTypes.Length > 0)
{
VerifyGraphicsAPICompatibility(loaderManager, deviceTypes[0]);
}
else
{
Debug.LogWarning("No Graphics APIs have been configured in Player Settings.");
}
PreInitInfo preInitInfo = null;
preInitInfo = new PreInitInfo(loaders[0] as IXRLoaderPreInit, report.summary.platform, report.summary.platformGroup);
var loader = preInitInfo?.loader ?? null;
if (loader != null)
{
BootConfig bootConfig = new BootConfig(report);
bootConfig.ReadBootConfig();
string preInitLibraryName = loader.GetPreInitLibraryName(preInitInfo.buildTarget, preInitInfo.buildTargetGroup);
bootConfig.SetValueForKey(kPreInitLibraryKey, preInitLibraryName);
bootConfig.WriteBootConfig();
}
}
}
UnityEngine.Object[] preloadedAssets = PlayerSettings.GetPreloadedAssets();
var settingsIncludedInPreloadedAssets = preloadedAssets.Contains(settings);
// If there are no loaders present in the current manager instance, then the settings will not be included in the current build.
if (!settingsIncludedInPreloadedAssets && loaderManager.activeLoaders.Count > 0)
{
var assets = preloadedAssets.ToList();
assets.Add(settings);
PlayerSettings.SetPreloadedAssets(assets.ToArray());
}
else
{
CleanOldSettings();
}
}
public static void VerifyGraphicsAPICompatibility(XRManagerSettings loaderManager, GraphicsDeviceType selectedDeviceType)
{
HashSet<GraphicsDeviceType> allLoaderGraphicsDeviceTypes = new HashSet<GraphicsDeviceType>();
foreach (var loader in loaderManager.activeLoaders)
{
List<GraphicsDeviceType> supporteDeviceTypes = loader.GetSupportedGraphicsDeviceTypes(true);
// To help with backward compatibility, if we find that any of the compatibility lists are empty we assume that at least one of the loaders does not implement the GetSupportedGraphicsDeviceTypes method
// Therefore we revert to the previous behavior of building the app regardless of gfx api settings.
if (supporteDeviceTypes.Count == 0)
{
allLoaderGraphicsDeviceTypes.Clear();
break;
}
foreach (var supportedGraphicsDeviceType in supporteDeviceTypes)
{
allLoaderGraphicsDeviceTypes.Add(supportedGraphicsDeviceType);
}
}
if (allLoaderGraphicsDeviceTypes.Count > 0 && !allLoaderGraphicsDeviceTypes.Contains(selectedDeviceType))
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendFormat(
"The selected grpahics API, {0}, is not supported by any of the current loaders. Please change the preferred Graphics API setting in Player Settings.\n",
selectedDeviceType);
foreach (var loader in loaderManager.activeLoaders)
{
stringBuilder.AppendLine(loader.name + " supports:");
foreach (var supportedGraphicsDeviceType in loader.GetSupportedGraphicsDeviceTypes(true))
{
stringBuilder.AppendLine("\t -" + supportedGraphicsDeviceType);
}
}
throw new BuildFailedException(stringBuilder.ToString());
}
}
public void OnPostprocessBuild(BuildReport report)
{
// Always remember to cleanup preloaded assets after build to make sure we don't
// dirty later builds with assets that may not be needed or are out of date.
CleanOldSettings();
}
}
}

View file

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

View file

@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.XR.Management;
using UnityEditor.XR.Management.Metadata;
namespace UnityEditor.XR.Management
{
#if UNITY_EDITOR
[InitializeOnLoad]
#endif
/// <summary>Container class that holds general settings for each build target group installed in Unity.</summary>
public class XRGeneralSettingsPerBuildTarget : ScriptableObject, ISerializationCallbackReceiver
{
[SerializeField]
List<BuildTargetGroup> Keys = new List<BuildTargetGroup>();
[SerializeField]
List<XRGeneralSettings> Values = new List<XRGeneralSettings>();
Dictionary<BuildTargetGroup, XRGeneralSettings> Settings = new Dictionary<BuildTargetGroup, XRGeneralSettings>();
#if UNITY_EDITOR
static XRGeneralSettingsPerBuildTarget()
{
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
EditorApplication.playModeStateChanged += PlayModeStateChanged;
}
// Simple class to give us updates when the asset database changes.
class AssetCallbacks : AssetPostprocessor
{
static bool m_Upgrade = true;
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
if (m_Upgrade)
{
m_Upgrade = false;
BeginUpgradeSettings();
}
}
static void BeginUpgradeSettings()
{
string searchText = "t:XRGeneralSettings";
string[] assets = AssetDatabase.FindAssets(searchText);
if (assets.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(assets[0]);
XRGeneralSettingsUpgrade.UpgradeSettingsToPerBuildTarget(path);
}
}
}
void OnEnable()
{
foreach (var setting in Settings.Values)
{
var assignedSettings = setting.AssignedSettings;
if (assignedSettings == null)
continue;
var filteredLoaders = from ldr in assignedSettings.activeLoaders where ldr != null select ldr;
assignedSettings.TrySetLoaders(filteredLoaders.ToList<XRLoader>());
}
XRGeneralSettings.Instance = XRGeneralSettingsForBuildTarget(BuildTargetGroup.Standalone);
}
static void PlayModeStateChanged(PlayModeStateChange state)
{
XRGeneralSettingsPerBuildTarget buildTargetSettings = null;
EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings);
if (buildTargetSettings == null)
{
buildTargetSettings = GetOrCreate();
}
XRGeneralSettings instance = buildTargetSettings.SettingsForBuildTarget(BuildTargetGroup.Standalone);
if (instance == null || !instance.InitManagerOnStart)
return;
instance.InternalPlayModeStateChanged(state);
}
internal static bool ContainsLoaderForAnyBuildTarget(string loaderTypeName)
{
XRGeneralSettingsPerBuildTarget buildTargetSettings = null;
EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings);
if (buildTargetSettings == null)
return false;
foreach (var settings in buildTargetSettings.Settings.Values)
{
if (XRPackageMetadataStore.IsLoaderAssigned(settings.Manager, loaderTypeName))
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.Synchronized)]
internal static XRGeneralSettingsPerBuildTarget GetOrCreate()
{
EditorBuildSettings.TryGetConfigObject<XRGeneralSettingsPerBuildTarget>(XRGeneralSettings.k_SettingsKey, out var generalSettings);
if (generalSettings == null)
{
string searchText = "t:XRGeneralSettings";
string[] assets = AssetDatabase.FindAssets(searchText);
if (assets.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(assets[0]);
generalSettings = AssetDatabase.LoadAssetAtPath(path, typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget;
}
}
if (generalSettings == null)
{
generalSettings = CreateInstance(typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget;
string assetPath = EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultGeneralSettingsPath);
if (!string.IsNullOrEmpty(assetPath))
{
assetPath = Path.Combine(assetPath, "XRGeneralSettings.asset");
AssetDatabase.CreateAsset(generalSettings, assetPath);
}
}
EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, generalSettings, true);
return generalSettings;
}
#endif
/// <summary>
/// Query this settings store to see if there are settings for a specific <see cref="BuildTargetGroup"/>.
/// </summary>
/// <param name="buildTargetGroup">Build target to check</param>
/// <returns>True if there are settings, otherwise false.</returns>
public bool HasSettingsForBuildTarget(BuildTargetGroup buildTargetGroup)
{
return SettingsForBuildTarget(buildTargetGroup) != null;
}
/// <summary>
/// Create default settings for a given build target.
///
/// This <b>will overwrite</b> any current settings for that build target.
/// </summary>
/// <param name="buildTargetGroup">Build target to create default settings for.</param>
public void CreateDefaultSettingsForBuildTarget(BuildTargetGroup buildTargetGroup)
{
var settings = ScriptableObject.CreateInstance<XRGeneralSettings>() as XRGeneralSettings;
SetSettingsForBuildTarget(buildTargetGroup, settings);
settings.name = $"{buildTargetGroup.ToString()} Settings";
AssetDatabase.AddObjectToAsset(settings, AssetDatabase.GetAssetOrScenePath(this));
AssetDatabase.SaveAssets();
}
/// <summary>Set specific settings for a given build target.</summary>
///
/// <param name="targetGroup">An enum specifying which platform group this build is for.</param>
/// <param name="settings">An instance of <see cref="XRGeneralSettings"/> to assign for the given key.</param>
public void SetSettingsForBuildTarget(BuildTargetGroup targetGroup, XRGeneralSettings settings)
{
// Ensures the editor's "runtime instance" is the most current for standalone settings
if (targetGroup == BuildTargetGroup.Standalone)
XRGeneralSettings.Instance = settings;
Settings[targetGroup] = settings;
}
/// <summary>Get specific settings for a given build target.</summary>
/// <param name="targetGroup">An enum specifying which platform group this build is for.</param>
/// <returns>The instance of <see cref="XRGeneralSettings"/> assigned to the key, or null if not.</returns>
public XRGeneralSettings SettingsForBuildTarget(BuildTargetGroup targetGroup)
{
XRGeneralSettings ret = null;
Settings.TryGetValue(targetGroup, out ret);
return ret;
}
/// <summary>
/// Check if current settings instance has an instance of <see cref="XRManagerSettings"/>.
/// </summary>
/// <param name="targetGroup">An enum specifying which platform group this build is for.</param>
/// <returns>True if it exists, false otherwise.</returns>
public bool HasManagerSettingsForBuildTarget(BuildTargetGroup targetGroup)
{
return (SettingsForBuildTarget(targetGroup)?.Manager ?? null) != null;
}
/// <summary>
/// Create a new default instance of <see cref="XRManagerSettings"/> for a build target. Requires
/// that the there exists a settings instance for the build target. If there isn't, then one is created.
///
/// This <b>will overwrite</b> any current settings for that build target.
/// </summary>
/// <param name="targetGroup">An enum specifying which platform group this build is for.</param>
public void CreateDefaultManagerSettingsForBuildTarget(BuildTargetGroup targetGroup)
{
if (!HasSettingsForBuildTarget(targetGroup))
CreateDefaultSettingsForBuildTarget(targetGroup);
var xrManagerSettings = ScriptableObject.CreateInstance<XRManagerSettings>() as XRManagerSettings;
xrManagerSettings.name = $"{targetGroup.ToString()} Providers";
SettingsForBuildTarget(targetGroup).Manager = xrManagerSettings;
AssetDatabase.AddObjectToAsset(xrManagerSettings, AssetDatabase.GetAssetOrScenePath(this));
AssetDatabase.SaveAssets();
}
/// <summary>
/// Return the current instance of <see cref="XRManagerSettings"/> for a build target.
/// </summary>
/// <param name="targetGroup">An enum specifying which platform group this build is for.</param>
/// <returns></returns>
public XRManagerSettings ManagerSettingsForBuildTarget(BuildTargetGroup targetGroup)
{
return SettingsForBuildTarget(targetGroup)?.Manager ?? null;
}
/// <summary>Serialization override.</summary>
public void OnBeforeSerialize()
{
Keys.Clear();
Values.Clear();
foreach (var kv in Settings)
{
Keys.Add(kv.Key);
Values.Add(kv.Value);
}
}
/// <summary>Serialization override.</summary>
public void OnAfterDeserialize()
{
Settings = new Dictionary<BuildTargetGroup, XRGeneralSettings>();
for (int i = 0; i < Math.Min(Keys.Count, Values.Count); i++)
{
Settings.Add(Keys[i], Values[i]);
}
}
/// <summary>Given a build target, get the general settings container assigned to it.</summary>
/// <param name="targetGroup">An enum specifying which platform group this build is for.</param>
/// <returns>The instance of <see cref="XRGeneralSettings"/> assigned to the key, or null if not.</returns>
public static XRGeneralSettings XRGeneralSettingsForBuildTarget(BuildTargetGroup targetGroup)
{
XRGeneralSettingsPerBuildTarget buildTargetSettings = null;
EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings);
if (buildTargetSettings == null)
return null;
return buildTargetSettings.SettingsForBuildTarget(targetGroup);
}
}
}

View file

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

View file

@ -0,0 +1,54 @@
using UnityEngine;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management
{
/// <summary>Helper class to auto update settings across versions.</summary>
public static class XRGeneralSettingsUpgrade
{
/// <summary>Worker API to do the actual upgrade</summary>
/// <param name="path">Path to asset to upgrade</param>
/// <returns>True if settings were successfullly upgraded, else false.</returns>
public static bool UpgradeSettingsToPerBuildTarget(string path)
{
var generalSettings = GetXRGeneralSettingsInstance(path);
if (generalSettings == null)
return false;
if (!AssetDatabase.IsMainAsset(generalSettings))
return false;
XRGeneralSettings newSettings = ScriptableObject.CreateInstance<XRGeneralSettings>() as XRGeneralSettings;
newSettings.Manager = generalSettings.Manager;
generalSettings = null;
AssetDatabase.RemoveObjectFromAsset(newSettings.Manager); // Remove object from asset, before deleting asset
AssetDatabase.DeleteAsset(path);
XRGeneralSettingsPerBuildTarget buildTargetSettings = ScriptableObject.CreateInstance<XRGeneralSettingsPerBuildTarget>() as XRGeneralSettingsPerBuildTarget;
AssetDatabase.CreateAsset(buildTargetSettings, path);
buildTargetSettings.SetSettingsForBuildTarget(EditorUserBuildSettings.selectedBuildTargetGroup, newSettings);
newSettings.name = $"{EditorUserBuildSettings.selectedBuildTargetGroup.ToString()} Settings";
AssetDatabase.AddObjectToAsset(newSettings, path);
AssetDatabase.SaveAssets();
Debug.LogWarningFormat("XR General Settings have been upgraded to be per-Build Target Group. Original settings were moved to Build Target Group {0}.", EditorUserBuildSettings.selectedBuildTargetGroup);
return true;
}
private static XRGeneralSettings GetXRGeneralSettingsInstance(string pathToSettings)
{
XRGeneralSettings ret = null;
if (pathToSettings.Length > 0)
{
ret = AssetDatabase.LoadAssetAtPath(pathToSettings, typeof(XRGeneralSettings)) as XRGeneralSettings;
}
return ret;
}
}
}

View file

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

View file

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management
{
internal class XRLoaderInfo : IEquatable<XRLoaderInfo>
{
public Type loaderType;
public string assetName;
public XRLoader instance;
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is XRLoaderInfo && Equals((XRLoaderInfo)obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (loaderType != null ? loaderType.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (instance != null ? instance.GetHashCode() : 0);
return hashCode;
}
}
public bool Equals(XRLoaderInfo other)
{
return other != null && Equals(loaderType, other.loaderType) && Equals(instance, other.instance);
}
static string[] s_LoaderBlackList = { "DummyLoader", "SampleLoader", "XRLoaderHelper" };
internal static void GetAllKnownLoaderInfos(List<XRLoaderInfo> newInfos)
{
var loaderTypes = TypeLoaderExtensions.GetAllTypesWithInterface<XRLoader>();
foreach (Type loaderType in loaderTypes)
{
if (loaderType.IsAbstract)
continue;
if (s_LoaderBlackList.Contains(loaderType.Name))
continue;
var assets = AssetDatabase.FindAssets(String.Format("t:{0}", loaderType));
if (!assets.Any())
{
XRLoaderInfo info = new XRLoaderInfo();
info.loaderType = loaderType;
newInfos.Add(info);
}
else
{
foreach (var asset in assets)
{
string path = AssetDatabase.GUIDToAssetPath(asset);
XRLoaderInfo info = new XRLoaderInfo();
info.loaderType = loaderType;
info.instance = AssetDatabase.LoadAssetAtPath(path, loaderType) as XRLoader;
info.assetName = Path.GetFileNameWithoutExtension(path);
newInfos.Add(info);
}
}
}
}
}
}

View file

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

View file

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management
{
internal class XRLoaderInfoManager : IXRLoaderOrderManager
{
// Simple class to give us updates when the asset database changes.
internal class AssetCallbacks : AssetPostprocessor
{
static bool s_EditorUpdatable = false;
internal static System.Action Callback { get; set; }
static AssetCallbacks()
{
if (!s_EditorUpdatable)
{
EditorApplication.update += EditorUpdatable;
}
EditorApplication.projectChanged += EditorApplicationOnProjectChanged;
}
static void EditorApplicationOnProjectChanged()
{
if (Callback != null)
Callback.Invoke();
}
static void EditorUpdatable()
{
s_EditorUpdatable = true;
EditorApplication.update -= EditorUpdatable;
if (Callback != null)
Callback.Invoke();
}
}
private SerializedObject m_SerializedObject;
SerializedProperty m_RequiresSettingsUpdate = null;
SerializedProperty m_LoaderList = null;
public SerializedObject SerializedObjectData
{
get { return m_SerializedObject; }
set
{
if (m_SerializedObject != value)
{
m_SerializedObject = value;
PopulateProperty("m_RequiresSettingsUpdate", ref m_RequiresSettingsUpdate);
PopulateProperty("m_Loaders", ref m_LoaderList);
ShouldReload = true;
}
}
}
List<XRLoaderInfo> m_AllLoaderInfos = new List<XRLoaderInfo>();
List<XRLoaderInfo> m_AllLoaderInfosForBuildTarget = new List<XRLoaderInfo>();
List<XRLoaderInfo> m_AssignedLoaderInfos = new List<XRLoaderInfo>();
List<XRLoaderInfo> m_UnassignedLoaderInfos = new List<XRLoaderInfo>();
private BuildTargetGroup m_BuildTargetGroup = BuildTargetGroup.Unknown;
internal BuildTargetGroup BuildTarget
{
get { return m_BuildTargetGroup; }
set
{
if (m_BuildTargetGroup != value)
{
m_BuildTargetGroup = value;
ShouldReload = true;
}
}
}
void AssetProcessorCallback()
{
ShouldReload = true;
}
public void OnEnable()
{
AssetCallbacks.Callback += AssetProcessorCallback;
ShouldReload = true;
}
public bool ShouldReload
{
get
{
if (m_RequiresSettingsUpdate != null)
{
SerializedObjectData.Update();
return m_RequiresSettingsUpdate.boolValue;
}
return false;
}
set
{
if (m_RequiresSettingsUpdate != null && m_RequiresSettingsUpdate.boolValue != value)
{
m_RequiresSettingsUpdate.boolValue = value;
SerializedObjectData.ApplyModifiedProperties();
}
}
}
public void OnDisable()
{
AssetCallbacks.Callback -= null;
}
public void ReloadData()
{
if (m_LoaderList == null)
return;
PopulateAllLoaderInfos();
PopulateLoadersForBuildTarget();
PopulateAssignedLoaderInfos();
PopulateUnassignedLoaderInfos();
ShouldReload = false;
}
void PopulateAllLoaderInfos()
{
m_AllLoaderInfos.Clear();
XRLoaderInfo.GetAllKnownLoaderInfos(m_AllLoaderInfos);
}
void CleanupLostAssignedLoaders()
{
var missingLoaders = from info in m_AssignedLoaderInfos
where info.instance == null
select info;
if (missingLoaders.Any())
{
m_AssignedLoaderInfos = m_AssignedLoaderInfos.Except(missingLoaders).ToList();
}
}
void PopulateAssignedLoaderInfos()
{
m_AssignedLoaderInfos.Clear();
for (int i = 0; i < m_LoaderList.arraySize; i++)
{
var prop = m_LoaderList.GetArrayElementAtIndex(i);
XRLoaderInfo info = new XRLoaderInfo();
info.loaderType = (prop.objectReferenceValue == null) ? null : prop.objectReferenceValue.GetType();
info.assetName = AssetNameFromInstance(prop.objectReferenceValue);
info.instance = prop.objectReferenceValue as XRLoader;
m_AssignedLoaderInfos.Add(info);
}
CleanupLostAssignedLoaders();
}
string AssetNameFromInstance(UnityEngine.Object asset)
{
if (asset == null)
return "";
string assetPath = AssetDatabase.GetAssetPath(asset);
return Path.GetFileNameWithoutExtension(assetPath);
}
void PopulateLoadersForBuildTarget()
{
m_AllLoaderInfosForBuildTarget = FilteredLoaderInfos(m_AllLoaderInfos);
}
void PopulateUnassignedLoaderInfos()
{
m_UnassignedLoaderInfos.Clear();
foreach (var info in m_AllLoaderInfosForBuildTarget)
{
var assigned = from ai in m_AssignedLoaderInfos where ai.loaderType == info.loaderType select ai;
if (!assigned.Any()) m_UnassignedLoaderInfos.Add(info);
}
}
void PopulateProperty(string propertyPath, ref SerializedProperty prop)
{
if (SerializedObjectData != null && prop == null) prop = SerializedObjectData.FindProperty(propertyPath);
}
private List<XRLoaderInfo> FilteredLoaderInfos(List<XRLoaderInfo> loaderInfos)
{
List<XRLoaderInfo> ret = new List<XRLoaderInfo>();
foreach (var info in loaderInfos)
{
if (info.loaderType == null)
continue;
object[] attrs;
try
{
attrs = info.loaderType.GetCustomAttributes(typeof(XRSupportedBuildTargetAttribute), true);
}
catch (Exception)
{
attrs = default;
}
if (attrs.Length == 0)
{
// If unmarked we assume it will be applied to all build targets.
ret.Add(info);
}
else
{
foreach (XRSupportedBuildTargetAttribute attr in attrs)
{
if (attr.buildTargetGroup == m_BuildTargetGroup)
{
ret.Add(info);
break;
}
}
}
}
return ret;
}
void UpdateSerializedProperty()
{
if (m_LoaderList != null && m_LoaderList.isArray)
{
m_LoaderList.ClearArray();
int index = 0;
foreach (XRLoaderInfo info in m_AssignedLoaderInfos)
{
m_LoaderList.InsertArrayElementAtIndex(index);
var prop = m_LoaderList.GetArrayElementAtIndex(index);
prop.objectReferenceValue = info.instance;
index++;
}
}
SerializedObjectData.ApplyModifiedProperties();
}
#region IXRLoaderOrderManager
List<XRLoaderInfo> IXRLoaderOrderManager.AssignedLoaders { get { return m_AssignedLoaderInfos; } }
List<XRLoaderInfo> IXRLoaderOrderManager.UnassignedLoaders { get { return m_UnassignedLoaderInfos; } }
void IXRLoaderOrderManager.AssignLoader(XRLoaderInfo assignedInfo)
{
m_AssignedLoaderInfos.Add(assignedInfo);
m_UnassignedLoaderInfos.Remove(assignedInfo);
UpdateSerializedProperty();
ShouldReload = true;
}
void IXRLoaderOrderManager.UnassignLoader(XRLoaderInfo unassignedInfo)
{
m_AssignedLoaderInfos.Remove(unassignedInfo);
m_UnassignedLoaderInfos.Add(unassignedInfo);
UpdateSerializedProperty();
ShouldReload = true;
}
void IXRLoaderOrderManager.Update()
{
UpdateSerializedProperty();
ShouldReload = true;
}
#endregion
}
}

View file

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

View file

@ -0,0 +1,322 @@
using System;
using System.Collections.Generic;
using UnityEditorInternal;
using UnityEngine;
using UnityEditor.XR.Management.Metadata;
namespace UnityEditor.XR.Management
{
internal interface IXRLoaderOrderManager
{
List<XRLoaderInfo> AssignedLoaders { get; }
List<XRLoaderInfo> UnassignedLoaders { get; }
void AssignLoader(XRLoaderInfo assignedInfo);
void UnassignLoader(XRLoaderInfo unassignedInfo);
void Update();
}
internal class XRLoaderOrderUI
{
struct LoaderInformation
{
public string packageName;
public string packageId;
public string loaderName;
public string loaderType;
public bool toggled;
public bool stateChanged;
public bool disabled;
public IXRCustomLoaderUI customLoaderUI;
}
const string k_AtNoLoaderInstance = "There are no XR plugins applicable to this platform.";
private List<LoaderInformation> m_LoaderMetadata = null;
ReorderableList m_OrderedList = null;
public BuildTargetGroup CurrentBuildTargetGroup { get; set; }
struct Content
{
public static readonly string k_HelpUri = "https://docs.unity3d.com/Packages/com.unity.xr.management@4.0/manual/EndUser.html";
public static readonly GUIContent k_LoaderUITitle = EditorGUIUtility.TrTextContent("Plug-in Providers");
public static readonly GUIContent k_HelpContent = new GUIContent("",
EditorGUIUtility.IconContent("_Help@2x").image,
"Selecting an XR Plug-in Provider installs and loads the corresponding package in your project. You can view and manage these packages in the Package Manager.");
}
struct DeprecationInfo
{
public GUIContent icon;
public GUIContent renderContent;
}
static Dictionary<string, DeprecationInfo> s_DeprecationInfo = new Dictionary<string, DeprecationInfo>();
static bool s_DidPopulateDeprecationInfo = false;
#if UNITY_2021_1_OR_NEWER
static string k_DeprecatedWMRLoaderName = "Windows Mixed Reality";
private static string k_DeprecatedLuminLoaderName = "Magic Leap - Note: Lumin Platform will be deprecated in Unity 2021.2!";
#endif //UNITY_2021_1_OR_NEWER
static bool IsDeprecated(string loaderName)
{
#if UNITY_2021_2_OR_NEWER
if (loaderName == k_DeprecatedWMRLoaderName || loaderName == k_DeprecatedLuminLoaderName)
return true;
#endif //UNITY_2021_2_OR_NEWER
return false;
}
static void PopulateDeprecationInfo()
{
if (s_DidPopulateDeprecationInfo)
return;
s_DidPopulateDeprecationInfo = true;
#if UNITY_2021_1_OR_NEWER
s_DeprecationInfo[k_DeprecatedWMRLoaderName] = new DeprecationInfo{
icon = EditorGUIUtility.IconContent("console.warnicon.sml"),
renderContent = new GUIContent("",
EditorGUIUtility.IconContent("console.warnicon.sml").image,
@"Microsoft has transitioned support of Windows MR devices to OpenXR in Unity 2021, and recommends using Unity's OpenXR plugin. As such, this Windows XR plugin is marked as deprecated and will be removed in the 2021.2 release. It will continue to be supported in the 2020 LTS.")
};
s_DeprecationInfo[k_DeprecatedLuminLoaderName] = new DeprecationInfo {
icon = EditorGUIUtility.IconContent("console.warnicon.sml"),
renderContent = new GUIContent("",
EditorGUIUtility.IconContent("console.warnicon.sml").image,
@"Unity 2020 LTS will be the last version of the editor which supports Magic Leap 1.
Developers can continue to build for Magic Leap 1 using Unity 2020 LTS or 2019 LTS.")
};
#endif //UNITY_2021_1_OR_NEWER
}
internal XRLoaderOrderUI()
{
}
void SetDisablesStateOnLoadersFromLoader(LoaderInformation li)
{
for (int i = 0; i < m_LoaderMetadata.Count; i++)
{
var otherLi = m_LoaderMetadata[i];
if (otherLi.loaderType == li.loaderType)
continue;
if (li.customLoaderUI != null && Array.IndexOf(li.customLoaderUI.IncompatibleLoaders, otherLi.loaderType) >= 0)
{
if (li.toggled && otherLi.toggled)
{
otherLi.toggled = false;
otherLi.stateChanged = true;
}
otherLi.disabled = li.toggled;
m_LoaderMetadata[i] = otherLi;
}
}
}
void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
{
var li = m_LoaderMetadata[index];
if (PackageNotificationUtils.registeredPackagesWithNotifications.TryGetValue(li.packageId, out var notificationInfo))
PackageNotificationUtils.DrawNotificationIconUI(notificationInfo, rect);
li.toggled = XRPackageMetadataStore.IsLoaderAssigned(li.loaderType, CurrentBuildTargetGroup);
var preToggledState = li.toggled;
EditorGUI.BeginDisabledGroup(li.disabled);
if (li.customLoaderUI != null)
{
li.customLoaderUI.OnGUI(rect);
li.toggled = li.customLoaderUI.IsLoaderEnabled;
}
else
{
string name = li.loaderName;
if (s_DeprecationInfo.ContainsKey(name))
{
var depInfo = s_DeprecationInfo[name];
var labelRect = rect;
var size = EditorStyles.label.CalcSize(depInfo.icon);
labelRect.width -= size.y + 1;
var imageRect = new Rect(rect);
imageRect.xMin = labelRect.xMax + 1;
imageRect.width = size.y;
li.toggled = EditorGUI.ToggleLeft(labelRect, li.loaderName, preToggledState);
EditorGUI.LabelField(imageRect, depInfo.renderContent);
}
else
{
li.toggled = EditorGUI.ToggleLeft(rect, li.loaderName, preToggledState);
}
}
li.stateChanged = (li.toggled != preToggledState);
m_LoaderMetadata[index] = li;
EditorGUI.EndDisabledGroup();
}
float GetElementHeight(int index)
{
var li = m_LoaderMetadata[index];
if (li.customLoaderUI != null)
{
li.customLoaderUI.SetRenderedLineHeight(m_OrderedList.elementHeight);
return li.customLoaderUI.RequiredRenderHeight;
}
return m_OrderedList.elementHeight;
}
internal bool OnGUI(BuildTargetGroup buildTargetGroup)
{
PopulateDeprecationInfo();
var settings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup);
if (buildTargetGroup != CurrentBuildTargetGroup || m_LoaderMetadata == null)
{
CurrentBuildTargetGroup = buildTargetGroup;
if (m_LoaderMetadata == null)
m_LoaderMetadata = new List<LoaderInformation>();
else
m_LoaderMetadata.Clear();
foreach (var pmd in XRPackageMetadataStore.GetLoadersForBuildTarget(buildTargetGroup))
{
if (IsDeprecated(pmd.loaderName))
continue;
var newLi = new LoaderInformation() {
packageName = pmd.packageName,
packageId = pmd.packageId,
loaderName = pmd.loaderName,
loaderType = pmd.loaderType,
toggled = XRPackageMetadataStore.IsLoaderAssigned(pmd.loaderType, buildTargetGroup),
disabled = false,
customLoaderUI = XRCustomLoaderUIManager.GetCustomLoaderUI(pmd.loaderType, buildTargetGroup)
};
if (newLi.customLoaderUI != null)
{
newLi.customLoaderUI.IsLoaderEnabled = newLi.toggled;
newLi.customLoaderUI.ActiveBuildTargetGroup = CurrentBuildTargetGroup;
}
m_LoaderMetadata.Add(newLi);
}
if (settings != null)
{
List<LoaderInformation> loadersWantingToDisableOtherLoaders = new List<LoaderInformation>();
LoaderInformation li;
for (int i = 0; i < m_LoaderMetadata.Count; i++)
{
li = m_LoaderMetadata[i];
if (XRPackageMetadataStore.IsLoaderAssigned(settings.AssignedSettings, li.loaderType))
{
li.toggled = true;
m_LoaderMetadata[i] = li;
if (li.customLoaderUI != null)
{
loadersWantingToDisableOtherLoaders.Add(li);
}
break;
}
}
foreach(var loader in loadersWantingToDisableOtherLoaders)
{
SetDisablesStateOnLoadersFromLoader(loader);
}
}
m_OrderedList = new ReorderableList(m_LoaderMetadata, typeof(LoaderInformation), false, true, false, false);
m_OrderedList.drawHeaderCallback = (rect) =>
{
var labelSize = EditorStyles.label.CalcSize(Content.k_LoaderUITitle);
var labelRect = new Rect(rect);
labelRect.width = labelSize.x;
labelSize = EditorStyles.label.CalcSize(Content.k_HelpContent);
var imageRect = new Rect(rect);
imageRect.xMin = labelRect.xMax + 1;
imageRect.width = labelSize.x;
EditorGUI.LabelField(labelRect, Content.k_LoaderUITitle, EditorStyles.label);
if (GUI.Button(imageRect, Content.k_HelpContent, EditorStyles.label))
{
System.Diagnostics.Process.Start(Content.k_HelpUri);
}
};
m_OrderedList.drawElementCallback = (rect, index, isActive, isFocused) => DrawElementCallback(rect, index, isActive, isFocused);
m_OrderedList.drawElementBackgroundCallback = (rect, index, isActive, isFocused) =>
{
var tex = GUI.skin.label.normal.background;
if (tex == null && GUI.skin.label.normal.scaledBackgrounds.Length > 0) tex = GUI.skin.label.normal.scaledBackgrounds[0];
if (tex == null) return;
GUI.DrawTexture(rect, GUI.skin.label.normal.background);
};
m_OrderedList.drawFooterCallback = (rect) =>
{
var status = XRPackageMetadataStore.GetCurrentStatusDisplayText();
GUI.Label(rect, EditorGUIUtility.TrTextContent(status), EditorStyles.label);
};
m_OrderedList.elementHeightCallback = (index) => GetElementHeight(index);
}
if (m_LoaderMetadata == null || m_LoaderMetadata.Count == 0)
{
EditorGUILayout.HelpBox(k_AtNoLoaderInstance, MessageType.Info);
}
else
{
m_OrderedList.DoLayoutList();
if (settings != null)
{
LoaderInformation li;
for (int i = 0; i < m_LoaderMetadata.Count; i++)
{
li = m_LoaderMetadata[i];
if (li.stateChanged && li.customLoaderUI != null)
SetDisablesStateOnLoadersFromLoader(li);
}
for (int i = 0; i < m_LoaderMetadata.Count; i++)
{
li = m_LoaderMetadata[i];
if (li.stateChanged)
{
if (li.toggled)
{
XRPackageMetadataStore.InstallPackageAndAssignLoaderForBuildTarget(li.packageId, li.loaderType, buildTargetGroup);
}
else
{
XRPackageMetadataStore.RemoveLoader(settings.AssignedSettings, li.loaderType, buildTargetGroup);
}
li.stateChanged = false;
m_LoaderMetadata[i] = li;
}
}
}
}
return false;
}
}
}

View file

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

View file

@ -0,0 +1,35 @@
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management
{
[CustomEditor(typeof(XRManagerSettings))]
internal class XRManagerSettingsEditor : Editor
{
XRLoaderOrderUI m_LoaderUi = new XRLoaderOrderUI();
internal BuildTargetGroup BuildTarget
{
get;
set;
}
public void Reload()
{
m_LoaderUi.CurrentBuildTargetGroup = BuildTargetGroup.Unknown;
}
/// <summary><see href="https://docs.unity3d.com/ScriptReference/Editor.OnInspectorGUI.html">Editor Documentation</see></summary>
public override void OnInspectorGUI()
{
if (serializedObject == null || serializedObject.targetObject == null)
return;
serializedObject.Update();
m_LoaderUi.OnGUI(BuildTarget);
serializedObject.ApplyModifiedProperties();
}
}
}

View file

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

View file

@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor.XR.Management.Metadata;
namespace UnityEditor.XR.Management
{
/// <summary>Interface for specifying package initialization information</summary>
public interface XRPackageInitializationBase
{
/// <summary>Package name property</summary>
/// <value>The name of the package</value>
string PackageName { get; }
/// <summary>The loader full type name for this package</summary>
/// <value>Loader fulltype name</value>
string LoaderFullTypeName { get; }
/// <summary>The loader type name for this package</summary>
/// <value>Loader type name</value>
string LoaderTypeName { get; }
/// <summary>The settings full type name for this package</summary>
/// <value>Settings full type name</value>
string SettingsFullTypeName { get; }
/// <summary>The settings type name for this package</summary>
/// <value>Settings type name</value>
string SettingsTypeName { get; }
/// <summary>Package initialization key</summary>
/// <value>The init key for the package</value>
string PackageInitKey { get; }
/// <summary>Initialize package settings</summary>
/// <param name="obj">The scriptable object instance to initialize</param>
/// <returns>True if successful, false if not.</returns>
bool PopulateSettingsOnInitialization(ScriptableObject obj);
}
[InitializeOnLoad]
class XRPackageInitializationBootstrap
{
static XRPackageInitializationBootstrap()
{
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
EditorApplication.playModeStateChanged += PlayModeStateChanged;
}
private static void PlayModeStateChanged(PlayModeStateChange state)
{
switch (state)
{
case PlayModeStateChange.EnteredPlayMode:
BeginPackageInitialization();
break;
case PlayModeStateChange.EnteredEditMode:
BeginPackageInitialization();
break;
}
}
internal static void BeginPackageInitialization()
{
EditorApplication.update -= BeginPackageInitialization;
foreach (var t in TypeLoaderExtensions.GetAllTypesWithInterface<IXRPackage>())
{
if (t.IsInterface || t.FullName.Contains("Unity.XR.Management.TestPackage") || t.FullName.Contains("UnityEditor.XR.Management.Metadata.KnownPackages"))
continue;
IXRPackage package = Activator.CreateInstance(t) as IXRPackage;
if (package == null)
{
Debug.LogError($"Unable to find an implementation for expected package type {t.FullName}.");
continue;
}
InitPackage(package);
}
foreach (var t in TypeLoaderExtensions.GetAllTypesWithInterface<XRPackageInitializationBase>())
{
if (t.IsInterface)
continue;
XRPackageInitializationBase packageInit = Activator.CreateInstance(t) as XRPackageInitializationBase;
if (packageInit == null)
{
Debug.LogError($"Unable to find an implementation for expected package type {t.FullName}.");
continue;
}
InitPackage(packageInit);
}
if (XRSettingsManager.Instance != null) XRSettingsManager.Instance.ResetUi = true;
}
internal static void InitPackage(IXRPackage package)
{
var packageMetadata = package.metadata;
if (packageMetadata == null)
{
Debug.LogError($"Package {package.GetType().Name} has a package definition but has no metadata. Skipping initialization.");
return;
}
XRPackageMetadataStore.AddPluginPackage(package);
if (!InitializePackageFromMetadata(package, packageMetadata))
{
Debug.LogWarning(
String.Format("{0} package Initialization not completed. You will need to create any instances of the loaders and settings manually before you can use the intended XR Plug-in Package.", packageMetadata.packageName));
}
}
static bool InitializePackageFromMetadata(IXRPackage package, IXRPackageMetadata packageMetadata)
{
bool ret = true;
ret = ret && InitializeLoaderFromMetadata(packageMetadata.packageName, packageMetadata.loaderMetadata);
ret = ret && InitializeSettingsFromMetadata(package, packageMetadata.packageName, packageMetadata.settingsType);
return ret;
}
static bool InitializeLoaderFromMetadata(string packageName, List<IXRLoaderMetadata> loaderMetadatas)
{
if (String.IsNullOrEmpty(packageName))
return false;
if (loaderMetadatas == null || loaderMetadatas.Count == 0)
{
Debug.LogWarning($"Package {packageName} has no loader metadata. Skipping loader initialization.");
return true;
}
bool ret = true;
foreach (var loader in loaderMetadatas)
{
bool hasInstance = EditorUtilities.AssetDatabaseHasInstanceOfType(loader.loaderType);
if (!hasInstance)
{
var obj = EditorUtilities.CreateScriptableObjectInstance(loader.loaderType,
EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultLoaderPath));
hasInstance = (obj != null);
if (!hasInstance)
{
Debug.LogError($"Error creating instance of loader {loader.loaderName} for package {packageName}");
}
}
ret |= hasInstance;
}
return ret;
}
static bool InitializeSettingsFromMetadata(IXRPackage package, string packageName, string settingsType)
{
if (String.IsNullOrEmpty(packageName))
return false;
if (settingsType == null)
{
Debug.LogWarning($"Package {packageName} has no settings metadata. Skipping settings initialization.");
return true;
}
bool ret = EditorUtilities.AssetDatabaseHasInstanceOfType(settingsType);
if (!ret)
{
var obj = EditorUtilities.CreateScriptableObjectInstance( settingsType,
EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultSettingsPath));
ret = package.PopulateNewSettingsInstance(obj);
}
return ret;
}
static void InitPackage(XRPackageInitializationBase packageInit)
{
if (!InitializeLoaderInstance(packageInit))
{
Debug.LogWarning(
String.Format("{0} Loader Initialization not completed. You will need to create an instance of the loader manually before you can use the intended XR Plug-in Package.", packageInit.PackageName));
}
if (!InitializeSettingsInstance(packageInit))
{
Debug.LogWarning(
String.Format("{0} Settings Initialization not completed. You will need to create an instance of settings to customize options specific to this package.", packageInit.PackageName));
}
}
static bool InitializeLoaderInstance(XRPackageInitializationBase packageInit)
{
bool ret = EditorUtilities.AssetDatabaseHasInstanceOfType(packageInit.LoaderTypeName);
if (!ret)
{
var obj = EditorUtilities.CreateScriptableObjectInstance(packageInit.LoaderFullTypeName,
EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultLoaderPath));
ret = (obj != null);
}
return ret;
}
static bool InitializeSettingsInstance(XRPackageInitializationBase packageInit)
{
bool ret = EditorUtilities.AssetDatabaseHasInstanceOfType(packageInit.SettingsTypeName);
if (!ret)
{
var obj = EditorUtilities.CreateScriptableObjectInstance(packageInit.SettingsFullTypeName,
EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultSettingsPath));
ret = packageInit.PopulateSettingsOnInitialization(obj);
}
return ret;
}
}
}

View file

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

View file

@ -0,0 +1,98 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime;
using UnityEngine;
using UnityEditor;
namespace UnityEditor.XR.Management
{
internal class XRPackageInitializationSettings : ScriptableObject
{
private static XRPackageInitializationSettings s_PackageSettings = null;
private static object s_Lock = new object();
internal static string s_ProjectSettingsAssetName = "XRPackageSettings.asset";
internal static string s_ProjectSettingsFolder = "../ProjectSettings";
internal static string s_ProjectSettingsPath;
internal static string s_PackageInitPath;
[SerializeField]
private List<string> m_Settings = new List<string>();
private XRPackageInitializationSettings(){ }
internal static XRPackageInitializationSettings Instance
{
get
{
if (s_PackageSettings == null)
{
lock(s_Lock)
{
if (s_PackageSettings == null)
{
s_PackageSettings = ScriptableObject.CreateInstance<XRPackageInitializationSettings>();
s_PackageSettings.LoadSettings();
}
}
}
return s_PackageSettings;
}
}
void InitPaths()
{
if (String.IsNullOrEmpty(s_ProjectSettingsPath)) s_ProjectSettingsPath = Path.Combine(Application.dataPath, s_ProjectSettingsFolder);
if (String.IsNullOrEmpty(s_PackageInitPath)) s_PackageInitPath = Path.Combine(s_ProjectSettingsPath, s_ProjectSettingsAssetName);
}
void OnEnable()
{
InitPaths();
}
internal void LoadSettings()
{
InitPaths();
if (File.Exists(s_PackageInitPath))
{
using (StreamReader sr = new StreamReader(s_PackageInitPath))
{
string settings = sr.ReadToEnd();
JsonUtility.FromJsonOverwrite(settings, this);
}
}
}
internal void SaveSettings()
{
InitPaths();
if (!Directory.Exists(s_ProjectSettingsPath))
Directory.CreateDirectory(s_ProjectSettingsPath);
using (StreamWriter sw = new StreamWriter(s_PackageInitPath))
{
string settings = JsonUtility.ToJson(this, true);
sw.Write(settings);
}
}
internal bool HasSettings(string key)
{
return m_Settings.Contains(key);
}
internal void AddSettings(string key)
{
if (!HasSettings(key))
m_Settings.Add(key);
}
}
}

View file

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

View file

@ -0,0 +1,280 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor.XR.Management.Metadata;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.XR.Management;
namespace UnityEditor.XR.Management
{
class XRSettingsManager : SettingsProvider
{
internal static class Styles
{
public static readonly GUIStyle k_UrlLabelPersonal = new GUIStyle(EditorStyles.label)
{
name = "url-label",
richText = true,
normal = new GUIStyleState { textColor = new Color(8 / 255f, 8 / 255f, 252 / 255f) },
};
public static readonly GUIStyle k_UrlLabelProfessional = new GUIStyle(EditorStyles.label)
{
name = "url-label",
richText = true,
normal = new GUIStyleState { textColor = new Color(79 / 255f, 128 / 255f, 248 / 255f) },
};
public static readonly GUIStyle k_LabelWordWrap = new GUIStyle(EditorStyles.label) { wordWrap = true };
}
struct Content
{
public static readonly GUIContent k_InitializeOnStart = new GUIContent("Initialize XR on Startup");
public static readonly GUIContent k_XRConfigurationText = new GUIContent("Information about configuration, tracking and migration can be found below.");
public static readonly GUIContent k_XRConfigurationDocUriText = new GUIContent("View Documentation");
public static readonly Uri k_XRConfigurationUri = new Uri(" https://docs.unity3d.com/Manual/configuring-project-for-xr.html");
}
internal static GUIStyle GetStyle(string styleName)
{
GUIStyle s = GUI.skin.FindStyle(styleName) ?? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle(styleName);
if (s == null)
{
Debug.LogError("Missing built-in guistyle " + styleName);
s = GUI.skin.box;
}
return s;
}
static string s_SettingsRootTitle = $"Project/{XRConstants.k_XRPluginManagement}";
static XRSettingsManager s_SettingsManager = null;
internal static XRSettingsManager Instance => s_SettingsManager;
private bool resetUi = false;
internal bool ResetUi
{
get
{
return resetUi;
}
set
{
resetUi = value;
if (resetUi)
Repaint();
}
}
SerializedObject m_SettingsWrapper;
private Dictionary<BuildTargetGroup, XRManagerSettingsEditor> CachedSettingsEditor = new Dictionary<BuildTargetGroup, XRManagerSettingsEditor>();
private BuildTargetGroup m_LastBuildTargetGroup = BuildTargetGroup.Unknown;
static XRGeneralSettingsPerBuildTarget currentSettings
{
get
{
return XRGeneralSettingsPerBuildTarget.GetOrCreate();
}
}
[UnityEngine.Internal.ExcludeFromDocs]
XRSettingsManager(string path, SettingsScope scopes = SettingsScope.Project) : base(path, scopes)
{
}
[SettingsProvider]
[UnityEngine.Internal.ExcludeFromDocs]
static SettingsProvider Create()
{
if (s_SettingsManager == null)
{
s_SettingsManager = new XRSettingsManager(s_SettingsRootTitle);
}
return s_SettingsManager;
}
[SettingsProviderGroup]
[UnityEngine.Internal.ExcludeFromDocs]
static SettingsProvider[] CreateAllChildSettingsProviders()
{
List<SettingsProvider> ret = new List<SettingsProvider>();
if (s_SettingsManager != null)
{
var ats = TypeLoaderExtensions.GetAllTypesWithAttribute<XRConfigurationDataAttribute>();
foreach (var at in ats)
{
if (at.FullName.Contains("Unity.XR.Management.TestPackage"))
continue;
XRConfigurationDataAttribute xrbda = at.GetCustomAttributes(typeof(XRConfigurationDataAttribute), true)[0] as XRConfigurationDataAttribute;
string settingsPath = String.Format("{1}/{0}", xrbda.displayName, s_SettingsRootTitle);
var resProv = new XRConfigurationProvider(settingsPath, xrbda.buildSettingsKey, at);
ret.Add(resProv);
}
}
return ret.ToArray();
}
void InitEditorData(ScriptableObject settings)
{
if (settings != null)
{
m_SettingsWrapper = new SerializedObject(settings);
}
}
/// <summary>See <see href="https://docs.unity3d.com/ScriptReference/SettingsProvider.html">SettingsProvider documentation</see>.</summary>
public override void OnActivate(string searchContext, VisualElement rootElement)
{
InitEditorData(currentSettings);
}
/// <summary>See <see href="https://docs.unity3d.com/ScriptReference/SettingsProvider.html">SettingsProvider documentation</see>.</summary>
public override void OnDeactivate()
{
m_SettingsWrapper = null;
CachedSettingsEditor.Clear();
}
private void DisplayLoaderSelectionUI()
{
BuildTargetGroup buildTargetGroup = EditorGUILayout.BeginBuildTargetSelectionGrouping();
try
{
bool buildTargetChanged = m_LastBuildTargetGroup != buildTargetGroup;
if (buildTargetChanged)
m_LastBuildTargetGroup = buildTargetGroup;
if (!currentSettings.HasManagerSettingsForBuildTarget(buildTargetGroup))
{
currentSettings.CreateDefaultManagerSettingsForBuildTarget(buildTargetGroup);
}
XRGeneralSettings settings = currentSettings.SettingsForBuildTarget(buildTargetGroup);
var serializedSettingsObject = new SerializedObject(settings);
serializedSettingsObject.Update();
SerializedProperty initOnStart = serializedSettingsObject.FindProperty("m_InitManagerOnStart");
EditorGUILayout.PropertyField(initOnStart, Content.k_InitializeOnStart);
EditorGUILayout.Space();
SerializedProperty loaderProp = serializedSettingsObject.FindProperty("m_LoaderManagerInstance");
var obj = loaderProp.objectReferenceValue;
if (obj != null)
{
loaderProp.objectReferenceValue = obj;
if (!CachedSettingsEditor.ContainsKey(buildTargetGroup))
{
CachedSettingsEditor.Add(buildTargetGroup, null);
}
if (CachedSettingsEditor[buildTargetGroup] == null)
{
CachedSettingsEditor[buildTargetGroup] = Editor.CreateEditor(obj) as XRManagerSettingsEditor;
if (CachedSettingsEditor[buildTargetGroup] == null)
{
Debug.LogError("Failed to create a view for XR Manager Settings Instance");
}
}
if (CachedSettingsEditor[buildTargetGroup] != null)
{
if (ResetUi)
{
ResetUi = false;
CachedSettingsEditor[buildTargetGroup].Reload();
}
CachedSettingsEditor[buildTargetGroup].BuildTarget = buildTargetGroup;
CachedSettingsEditor[buildTargetGroup].OnInspectorGUI();
}
}
else if (obj == null)
{
settings.AssignedSettings = null;
loaderProp.objectReferenceValue = null;
}
serializedSettingsObject.ApplyModifiedProperties();
}
catch (Exception ex)
{
Debug.LogError($"Error trying to display plug-in assingment UI : {ex.Message}");
}
EditorGUILayout.EndBuildTargetSelectionGrouping();
}
private void DisplayLink(GUIContent text, Uri link, int leftMargin)
{
var labelStyle = EditorGUIUtility.isProSkin ? Styles.k_UrlLabelProfessional : Styles.k_UrlLabelPersonal;
var size = labelStyle.CalcSize(text);
var uriRect = GUILayoutUtility.GetRect(text, labelStyle);
uriRect.x += leftMargin;
uriRect.width = size.x;
if (GUI.Button(uriRect, text, labelStyle))
{
System.Diagnostics.Process.Start(link.AbsoluteUri);
}
EditorGUIUtility.AddCursorRect(uriRect, MouseCursor.Link);
EditorGUI.DrawRect(new Rect(uriRect.x, uriRect.y + uriRect.height - 1, uriRect.width, 1), labelStyle.normal.textColor);
}
private void DisplayXRTrackingDocumentationLink()
{
GUILayout.BeginVertical(EditorStyles.helpBox);
{
EditorGUILayout.LabelField(Content.k_XRConfigurationText, Styles.k_LabelWordWrap);
DisplayLink(Content.k_XRConfigurationDocUriText, Content.k_XRConfigurationUri, 2);
}
GUILayout.EndVertical();
EditorGUILayout.Space();
}
private void DisplayLoadOrderUi()
{
EditorGUILayout.Space();
EditorGUI.BeginDisabledGroup(XRPackageMetadataStore.isDoingQueueProcessing || EditorApplication.isPlaying || EditorApplication.isPaused);
if (m_SettingsWrapper != null && m_SettingsWrapper.targetObject != null)
{
m_SettingsWrapper.Update();
EditorGUILayout.Space();
DisplayLoaderSelectionUI();
m_SettingsWrapper.ApplyModifiedProperties();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space();
}
/// <summary>See <see href="https://docs.unity3d.com/ScriptReference/SettingsProvider.html">SettingsProvider documentation</see>.</summary>
public override void OnGUI(string searchContext)
{
EditorGUILayout.Space();
DisplayLoadOrderUi();
DisplayXRTrackingDocumentationLink();
base.OnGUI(searchContext);
}
}
}

View file

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

View file

@ -0,0 +1,45 @@
using System;
#if UNITY_EDITOR
using UnityEditor;
namespace UnityEditor.XR.Management
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class XRSupportedBuildTargetAttribute : Attribute
{
/// <summary>
/// String representation of <see href="https://docs.unity3d.com/ScriptReference/BuildTargetGroup.html">UnityEditor.Build.BuildTargetGroup
/// </summary>
public BuildTargetGroup buildTargetGroup { get; set; }
/// <summary>
/// Array of BuildTargets, each of which is the representation of <see href="https://docs.unity3d.com/ScriptReference/BuildTarget.html">UnityEditor.Build.BuildTarget
/// aligned with <see cref="buildTargetGroup"/>.
///
/// Currently only advisory.
/// </summary>
public BuildTarget[] buildTargets { get; set; }
private XRSupportedBuildTargetAttribute() { }
/// <summary>Constructor for attribute. We assume that all build targets for this group will be supported.</summary>
/// <param name="buildTargetGroup">Build Target Group that will be supported.</param>
public XRSupportedBuildTargetAttribute(BuildTargetGroup buildTargetGroup)
{
this.buildTargetGroup = buildTargetGroup;
}
/// <summary>Constructor for attribute</summary>
/// <param name="buildTargetGroup">Build Target Group that will be supported.</param>
/// <param name="buildTargets">The set of build targets of Build Target Group that will be supported.</param>
public XRSupportedBuildTargetAttribute(BuildTargetGroup buildTargetGroup, BuildTarget[] buildTargets)
{
this.buildTargetGroup = buildTargetGroup;
this.buildTargets = buildTargets;
}
}
}
#endif

View file

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