392 lines
17 KiB
C#
392 lines
17 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor.Build;
|
|
using UnityEditor.Build.Reporting;
|
|
using UnityEditor.Rendering;
|
|
using UnityEditor.XR.ARSubsystems;
|
|
using UnityEditor.XR.Management;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.XR.ARKit;
|
|
using Unity.EditorCoroutines.Editor;
|
|
#if UNITY_IOS
|
|
using System.IO;
|
|
using UnityEditor.iOS;
|
|
using UnityEditor.iOS.Xcode;
|
|
#endif
|
|
|
|
using OSVersion = UnityEngine.XR.ARKit.OSVersion;
|
|
|
|
namespace UnityEditor.XR.ARKit
|
|
{
|
|
static class ARKitBuildProcessor
|
|
{
|
|
public static bool loaderEnabled;
|
|
public static bool faceTrackingEnabled;
|
|
|
|
class Preprocessor : IPreprocessBuildWithReport, IPreprocessShaders
|
|
{
|
|
// Magic value according to
|
|
// https://docs.unity3d.com/ScriptReference/PlayerSettings.GetArchitecture.html
|
|
// "0 - None, 1 - ARM64, 2 - Universal."
|
|
const int k_TargetArchitectureArm64 = 1;
|
|
const int k_TargetArchitectureUniversal = 2;
|
|
|
|
// The minimum target Xcode version for the plug-in
|
|
const int k_TargetMinimumMajorXcodeVersion = 11;
|
|
const int k_TargetMinimumMinorXcodeVersion = 0;
|
|
const int k_TargetMinimumPatchXcodeVersion = 0;
|
|
|
|
readonly string[] runtimePluginNames =
|
|
{
|
|
"libUnityARKit.a",
|
|
"UnityARKit.m",
|
|
"libUnityARKitFaceTracking.a",
|
|
};
|
|
|
|
public int callbackOrder => 0;
|
|
|
|
void IPreprocessShaders.OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
|
|
{
|
|
#if UNITY_IOS && UNITY_XR_ARKIT_LOADER_ENABLED
|
|
ProcessShader(shader, snippet, data);
|
|
#endif
|
|
}
|
|
|
|
static void ProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
|
|
{
|
|
// Remove shader variants for the camera background shader that will fail compilation because of package dependencies.
|
|
foreach (var backgroundShaderName in ARKitCameraSubsystem.backgroundShaderNames)
|
|
{
|
|
if (backgroundShaderName.Equals(shader.name))
|
|
{
|
|
foreach (var backgroundShaderKeywordToNotCompile in ARKitCameraSubsystem.backgroundShaderKeywordsToNotCompile)
|
|
{
|
|
ShaderKeyword shaderKeywordToNotCompile = new ShaderKeyword(shader, backgroundShaderKeywordToNotCompile);
|
|
|
|
for (int i = (data.Count - 1); i >= 0; --i)
|
|
{
|
|
if (data[i].shaderKeywordSet.IsEnabled(shaderKeywordToNotCompile))
|
|
{
|
|
data.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
|
|
{
|
|
// Sets delegate to return whether or not a plugin should be included in the build
|
|
SetRuntimePluginCopyDelegate();
|
|
#if UNITY_IOS && UNITY_XR_ARKIT_LOADER_ENABLED
|
|
PreprocessBuild(report);
|
|
#elif UNITY_XR_ARKIT_LOADER_ENABLED
|
|
if (report.summary.platform == BuildTarget.iOS)
|
|
{
|
|
Debug.LogWarning(
|
|
"Apple ARKit XR Plug-in requires the UNITY_IOS preprocessor directive to build a correctly " +
|
|
"configured app for XR. You are building for the iOS platform with the Apple ARKit XR Plug-in " +
|
|
"enabled, but Unity's active build target is not set to iOS. This may result in runtime " +
|
|
"errors in your build.\n To ensure that your build is correctly configured, set Unity's " +
|
|
"active build target to iOS and reload the domain before building, or use the " +
|
|
"'-buildTarget iOS' command line argument when building in batch mode.");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// ReSharper disable once UnusedMember.Local
|
|
void SetRuntimePluginCopyDelegate()
|
|
{
|
|
var allPlugins = PluginImporter.GetAllImporters();
|
|
foreach (var plugin in allPlugins)
|
|
{
|
|
if (plugin.isNativePlugin)
|
|
{
|
|
foreach (var pluginName in runtimePluginNames)
|
|
{
|
|
if (plugin.assetPath.Contains(pluginName))
|
|
{
|
|
plugin.SetIncludeInBuildDelegate(ShouldIncludeRuntimePluginsInBuild);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ReSharper disable once UnusedMember.Local
|
|
static void PreprocessBuild(BuildReport report)
|
|
{
|
|
if (Application.isBatchMode)
|
|
{
|
|
// by the time we reach the preprocessor in batch mode the AssetDatabase is ready to load our iOS XR
|
|
// settings, so we manually update the arkit defines here.
|
|
LoaderEnabledCheck.UpdateARKitDefines();
|
|
}
|
|
|
|
if (report.summary.platform != BuildTarget.iOS || !loaderEnabled)
|
|
return;
|
|
|
|
if (string.IsNullOrEmpty(PlayerSettings.iOS.cameraUsageDescription))
|
|
throw new BuildFailedException(
|
|
"ARKit requires a Camera Usage Description (Player Settings > iOS > Other Settings > Camera Usage Description)");
|
|
|
|
EnsureMinimumXcodeVersion();
|
|
|
|
EnsureMetalIsFirstApi();
|
|
|
|
if (ARKitSettings.GetOrCreateSettings().requirement == ARKitSettings.Requirement.Required)
|
|
{
|
|
EnsureMinimumBuildTarget();
|
|
EnsureTargetArchitecturesAreSupported(report.summary.platformGroup);
|
|
}
|
|
|
|
foreach (var backgroundShaderName in ARKitCameraSubsystem.backgroundShaderNames)
|
|
{
|
|
BuildHelper.AddBackgroundShaderToProject(backgroundShaderName);
|
|
}
|
|
}
|
|
|
|
static void EnsureMinimumBuildTarget()
|
|
{
|
|
var userSetTargetVersion = OSVersion.Parse(PlayerSettings.iOS.targetOSVersionString);
|
|
if (userSetTargetVersion < new OSVersion(11))
|
|
{
|
|
throw new BuildFailedException(
|
|
$"You have selected a minimum target iOS version of {userSetTargetVersion} and have " +
|
|
" the Apple ARKit XR Plug-in package installed. ARKit requires at least iOS version 11.0. " +
|
|
"See Player Settings > Other Settings > Target minimum iOS Version.");
|
|
}
|
|
}
|
|
|
|
static void EnsureMinimumXcodeVersion()
|
|
{
|
|
#if UNITY_IOS && UNITY_EDITOR_OSX
|
|
var xcodeIndex = Math.Max(0, XcodeApplications.GetPreferedXcodeIndex());
|
|
var xcodeVersion = OSVersion.Parse(XcodeApplications.GetXcodeApplicationPublicName(xcodeIndex));
|
|
if (xcodeVersion == new OSVersion(0))
|
|
throw new BuildFailedException(
|
|
$"Could not determine which version of Xcode was selected in the Build Settings. Xcode app was computed as \"{XcodeApplications.GetXcodeApplicationPublicName(xcodeIndex)}\".");
|
|
|
|
if (xcodeVersion < new OSVersion(
|
|
k_TargetMinimumMajorXcodeVersion,
|
|
k_TargetMinimumMinorXcodeVersion,
|
|
k_TargetMinimumPatchXcodeVersion))
|
|
throw new BuildFailedException(
|
|
$"The selected Xcode version: {xcodeVersion} is below the minimum Xcode required Xcode version for the Apple ARKit XR Plug-in. Please target at least Xcode version {k_TargetMinimumMajorXcodeVersion}.{k_TargetMinimumMinorXcodeVersion}.{k_TargetMinimumPatchXcodeVersion}.");
|
|
#endif
|
|
}
|
|
|
|
static void EnsureTargetArchitecturesAreSupported(BuildTargetGroup buildTargetGroup)
|
|
{
|
|
var buildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
|
|
|
|
if (PlayerSettings.GetArchitecture(buildTarget) != k_TargetArchitectureArm64)
|
|
throw new BuildFailedException(
|
|
"Apple ARKit XR Plug-in only supports the ARM64 architecture. " +
|
|
"See Player Settings > Other Settings > Architecture.");
|
|
}
|
|
|
|
static void EnsureMetalIsFirstApi()
|
|
{
|
|
var graphicsApis = PlayerSettings.GetGraphicsAPIs(BuildTarget.iOS);
|
|
if (graphicsApis.Length > 0 && graphicsApis[0] != GraphicsDeviceType.Metal)
|
|
{
|
|
throw new BuildFailedException($"You currently have {graphicsApis[0]} at the top of the list of Graphics APis. However, Metal needs to be first in the list. (See Player Settings > Other Settings > Graphics APIs)");
|
|
}
|
|
}
|
|
|
|
static bool ShouldIncludeRuntimePluginsInBuild(string path)
|
|
{
|
|
if (!loaderEnabled)
|
|
return false;
|
|
|
|
if (path.Contains("libUnityARKitFaceTracking.a"))
|
|
return faceTrackingEnabled;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Our PostProcessor depends on UnityEditor.iOS.PlistDocument, a class in Unity's iOS module.
|
|
// To ensure that this package successfully compiles in projects that don't have the iOS module installed, we must
|
|
// wrap the PostProcessor with a UNITY_IOS preprocessor directive.
|
|
#if UNITY_IOS
|
|
class PostProcessor : IPostprocessBuildWithReport
|
|
{
|
|
// Needs to be > 0 to make sure we remove the shader since the Input System overwrites the preloaded assets array
|
|
public int callbackOrder => 1;
|
|
|
|
void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report)
|
|
{
|
|
#if UNITY_XR_ARKIT_LOADER_ENABLED
|
|
PostprocessBuild(report);
|
|
#endif
|
|
}
|
|
|
|
static void PostprocessBuild(BuildReport report)
|
|
{
|
|
if (report.summary.platform != BuildTarget.iOS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var shaderName in ARKitCameraSubsystem.backgroundShaderNames)
|
|
BuildHelper.RemoveShaderFromProject(shaderName);
|
|
|
|
HandleARKitRequiredFlag(report.summary.outputPath);
|
|
}
|
|
|
|
static void HandleARKitRequiredFlag(string pathToBuiltProject)
|
|
{
|
|
var arkitSettings = ARKitSettings.GetOrCreateSettings();
|
|
string plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
|
|
PlistDocument plist = new PlistDocument();
|
|
plist.ReadFromString(File.ReadAllText(plistPath));
|
|
PlistElementDict rootDict = plist.root;
|
|
|
|
// Get or create array to manage device capabilities
|
|
const string capsKey = "UIRequiredDeviceCapabilities";
|
|
PlistElementArray capsArray;
|
|
if (rootDict.values.TryGetValue(capsKey, out PlistElement pel))
|
|
{
|
|
capsArray = pel.AsArray();
|
|
}
|
|
else
|
|
{
|
|
capsArray = rootDict.CreateArray(capsKey);
|
|
}
|
|
|
|
// Remove any existing "arkit" plist entries
|
|
const string arkitStr = "arkit";
|
|
capsArray.values.RemoveAll(x => arkitStr.Equals(x.AsString()));
|
|
if (arkitSettings.requirement == ARKitSettings.Requirement.Required)
|
|
{
|
|
// Add "arkit" plist entry
|
|
capsArray.AddString(arkitStr);
|
|
}
|
|
|
|
File.WriteAllText(plistPath, plist.WriteToString());
|
|
|
|
// update for Unity-iOS.xcodeproj's Build Setting
|
|
var projectPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
|
|
var project = new PBXProject();
|
|
project.ReadFromFile(projectPath);
|
|
|
|
// target : unity-iphone
|
|
string targetGUID = project.GetUnityMainTargetGuid();
|
|
project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-ld64");
|
|
project.SetBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
|
|
project.AddBuildProperty(targetGUID, "LD_RUNPATH_SEARCH_PATHS", "/usr/lib/swift");
|
|
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)");
|
|
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)");
|
|
|
|
// target : unityframework
|
|
targetGUID = project.GetUnityFrameworkTargetGuid();
|
|
project.AddBuildProperty(targetGUID, "OTHER_LDFLAGS", "-ld64");
|
|
project.SetBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
|
|
project.AddBuildProperty(targetGUID, "LD_RUNPATH_SEARCH_PATHS", "/usr/lib/swift");
|
|
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "/usr/lib/swift");
|
|
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)");
|
|
project.AddBuildProperty(targetGUID, "LIBRARY_SEARCH_PATHS", "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)");
|
|
|
|
// After we're done editing the build settings we save it
|
|
project.WriteToFile(projectPath);
|
|
}
|
|
}
|
|
#endif // UNITY_IOS
|
|
}
|
|
|
|
[InitializeOnLoad]
|
|
class LoaderEnabledCheck
|
|
{
|
|
static readonly NamedBuildTarget s_iOSTarget =
|
|
NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(BuildTarget.iOS));
|
|
|
|
static ARKitSettings s_ARKitSettings;
|
|
|
|
static LoaderEnabledCheck()
|
|
{
|
|
s_ARKitSettings = ARKitSettings.GetOrCreateSettings();
|
|
ARKitBuildProcessor.loaderEnabled = false;
|
|
|
|
// in batch mode, the AssetDatabase may not yet be properly initialized and trying to load the xr settings
|
|
// and check the loader won't work. This can leave us in a state where the build processor can't execute
|
|
// properly down the line even though the loader is enabled in the settings
|
|
if (Application.isBatchMode)
|
|
return;
|
|
|
|
UpdateARKitDefines();
|
|
EditorCoroutineUtility.StartCoroutineOwnerless(UpdateARKitDefinesCoroutine());
|
|
}
|
|
|
|
static IEnumerator UpdateARKitDefinesCoroutine()
|
|
{
|
|
var waitObj = new EditorWaitForSeconds(.25f);
|
|
|
|
while (true)
|
|
{
|
|
UpdateARKitDefines();
|
|
yield return waitObj;
|
|
}
|
|
}
|
|
|
|
internal static void UpdateARKitDefines()
|
|
{
|
|
var iOSXRSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(
|
|
BuildPipeline.GetBuildTargetGroup(BuildTarget.iOS));
|
|
|
|
if (iOSXRSettings == null)
|
|
return;
|
|
|
|
var arkitLoaderShouldBeEnabled = iOSXRSettings.Manager.activeLoaders.OfType<ARKitLoader>().Any();
|
|
|
|
if (arkitLoaderShouldBeEnabled && !ARKitBuildProcessor.loaderEnabled)
|
|
{
|
|
AddDefine("UNITY_XR_ARKIT_LOADER_ENABLED");
|
|
}
|
|
else if (!arkitLoaderShouldBeEnabled && ARKitBuildProcessor.loaderEnabled)
|
|
{
|
|
RemoveDefine("UNITY_XR_ARKIT_LOADER_ENABLED");
|
|
}
|
|
|
|
if (arkitLoaderShouldBeEnabled && s_ARKitSettings.faceTracking && !ARKitBuildProcessor.faceTrackingEnabled)
|
|
{
|
|
AddDefine("UNITY_XR_ARKIT_FACE_TRACKING_ENABLED");
|
|
}
|
|
else if (!s_ARKitSettings.faceTracking && ARKitBuildProcessor.faceTrackingEnabled)
|
|
{
|
|
RemoveDefine("UNITY_XR_ARKIT_FACE_TRACKING_ENABLED");
|
|
}
|
|
|
|
ARKitBuildProcessor.loaderEnabled = arkitLoaderShouldBeEnabled;
|
|
ARKitBuildProcessor.faceTrackingEnabled = arkitLoaderShouldBeEnabled && s_ARKitSettings.faceTracking;
|
|
}
|
|
|
|
static void AddDefine(string define)
|
|
{
|
|
var definesString = PlayerSettings.GetScriptingDefineSymbols(s_iOSTarget);
|
|
var allDefines = new HashSet<string>(definesString.Split(';'));
|
|
|
|
if (allDefines.Contains(define))
|
|
return;
|
|
|
|
allDefines.Add(define);
|
|
PlayerSettings.SetScriptingDefineSymbols(s_iOSTarget, string.Join(";", allDefines));
|
|
}
|
|
|
|
static void RemoveDefine(string define)
|
|
{
|
|
var definesString = PlayerSettings.GetScriptingDefineSymbols(s_iOSTarget);
|
|
var allDefines = new HashSet<string>(definesString.Split(';'));
|
|
allDefines.Remove(define);
|
|
PlayerSettings.SetScriptingDefineSymbols(s_iOSTarget, string.Join(";", allDefines));
|
|
}
|
|
}
|
|
}
|