using System; using UnityEngine; using System.Collections.Generic; using System.Collections; namespace UnityEditor.Rendering.LookDev { /// /// Different working views in LookDev /// public enum ViewIndex { /// First view First, /// Second view Second }; /// /// Same as plus a compound value /// public enum ViewCompositionIndex { /// First view First = ViewIndex.First, /// Second view Second = ViewIndex.Second, /// Composite view (Several view on screen) Composite }; // /!\ WARNING: these value name are used as uss file too. // if your rename here, rename in the uss too. /// /// Different layout supported in LookDev /// public enum Layout { /// First view display fully FullFirstView, /// Second view display fully FullSecondView, /// First and second views displayed splitted horizontally HorizontalSplit, /// First and second views displayed splitted vertically VerticalSplit, /// First and second views displayed with stacking and orientation customizable split CustomSplit } /// /// Status of the side panel of the LookDev window /// public enum SidePanel { /// No side panel None = -1, /// Environment side panel Environment, /// Debug side panel Debug, } /// /// The target views of the debug panel /// public enum TargetDebugView { /// First Debug view First, /// Both Debug view Both, /// Second Debug view Second }; /// /// Class containing all data used by the LookDev Window to render /// [System.Serializable] public class Context : ScriptableObject, IDisposable { [SerializeField] string m_EnvironmentLibraryGUID = ""; //Empty GUID [SerializeField] bool m_CameraSynced = true; EnvironmentLibrary m_EnvironmentLibrary; /// The currently used Environment public EnvironmentLibrary environmentLibrary { get { //check if asset deleted by user if (m_EnvironmentLibrary != null && AssetDatabase.Contains(m_EnvironmentLibrary)) return m_EnvironmentLibrary; if (!String.IsNullOrEmpty(m_EnvironmentLibraryGUID)) { //user deleted the EnvironmentLibrary asset m_EnvironmentLibraryGUID = ""; //Empty GUID LookDev.currentEnvironmentDisplayer.Repaint(); } return null; } private set => m_EnvironmentLibrary = value; } /// The currently used layout [field: SerializeField] public LayoutContext layout { get; private set; } = new LayoutContext(); /// /// State if both views camera movement are synced or not /// public bool cameraSynced { get => m_CameraSynced; set { if (m_CameraSynced ^ value) { if (value) EditorApplication.update += SynchronizeCameraStates; else EditorApplication.update -= SynchronizeCameraStates; m_CameraSynced = value; } } } [SerializeField] ViewContext[] m_Views = new ViewContext[2] { new ViewContext(), new ViewContext() }; /// /// Helper class to iterate on views /// public struct ViewIterator : IEnumerable { ViewContext[] m_Views; internal ViewIterator(ViewContext[] views) => m_Views = views; /// /// Helper function to enumerates on ViewContexts /// /// Enumerator on ViewContext IEnumerator IEnumerable.GetEnumerator() => m_Views.GetEnumerator(); /// /// Helper function to enumerates on ViewContexts /// /// Enumerator on ViewContext IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)m_Views).GetEnumerator(); } /// /// Helper function to get ViewIterator on ViewContexts /// public ViewIterator viewContexts => new ViewIterator(m_Views); /// /// Get datas relative to a view /// /// The view index to look at /// Datas for the selected view public ViewContext GetViewContent(ViewIndex index) => m_Views[(int)index]; internal void Init() { LoadEnvironmentLibraryFromGUID(); //recompute non serialized computes states layout.gizmoState.Init(); if (cameraSynced) EditorApplication.update += SynchronizeCameraStates; } /// Update the environment library used. /// The new EnvironmentLibrary public void UpdateEnvironmentLibrary(EnvironmentLibrary library) { m_EnvironmentLibraryGUID = ""; environmentLibrary = null; if (library == null || library.Equals(null)) return; m_EnvironmentLibraryGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(library)); environmentLibrary = library; } void LoadEnvironmentLibraryFromGUID() { environmentLibrary = null; GUID storedGUID; GUID.TryParse(m_EnvironmentLibraryGUID, out storedGUID); if (storedGUID.Empty()) return; string path = AssetDatabase.GUIDToAssetPath(m_EnvironmentLibraryGUID); environmentLibrary = AssetDatabase.LoadAssetAtPath(path); } /// /// Synchronize cameras from both view using data from the baseCameraState /// /// The to be used as reference public void SynchronizeCameraStates(ViewIndex baseCameraState) { switch (baseCameraState) { case ViewIndex.First: m_Views[1].camera.SynchronizeFrom(m_Views[0].camera); break; case ViewIndex.Second: m_Views[0].camera.SynchronizeFrom(m_Views[1].camera); break; default: throw new System.ArgumentException("Unknow ViewIndex given in parameter."); } } void SynchronizeCameraStates() => SynchronizeCameraStates(layout.lastFocusedView); /// /// Change focused view. /// Focused view is the base view to copy data when syncing views' cameras /// /// The index of the view public void SetFocusedCamera(ViewIndex index) => layout.lastFocusedView = index; private bool disposedValue = false; // To detect redundant calls /// Disposable behaviour void IDisposable.Dispose() { if (!disposedValue) { if (cameraSynced) EditorApplication.update -= SynchronizeCameraStates; disposedValue = true; } } internal bool HasLibraryAssetChanged(EnvironmentLibrary environmentLibrary) { if (environmentLibrary == null) return !String.IsNullOrEmpty(m_EnvironmentLibraryGUID); return m_EnvironmentLibraryGUID != AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(environmentLibrary)); } } /// /// Data regarding the layout currently used in LookDev /// [System.Serializable] public class LayoutContext { /// The layout used public Layout viewLayout; /// The last focused view public ViewIndex lastFocusedView = ViewIndex.First; /// The state of the side panel public SidePanel showedSidePanel; /// The view to change when manipulating the Debug side panel [NonSerialized] public TargetDebugView debugPanelSource = TargetDebugView.Both; [SerializeField] internal ComparisonGizmoState gizmoState = new ComparisonGizmoState(); internal bool isSimpleView => viewLayout == Layout.FullFirstView || viewLayout == Layout.FullSecondView; internal bool isMultiView => viewLayout == Layout.HorizontalSplit || viewLayout == Layout.VerticalSplit; internal bool isCombinedView => viewLayout == Layout.CustomSplit; } /// /// Data container containing content of a view /// [System.Serializable] public class ViewContext { /// The position and rotation of the camera [field: SerializeField] public CameraState camera { get; private set; } = new CameraState(); /// The currently viewed debugState public DebugContext debug { get; private set; } = new DebugContext(); //Environment asset, sub-asset (under a library) or cubemap [SerializeField] string m_EnvironmentGUID = ""; //Empty GUID /// /// Check if an Environment is registered for this view. /// The result will be accurate even if the Environment have not been reloaded yet. /// public bool hasEnvironment => !String.IsNullOrEmpty(m_EnvironmentGUID); /// The currently used Environment public Environment environment { get; private set; } [SerializeField] string viewedObjectAssetGUID = ""; //Empty GUID // Careful here: we want to keep it while reloading script. // But from one unity editor to an other, ID are not kept. // So, only use it when reloading from script update. [SerializeField] int viewedObjecHierarchytInstanceID; /// /// Check if an Environment is registered for this view. /// The result will be accurate even if the object have not been reloaded yet. /// public bool hasViewedObject => !String.IsNullOrEmpty(viewedObjectAssetGUID) || viewedObjecHierarchytInstanceID != 0; /// Reference to the object given for instantiation. public GameObject viewedObjectReference { get; private set; } /// /// The currently displayed instance of . /// It will be instantiated when pushing changes to renderer. /// See /// public GameObject viewedInstanceInPreview { get; internal set; } /// Update the environment used. /// /// The new to use. /// Or the to use to build a new one. /// Other types will raise an ArgumentException. /// public void UpdateEnvironment(UnityEngine.Object environmentOrCubemapAsset) { m_EnvironmentGUID = ""; environment = null; if (environmentOrCubemapAsset == null || environmentOrCubemapAsset.Equals(null)) return; if (!(environmentOrCubemapAsset is Environment) && !(environmentOrCubemapAsset is Cubemap)) throw new System.ArgumentException("Only Environment or Cubemap accepted for environmentOrCubemapAsset parameter"); string GUID; long localIDInFile; AssetDatabase.TryGetGUIDAndLocalFileIdentifier(environmentOrCubemapAsset, out GUID, out localIDInFile); m_EnvironmentGUID = $"{GUID},{localIDInFile}"; if (environmentOrCubemapAsset is Environment) environment = environmentOrCubemapAsset as Environment; else //Cubemap environment = Environment.GetTemporaryEnvironmentForCubemap(environmentOrCubemapAsset as Cubemap); } void LoadEnvironmentFromGUID() { environment = null; GUID storedGUID; string[] GUIDAndLocalIDInFile = m_EnvironmentGUID.Split(new[] { ',' }); GUID.TryParse(GUIDAndLocalIDInFile[0], out storedGUID); if (storedGUID.Empty()) return; long localIDInFile = GUIDAndLocalIDInFile.Length < 2 ? 0L : long.Parse(GUIDAndLocalIDInFile[1]); string path = AssetDatabase.GUIDToAssetPath(GUIDAndLocalIDInFile[0]); Type savedType = AssetDatabase.GetMainAssetTypeAtPath(path); if (savedType == typeof(EnvironmentLibrary)) { object[] loaded = AssetDatabase.LoadAllAssetsAtPath(path); for (int i = 0; i < loaded.Length; ++i) { string garbage; long testedLocalIndex; if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier((UnityEngine.Object)loaded[i], out garbage, out testedLocalIndex) && testedLocalIndex == localIDInFile) { environment = loaded[i] as Environment; break; } } } else if (savedType == typeof(Environment)) environment = AssetDatabase.LoadAssetAtPath(path); else if (savedType == typeof(Cubemap)) { Cubemap cubemap = AssetDatabase.LoadAssetAtPath(path); environment = Environment.GetTemporaryEnvironmentForCubemap(cubemap); } } /// Update the object reference used for instantiation. /// The new reference. public void UpdateViewedObject(GameObject viewedObject) { viewedObjectAssetGUID = ""; viewedObjecHierarchytInstanceID = 0; viewedObjectReference = null; if (viewedObject == null || viewedObject.Equals(null)) return; bool fromHierarchy = viewedObject.scene.IsValid(); if (fromHierarchy) viewedObjecHierarchytInstanceID = viewedObject.GetInstanceID(); else viewedObjectAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(viewedObject)); viewedObjectReference = viewedObject; } //WARNING: only for script reloading void LoadViewedObject() { viewedObjectReference = null; GUID storedGUID; GUID.TryParse(viewedObjectAssetGUID, out storedGUID); if (!storedGUID.Empty()) { string path = AssetDatabase.GUIDToAssetPath(viewedObjectAssetGUID); viewedObjectReference = AssetDatabase.LoadAssetAtPath(path); } else if (viewedObjecHierarchytInstanceID != 0) { viewedObjectReference = EditorUtility.InstanceIDToObject(viewedObjecHierarchytInstanceID) as GameObject; } } internal void LoadAll(bool reloadWithTemporaryID) { if (!reloadWithTemporaryID) CleanTemporaryObjectIndexes(); LoadEnvironmentFromGUID(); LoadViewedObject(); } internal void CleanTemporaryObjectIndexes() => viewedObjecHierarchytInstanceID = 0; /// Reset the camera state to default values public void ResetCameraState() => camera.Reset(); } /// /// Class that will contain debug value used. /// public class DebugContext { /// Display shadows in view. public bool shadow = true; /// Debug mode displayed. -1 means none. public int viewMode = -1; ///// Display the debug grey balls //public bool greyBalls; //[SerializeField] //string colorChartGUID = ""; //Empty GUID ///// The currently used color chart //public Texture2D colorChart { get; private set; } } }