Context.cs 18 KB

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