VR4Medical/ICI/Library/PackageCache/com.unity.xr.openxr@3903c1059bcf/Editor/OpenXRProjectValidationRulesSetup.cs
2025-07-29 13:45:50 +03:00

228 lines
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Callbacks;
using UnityEditor.PackageManager;
using UnityEngine.XR.OpenXR;
#if XR_COMPOSITION_LAYERS
using UnityEngine.XR.OpenXR.Features.CompositionLayers;
#endif
using UnityEngine.XR.OpenXR.Features;
using Unity.XR.CoreUtils.Editor;
using UnityEditor.XR.Management;
using UnityEngine.XR.Management;
[assembly: InternalsVisibleTo("UnityEditor.XR.OpenXR.Tests")]
namespace UnityEditor.XR.OpenXR
{
internal class OpenXRProjectValidationRulesSetup
{
static BuildTargetGroup[] s_BuildTargetGroups =
((BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup))).Distinct().ToArray();
static BuildTargetGroup s_SelectedBuildTargetGroup = BuildTargetGroup.Unknown;
internal const string OpenXRProjectValidationSettingsPath = "Project/XR Plug-in Management/Project Validation";
[InitializeOnLoadMethod]
static void OpenXRProjectValidationCheck()
{
UnityEditor.PackageManager.Events.registeredPackages += (packageRegistrationEventArgs) =>
{
// In the Player Settings UI we have to delay the call one frame to let OpenXRSettings constructor to get initialized
EditorApplication.delayCall += () =>
{
if (HasXRPackageVersionChanged(packageRegistrationEventArgs))
{
ShowWindowIfIssuesExist();
}
};
};
AddOpenXRValidationRules();
}
private static bool HasXRPackageVersionChanged(PackageRegistrationEventArgs packageRegistrationEventArgs)
{
bool packageChanged = packageRegistrationEventArgs.changedTo.Any(p => p.name.Equals(OpenXRManagementSettings.PackageId));
OpenXRSettings.Instance.versionChanged = packageChanged;
return packageRegistrationEventArgs.added.Any(p => p.name.Equals(OpenXRManagementSettings.PackageId)) || packageChanged;
}
private static void ShowWindowIfIssuesExist()
{
List<OpenXRFeature.ValidationRule> failures = new List<OpenXRFeature.ValidationRule>();
BuildTargetGroup activeBuildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
OpenXRProjectValidation.GetCurrentValidationIssues(failures, activeBuildTargetGroup);
if (failures.Count > 0)
ShowWindow();
}
internal static BuildValidationRule ConvertRuleToBuildValidationRule(OpenXRFeature.ValidationRule rule, BuildTargetGroup buildTargetGroup)
{
Type featureType = null;
if (rule.feature != null)
{
featureType = rule.feature.GetType();
}
return new BuildValidationRule
{
// This will hide the rules given a condition so that when you click "Show all" it doesn't show up as passed
IsRuleEnabled = () =>
{
// If OpenXR isn't enabled, no need to show the rule
if (!BuildHelperUtils.HasLoader(buildTargetGroup, typeof(OpenXRLoaderBase)))
return false;
// If a rule specific feature isn't enabled, don't show this rule
if (featureType != null)
{
OpenXRSettings openXrSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup);
if (openXrSettings == null)
return false;
OpenXRFeature feature = openXrSettings.GetFeature(featureType);
if (feature == null || !feature.enabled)
return false;
}
return true;
},
CheckPredicate = rule.checkPredicate,
Error = rule.error,
FixIt = rule.fixIt,
FixItAutomatic = rule.fixItAutomatic,
FixItMessage = rule.fixItMessage,
HelpLink = rule.helpLink,
HelpText = rule.helpText,
HighlighterFocus = new BuildValidationRule.HighlighterFocusData
{
WindowTitle = rule.highlighterFocus.windowTitle,
SearchText = rule.highlighterFocus.searchText
},
Message = rule.message,
Category = rule.feature != null ? rule.feature.nameUi : "OpenXR",
SceneOnlyValidation = false
};
}
static void AddOpenXRValidationRules()
{
foreach (var buildTargetGroup in s_BuildTargetGroups)
{
bool isOpenXRSupportedPlatform = buildTargetGroup == BuildTargetGroup.Standalone || buildTargetGroup == BuildTargetGroup.Android || buildTargetGroup == BuildTargetGroup.WSA;
if (!isOpenXRSupportedPlatform)
continue;
var coreIssues = new List<BuildValidationRule>() { GetDefaultBuildValidationRule(buildTargetGroup) };
var issues = new List<OpenXRFeature.ValidationRule>();
OpenXRProjectValidation.GetAllValidationIssues(issues, buildTargetGroup);
foreach (var issue in issues)
{
coreIssues.Add(ConvertRuleToBuildValidationRule(issue, buildTargetGroup));
}
BuildValidator.AddRules(buildTargetGroup, coreIssues);
}
}
static BuildValidationRule GetDefaultBuildValidationRule(BuildTargetGroup targetGroup)
{
var defaultRule = new BuildValidationRule()
{
// This will hide the rules given a condition so that when you click "Show all" it doesn't show up as passed
IsRuleEnabled = () =>
{
// Only show this rule if there are enabled OpenXR features that aren't interaction profiles
return ExistsOpenXRFeaturesEnabledForBuildTarget(targetGroup);
},
Message = "[OpenXR] Enabled OpenXR Features require OpenXR to be selected as the active loader for this platform",
CheckPredicate = () => BuildHelperUtils.HasLoader(targetGroup, typeof(OpenXRLoaderBase)),
Error = false,
FixIt = () => { SettingsService.OpenProjectSettings("Project/XR Plug-in Management"); },
FixItAutomatic = false,
FixItMessage = "Open Project Settings to select OpenXR as the active loader for this platform."
};
return defaultRule;
}
/// <summary>
/// Check if there are any features enabled for this build target
/// </summary>
static bool ExistsOpenXRFeaturesEnabledForBuildTarget(BuildTargetGroup buildTargetGroup)
{
var openXrSettings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup);
foreach (var feature in openXrSettings?.features)
{
if (feature != null && feature.enabled && !(feature is OpenXRInteractionFeature interactionFeature && !interactionFeature.IsAdditive))
{
return true;
}
}
return false;
}
[MenuItem("Window/XR/OpenXR/Project Validation")]
private static void MenuItem()
{
ShowWindow();
}
internal static void SetSelectedBuildTargetGroup(BuildTargetGroup buildTargetGroup)
{
if (s_SelectedBuildTargetGroup == buildTargetGroup)
return;
s_SelectedBuildTargetGroup = buildTargetGroup;
}
internal static void ShowWindow(BuildTargetGroup buildTargetGroup = BuildTargetGroup.Unknown)
{
// Delay opening the window since sometimes other settings in the player settings provider redirect to the
// project validation window causing serialized objects to be nullified
EditorApplication.delayCall += () =>
{
SetSelectedBuildTargetGroup(buildTargetGroup);
SettingsService.OpenProjectSettings(OpenXRProjectValidationSettingsPath);
};
}
internal static void CloseWindow() { }
}
internal class OpenXRProjectValidationBuildStep : IPreprocessBuildWithReport
{
[OnOpenAsset(0)]
static bool ConsoleErrorDoubleClicked(int instanceId, int line)
{
var objName = EditorUtility.InstanceIDToObject(instanceId).name;
if (objName == "OpenXRProjectValidation")
{
OpenXRProjectValidationRulesSetup.ShowWindow();
return true;
}
return false;
}
public int callbackOrder { get; }
public void OnPreprocessBuild(BuildReport report)
{
if (!BuildHelperUtils.HasLoader(report.summary.platformGroup, typeof(OpenXRLoaderBase)))
return;
if (OpenXRProjectValidation.LogBuildValidationIssues(report.summary.platformGroup))
throw new BuildFailedException("OpenXR Build Failed.");
}
}
}