Files
PrinceOfGlory/Packages/PICO Unity Integration SDK-3.0.0-240902/Editor/ProjectValidation/PXR_ProjectValidationRequired.cs
kridoo 6e91a0c7f0 111
2025-09-15 17:32:08 +08:00

520 lines
25 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Unity.XR.CoreUtils.Editor;
using Unity.XR.PXR;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.SceneManagement;
using UnityEditor.XR.Management;
using UnityEditor.XR.Management.Metadata;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.XR.Management;
using Unity.XR.CoreUtils;
#if URP
using UnityEngine.Rendering.Universal;
#endif
#if AR_FOUNDATION
using UnityEngine.XR.ARFoundation;
#endif
static class PXR_ProjectValidationRequired
{
const string k_Catergory = "PICO XR Required";
[InitializeOnLoadMethod]
static void AddRequiredRules()
{
#if UNITY_2021_1_OR_NEWER
NamedBuildTarget recommendedBuildTarget = NamedBuildTarget.Android;
#else
BuildTargetGroup recommendedBuildTarget = BuildTargetGroup.Android;
#endif
const int minSdkVersionInEditor = 29;
const string minSdkNameInEditor = "Android 10.0";
var androidGlobalRules = new[]
{
new BuildValidationRule
{
Category = k_Catergory,
Message = $"PICO XR SDK targeting minimum Android 10.0 is required or {minSdkNameInEditor} API Level.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return (int)PlayerSettings.Android.minSdkVersion >= minSdkVersionInEditor;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > Android tab to set PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel29.",
FixIt = () =>
{
PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel29;
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "'Target Architectures' and 'Scripting Backend' must be matched! Recommended use ARM64 architecture and IL2CPP scripting.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
if ((PlayerSettings.Android.targetArchitectures & AndroidArchitecture.ARM64) != AndroidArchitecture.None)
{
return PlayerSettings.GetScriptingBackend(recommendedBuildTarget) == ScriptingImplementation.IL2CPP;
}else if((PlayerSettings.Android.targetArchitectures & AndroidArchitecture.ARMv7) != AndroidArchitecture.None)
{
return PlayerSettings.GetScriptingBackend(recommendedBuildTarget) == ScriptingImplementation.Mono2x;
}
return false;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > Android tab and ensure 'Scripting Backend'" +
" is set to 'IL2CPP'. Then under 'Target Architectures' enable 'ARM64'.",
FixIt = () =>
{
PlayerSettings.SetScriptingBackend(recommendedBuildTarget, ScriptingImplementation.IL2CPP);
PlayerSettings.Android.targetArchitectures |= AndroidArchitecture.ARM64;
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "Using 'UIOrientation.LandscapeLeft'.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return PlayerSettings.defaultInterfaceOrientation == UIOrientation.LandscapeLeft;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Resolution and Presentation > 'Default Orientation' set 'LandscapeLeft'.",
FixIt = () =>
{
PlayerSettings.defaultInterfaceOrientation = UIOrientation.LandscapeLeft;
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = $"When using FaceTracking, it is necessary to allow unsafe codes!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
if (PXR_ProjectSetting.GetProjectConfig().faceTracking)
{
return PlayerSettings.allowUnsafeCode;
}
return true;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > Allow 'unsafe' Code",
FixIt = () =>
{
if (PXR_ProjectSetting.GetProjectConfig().faceTracking)
{
PlayerSettings.allowUnsafeCode = true;
}
},
Error = true
},
#if UNITY_2022
new BuildValidationRule
{
Category = k_Catergory,
Message = $"On Unity2022, it is not allowed to check 'Development Build' when using Vulkan!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return !(GraphicsDeviceType.Vulkan == PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget)[0] && EditorUserBuildSettings.development);
},
FixItMessage = "Build Settings > uncheck 'Development Build'",
FixIt = () =>
{
EditorUserBuildSettings.development = false;
},
Error = true
},
#endif
#if UNITY_2023_1_OR_NEWER
new BuildValidationRule
{
Category = k_Catergory,
Message = $"Please use Activity instead of GameActivity!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return PlayerSettings.Android.applicationEntry == AndroidApplicationEntry.Activity;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > Application Entry Point: Activity",
FixIt = () =>
{
PlayerSettings.Android.applicationEntry = AndroidApplicationEntry.Activity;
},
Error = true
},
#endif
new BuildValidationRule
{
Category = k_Catergory,
Message = $"Build target platform needs to be modified to Android!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android;
},
FixItMessage = "Open Project Settings > Platform> Android",
FixIt = () =>
{
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = $"'PXR_Manager' needs to be added in the scene!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
#if AR_FOUNDATION
if (PXR_ProjectSetting.GetProjectConfig().arFoundation)
{
List<ARCameraManager> components = FindComponentsInScene<ARCameraManager>().Where(component => (component.enabled && component.gameObject.CompareTag("MainCamera"))).ToList();
if (components.Count > 0)
{
return true;
}
}
#endif
return FindComponentsInScene<PXR_Manager>().Where(component => component.isActiveAndEnabled).ToList().Count >= 1;
},
FixItMessage = "Add 'PXR_Manager' on 'MainCamera''s root parent transform",
FixIt = () =>
{
List<Camera> components = FindComponentsInScene<Camera>().Where(component => (component.enabled && component.gameObject.CompareTag("MainCamera"))).ToList();
Debug.LogFormat($"components.Count = {components.Count}");
for (int i = 0; i < components.Count; i++)
{
GameObject gameObject = components[i].transform.gameObject;
XROrigin[] xROrigins = gameObject.GetComponentsInParent<XROrigin>();
if(xROrigins.Length > 0)
{
Transform rootTransform = xROrigins[0].transform;
if(!rootTransform.GetComponent<PXR_Manager>())
{
rootTransform.gameObject.AddComponent<PXR_Manager>();
}
else
{
rootTransform.GetComponent<PXR_Manager>().enabled = true;
}
}
}
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "Only one 'XROrigin' is allowed in the scene!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return FindComponentsInScene<XROrigin>().Where(component => component.isActiveAndEnabled).ToList().Count ==1;
},
FixItMessage = "XROrigin > Disable.",
FixIt = () =>
{
List<XROrigin> components = FindComponentsInScene<XROrigin>().Where(component => component.isActiveAndEnabled).ToList();
if (components.Count == 0)
{
if(!EditorApplication.ExecuteMenuItem("GameObject/XR/XR Origin (VR)"))
{
EditorApplication.ExecuteMenuItem("GameObject/XR/XR Origin (Action-based)");
}
return;
}
for(int i=1; i < components.Count; i++)
{
components[i].transform.gameObject.SetActive(false);
}
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = $"Only one 'MainCamera' is allowed in the scene!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
List<Camera> components = FindComponentsInScene<Camera>().Where(component => (component.isActiveAndEnabled && component.gameObject.CompareTag("MainCamera"))).ToList();
if (components.Count == 1)
{
GameObject gameObject = components[0].transform.gameObject;
return gameObject.GetComponentsInParent<XROrigin>().Length == 1;
}
return false;
},
FixItMessage = "Scene > MainCamera > Disable.",
FixIt = () =>
{
List<Camera> components = FindComponentsInScene<Camera>().Where(component => (component.enabled && component.gameObject.CompareTag("MainCamera"))).ToList();
for(int i=0; i < components.Count; i++)
{
GameObject gameObject = components[i].transform.gameObject;
if(gameObject.GetComponentsInParent<XROrigin>().Length == 1)
{
gameObject.SetActive(true);
}
else
{
gameObject.tag = $"Camera{i}";
gameObject.SetActive(false);
}
}
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = $"Only one 'AudioListener' is allowed in the scene!",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
return FindComponentsInScene<AudioListener>().Where(component => component.isActiveAndEnabled).ToList().Count <= 1;
},
FixItMessage = "Disable 'AudioListener' on non 'MainCamera'",
FixIt = () =>
{
List<AudioListener> components = FindComponentsInScene<AudioListener>().Where(component => component.isActiveAndEnabled).ToList();
foreach (var component in components)
{
component.enabled = component.gameObject.CompareTag("MainCamera");
EditorSceneManager.MarkSceneDirty(component.gameObject.scene);
}
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "Set the Graphics API order (Vulkan or OpenGLES3) for Android.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
var buildTarget = BuildTarget.Android;
if (PlayerSettings.GetUseDefaultGraphicsAPIs(buildTarget))
{
return true;
}
return PlayerSettings.GetGraphicsAPIs(buildTarget).Any(item => item == GraphicsDeviceType.OpenGLES3 || item == GraphicsDeviceType.Vulkan);
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > 'Graphics API' set Vulkan for Android.",
FixIt = () =>
{
PlayerSettings.SetUseDefaultGraphicsAPIs(BuildTarget.Android, false);
PlayerSettings.SetGraphicsAPIs(BuildTarget.Android, new[] { GraphicsDeviceType.Vulkan });
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "When using ETFR, need to set Graphics API: 'OpenGLES3'.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
if (PXR_ProjectSetting.GetProjectConfig().enableETFR)
{
var buildTarget = BuildTarget.Android;
if (PlayerSettings.GetUseDefaultGraphicsAPIs(buildTarget))
{
return false;
}
return GraphicsDeviceType.OpenGLES3 == PlayerSettings.GetGraphicsAPIs(buildTarget)[0];
}
return true;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > 'Graphics API' set OpenGLES3 for Android.",
FixIt = () =>
{
PlayerSettings.SetUseDefaultGraphicsAPIs(BuildTarget.Android, false);
PlayerSettings.SetGraphicsAPIs(BuildTarget.Android, new[] { GraphicsDeviceType.OpenGLES3 });
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "When using MR features, need to set ARM64 architecture and IL2CPP scripting.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
if (PXR_ProjectSetting.GetProjectConfig().spatialAnchor || PXR_ProjectSetting.GetProjectConfig().sceneCapture || PXR_ProjectSetting.GetProjectConfig().spatialMesh || PXR_ProjectSetting.GetProjectConfig().sharedAnchor)
{
return (PlayerSettings.Android.targetArchitectures & AndroidArchitecture.ARM64) != AndroidArchitecture.None && PlayerSettings.GetScriptingBackend(recommendedBuildTarget) == ScriptingImplementation.IL2CPP;
}
return true;
},
FixItMessage = "Open Project Settings > Player Settings > Player> Other Settings > Android tab and ensure 'Scripting Backend'" +
" is set to 'IL2CPP'. Then under 'Target Architectures' enable 'ARM64'.",
FixIt = () =>
{
PlayerSettings.SetScriptingBackend(recommendedBuildTarget, ScriptingImplementation.IL2CPP);
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
},
Error = true
},
new BuildValidationRule
{
Category = k_Catergory,
Message = "PICO XR plugin needs to be enabled and unique.",
CheckPredicate = () =>
{
var generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTargetGroup.Android);
if (!generalSettings)
{
return false;
}
IReadOnlyList<XRLoader> list = generalSettings.Manager.activeLoaders;
if (list.Count == 0)
{
return false;
}else if (list.Count > 1)
{
return false;
}
else
{
return IsPXRPluginEnabled();
}
},
FixItMessage = "Open Project Settings > Player Settings > XR Plug-in Management> enable 'PICO'.",
FixIt = () =>
{
var generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTargetGroup.Android);
if (generalSettings)
{
IReadOnlyList<XRLoader> list = generalSettings.Manager.activeLoaders;
while (list.Count > 0)
{
string nameTemp = list[0].GetType().FullName;
XRPackageMetadataStore.RemoveLoader(generalSettings.Manager, nameTemp, BuildTargetGroup.Android);
}
XRPackageMetadataStore.AssignLoader(generalSettings.Manager, "PXR_Loader", BuildTargetGroup.Android);
}
},
Error = true
},
#if URP
#if UNITY_2021_3_OR_NEWER || UNITY_2022_3_OR_NEWER
new BuildValidationRule
{
Category = k_Catergory,
Message = "When using URP, it is necessary to set Quality > Render Pipeline Asset.",
CheckPredicate = () =>
{
if (GraphicsSettings.currentRenderPipeline!= null)
{
return QualitySettings.renderPipeline != null;
}
return true;
},
FixItMessage = "Open Project Settings > Player Settings > Quality> Render Pipeline Asset.",
FixIt = () =>
{
var pipelineAssets = new List<RenderPipelineAsset>();
QualitySettings.GetAllRenderPipelineAssetsForPlatform("Android", ref pipelineAssets);
RenderPipelineAsset renderPipeline = pipelineAssets[0];
if (QualitySettings.renderPipeline == null)
{
QualitySettings.renderPipeline = renderPipeline;
}
},
Error = true
},
#endif
#if UNITY_2022
new BuildValidationRule
{
Category = k_Catergory,
Message = $"On Unity2022, it is not recommended msaa4 when using URP+Linear+OpenGLES3.",
IsRuleEnabled = IsPXRPluginEnabled,
CheckPredicate = () =>
{
if (QualitySettings.renderPipeline != null && GraphicsSettings.currentRenderPipeline!= null && PlayerSettings.colorSpace == ColorSpace.Linear
&& GraphicsDeviceType.OpenGLES3 == PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget)[0])
{
UniversalRenderPipelineAsset universalRenderPipelineAsset = (UniversalRenderPipelineAsset)GraphicsSettings.renderPipelineAsset;
return universalRenderPipelineAsset.msaaSampleCount != 4;
}
return true;
},
FixItMessage = "Build Settings > uncheck 'Development Build'",
FixIt = () =>
{
UniversalRenderPipelineAsset universalRenderPipelineAsset = (UniversalRenderPipelineAsset)GraphicsSettings.renderPipelineAsset;
universalRenderPipelineAsset.msaaSampleCount = 1;
},
Error = true
},
#endif
new BuildValidationRule
{
Category = k_Catergory,
Message = "When using URP, HDR needs to be disabled.",
CheckPredicate = () =>
{
if (QualitySettings.renderPipeline != null && GraphicsSettings.currentRenderPipeline!= null)
{
UniversalRenderPipelineAsset universalRenderPipelineAsset = (UniversalRenderPipelineAsset)GraphicsSettings.renderPipelineAsset;
return !universalRenderPipelineAsset.supportsHDR;
}
return true;
},
FixItMessage = "Open Universal Render Pipeline Asset > Quality > disable HDR.",
FixIt = () =>
{
UniversalRenderPipelineAsset universalRenderPipelineAsset = (UniversalRenderPipelineAsset)GraphicsSettings.renderPipelineAsset;
universalRenderPipelineAsset.supportsHDR = false;
},
Error = true
},
#endif
};
BuildValidator.AddRules(BuildTargetGroup.Android, androidGlobalRules);
}
static bool IsPXRPluginEnabled()
{
var generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(
BuildTargetGroup.Android);
if (generalSettings == null)
return false;
var managerSettings = generalSettings.AssignedSettings;
return managerSettings != null && managerSettings.activeLoaders.Any(loader => loader is PXR_Loader);
}
public static List<T> FindComponentsInScene<T>() where T : Component
{
var activeScene = SceneManager.GetActiveScene();
var foundComponents = new List<T>();
var rootObjects = activeScene.GetRootGameObjects();
foreach (var rootObject in rootObjects)
{
var components = rootObject.GetComponentsInChildren<T>(true);
foundComponents.AddRange(components);
}
return foundComponents;
}
}