set up addressable geo tile streaming for quest
- add addressables settings/groups for tile prefabs with custom TileBuildPath/TileLoadPath profiles and link.xml preservation - add editor tools for building tile addressables, configuring openxr quest loaders, removing missing scripts, and forcing android tool paths - add runtime loader + manifest model to stream tile bundles from persistent data with radius-based load/unload - add TestArea1 scene wired to GeoTileAddressablesLoader and update build settings to enable it - update geo tile prefab importer output path to Assets/TilePrefabs - update project/xr/android settings: min sdk 34, app id, openxr composition layers + quest devices, scripting define symbols, and renderer tweaks - update packages (addressables 2.8, ar foundation 6.3.2, composition layers 2.3, collab proxy 2.11.2) and record scriptable build pipeline config - remove temporary recovery scene files and add Notes plan/progress docs
This commit is contained in:
313
Assets/Scripts/Editor/GeoTileAddressablesBuilder.cs
Normal file
313
Assets/Scripts/Editor/GeoTileAddressablesBuilder.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEngine;
|
||||
|
||||
public static class GeoTileAddressablesBuilder
|
||||
{
|
||||
private const string TilePrefabsDir = "Assets/TilePrefabs";
|
||||
private const string TileIndexCsvPath = "Assets/GeoData/tile_index.csv";
|
||||
private const string GroupName = "TilePrefabs";
|
||||
private const string TileLabel = "tile";
|
||||
private const string ManifestFileName = "TileManifest.json";
|
||||
private const float TileSizeMeters = 1000f;
|
||||
|
||||
private const string BuildPathVariable = "TileBuildPath";
|
||||
private const string LoadPathVariable = "TileLoadPath";
|
||||
private const string BuildPathValue = "ServerData/TileBundles/[BuildTarget]";
|
||||
private const string LoadPathValue = "file://{UnityEngine.Application.persistentDataPath}/TileBundles/[BuildTarget]";
|
||||
|
||||
[MenuItem("Tools/Geo Tiles/Build Tile Addressables (Android)")]
|
||||
public static void BuildAndroid()
|
||||
{
|
||||
BuildForTarget(BuildTarget.Android, BuildTargetGroup.Android);
|
||||
}
|
||||
|
||||
private static void BuildForTarget(BuildTarget target, BuildTargetGroup group)
|
||||
{
|
||||
if (!Directory.Exists(TilePrefabsDir))
|
||||
{
|
||||
Debug.LogError($"[GeoTileAddressablesBuilder] Prefab directory missing: {TilePrefabsDir}");
|
||||
return;
|
||||
}
|
||||
if (!File.Exists(TileIndexCsvPath))
|
||||
{
|
||||
Debug.LogError($"[GeoTileAddressablesBuilder] CSV missing: {TileIndexCsvPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = AddressableAssetSettingsDefaultObject.GetSettings(true);
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogError("[GeoTileAddressablesBuilder] Addressables settings not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureProfileVariable(settings, BuildPathVariable, BuildPathValue);
|
||||
EnsureProfileVariable(settings, LoadPathVariable, LoadPathValue);
|
||||
EnsureRemoteCatalogPaths(settings);
|
||||
|
||||
var groupAsset = GetOrCreateGroup(settings);
|
||||
ConfigureGroup(settings, groupAsset);
|
||||
AssignPrefabs(settings, groupAsset);
|
||||
|
||||
settings.SetDirty(AddressableAssetSettings.ModificationEvent.BatchModification, null, true);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
var previousTarget = EditorUserBuildSettings.activeBuildTarget;
|
||||
if (previousTarget != target)
|
||||
{
|
||||
if (!EditorUserBuildSettings.SwitchActiveBuildTarget(group, target))
|
||||
{
|
||||
Debug.LogError($"[GeoTileAddressablesBuilder] Failed to switch build target to {target}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AddressableAssetSettings.BuildPlayerContent();
|
||||
|
||||
var outputPath = GetRemoteCatalogBuildPath(settings, target);
|
||||
if (!Directory.Exists(outputPath))
|
||||
{
|
||||
Debug.LogError($"[GeoTileAddressablesBuilder] Build output not found: {outputPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var (catalogFile, catalogHashFile) = FindCatalogFiles(outputPath);
|
||||
if (string.IsNullOrEmpty(catalogFile))
|
||||
{
|
||||
Debug.LogError($"[GeoTileAddressablesBuilder] Catalog file not found in: {outputPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
var manifest = BuildManifest(target.ToString(), catalogFile, catalogHashFile);
|
||||
var manifestPath = Path.Combine(outputPath, ManifestFileName);
|
||||
File.WriteAllText(manifestPath, JsonUtility.ToJson(manifest, true));
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
Debug.Log($"[GeoTileAddressablesBuilder] DONE. Output={outputPath}");
|
||||
}
|
||||
|
||||
private static void AssignPrefabs(AddressableAssetSettings settings, AddressableAssetGroup group)
|
||||
{
|
||||
var prefabGuids = AssetDatabase.FindAssets("t:Prefab", new[] { TilePrefabsDir });
|
||||
foreach (var guid in prefabGuids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid).Replace("\\", "/");
|
||||
var parentDir = Path.GetDirectoryName(path)?.Replace("\\", "/");
|
||||
if (!string.Equals(parentDir, TilePrefabsDir, StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
var entry = settings.CreateOrMoveEntry(guid, group, false, false);
|
||||
entry.address = Path.GetFileNameWithoutExtension(path);
|
||||
entry.SetLabel(TileLabel, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static AddressableAssetGroup GetOrCreateGroup(AddressableAssetSettings settings)
|
||||
{
|
||||
var group = settings.FindGroup(GroupName);
|
||||
if (group != null)
|
||||
return group;
|
||||
|
||||
group = settings.CreateGroup(GroupName, false, false, false, new List<AddressableAssetGroupSchema>());
|
||||
group.AddSchema<BundledAssetGroupSchema>();
|
||||
group.AddSchema<ContentUpdateGroupSchema>();
|
||||
return group;
|
||||
}
|
||||
|
||||
private static void ConfigureGroup(AddressableAssetSettings settings, AddressableAssetGroup group)
|
||||
{
|
||||
var bundleSchema = group.GetSchema<BundledAssetGroupSchema>();
|
||||
if (bundleSchema == null)
|
||||
bundleSchema = group.AddSchema<BundledAssetGroupSchema>();
|
||||
|
||||
bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately;
|
||||
bundleSchema.BuildPath.SetVariableByName(settings, BuildPathVariable);
|
||||
bundleSchema.LoadPath.SetVariableByName(settings, LoadPathVariable);
|
||||
|
||||
var updateSchema = group.GetSchema<ContentUpdateGroupSchema>();
|
||||
if (updateSchema != null)
|
||||
updateSchema.StaticContent = true;
|
||||
}
|
||||
|
||||
private static void EnsureProfileVariable(AddressableAssetSettings settings, string name, string value)
|
||||
{
|
||||
var profileId = settings.activeProfileId;
|
||||
var current = settings.profileSettings.GetValueByName(profileId, name);
|
||||
if (string.IsNullOrEmpty(current))
|
||||
settings.profileSettings.CreateValue(name, value);
|
||||
|
||||
settings.profileSettings.SetValue(profileId, name, value);
|
||||
}
|
||||
|
||||
private static void EnsureRemoteCatalogPaths(AddressableAssetSettings settings)
|
||||
{
|
||||
settings.BuildRemoteCatalog = true;
|
||||
settings.RemoteCatalogBuildPath.SetVariableByName(settings, BuildPathVariable);
|
||||
settings.RemoteCatalogLoadPath.SetVariableByName(settings, LoadPathVariable);
|
||||
}
|
||||
|
||||
private static (string catalogFile, string catalogHashFile) FindCatalogFiles(string outputPath)
|
||||
{
|
||||
var catalogFiles = Directory.GetFiles(outputPath, "*catalog*.json");
|
||||
if (catalogFiles.Length == 0)
|
||||
catalogFiles = Directory.GetFiles(outputPath, "*catalog*.bin");
|
||||
|
||||
var catalogFile = catalogFiles.Length > 0 ? Path.GetFileName(catalogFiles[0]) : null;
|
||||
|
||||
string catalogHash = null;
|
||||
if (!string.IsNullOrEmpty(catalogFile))
|
||||
{
|
||||
var baseName = Path.GetFileNameWithoutExtension(catalogFile);
|
||||
var hashCandidate = Path.Combine(outputPath, $"{baseName}.hash");
|
||||
if (File.Exists(hashCandidate))
|
||||
{
|
||||
catalogHash = Path.GetFileName(hashCandidate);
|
||||
}
|
||||
else
|
||||
{
|
||||
var hashFiles = Directory.GetFiles(outputPath, "*catalog*.hash");
|
||||
if (hashFiles.Length > 0)
|
||||
catalogHash = Path.GetFileName(hashFiles[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return (catalogFile, catalogHash);
|
||||
}
|
||||
|
||||
private static TileManifest BuildManifest(string buildTarget, string catalogFile, string catalogHashFile)
|
||||
{
|
||||
var tiles = ParseTilesCsv();
|
||||
if (tiles.Count == 0)
|
||||
throw new InvalidOperationException("No tiles parsed from tile_index.csv");
|
||||
|
||||
double minX = double.PositiveInfinity;
|
||||
double minY = double.PositiveInfinity;
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
minX = Math.Min(minX, tile.Xmin);
|
||||
minY = Math.Min(minY, tile.Ymin);
|
||||
}
|
||||
|
||||
var entries = new TileEntry[tiles.Count];
|
||||
for (int i = 0; i < tiles.Count; i++)
|
||||
{
|
||||
var tile = tiles[i];
|
||||
entries[i] = new TileEntry
|
||||
{
|
||||
tileId = tile.TileId,
|
||||
offsetX = (float)(tile.Xmin - minX),
|
||||
offsetZ = (float)(tile.Ymin - minY),
|
||||
baseY = (float)tile.GlobalMin
|
||||
};
|
||||
}
|
||||
|
||||
return new TileManifest
|
||||
{
|
||||
buildTarget = buildTarget,
|
||||
catalogFile = catalogFile,
|
||||
catalogHashFile = catalogHashFile,
|
||||
tileSizeMeters = TileSizeMeters,
|
||||
tiles = entries
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetRemoteCatalogBuildPath(AddressableAssetSettings settings, BuildTarget target)
|
||||
{
|
||||
var rawPath = settings.RemoteCatalogBuildPath.GetValue(settings);
|
||||
if (string.IsNullOrWhiteSpace(rawPath))
|
||||
rawPath = BuildPathValue.Replace("[BuildTarget]", target.ToString());
|
||||
|
||||
return Path.GetFullPath(rawPath.Replace("[BuildTarget]", target.ToString()));
|
||||
}
|
||||
|
||||
private struct TileRecord
|
||||
{
|
||||
public string TileId;
|
||||
public double Xmin;
|
||||
public double Ymin;
|
||||
public double GlobalMin;
|
||||
public double GlobalMax;
|
||||
}
|
||||
|
||||
private static List<TileRecord> ParseTilesCsv()
|
||||
{
|
||||
var tiles = new List<TileRecord>();
|
||||
var ci = CultureInfo.InvariantCulture;
|
||||
var lines = File.ReadAllLines(TileIndexCsvPath);
|
||||
|
||||
if (lines.Length < 2)
|
||||
return tiles;
|
||||
|
||||
var headerMap = BuildHeaderMap(lines[0]);
|
||||
string[] required = { "tile_id", "xmin", "ymin", "global_min", "global_max" };
|
||||
if (!HasAll(headerMap, required))
|
||||
{
|
||||
Debug.LogError("[GeoTileAddressablesBuilder] CSV missing required columns.");
|
||||
return tiles;
|
||||
}
|
||||
|
||||
int IDX_TILE = headerMap["tile_id"];
|
||||
int IDX_XMIN = headerMap["xmin"];
|
||||
int IDX_YMIN = headerMap["ymin"];
|
||||
int IDX_GMIN = headerMap["global_min"];
|
||||
int IDX_GMAX = headerMap["global_max"];
|
||||
|
||||
for (int i = 1; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
var parts = line.Split(',');
|
||||
int maxIdx = Math.Max(IDX_TILE, Math.Max(IDX_XMIN, Math.Max(IDX_YMIN, Math.Max(IDX_GMIN, IDX_GMAX))));
|
||||
if (parts.Length <= maxIdx)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
tiles.Add(new TileRecord
|
||||
{
|
||||
TileId = parts[IDX_TILE].Trim(),
|
||||
Xmin = double.Parse(parts[IDX_XMIN], ci),
|
||||
Ymin = double.Parse(parts[IDX_YMIN], ci),
|
||||
GlobalMin = double.Parse(parts[IDX_GMIN], ci),
|
||||
GlobalMax = double.Parse(parts[IDX_GMAX], ci)
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[GeoTileAddressablesBuilder] Parse error line {i + 1}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
private static Dictionary<string, int> BuildHeaderMap(string headerLine)
|
||||
{
|
||||
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
var headers = headerLine.Split(',');
|
||||
for (int i = 0; i < headers.Length; i++)
|
||||
map[headers[i].Trim()] = i;
|
||||
return map;
|
||||
}
|
||||
|
||||
private static bool HasAll(Dictionary<string, int> map, string[] keys)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
if (!map.ContainsKey(key))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Editor/GeoTileAddressablesBuilder.cs.meta
Normal file
2
Assets/Scripts/Editor/GeoTileAddressablesBuilder.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cd42659539662ebfaeb5b8d10d5ad73
|
||||
@@ -21,7 +21,7 @@ public class GeoTilePrefabImporter : EditorWindow
|
||||
private string enhancedTreesDir = "Assets/GeoData/trees_enhanced";
|
||||
|
||||
// Output settings
|
||||
private string prefabOutputDir = "Assets/GeoData/TilePrefabs";
|
||||
private string prefabOutputDir = "Assets/TilePrefabs";
|
||||
private bool overwriteExisting = false;
|
||||
|
||||
// Terrain settings
|
||||
|
||||
138
Assets/Scripts/Editor/MissingScriptUtility.cs
Normal file
138
Assets/Scripts/Editor/MissingScriptUtility.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public static class MissingScriptUtility
|
||||
{
|
||||
[MenuItem("Tools/Diagnostics/Find Missing Scripts in Open Scenes")]
|
||||
public static void FindMissingScriptsInOpenScenes()
|
||||
{
|
||||
int missingCount = 0;
|
||||
var reported = new HashSet<GameObject>();
|
||||
|
||||
foreach (var scene in GetLoadedScenes())
|
||||
{
|
||||
foreach (var root in scene.GetRootGameObjects())
|
||||
ScanHierarchy(root, ref missingCount, reported);
|
||||
}
|
||||
|
||||
Debug.Log($"[MissingScriptUtility] Missing scripts found: {missingCount}");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Diagnostics/Find Missing Scripts in Prefabs")]
|
||||
public static void FindMissingScriptsInPrefabs()
|
||||
{
|
||||
var prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
||||
int missingCount = 0;
|
||||
|
||||
for (int i = 0; i < prefabGuids.Length; i++)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]);
|
||||
EditorUtility.DisplayProgressBar("Scanning Prefabs", path, (float)i / prefabGuids.Length);
|
||||
|
||||
var root = PrefabUtility.LoadPrefabContents(path);
|
||||
if (root == null)
|
||||
continue;
|
||||
|
||||
ScanHierarchy(root, ref missingCount, null, path);
|
||||
PrefabUtility.UnloadPrefabContents(root);
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log($"[MissingScriptUtility] Missing scripts found in prefabs: {missingCount}");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Diagnostics/Remove Missing Scripts in Open Scenes")]
|
||||
public static void RemoveMissingScriptsInOpenScenes()
|
||||
{
|
||||
int removed = 0;
|
||||
|
||||
foreach (var scene in GetLoadedScenes())
|
||||
{
|
||||
foreach (var root in scene.GetRootGameObjects())
|
||||
removed += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(root);
|
||||
}
|
||||
|
||||
if (removed > 0)
|
||||
EditorSceneManager.MarkAllScenesDirty();
|
||||
|
||||
Debug.Log($"[MissingScriptUtility] Removed missing scripts: {removed}");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Diagnostics/Remove Missing Scripts in Prefabs")]
|
||||
public static void RemoveMissingScriptsInPrefabs()
|
||||
{
|
||||
var prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
||||
int removed = 0;
|
||||
|
||||
for (int i = 0; i < prefabGuids.Length; i++)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]);
|
||||
EditorUtility.DisplayProgressBar("Cleaning Prefabs", path, (float)i / prefabGuids.Length);
|
||||
|
||||
var root = PrefabUtility.LoadPrefabContents(path);
|
||||
if (root == null)
|
||||
continue;
|
||||
|
||||
int removedInPrefab = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(root);
|
||||
if (removedInPrefab > 0)
|
||||
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
|
||||
removed += removedInPrefab;
|
||||
PrefabUtility.UnloadPrefabContents(root);
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (removed > 0)
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
Debug.Log($"[MissingScriptUtility] Removed missing scripts in prefabs: {removed}");
|
||||
}
|
||||
|
||||
private static IEnumerable<Scene> GetLoadedScenes()
|
||||
{
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
{
|
||||
var scene = SceneManager.GetSceneAt(i);
|
||||
if (scene.isLoaded)
|
||||
yield return scene;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScanHierarchy(GameObject go, ref int missingCount, HashSet<GameObject> reported, string assetPath = null)
|
||||
{
|
||||
var components = go.GetComponents<Component>();
|
||||
for (int i = 0; i < components.Length; i++)
|
||||
{
|
||||
if (components[i] == null)
|
||||
{
|
||||
missingCount++;
|
||||
if (reported == null || reported.Add(go))
|
||||
{
|
||||
var label = string.IsNullOrWhiteSpace(assetPath) ? "scene object" : $"prefab {assetPath}";
|
||||
Debug.LogWarning($"[MissingScriptUtility] Missing script on {label}: {GetHierarchyPath(go.transform)}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Transform child in go.transform)
|
||||
ScanHierarchy(child.gameObject, ref missingCount, reported);
|
||||
}
|
||||
|
||||
private static string GetHierarchyPath(Transform transform)
|
||||
{
|
||||
var sb = new StringBuilder(transform.name);
|
||||
while (transform.parent != null)
|
||||
{
|
||||
transform = transform.parent;
|
||||
sb.Insert(0, transform.name + "/");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
2
Assets/Scripts/Editor/MissingScriptUtility.cs.meta
Normal file
2
Assets/Scripts/Editor/MissingScriptUtility.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 203230cc591896b4caa395c2a5681573
|
||||
107
Assets/Scripts/Editor/OpenXRQuestSetup.cs
Normal file
107
Assets/Scripts/Editor/OpenXRQuestSetup.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.Management;
|
||||
using UnityEditor.XR.Management.Metadata;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Management;
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features.CompositionLayers;
|
||||
#endif
|
||||
|
||||
namespace DTrierFlood.Editor
|
||||
{
|
||||
static class OpenXRQuestSetup
|
||||
{
|
||||
const string GeneralSettingsPath = "Assets/XR/XRGeneralSettingsPerBuildTarget.asset";
|
||||
const string OpenXRLoaderType = "UnityEngine.XR.OpenXR.OpenXRLoader";
|
||||
|
||||
[MenuItem("Tools/XR/Setup OpenXR for Quest (No Meta SDK)")]
|
||||
static void Setup()
|
||||
{
|
||||
var generalSettings = EnsureGeneralSettingsAsset();
|
||||
EnsureBuildTarget(generalSettings, BuildTargetGroup.Android);
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
EnableCompositionLayersFeature(BuildTargetGroup.Android);
|
||||
EnableCompositionLayersFeature(BuildTargetGroup.Standalone);
|
||||
#endif
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
Debug.Log("OpenXR is configured for Android. Build the project for Quest using OpenXR.");
|
||||
}
|
||||
|
||||
static XRGeneralSettingsPerBuildTarget EnsureGeneralSettingsAsset()
|
||||
{
|
||||
var settings = AssetDatabase.LoadAssetAtPath<XRGeneralSettingsPerBuildTarget>(GeneralSettingsPath);
|
||||
if (settings != null)
|
||||
{
|
||||
EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, settings, true);
|
||||
return settings;
|
||||
}
|
||||
|
||||
if (!AssetDatabase.IsValidFolder("Assets/XR"))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "XR");
|
||||
}
|
||||
|
||||
settings = ScriptableObject.CreateInstance<XRGeneralSettingsPerBuildTarget>();
|
||||
AssetDatabase.CreateAsset(settings, GeneralSettingsPath);
|
||||
EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, settings, true);
|
||||
EditorUtility.SetDirty(settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
static void EnsureBuildTarget(XRGeneralSettingsPerBuildTarget settings, BuildTargetGroup targetGroup)
|
||||
{
|
||||
if (!settings.HasSettingsForBuildTarget(targetGroup))
|
||||
{
|
||||
settings.CreateDefaultSettingsForBuildTarget(targetGroup);
|
||||
}
|
||||
|
||||
if (!settings.HasManagerSettingsForBuildTarget(targetGroup))
|
||||
{
|
||||
settings.CreateDefaultManagerSettingsForBuildTarget(targetGroup);
|
||||
}
|
||||
|
||||
var generalSettings = settings.SettingsForBuildTarget(targetGroup);
|
||||
if (generalSettings != null)
|
||||
{
|
||||
generalSettings.InitManagerOnStart = true;
|
||||
EditorUtility.SetDirty(generalSettings);
|
||||
}
|
||||
|
||||
var managerSettings = settings.ManagerSettingsForBuildTarget(targetGroup);
|
||||
if (managerSettings != null)
|
||||
{
|
||||
XRPackageMetadataStore.AssignLoader(managerSettings, OpenXRLoaderType, targetGroup);
|
||||
EditorUtility.SetDirty(managerSettings);
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(settings);
|
||||
}
|
||||
|
||||
#if XR_COMPOSITION_LAYERS
|
||||
static void EnableCompositionLayersFeature(BuildTargetGroup targetGroup)
|
||||
{
|
||||
var settings = OpenXRSettings.GetSettingsForBuildTargetGroup(targetGroup);
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning($"OpenXR settings not found for {targetGroup}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var feature = settings.GetFeature<OpenXRCompositionLayersFeature>();
|
||||
if (feature == null)
|
||||
{
|
||||
Debug.LogWarning($"OpenXR Composition Layers feature not found for {targetGroup}.");
|
||||
return;
|
||||
}
|
||||
|
||||
feature.enabled = true;
|
||||
EditorUtility.SetDirty(feature);
|
||||
EditorUtility.SetDirty(settings);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Editor/OpenXRQuestSetup.cs.meta
Normal file
2
Assets/Scripts/Editor/OpenXRQuestSetup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd67663123673d9e5a715676d1c5839a
|
||||
Reference in New Issue
Block a user