mixedreality/com.microsoft.mixedreality..../Core/Definitions/SceneSystem/MixedRealitySceneSystemProf...

392 lines
14 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.SceneSystem
{
/// <summary>
/// Configuration profile settings for setting up scene system.
/// </summary>
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Scene System Profile", fileName = "MixedRealitySceneSystemProfile", order = (int)CreateProfileMenuItemIndices.SceneSystem)]
[MixedRealityServiceProfile(typeof(IMixedRealitySceneSystem))]
[HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/scene-system/scene-system-getting-started")]
public class MixedRealitySceneSystemProfile : BaseMixedRealityProfile
{
/// <summary>
/// Internal class used to cache lighting settings associated with a scene.
/// </summary>
[Serializable]
internal sealed class CachedLightingSettings
{
public string SceneName;
public RuntimeRenderSettings RenderSettings;
public RuntimeLightingSettings LightingSettings;
public RuntimeSunlightSettings SunlightSettings;
public DateTime TimeStamp;
}
public bool UseManagerScene { get { return useManagerScene && !managerScene.IsEmpty; } }
public bool UseLightingScene { get { return useLightingScene && lightingScenes.Count > 0; } }
public SceneInfo ManagerScene => managerScene;
public SceneInfo DefaultLightingScene { get { return lightingScenes[defaultLightingSceneIndex]; } }
public IEnumerable<SceneInfo> LightingScenes { get { return lightingScenes; } }
public IEnumerable<SceneInfo> ContentScenes { get { return contentScenes; } }
public IEnumerable<string> ContentTags { get { return contentTags; } }
public int NumLightingScenes { get { return lightingScenes.Count; } }
public int NumContentScenes { get { return contentScenes.Count; } }
public IEnumerable<Type> PermittedLightingSceneComponentTypes
{
get
{
foreach (SystemType systemType in permittedLightingSceneComponentTypes) { yield return systemType.Type; }
}
}
#if UNITY_EDITOR
public bool EditorManageBuildSettings => editorManageBuildSettings;
public bool EditorManageLoadedScenes => editorManageLoadedScenes;
public bool EditorEnforceSceneOrder => editorEnforceSceneOrder;
public bool EditorEnforceLightingSceneTypes => editorEnforceLightingSceneTypes;
public bool EditorLightingCacheOutOfDate => editorLightingCacheOutOfDate;
public bool EditorLightingCacheUpdateRequested { get; set; }
#endif
[SerializeField]
private bool useManagerScene = true;
[SerializeField]
private SceneInfo managerScene = default(SceneInfo);
[SerializeField]
private bool useLightingScene = true;
[SerializeField]
private int defaultLightingSceneIndex = 0;
[SerializeField]
private List<SceneInfo> lightingScenes = new List<SceneInfo>();
[SerializeField]
private List<SceneInfo> contentScenes = new List<SceneInfo>();
[SerializeField]
private SystemType[] permittedLightingSceneComponentTypes = new SystemType[] {
new SystemType(typeof(Transform)),
new SystemType(typeof(GameObject)),
new SystemType(typeof(Light)),
new SystemType(typeof(ReflectionProbe)),
new SystemType(typeof(LightProbeGroup)),
new SystemType(typeof(LightProbeProxyVolume)),
};
// These will be hidden by the default inspector.
[SerializeField]
[Tooltip("Cached content tags found in your content scenes")]
private List<string> contentTags = new List<string>();
// These will be hidden by the default inspector.
[SerializeField]
[Tooltip("Cached lighting settings from your lighting scenes")]
private List<CachedLightingSettings> cachedLightingSettings = new List<CachedLightingSettings>();
#region editor settings
// CS414 is disabled during this section because these properties are being used in the editor
// scenario - when this file is build for player scenario, these serialized fields still exist
// but are not used.
#pragma warning disable 414
[SerializeField]
[Tooltip("If true, the service will update your build settings automatically, ensuring that all manager, lighting and content scenes are added. Disable this if you want total control over build settings.")]
private bool editorManageBuildSettings = true;
[SerializeField]
[Tooltip("If true, the service will ensure manager scene is displayed first in scene hierarchy, followed by lighting and then content. Disable this if you want total control over scene hierarchy.")]
private bool editorEnforceSceneOrder = true;
[SerializeField]
[Tooltip("If true, service will ensure that manager scenes and lighting scenes are always loaded. Disable if you want total control over which scenes are loaded in editor.")]
private bool editorManageLoadedScenes = true;
[SerializeField]
[Tooltip("If true, service will ensure that only lighting-related components are allowed in lighting scenes. Disable if you want total control over the content of lighting scenes.")]
private bool editorEnforceLightingSceneTypes = true;
[SerializeField]
private bool editorLightingCacheOutOfDate = false;
#pragma warning restore 414
#endregion
public bool GetLightingSceneSettings(
string lightingSceneName,
out SceneInfo lightingScene,
out RuntimeLightingSettings lightingSettings,
out RuntimeRenderSettings renderSettings,
out RuntimeSunlightSettings sunlightSettings)
{
lightingSettings = default(RuntimeLightingSettings);
renderSettings = default(RuntimeRenderSettings);
sunlightSettings = default(RuntimeSunlightSettings);
lightingScene = SceneInfo.Empty;
for (int i = 0; i < lightingScenes.Count; i++)
{
if (lightingScenes[i].Name == lightingSceneName)
{
lightingScene = lightingScenes[i];
break;
}
}
if (lightingScene.IsEmpty)
{ // If we didn't find a lighting scene, don't bother looking for a cache
return false;
}
bool foundCache = false;
for (int i = 0; i < cachedLightingSettings.Count; i++)
{
CachedLightingSettings cache = cachedLightingSettings[i];
if (cache.SceneName == lightingSceneName)
{
lightingSettings = cache.LightingSettings;
renderSettings = cache.RenderSettings;
sunlightSettings = cache.SunlightSettings;
foundCache = true;
break;
}
}
return foundCache;
}
public IEnumerable<string> GetContentSceneNamesByTag(string tag)
{
foreach (SceneInfo contentScene in contentScenes)
{
if (contentScene.Tag == tag)
yield return contentScene.Name;
}
}
#if UNITY_EDITOR
#region validation
private void OnValidate()
{
if (Application.isPlaying || EditorApplication.isCompiling)
{
return;
}
bool saveChanges = false;
// Remove any duplicate entries from our lighting and content scene lists
saveChanges |= (RemoveOrClearDuplicateEntries(lightingScenes) || RemoveOrClearDuplicateEntries(contentScenes));
// Ensure that manager scenes are not contained in content or lighting scenes
saveChanges |= (UseManagerScene && (RemoveScene(lightingScenes, managerScene) || RemoveScene(contentScenes, managerScene)));
// Ensure that content scenes are not included in lighting scenes
saveChanges |= (UseLightingScene && RemoveScenes(lightingScenes, contentScenes));
// Build our content tags
List<string> newContentTags = new List<string>();
foreach (SceneInfo contentScene in contentScenes)
{
if (string.IsNullOrEmpty(contentScene.Tag))
{
continue;
}
if (contentScene.Tag == "Untagged")
{
continue;
}
if (!newContentTags.Contains(contentScene.Tag))
{
newContentTags.Add(contentScene.Tag);
}
}
// See if our content tags have changed
if (!contentTags.SequenceEqual(newContentTags))
{
contentTags = newContentTags;
saveChanges = true;
}
defaultLightingSceneIndex = Mathf.Clamp(defaultLightingSceneIndex, 0, lightingScenes.Count - 1);
if (saveChanges)
{ // We need to tie this directly to lighting scenes somehow
editorLightingCacheOutOfDate = true;
// Make sure our changes are saved to disk!
AssetDatabase.Refresh();
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
}
/// <summary>
/// Clears cached lighting settings.
/// Used to ensure we don't end up with 'dead' cached data.
/// </summary>
public void ClearLightingCache()
{
cachedLightingSettings.Clear();
}
/// <summary>
/// Used to update the cached lighting / render settings.
/// Since extracting them is complex and requires scene loading, I thought it best to avoid having the profile do it.
/// </summary>
/// <param name="sceneInfo">The scene these settings belong to.</param>
public void SetLightingCache(SceneInfo sceneInfo, RuntimeLightingSettings lightingSettings, RuntimeRenderSettings renderSettings, RuntimeSunlightSettings sunlightSettings)
{
CachedLightingSettings settings = new CachedLightingSettings();
settings.SceneName = sceneInfo.Name;
settings.LightingSettings = lightingSettings;
settings.RenderSettings = renderSettings;
settings.SunlightSettings = sunlightSettings;
settings.TimeStamp = DateTime.Now;
cachedLightingSettings.Add(settings);
editorLightingCacheOutOfDate = false;
}
/// <summary>
/// Sets editorLightingCacheOutOfDate to true and saves the profile.
/// </summary>
public void SetLightingCacheDirty()
{
editorLightingCacheOutOfDate = true;
AssetDatabase.Refresh();
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
public DateTime GetEarliestLightingCacheTimestamp()
{
if (cachedLightingSettings.Count <= 0)
{
return DateTime.MinValue;
}
DateTime earliestTimeStamp = DateTime.MaxValue;
foreach (CachedLightingSettings settings in cachedLightingSettings)
{
if (settings.TimeStamp < earliestTimeStamp)
{
earliestTimeStamp = settings.TimeStamp;
}
}
return earliestTimeStamp;
}
private static bool RemoveScenes(List<SceneInfo> sceneList, List<SceneInfo> scenesToRemove)
{
bool changed = false;
for (int i = sceneList.Count - 1; i >= 0; i--)
{
if (sceneList[i].IsEmpty)
{
continue;
}
foreach (SceneInfo sceneToRemove in scenesToRemove)
{
if (sceneToRemove.IsEmpty)
{
continue;
}
if (sceneList[i].Asset == sceneToRemove.Asset)
{
Debug.LogWarning("Removing scene " + sceneToRemove.Name + " from scene list.");
sceneList[i] = SceneInfo.Empty;
changed = true;
break;
}
}
}
return changed;
}
private static bool RemoveScene(List<SceneInfo> sceneList, SceneInfo sceneToRemove)
{
bool changed = false;
for (int i = sceneList.Count - 1; i >= 0; i--)
{
if (sceneList[i].IsEmpty)
{
continue;
}
if (sceneList[i].Asset == sceneToRemove.Asset)
{
Debug.LogWarning("Removing manager scene " + sceneToRemove.Name + " from scene list.");
sceneList[i] = SceneInfo.Empty;
changed = true;
}
}
return changed;
}
private static bool RemoveOrClearDuplicateEntries(List<SceneInfo> sceneList)
{
HashSet<string> scenePaths = new HashSet<string>();
bool changed = false;
for (int i = 0; i < sceneList.Count; i++)
{
if (sceneList[i].IsEmpty)
{
continue;
}
if (!scenePaths.Add(sceneList[i].Path))
{ // If we encounter a duplicate, just set it to empty.
// This will ensure we don't get duplicates when we add new elements to the array.
Debug.LogWarning("Found duplicate entry in scene list at " + i + ", removing");
sceneList[i] = SceneInfo.Empty;
changed = true;
}
}
return changed;
}
#endregion
#endif
}
}