A game about forced loneliness, made by TACStudios
1using System;
2using UnityEngine;
3using System.Collections.Generic;
4using System.Collections;
5
6namespace UnityEditor.Rendering.LookDev
7{
8 /// <summary>
9 /// Different working views in LookDev
10 /// </summary>
11 public enum ViewIndex
12 {
13 /// <summary>First view</summary>
14 First,
15 /// <summary>Second view</summary>
16 Second
17 };
18
19 /// <summary>
20 /// Same as <see cref="ViewIndex"/> plus a compound value
21 /// </summary>
22 public enum ViewCompositionIndex
23 {
24 /// <summary>First view</summary>
25 First = ViewIndex.First,
26 /// <summary>Second view</summary>
27 Second = ViewIndex.Second,
28 /// <summary>Composite view (Several view on screen)</summary>
29 Composite
30 };
31
32 // /!\ WARNING: these value name are used as uss file too.
33 // if your rename here, rename in the uss too.
34 /// <summary>
35 /// Different layout supported in LookDev
36 /// </summary>
37 public enum Layout
38 {
39 /// <summary>First view display fully</summary>
40 FullFirstView,
41 /// <summary>Second view display fully</summary>
42 FullSecondView,
43 /// <summary>First and second views displayed splitted horizontally</summary>
44 HorizontalSplit,
45 /// <summary>First and second views displayed splitted vertically</summary>
46 VerticalSplit,
47 /// <summary>First and second views displayed with stacking and orientation customizable split</summary>
48 CustomSplit
49 }
50
51 /// <summary>
52 /// Status of the side panel of the LookDev window
53 /// </summary>
54 public enum SidePanel
55 {
56 /// <summary>No side panel</summary>
57 None = -1,
58 /// <summary>Environment side panel</summary>
59 Environment,
60 /// <summary>Debug side panel</summary>
61 Debug,
62 }
63
64 /// <summary>
65 /// The target views of the debug panel
66 /// </summary>
67 public enum TargetDebugView
68 {
69 /// <summary>First Debug view</summary>
70 First,
71 /// <summary>Both Debug view</summary>
72 Both,
73 /// <summary>Second Debug view</summary>
74 Second
75 };
76
77 /// <summary>
78 /// Class containing all data used by the LookDev Window to render
79 /// </summary>
80 [System.Serializable]
81 public class Context : ScriptableObject, IDisposable
82 {
83 [SerializeField]
84 string m_EnvironmentLibraryGUID = ""; //Empty GUID
85
86 [SerializeField]
87 bool m_CameraSynced = true;
88
89 EnvironmentLibrary m_EnvironmentLibrary;
90
91 /// <summary>The currently used Environment</summary>
92 public EnvironmentLibrary environmentLibrary
93 {
94 get
95 {
96 //check if asset deleted by user
97 if (m_EnvironmentLibrary != null && AssetDatabase.Contains(m_EnvironmentLibrary))
98 return m_EnvironmentLibrary;
99
100 if (!String.IsNullOrEmpty(m_EnvironmentLibraryGUID))
101 {
102 //user deleted the EnvironmentLibrary asset
103 m_EnvironmentLibraryGUID = ""; //Empty GUID
104 LookDev.currentEnvironmentDisplayer.Repaint();
105 }
106 return null;
107 }
108 private set => m_EnvironmentLibrary = value;
109 }
110
111 /// <summary>The currently used layout</summary>
112 [field: SerializeField]
113 public LayoutContext layout { get; private set; } = new LayoutContext();
114
115 /// <summary>
116 /// State if both views camera movement are synced or not
117 /// </summary>
118 public bool cameraSynced
119 {
120 get => m_CameraSynced;
121 set
122 {
123 if (m_CameraSynced ^ value)
124 {
125 if (value)
126 EditorApplication.update += SynchronizeCameraStates;
127 else
128 EditorApplication.update -= SynchronizeCameraStates;
129 m_CameraSynced = value;
130 }
131 }
132 }
133
134 [SerializeField]
135 ViewContext[] m_Views = new ViewContext[2]
136 {
137 new ViewContext(),
138 new ViewContext()
139 };
140
141 /// <summary>
142 /// Helper class to iterate on views
143 /// </summary>
144 public struct ViewIterator : IEnumerable<ViewContext>
145 {
146 ViewContext[] m_Views;
147 internal ViewIterator(ViewContext[] views)
148 => m_Views = views;
149
150 /// <summary>
151 /// Helper function to enumerates on ViewContexts
152 /// </summary>
153 /// <returns>Enumerator on ViewContext</returns>
154 IEnumerator IEnumerable.GetEnumerator()
155 => m_Views.GetEnumerator();
156
157 /// <summary>
158 /// Helper function to enumerates on ViewContexts
159 /// </summary>
160 /// <returns>Enumerator on ViewContext</returns>
161 IEnumerator<ViewContext> IEnumerable<ViewContext>.GetEnumerator()
162 => ((IEnumerable<ViewContext>)m_Views).GetEnumerator();
163 }
164
165 /// <summary>
166 /// Helper function to get ViewIterator on ViewContexts
167 /// </summary>
168 public ViewIterator viewContexts
169 => new ViewIterator(m_Views);
170
171 /// <summary>
172 /// Get datas relative to a view
173 /// </summary>
174 /// <param name="index">The view index to look at</param>
175 /// <returns>Datas for the selected view</returns>
176 public ViewContext GetViewContent(ViewIndex index)
177 => m_Views[(int)index];
178
179 internal void Init()
180 {
181 LoadEnvironmentLibraryFromGUID();
182
183 //recompute non serialized computes states
184 layout.gizmoState.Init();
185
186 if (cameraSynced)
187 EditorApplication.update += SynchronizeCameraStates;
188 }
189
190 /// <summary>Update the environment library used.</summary>
191 /// <param name="library">The new EnvironmentLibrary</param>
192 public void UpdateEnvironmentLibrary(EnvironmentLibrary library)
193 {
194 m_EnvironmentLibraryGUID = "";
195 environmentLibrary = null;
196 if (library == null || library.Equals(null))
197 return;
198
199 m_EnvironmentLibraryGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(library));
200 environmentLibrary = library;
201 }
202
203 void LoadEnvironmentLibraryFromGUID()
204 {
205 environmentLibrary = null;
206
207 GUID storedGUID;
208 GUID.TryParse(m_EnvironmentLibraryGUID, out storedGUID);
209 if (storedGUID.Empty())
210 return;
211
212 string path = AssetDatabase.GUIDToAssetPath(m_EnvironmentLibraryGUID);
213 environmentLibrary = AssetDatabase.LoadAssetAtPath<EnvironmentLibrary>(path);
214 }
215
216 /// <summary>
217 /// Synchronize cameras from both view using data from the baseCameraState
218 /// </summary>
219 /// <param name="baseCameraState">The <see cref="ViewIndex"/> to be used as reference</param>
220 public void SynchronizeCameraStates(ViewIndex baseCameraState)
221 {
222 switch (baseCameraState)
223 {
224 case ViewIndex.First:
225 m_Views[1].camera.SynchronizeFrom(m_Views[0].camera);
226 break;
227 case ViewIndex.Second:
228 m_Views[0].camera.SynchronizeFrom(m_Views[1].camera);
229 break;
230 default:
231 throw new System.ArgumentException("Unknow ViewIndex given in parameter.");
232 }
233 }
234
235 void SynchronizeCameraStates()
236 => SynchronizeCameraStates(layout.lastFocusedView);
237
238 /// <summary>
239 /// Change focused view.
240 /// Focused view is the base view to copy data when syncing views' cameras
241 /// </summary>
242 /// <param name="index">The index of the view</param>
243 public void SetFocusedCamera(ViewIndex index)
244 => layout.lastFocusedView = index;
245
246
247 private bool disposedValue = false; // To detect redundant calls
248 /// <summary>Disposable behaviour</summary>
249 void IDisposable.Dispose()
250 {
251 if (!disposedValue)
252 {
253 if (cameraSynced)
254 EditorApplication.update -= SynchronizeCameraStates;
255
256 disposedValue = true;
257 }
258 }
259
260 internal bool HasLibraryAssetChanged(EnvironmentLibrary environmentLibrary)
261 {
262 if (environmentLibrary == null)
263 return !String.IsNullOrEmpty(m_EnvironmentLibraryGUID);
264
265 return m_EnvironmentLibraryGUID != AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(environmentLibrary));
266 }
267
268 internal void FullReimportEnvironmentLibrary()
269 {
270 if (environmentLibrary == null)
271 return;
272
273 // refresh AssetDatabase in case of undo/redo creating/destructing environment subasset
274 string libraryPath = AssetDatabase.GetAssetPath(environmentLibrary);
275 AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(environmentLibrary), ImportAssetOptions.DontDownloadFromCacheServer | ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.ImportRecursive);
276 UpdateEnvironmentLibrary(AssetDatabase.LoadAssetAtPath<EnvironmentLibrary>(libraryPath));
277 EditorUtility.SetDirty(environmentLibrary);
278 }
279 }
280
281 /// <summary>
282 /// Data regarding the layout currently used in LookDev
283 /// </summary>
284 [System.Serializable]
285 public class LayoutContext
286 {
287 /// <summary>The layout used</summary>
288 public Layout viewLayout;
289 /// <summary>The last focused view</summary>
290 public ViewIndex lastFocusedView = ViewIndex.First;
291 /// <summary>The state of the side panel</summary>
292 public SidePanel showedSidePanel;
293 /// <summary>The view to change when manipulating the Debug side panel</summary>
294 [NonSerialized]
295 public TargetDebugView debugPanelSource = TargetDebugView.Both;
296
297 [SerializeField]
298 internal ComparisonGizmoState gizmoState = new ComparisonGizmoState();
299
300 internal bool isSimpleView => viewLayout == Layout.FullFirstView || viewLayout == Layout.FullSecondView;
301 internal bool isMultiView => viewLayout == Layout.HorizontalSplit || viewLayout == Layout.VerticalSplit;
302 internal bool isCombinedView => viewLayout == Layout.CustomSplit;
303 }
304
305 /// <summary>
306 /// Data container containing content of a view
307 /// </summary>
308 [System.Serializable]
309 public class ViewContext
310 {
311 /// <summary>The position and rotation of the camera</summary>
312 [field: SerializeField]
313 public CameraState camera { get; private set; } = new CameraState();
314
315 /// <summary>The currently viewed debugState</summary>
316 public DebugContext debug { get; private set; } = new DebugContext();
317
318 //Environment asset, sub-asset (under a library) or cubemap
319 [SerializeField]
320 string m_EnvironmentGUID = ""; //Empty GUID
321
322 /// <summary>
323 /// Check if an Environment is registered for this view.
324 /// The result will be accurate even if the Environment have not been reloaded yet.
325 /// </summary>
326 public bool hasEnvironment => !String.IsNullOrEmpty(m_EnvironmentGUID);
327
328 /// <summary>The currently used Environment</summary>
329 public Environment environment { get; private set; }
330
331 [SerializeField]
332 string viewedObjectAssetGUID = ""; //Empty GUID
333
334 // Careful here: we want to keep it while reloading script.
335 // But from one unity editor to an other, ID are not kept.
336 // So, only use it when reloading from script update.
337 [SerializeField]
338 int viewedObjecHierarchytInstanceID;
339
340 /// <summary>
341 /// Check if an Environment is registered for this view.
342 /// The result will be accurate even if the object have not been reloaded yet.
343 /// </summary>
344 public bool hasViewedObject =>
345 !String.IsNullOrEmpty(viewedObjectAssetGUID)
346 || viewedObjecHierarchytInstanceID != 0;
347
348 /// <summary>Reference to the object given for instantiation.</summary>
349 public GameObject viewedObjectReference { get; private set; }
350
351 /// <summary>
352 /// The currently displayed instance of <see cref="viewedObjectReference"/>.
353 /// It will be instantiated when pushing changes to renderer.
354 /// See <see cref="LookDev.SaveContextChangeAndApply(ViewIndex)"/>
355 /// </summary>
356 public GameObject viewedInstanceInPreview { get; internal set; }
357
358 /// <summary>Update the environment used.</summary>
359 /// <param name="environmentOrCubemapAsset">
360 /// The new <see cref="Environment"/> to use.
361 /// Or the <see cref="Cubemap"/> to use to build a new one.
362 /// Other types will raise an ArgumentException.
363 /// </param>
364 public void UpdateEnvironment(UnityEngine.Object environmentOrCubemapAsset)
365 {
366 m_EnvironmentGUID = "";
367 environment = null;
368 if (environmentOrCubemapAsset == null || environmentOrCubemapAsset.Equals(null))
369 return;
370
371 if (!(environmentOrCubemapAsset is Environment)
372 && !(environmentOrCubemapAsset is Cubemap))
373 throw new System.ArgumentException("Only Environment or Cubemap accepted for environmentOrCubemapAsset parameter");
374
375 string GUID;
376 long localIDInFile;
377 AssetDatabase.TryGetGUIDAndLocalFileIdentifier(environmentOrCubemapAsset, out GUID, out localIDInFile);
378 m_EnvironmentGUID = $"{GUID},{localIDInFile}";
379
380 if (environmentOrCubemapAsset is Environment)
381 environment = environmentOrCubemapAsset as Environment;
382 else //Cubemap
383 environment = Environment.GetTemporaryEnvironmentForCubemap(environmentOrCubemapAsset as Cubemap);
384 }
385
386 void LoadEnvironmentFromGUID()
387 {
388 environment = null;
389
390 GUID storedGUID;
391 string[] GUIDAndLocalIDInFile = m_EnvironmentGUID.Split(new[] { ',' });
392 GUID.TryParse(GUIDAndLocalIDInFile[0], out storedGUID);
393 if (storedGUID.Empty())
394 return;
395 long localIDInFile = GUIDAndLocalIDInFile.Length < 2 ? 0L : long.Parse(GUIDAndLocalIDInFile[1]);
396
397 string path = AssetDatabase.GUIDToAssetPath(GUIDAndLocalIDInFile[0]);
398
399 Type savedType = AssetDatabase.GetMainAssetTypeAtPath(path);
400 if (savedType == typeof(EnvironmentLibrary))
401 {
402 object[] loaded = AssetDatabase.LoadAllAssetsAtPath(path);
403 for (int i = 0; i < loaded.Length; ++i)
404 {
405 string garbage;
406 long testedLocalIndex;
407 if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier((UnityEngine.Object)loaded[i], out garbage, out testedLocalIndex)
408 && testedLocalIndex == localIDInFile)
409 {
410 environment = loaded[i] as Environment;
411 break;
412 }
413 }
414 }
415 else if (savedType == typeof(Environment))
416 environment = AssetDatabase.LoadAssetAtPath<Environment>(path);
417 else if (savedType == typeof(Cubemap))
418 {
419 Cubemap cubemap = AssetDatabase.LoadAssetAtPath<Cubemap>(path);
420 environment = Environment.GetTemporaryEnvironmentForCubemap(cubemap);
421 }
422 }
423
424 /// <summary>Update the object reference used for instantiation.</summary>
425 /// <param name="viewedObject">The new reference.</param>
426 public void UpdateViewedObject(GameObject viewedObject)
427 {
428 viewedObjectAssetGUID = "";
429 viewedObjecHierarchytInstanceID = 0;
430 viewedObjectReference = null;
431 if (viewedObject == null || viewedObject.Equals(null))
432 return;
433
434 bool fromHierarchy = viewedObject.scene.IsValid();
435 if (fromHierarchy)
436 viewedObjecHierarchytInstanceID = viewedObject.GetInstanceID();
437 else
438 viewedObjectAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(viewedObject));
439 viewedObjectReference = viewedObject;
440 }
441
442 //WARNING: only for script reloading
443 void LoadViewedObject()
444 {
445 viewedObjectReference = null;
446
447 GUID storedGUID;
448 GUID.TryParse(viewedObjectAssetGUID, out storedGUID);
449 if (!storedGUID.Empty())
450 {
451 string path = AssetDatabase.GUIDToAssetPath(viewedObjectAssetGUID);
452 viewedObjectReference = AssetDatabase.LoadAssetAtPath<GameObject>(path);
453 }
454 else if (viewedObjecHierarchytInstanceID != 0)
455 {
456 viewedObjectReference = EditorUtility.InstanceIDToObject(viewedObjecHierarchytInstanceID) as GameObject;
457 }
458 }
459
460 internal void LoadAll(bool reloadWithTemporaryID)
461 {
462 if (!reloadWithTemporaryID)
463 CleanTemporaryObjectIndexes();
464 LoadEnvironmentFromGUID();
465 LoadViewedObject();
466 }
467
468 internal void CleanTemporaryObjectIndexes()
469 => viewedObjecHierarchytInstanceID = 0;
470
471 /// <summary>Reset the camera state to default values</summary>
472 public void ResetCameraState()
473 => camera.Reset();
474 }
475
476
477 /// <summary>
478 /// Class that will contain debug value used.
479 /// </summary>
480 public class DebugContext
481 {
482 /// <summary>Display shadows in view.</summary>
483 public bool shadow = true;
484
485 /// <summary>Debug mode displayed. -1 means none.</summary>
486 public int viewMode = -1;
487
488 ///// <summary>Display the debug grey balls</summary>
489 //public bool greyBalls;
490
491 //[SerializeField]
492 //string colorChartGUID = ""; //Empty GUID
493
494 ///// <summary>The currently used color chart</summary>
495 //public Texture2D colorChart { get; private set; }
496 }
497}