A game about forced loneliness, made by TACStudios
1using UnityEngine.Rendering; 2using UnityEngine.Rendering.LookDev; 3using UnityEditorInternal; 4using UnityEngine; 5using System.Linq; 6 7namespace UnityEditor.Rendering.LookDev 8{ 9 /// <summary> 10 /// Main entry point for scripting LookDev 11 /// </summary> 12 public static class LookDev 13 { 14 const string lastRenderingDataSavePath = "Library/LookDevConfig.asset"; 15 16 //TODO: ensure only one displayer at time for the moment 17 static IViewDisplayer s_ViewDisplayer; 18 static IEnvironmentDisplayer s_EnvironmentDisplayer; 19 static Compositer s_Compositor; 20 static StageCache s_Stages; 21 static Context s_CurrentContext; 22 23 internal static IDataProvider dataProvider 24 => RenderPipelineManager.currentPipeline as IDataProvider; 25 26 /// <summary> 27 /// Get all the data used in LookDev currently (views, layout, debug... ) 28 /// </summary> 29 internal static Context currentContext 30 { 31 //Lazy init: load it when needed instead in static even if you do not support lookdev 32 get 33 { 34 if (s_CurrentContext == null || s_CurrentContext.Equals(null)) 35 { 36 // In case the context is still alive somewhere but the static reference has been lost, we can find the object back with this 37 s_CurrentContext = TryFindCurrentContext(); 38 if (s_CurrentContext != null) 39 return s_CurrentContext; 40 41 s_CurrentContext = LoadConfigInternal(); 42 if (s_CurrentContext == null) 43 s_CurrentContext = defaultContext; 44 45 ReloadStage(false); 46 } 47 return s_CurrentContext; 48 } 49 private set => s_CurrentContext = value; 50 } 51 52 static Context TryFindCurrentContext() 53 { 54 Context context = s_CurrentContext; 55 56 if (context != null) 57 return context; 58 59 return Resources.FindObjectsOfTypeAll<Context>().FirstOrDefault(); 60 } 61 62 static Context defaultContext 63 { 64 get 65 { 66 var context = UnityEngine.ScriptableObject.CreateInstance<Context>(); 67 context.hideFlags = HideFlags.HideAndDontSave; 68 context.Init(); 69 return context; 70 } 71 } 72 73 //[TODO: not compatible with multiple displayer. To rework if needed] 74 internal static IViewDisplayer currentViewDisplayer => s_ViewDisplayer; 75 internal static IEnvironmentDisplayer currentEnvironmentDisplayer => s_EnvironmentDisplayer; 76 77 [MenuItem("Window/Rendering/Look Dev", false, 10001)] 78 static void OpenLookDev() => Open(); 79 80 [MenuItem("Window/Rendering/Look Dev", true, 10001)] 81 static bool LookDevAvailable() => supported; 82 83 internal static bool waitingConfigure { get; private set; } = true; 84 85 /// <summary>State of the LookDev window</summary> 86 public static bool open { get; private set; } 87 88 /// <summary> 89 /// Does LookDev is supported with the current render pipeline? 90 /// </summary> 91 public static bool supported => dataProvider != null; 92 93 /// <summary> 94 /// Reset all LookDevs datas to the default configuration 95 /// </summary> 96 public static void ResetConfig() 97 => currentContext = defaultContext; 98 99 static Context LoadConfigInternal(string path = lastRenderingDataSavePath) 100 { 101 var objs = InternalEditorUtility.LoadSerializedFileAndForget(path); 102 Context context = (objs.Length > 0 ? objs[0] : null) as Context; 103 if (context != null && !context.Equals(null)) 104 context.Init(); 105 return context; 106 } 107 108 /// <summary> 109 /// Load a different set of datas 110 /// </summary> 111 /// <param name="path">Path where to load</param> 112 internal static void LoadConfig(string path = lastRenderingDataSavePath) 113 { 114 var last = LoadConfigInternal(path); 115 if (last != null) 116 currentContext = last; 117 } 118 119 /// <summary> 120 /// Save the current set of datas 121 /// </summary> 122 /// <param name="path">[optional] Path to save. By default, saved in Library folder</param> 123 internal static void SaveConfig(string path = lastRenderingDataSavePath) 124 { 125 if (currentContext != null && !currentContext.Equals(null)) 126 InternalEditorUtility.SaveToSerializedFileAndForget(new[] { currentContext }, path, true); 127 } 128 129 /// <summary>Open the LookDev window</summary> 130 public static void Open() 131 { 132 EditorWindow.GetWindow<DisplayWindow>(); 133 } 134 135 /// <summary>Close the LookDev window</summary> 136 public static void Close() 137 { 138 (s_ViewDisplayer as EditorWindow)?.Close(); 139 s_ViewDisplayer = null; 140 (s_EnvironmentDisplayer as EditorWindow)?.Close(); 141 s_EnvironmentDisplayer = null; 142 } 143 144 internal static void Initialize(DisplayWindow window) 145 { 146 s_ViewDisplayer = window; 147 s_EnvironmentDisplayer = window; 148 open = true; 149 150 // Lookdev Initialize can be called when the window is re-created by the editor layout system. 151 // In that case, the current context won't be null and there might be objects to reload from the temp ID 152 ConfigureLookDev(reloadWithTemporaryID: TryFindCurrentContext() != null); 153 } 154 155 [Callbacks.DidReloadScripts] 156 static void OnEditorReload() 157 { 158 var windows = Resources.FindObjectsOfTypeAll<DisplayWindow>(); 159 s_ViewDisplayer = windows.Length > 0 ? windows[0] : null; 160 s_EnvironmentDisplayer = windows.Length > 0 ? windows[0] : null; 161 open = s_ViewDisplayer != null; 162 if (open) 163 ConfigureLookDev(reloadWithTemporaryID: true); 164 } 165 166 static void ConfigureLookDev(bool reloadWithTemporaryID) 167 { 168 open = true; 169 waitingConfigure = true; 170 if (s_CurrentContext == null || s_CurrentContext.Equals(null)) 171 LoadConfig(); 172 WaitingSRPReloadForConfiguringRenderer(5, reloadWithTemporaryID: reloadWithTemporaryID); 173 } 174 175 static void WaitingSRPReloadForConfiguringRenderer(int maxAttempt, bool reloadWithTemporaryID, int attemptNumber = 0) 176 { 177 if (supported) 178 { 179 waitingConfigure = false; 180 ConfigureRenderer(reloadWithTemporaryID); 181 LinkViewDisplayer(); 182 LinkEnvironmentDisplayer(); 183 ReloadStage(reloadWithTemporaryID); 184 } 185 else if (attemptNumber < maxAttempt) 186 EditorApplication.delayCall += 187 () => WaitingSRPReloadForConfiguringRenderer(maxAttempt, reloadWithTemporaryID, ++attemptNumber); 188 else 189 waitingConfigure = false; 190 } 191 192 static void ConfigureRenderer(bool reloadWithTemporaryID) 193 { 194 s_Stages?.Dispose(); //clean previous occurrence on reloading 195 s_Stages = new StageCache(dataProvider); 196 s_Compositor?.Dispose(); //clean previous occurrence on reloading 197 s_Compositor = new Compositer(s_ViewDisplayer, dataProvider, s_Stages); 198 } 199 200 static void LinkViewDisplayer() 201 { 202 s_ViewDisplayer.OnClosed += () => 203 { 204 s_Compositor?.Dispose(); 205 s_Compositor = null; 206 s_Stages?.Dispose(); 207 s_Stages = null; 208 s_ViewDisplayer = null; 209 //currentContext = null; 210 211 SaveConfig(); 212 213 open = false; 214 }; 215 s_ViewDisplayer.OnLayoutChanged += (layout, envPanelOpen) => 216 { 217 currentContext.layout.viewLayout = layout; 218 currentContext.layout.showedSidePanel = envPanelOpen; 219 SaveConfig(); 220 }; 221 s_ViewDisplayer.OnChangingObjectInView += (go, index, localPos) => 222 { 223 switch (index) 224 { 225 case ViewCompositionIndex.First: 226 case ViewCompositionIndex.Second: 227 currentContext.GetViewContent((ViewIndex)index).UpdateViewedObject(go); 228 SaveContextChangeAndApply((ViewIndex)index); 229 break; 230 case ViewCompositionIndex.Composite: 231 ViewIndex viewIndex = s_Compositor.GetViewFromComposition(localPos); 232 currentContext.GetViewContent(viewIndex).UpdateViewedObject(go); 233 SaveContextChangeAndApply(viewIndex); 234 break; 235 } 236 }; 237 s_ViewDisplayer.OnChangingEnvironmentInView += (obj, index, localPos) => 238 { 239 switch (index) 240 { 241 case ViewCompositionIndex.First: 242 case ViewCompositionIndex.Second: 243 currentContext.GetViewContent((ViewIndex)index).UpdateEnvironment(obj); 244 SaveContextChangeAndApply((ViewIndex)index); 245 break; 246 case ViewCompositionIndex.Composite: 247 ViewIndex viewIndex = s_Compositor.GetViewFromComposition(localPos); 248 currentContext.GetViewContent(viewIndex).UpdateEnvironment(obj); 249 SaveContextChangeAndApply(viewIndex); 250 break; 251 } 252 }; 253 } 254 255 static void LinkEnvironmentDisplayer() 256 { 257 s_EnvironmentDisplayer.OnChangingEnvironmentLibrary += UpdateEnvironmentLibrary; 258 } 259 260 static void UpdateEnvironmentLibrary(EnvironmentLibrary library) 261 { 262 LookDev.currentContext.UpdateEnvironmentLibrary(library); 263 } 264 265 static void ReloadStage(bool reloadWithTemporaryID) 266 { 267 currentContext.GetViewContent(ViewIndex.First).LoadAll(reloadWithTemporaryID); 268 ApplyContextChange(ViewIndex.First); 269 currentContext.GetViewContent(ViewIndex.Second).LoadAll(reloadWithTemporaryID); 270 ApplyContextChange(ViewIndex.Second); 271 } 272 273 static void ApplyContextChange(ViewIndex index) 274 { 275 s_Stages.UpdateSceneObjects(index); 276 s_Stages.UpdateSceneLighting(index, dataProvider); 277 s_ViewDisplayer.Repaint(); 278 } 279 280 /// <summary>Update the rendered element with element in the context</summary> 281 /// <param name="index">The index of the stage to update</param> 282 internal static void SaveContextChangeAndApply(ViewIndex index) 283 { 284 SaveConfig(); 285 ApplyContextChange(index); 286 } 287 } 288}