A game about forced loneliness, made by TACStudios
1using System;
2using UnityEditor.UIElements;
3using UnityEngine;
4using UnityEngine.Rendering;
5using UnityEngine.UIElements;
6
7using RenderPipelineManager = UnityEngine.Rendering.RenderPipelineManager;
8
9namespace UnityEditor.Rendering.LookDev
10{
11 /// <summary>Interface that must implement the viewer to communicate with the compositor and data management</summary>
12 public interface IViewDisplayer
13 {
14 /// <summary>Get the displayed rect to use</summary>
15 /// <param name="index">Index of this view</param>
16 /// <returns>The Rect to draw</returns>
17 Rect GetRect(ViewCompositionIndex index);
18 /// <summary>Set the computed texture in the view</summary>
19 /// <param name="index">Index of this view</param>
20 /// <param name="texture">The texture used</param>
21 void SetTexture(ViewCompositionIndex index, Texture texture);
22
23 /// <summary>Repaint the UI</summary>
24 void Repaint();
25
26 /// <summary>Callback on layout changed</summary>
27 event Action<Layout, SidePanel> OnLayoutChanged;
28
29 /// <summary>Callback on RenderDoc acquisition is triggered</summary>
30 event Action OnRenderDocAcquisitionTriggered;
31
32 /// <summary>Callback on ;ouse events in the view</summary>
33 event Action<IMouseEvent> OnMouseEventInView;
34
35 /// <summary>Callback on object changed in the view</summary>
36 event Action<GameObject, ViewCompositionIndex, Vector2> OnChangingObjectInView;
37 /// <summary>Callback on environment changed in the view</summary>
38 event Action<UnityEngine.Object, ViewCompositionIndex, Vector2> OnChangingEnvironmentInView;
39
40 /// <summary>Callback on closed</summary>
41 event Action OnClosed;
42
43 /// <summary>Callback on update requested</summary>
44 event Action OnUpdateRequested;
45 }
46
47 partial class DisplayWindow : EditorWindow, IViewDisplayer
48 {
49 static partial class Style
50 {
51 internal const string k_IconFolder = @"Packages/com.unity.render-pipelines.core/Editor/LookDev/Icons/";
52 internal const string k_uss = @"Packages/com.unity.render-pipelines.core/Editor/LookDev/DisplayWindow.uss";
53 internal const string k_uss_personal_overload = @"Packages/com.unity.render-pipelines.core/Editor/LookDev/DisplayWindow-PersonalSkin.uss";
54
55 internal static readonly GUIContent k_WindowTitleAndIcon = EditorGUIUtility.TrTextContentWithIcon("Look Dev", CoreEditorUtils.LoadIcon(k_IconFolder, "LookDev", forceLowRes: true));
56
57 internal static readonly (Texture2D icon, string tooltip) k_Layout1Icon =
58 (CoreEditorUtils.LoadIcon(Style.k_IconFolder, "Layout1", forceLowRes: true),
59 "First view");
60 internal static readonly (Texture2D icon, string tooltip) k_Layout2Icon =
61 (CoreEditorUtils.LoadIcon(Style.k_IconFolder, "Layout2", forceLowRes: true),
62 "Second view");
63 internal static readonly (Texture2D icon, string tooltip) k_LayoutVerticalIcon =
64 (CoreEditorUtils.LoadIcon(Style.k_IconFolder, "LayoutVertical", forceLowRes: true),
65 "Both views split vertically");
66 internal static readonly (Texture2D icon, string tooltip) k_LayoutHorizontalIcon =
67 (CoreEditorUtils.LoadIcon(Style.k_IconFolder, "LayoutHorizontal", forceLowRes: true),
68 "Both views split horizontally");
69 internal static readonly (Texture2D icon, string tooltip) k_LayoutStackIcon =
70 (CoreEditorUtils.LoadIcon(Style.k_IconFolder, "LayoutCustom", forceLowRes: true),
71 "Both views stacked");
72
73 internal static readonly Texture2D k_Camera1Icon = CoreEditorUtils.LoadIcon(Style.k_IconFolder, "Camera1", forceLowRes: true);
74 internal static readonly Texture2D k_Camera2Icon = CoreEditorUtils.LoadIcon(Style.k_IconFolder, "Camera2", forceLowRes: true);
75 internal static readonly Texture2D k_LinkIcon = CoreEditorUtils.LoadIcon(Style.k_IconFolder, "Link", forceLowRes: true);
76 internal static readonly Texture2D k_RightIcon = CoreEditorUtils.LoadIcon(Style.k_IconFolder, "RightArrow", forceLowRes: true);
77 internal static readonly Texture2D k_LeftIcon = CoreEditorUtils.LoadIcon(Style.k_IconFolder, "LeftArrow", forceLowRes: true);
78
79 internal static readonly Texture2D k_RenderdocIcon = CoreEditorUtils.LoadIcon(Style.k_IconFolder, "RenderDoc", forceLowRes: true);
80 internal const string k_RenderDocLabel = " Content";
81
82 internal const string k_CameraSyncTooltip = "Synchronize camera movement amongst views";
83 internal const string k_CameraMenuSync1On2 = "Align Camera 1 with Camera 2";
84 internal const string k_CameraMenuSync2On1 = "Align Camera 2 with Camera 1";
85 internal const string k_CameraMenuReset = "Reset Cameras";
86
87 internal const string k_EnvironmentSidePanelName = "Environment";
88 internal const string k_DebugSidePanelName = "Debug";
89
90 internal const string k_DragAndDropObject = "Drag and drop object here";
91 internal const string k_DragAndDropEnvironment = "Drag and drop environment from side panel here";
92
93 // /!\ WARNING:
94 //The following const are used in the uss.
95 //If you change them, update the uss file too.
96 internal const string k_MainContainerName = "mainContainer";
97 internal const string k_ViewContainerName = "viewContainer";
98 internal const string k_FirstViewName = "firstView";
99 internal const string k_SecondViewName = "secondView";
100 internal const string k_ToolbarName = "toolbar";
101 internal const string k_ToolbarRadioName = "toolbarRadio";
102 internal const string k_TabsRadioName = "tabsRadio";
103 internal const string k_SideToolbarName = "sideToolbar";
104 internal const string k_SharedContainerClass = "container";
105 internal const string k_FirstViewClass = "firstView";
106 internal const string k_SecondViewsClass = "secondView";
107 internal const string k_VerticalViewsClass = "verticalSplit";
108 internal const string k_DebugContainerName = "debugContainer";
109 internal const string k_ShowDebugPanelClass = "showDebugPanel";
110
111 internal const string k_EnvironmentContainerName = "environmentContainer";
112 internal const string k_ShowEnvironmentPanelClass = "showEnvironmentPanel";
113
114 internal const string k_CameraMenuName = "cameraMenu";
115 internal const string k_CameraButtonName = "cameraButton";
116 internal const string k_CameraSeparatorName = "cameraSeparator";
117
118 internal const string k_RenderDocContentName = "renderdoc-content";
119 }
120
121 VisualElement m_MainContainer;
122 VisualElement m_ViewContainer;
123 VisualElement m_DebugContainer;
124 Label m_NoEnvironmentList;
125 Label m_NoObject1;
126 Label m_NoEnvironment1;
127 Label m_NoObject2;
128 Label m_NoEnvironment2;
129
130 Image[] m_Views = new Image[2];
131
132 LayoutContext layout
133 => LookDev.currentContext.layout;
134
135 Layout viewLayout
136 {
137 get => layout.viewLayout;
138 set
139 {
140 if (layout.viewLayout != value)
141 {
142 OnLayoutChangedInternal?.Invoke(value, sidePanel);
143 ApplyLayout(value);
144 }
145 }
146 }
147
148 SidePanel sidePanel
149 {
150 get => layout.showedSidePanel;
151 set
152 {
153 if (layout.showedSidePanel != value)
154 {
155 OnLayoutChangedInternal?.Invoke(viewLayout, value);
156 ApplySidePanelChange(layout.showedSidePanel);
157 }
158 }
159 }
160
161 event Action<Layout, SidePanel> OnLayoutChangedInternal;
162 event Action<Layout, SidePanel> IViewDisplayer.OnLayoutChanged
163 {
164 add => OnLayoutChangedInternal += value;
165 remove => OnLayoutChangedInternal -= value;
166 }
167
168 event Action OnRenderDocAcquisitionTriggeredInternal;
169 event Action IViewDisplayer.OnRenderDocAcquisitionTriggered
170 {
171 add => OnRenderDocAcquisitionTriggeredInternal += value;
172 remove => OnRenderDocAcquisitionTriggeredInternal -= value;
173 }
174
175 event Action<IMouseEvent> OnMouseEventInViewPortInternal;
176 event Action<IMouseEvent> IViewDisplayer.OnMouseEventInView
177 {
178 add => OnMouseEventInViewPortInternal += value;
179 remove => OnMouseEventInViewPortInternal -= value;
180 }
181
182 event Action<GameObject, ViewCompositionIndex, Vector2> OnChangingObjectInViewInternal;
183 event Action<GameObject, ViewCompositionIndex, Vector2> IViewDisplayer.OnChangingObjectInView
184 {
185 add => OnChangingObjectInViewInternal += value;
186 remove => OnChangingObjectInViewInternal -= value;
187 }
188
189 //event Action<Material, ViewCompositionIndex, Vector2> OnChangingMaterialInViewInternal;
190 //event Action<Material, ViewCompositionIndex, Vector2> IViewDisplayer.OnChangingMaterialInView
191 //{
192 // add => OnChangingMaterialInViewInternal += value;
193 // remove => OnChangingMaterialInViewInternal -= value;
194 //}
195
196 event Action<UnityEngine.Object, ViewCompositionIndex, Vector2> OnChangingEnvironmentInViewInternal;
197 event Action<UnityEngine.Object, ViewCompositionIndex, Vector2> IViewDisplayer.OnChangingEnvironmentInView
198 {
199 add => OnChangingEnvironmentInViewInternal += value;
200 remove => OnChangingEnvironmentInViewInternal -= value;
201 }
202
203 event Action OnClosedInternal;
204 event Action IViewDisplayer.OnClosed
205 {
206 add => OnClosedInternal += value;
207 remove => OnClosedInternal -= value;
208 }
209
210 event Action OnUpdateRequestedInternal;
211 event Action IViewDisplayer.OnUpdateRequested
212 {
213 add => OnUpdateRequestedInternal += value;
214 remove => OnUpdateRequestedInternal -= value;
215 }
216
217 StyleSheet styleSheet = null;
218 StyleSheet styleSheetLight = null;
219
220 SwitchableCameraController m_FirstOrCompositeManipulator;
221 CameraController m_SecondManipulator;
222 ComparisonGizmoController m_GizmoManipulator;
223
224 void ReloadStyleSheets()
225 {
226 if (styleSheet == null || styleSheet.Equals(null))
227 {
228 styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(Style.k_uss);
229 if (styleSheet == null || styleSheet.Equals(null))
230 {
231 //Debug.LogWarning("[LookDev] Could not load Stylesheet.");
232 return;
233 }
234 }
235
236 if (!rootVisualElement.styleSheets.Contains(styleSheet))
237 rootVisualElement.styleSheets.Add(styleSheet);
238
239 //Additively load Light Skin
240 if (!EditorGUIUtility.isProSkin)
241 {
242 if (styleSheetLight == null || styleSheetLight.Equals(null))
243 {
244 styleSheetLight = AssetDatabase.LoadAssetAtPath<StyleSheet>(Style.k_uss_personal_overload);
245 if (styleSheetLight == null || styleSheetLight.Equals(null))
246 {
247 //Debug.LogWarning("[LookDev] Could not load Light skin.");
248 return;
249 }
250 }
251
252 if (!rootVisualElement.styleSheets.Contains(styleSheetLight))
253 rootVisualElement.styleSheets.Add(styleSheetLight);
254 }
255 }
256
257 void CreateGUI()
258 {
259 ReloadStyleSheets();
260
261 //Call the open function to configure LookDev
262 // in case the window where open when last editor session finished.
263 // (Else it will open at start and has nothing to display).
264 if (!LookDev.open)
265 LookDev.Initialize(this);
266
267 titleContent = Style.k_WindowTitleAndIcon;
268
269 // /!\ be sure to have a minSize that will allow a non negative sized viewport even with side panel open
270 this.minSize = new Vector2(600, 400);
271
272 CreateToolbar();
273
274 m_MainContainer = new VisualElement() { name = Style.k_MainContainerName };
275 m_MainContainer.AddToClassList(Style.k_SharedContainerClass);
276 rootVisualElement.Add(m_MainContainer);
277
278 CreateViews();
279 CreateEnvironment();
280 CreateDebug();
281 CreateDropAreas();
282
283 ApplyLayout(viewLayout);
284 ApplySidePanelChange(layout.showedSidePanel);
285 }
286
287 void OnEnable()
288 {
289 Undo.undoRedoPerformed += FullRefreshEnvironmentList;
290 }
291
292 void OnDisable()
293 {
294 Undo.undoRedoPerformed -= FullRefreshEnvironmentList;
295 OnClosedInternal?.Invoke();
296 }
297
298 void CreateToolbar()
299 {
300 // Layout swapper part
301 var layoutRadio = new ToolbarRadio() { name = Style.k_ToolbarRadioName };
302 layoutRadio.AddRadios(new[]
303 {
304 Style.k_Layout1Icon,
305 Style.k_Layout2Icon,
306 Style.k_LayoutVerticalIcon,
307 Style.k_LayoutHorizontalIcon,
308 Style.k_LayoutStackIcon,
309 });
310 layoutRadio.RegisterCallback((ChangeEvent<int> evt)
311 => viewLayout = (Layout)evt.newValue);
312 layoutRadio.SetValueWithoutNotify((int)viewLayout);
313
314 var cameraMenu = new ToolbarMenu() { name = Style.k_CameraMenuName };
315 cameraMenu.variant = ToolbarMenu.Variant.Popup;
316 var cameraToggle = new ToolbarToggle() { name = Style.k_CameraButtonName };
317 cameraToggle.value = LookDev.currentContext.cameraSynced;
318 cameraToggle.tooltip = Style.k_CameraSyncTooltip;
319
320 //Note: when having Image on top of the Toggle nested in the Menu, RegisterValueChangedCallback is not called
321 //cameraToggle.RegisterValueChangedCallback(evt => LookDev.currentContext.cameraSynced = evt.newValue);
322 cameraToggle.RegisterCallback<MouseUpEvent>(evt =>
323 {
324 LookDev.currentContext.cameraSynced ^= true;
325 cameraToggle.SetValueWithoutNotify(LookDev.currentContext.cameraSynced);
326 });
327
328 var cameraSeparator = new ToolbarToggle() { name = Style.k_CameraSeparatorName };
329 cameraToggle.Add(new Image() { image = Style.k_Camera1Icon });
330 cameraToggle.Add(new Image() { image = Style.k_LinkIcon });
331 cameraToggle.Add(new Image() { image = Style.k_Camera2Icon });
332 cameraMenu.Add(cameraSeparator);
333 cameraMenu.Add(cameraToggle);
334 cameraMenu.menu.AppendAction(Style.k_CameraMenuSync1On2,
335 (DropdownMenuAction a) => LookDev.currentContext.SynchronizeCameraStates(ViewIndex.Second),
336 DropdownMenuAction.AlwaysEnabled);
337 cameraMenu.menu.AppendAction(Style.k_CameraMenuSync2On1,
338 (DropdownMenuAction a) => LookDev.currentContext.SynchronizeCameraStates(ViewIndex.First),
339 DropdownMenuAction.AlwaysEnabled);
340 cameraMenu.menu.AppendAction(Style.k_CameraMenuReset,
341 (DropdownMenuAction a) =>
342 {
343 LookDev.currentContext.GetViewContent(ViewIndex.First).ResetCameraState();
344 LookDev.currentContext.GetViewContent(ViewIndex.Second).ResetCameraState();
345 },
346 DropdownMenuAction.AlwaysEnabled);
347
348 // Side part
349 var sideRadio = new ToolbarRadio(canDeselectAll: true)
350 {
351 name = Style.k_TabsRadioName
352 };
353 sideRadio.AddRadios(new[]
354 {
355 Style.k_EnvironmentSidePanelName,
356 Style.k_DebugSidePanelName,
357 });
358 sideRadio.SetValueWithoutNotify((int)sidePanel);
359 sideRadio.RegisterCallback((ChangeEvent<int> evt)
360 => sidePanel = (SidePanel)evt.newValue);
361
362 // Aggregate parts
363 var toolbar = new UIElements.Toolbar() { name = Style.k_ToolbarName };
364 toolbar.Add(layoutRadio);
365 toolbar.Add(new ToolbarSpacer());
366 toolbar.Add(cameraMenu);
367
368 toolbar.Add(new ToolbarSpacer() { flex = true });
369 if (UnityEditorInternal.RenderDoc.IsInstalled() && UnityEditorInternal.RenderDoc.IsLoaded())
370 {
371 var renderDocButton = new ToolbarButton(() => OnRenderDocAcquisitionTriggeredInternal?.Invoke())
372 {
373 name = Style.k_RenderDocContentName
374 };
375 renderDocButton.Add(new Image() { image = Style.k_RenderdocIcon });
376 renderDocButton.Add(new Label() { text = Style.k_RenderDocLabel });
377 toolbar.Add(renderDocButton);
378 toolbar.Add(new ToolbarSpacer());
379 }
380 toolbar.Add(sideRadio);
381 rootVisualElement.Add(toolbar);
382 }
383
384 void CreateViews()
385 {
386 if (m_MainContainer == null || m_MainContainer.Equals(null))
387 throw new System.MemberAccessException("m_MainContainer should be assigned prior CreateViews()");
388
389 m_ViewContainer = new VisualElement() { name = Style.k_ViewContainerName };
390 m_ViewContainer.AddToClassList(LookDev.currentContext.layout.isMultiView ? Style.k_SecondViewsClass : Style.k_FirstViewClass);
391 m_ViewContainer.AddToClassList(Style.k_SharedContainerClass);
392 m_MainContainer.Add(m_ViewContainer);
393 m_ViewContainer.RegisterCallback<MouseDownEvent>(evt => OnMouseEventInViewPortInternal?.Invoke(evt));
394 m_ViewContainer.RegisterCallback<MouseUpEvent>(evt => OnMouseEventInViewPortInternal?.Invoke(evt));
395 m_ViewContainer.RegisterCallback<MouseMoveEvent>(evt => OnMouseEventInViewPortInternal?.Invoke(evt));
396
397 m_Views[(int)ViewIndex.First] = new Image() { name = Style.k_FirstViewName, image = Texture2D.blackTexture };
398 m_ViewContainer.Add(m_Views[(int)ViewIndex.First]);
399 m_Views[(int)ViewIndex.Second] = new Image() { name = Style.k_SecondViewName, image = Texture2D.blackTexture };
400 m_ViewContainer.Add(m_Views[(int)ViewIndex.Second]);
401
402 m_FirstOrCompositeManipulator = new SwitchableCameraController(
403 this,
404 index =>
405 {
406 LookDev.currentContext.SetFocusedCamera(index);
407 var environment = LookDev.currentContext.GetViewContent(index).environment;
408 if (sidePanel == SidePanel.Environment && environment != null && LookDev.currentContext.environmentLibrary != null)
409 m_EnvironmentList.selectedIndex = LookDev.currentContext.environmentLibrary.IndexOf(environment);
410 });
411 m_SecondManipulator = new CameraController(
412 this,
413 () =>
414 {
415 LookDev.currentContext.SetFocusedCamera(ViewIndex.Second);
416 var environment = LookDev.currentContext.GetViewContent(ViewIndex.Second).environment;
417 if (sidePanel == SidePanel.Environment && environment != null && LookDev.currentContext.environmentLibrary != null)
418 m_EnvironmentList.selectedIndex = LookDev.currentContext.environmentLibrary.IndexOf(environment);
419 });
420 m_GizmoManipulator = new ComparisonGizmoController(m_FirstOrCompositeManipulator);
421 m_Views[(int)ViewIndex.First].AddManipulator(m_GizmoManipulator); //must take event first to switch the firstOrCompositeManipulator
422 m_Views[(int)ViewIndex.First].AddManipulator(m_FirstOrCompositeManipulator);
423 m_Views[(int)ViewIndex.Second].AddManipulator(m_SecondManipulator);
424
425 m_NoObject1 = new Label(Style.k_DragAndDropObject);
426 m_NoObject1.style.flexGrow = 1;
427 m_NoObject1.style.unityTextAlign = TextAnchor.MiddleCenter;
428 m_NoObject2 = new Label(Style.k_DragAndDropObject);
429 m_NoObject2.style.flexGrow = 1;
430 m_NoObject2.style.unityTextAlign = TextAnchor.MiddleCenter;
431 m_NoEnvironment1 = new Label(Style.k_DragAndDropEnvironment);
432 m_NoEnvironment1.style.flexGrow = 1;
433 m_NoEnvironment1.style.unityTextAlign = TextAnchor.MiddleCenter;
434 m_NoEnvironment1.style.whiteSpace = WhiteSpace.Normal;
435 m_NoEnvironment2 = new Label(Style.k_DragAndDropEnvironment);
436 m_NoEnvironment2.style.flexGrow = 1;
437 m_NoEnvironment2.style.unityTextAlign = TextAnchor.MiddleCenter;
438 m_NoEnvironment2.style.whiteSpace = WhiteSpace.Normal;
439 m_Views[(int)ViewIndex.First].Add(m_NoObject1);
440 m_Views[(int)ViewIndex.First].Add(m_NoEnvironment1);
441 m_Views[(int)ViewIndex.Second].Add(m_NoObject2);
442 m_Views[(int)ViewIndex.Second].Add(m_NoEnvironment2);
443 }
444
445 void CreateDropAreas()
446 {
447 // GameObject or Prefab in view
448 new DropArea(new[] { typeof(GameObject) }, m_Views[(int)ViewIndex.First], (obj, localPos) =>
449 {
450 if (viewLayout == Layout.CustomSplit)
451 OnChangingObjectInViewInternal?.Invoke(obj as GameObject, ViewCompositionIndex.Composite, localPos);
452 else
453 OnChangingObjectInViewInternal?.Invoke(obj as GameObject, ViewCompositionIndex.First, localPos);
454 m_NoObject1.style.visibility = obj == null || obj.Equals(null) ? Visibility.Visible : Visibility.Hidden;
455 });
456 new DropArea(new[] { typeof(GameObject) }, m_Views[(int)ViewIndex.Second], (obj, localPos) =>
457 {
458 OnChangingObjectInViewInternal?.Invoke(obj as GameObject, ViewCompositionIndex.Second, localPos);
459 m_NoObject2.style.visibility = obj == null || obj.Equals(null) ? Visibility.Visible : Visibility.Hidden;
460 });
461
462 // Material in view
463 //new DropArea(new[] { typeof(GameObject) }, m_Views[(int)ViewIndex.First], (obj, localPos) =>
464 //{
465 // if (layout == Layout.CustomSplit || layout == Layout.CustomCircular)
466 // OnChangingMaterialInViewInternal?.Invoke(obj as Material, ViewCompositionIndex.Composite, localPos);
467 // else
468 // OnChangingMaterialInViewInternal?.Invoke(obj as Material, ViewCompositionIndex.First, localPos);
469 //});
470 //new DropArea(new[] { typeof(Material) }, m_Views[(int)ViewIndex.Second], (obj, localPos)
471 // => OnChangingMaterialInViewInternal?.Invoke(obj as Material, ViewCompositionIndex.Second, localPos));
472
473 // Environment in view
474 new DropArea(new[] { typeof(Environment), typeof(Cubemap) }, m_Views[(int)ViewIndex.First], (obj, localPos) =>
475 {
476 if (viewLayout == Layout.CustomSplit)
477 OnChangingEnvironmentInViewInternal?.Invoke(obj, ViewCompositionIndex.Composite, localPos);
478 else
479 OnChangingEnvironmentInViewInternal?.Invoke(obj, ViewCompositionIndex.First, localPos);
480 m_NoEnvironment1.style.visibility = obj == null || obj.Equals(null) ? Visibility.Visible : Visibility.Hidden;
481 });
482 new DropArea(new[] { typeof(Environment), typeof(Cubemap) }, m_Views[(int)ViewIndex.Second], (obj, localPos) =>
483 {
484 OnChangingEnvironmentInViewInternal?.Invoke(obj, ViewCompositionIndex.Second, localPos);
485 m_NoEnvironment2.style.visibility = obj == null || obj.Equals(null) ? Visibility.Visible : Visibility.Hidden;
486 });
487
488 // Environment in library
489 //new DropArea(new[] { typeof(Environment), typeof(Cubemap) }, m_EnvironmentContainer, (obj, localPos) =>
490 //{
491 // //[TODO: check if this come from outside of library]
492 // OnAddingEnvironmentInternal?.Invoke(obj);
493 //});
494 new DropArea(new[] { typeof(EnvironmentLibrary) }, m_EnvironmentContainer, (obj, localPos) =>
495 {
496 OnChangingEnvironmentLibraryInternal?.Invoke(obj as EnvironmentLibrary);
497 RefreshLibraryDisplay();
498 });
499 }
500
501 Rect IViewDisplayer.GetRect(ViewCompositionIndex index)
502 {
503 switch (index)
504 {
505 case ViewCompositionIndex.First:
506 case ViewCompositionIndex.Composite: //display composition on first rect
507 return m_Views[(int)ViewIndex.First].contentRect;
508 case ViewCompositionIndex.Second:
509 return m_Views[(int)ViewIndex.Second].contentRect;
510 default:
511 throw new ArgumentException("Unknown ViewCompositionIndex: " + index);
512 }
513 }
514
515 Vector2 m_LastFirstViewSize = new Vector2();
516 Vector2 m_LastSecondViewSize = new Vector2();
517 void IViewDisplayer.SetTexture(ViewCompositionIndex index, Texture texture)
518 {
519 if (texture == null)
520 return;
521
522 bool updated = false;
523 switch (index)
524 {
525 case ViewCompositionIndex.First:
526 case ViewCompositionIndex.Composite: //display composition on first rect
527 if (updated |= m_Views[(int)ViewIndex.First].image != texture)
528 m_Views[(int)ViewIndex.First].image = texture;
529 else if (updated |= (m_LastFirstViewSize.x != texture.width
530 || m_LastFirstViewSize.y != texture.height))
531 {
532 m_Views[(int)ViewIndex.First].image = null; //force refresh else it will appear zoomed
533 m_Views[(int)ViewIndex.First].image = texture;
534 }
535 if (updated)
536 {
537 m_LastFirstViewSize.x = texture?.width ?? 0;
538 m_LastFirstViewSize.y = texture?.height ?? 0;
539 }
540 break;
541 case ViewCompositionIndex.Second:
542 if (m_Views[(int)ViewIndex.Second].image != texture)
543 m_Views[(int)ViewIndex.Second].image = texture;
544 else if (updated |= (m_LastSecondViewSize.x != texture.width
545 || m_LastSecondViewSize.y != texture.height))
546 {
547 m_Views[(int)ViewIndex.Second].image = null; //force refresh else it will appear zoomed
548 m_Views[(int)ViewIndex.Second].image = texture;
549 }
550 if (updated)
551 {
552 m_LastSecondViewSize.x = texture?.width ?? 0;
553 m_LastSecondViewSize.y = texture?.height ?? 0;
554 }
555 break;
556 default:
557 throw new ArgumentException("Unknown ViewCompositionIndex: " + index);
558 }
559 }
560
561 void IViewDisplayer.Repaint() => Repaint();
562
563 void ApplyLayout(Layout value)
564 {
565 m_NoObject1.style.visibility = LookDev.currentContext.GetViewContent(ViewIndex.First).hasViewedObject ? Visibility.Hidden : Visibility.Visible;
566 m_NoObject2.style.visibility = LookDev.currentContext.GetViewContent(ViewIndex.Second).hasViewedObject ? Visibility.Hidden : Visibility.Visible;
567 m_NoEnvironment1.style.visibility = LookDev.currentContext.GetViewContent(ViewIndex.First).hasEnvironment ? Visibility.Hidden : Visibility.Visible;
568 m_NoEnvironment2.style.visibility = LookDev.currentContext.GetViewContent(ViewIndex.Second).hasEnvironment ? Visibility.Hidden : Visibility.Visible;
569
570 switch (value)
571 {
572 case Layout.HorizontalSplit:
573 case Layout.VerticalSplit:
574 if (!m_ViewContainer.ClassListContains(Style.k_FirstViewClass))
575 m_ViewContainer.AddToClassList(Style.k_FirstViewClass);
576 if (!m_ViewContainer.ClassListContains(Style.k_SecondViewsClass))
577 m_ViewContainer.AddToClassList(Style.k_SecondViewsClass);
578 if (value == Layout.VerticalSplit)
579 {
580 m_ViewContainer.AddToClassList(Style.k_VerticalViewsClass);
581 if (!m_ViewContainer.ClassListContains(Style.k_VerticalViewsClass))
582 m_ViewContainer.AddToClassList(Style.k_FirstViewClass);
583 }
584 for (int i = 0; i < 2; ++i)
585 m_Views[i].style.display = DisplayStyle.Flex;
586 break;
587 case Layout.FullFirstView:
588 case Layout.CustomSplit: //display composition on first rect
589 if (!m_ViewContainer.ClassListContains(Style.k_FirstViewClass))
590 m_ViewContainer.AddToClassList(Style.k_FirstViewClass);
591 if (m_ViewContainer.ClassListContains(Style.k_SecondViewsClass))
592 m_ViewContainer.RemoveFromClassList(Style.k_SecondViewsClass);
593 m_Views[0].style.display = DisplayStyle.Flex;
594 m_Views[1].style.display = DisplayStyle.None;
595 break;
596 case Layout.FullSecondView:
597 if (m_ViewContainer.ClassListContains(Style.k_FirstViewClass))
598 m_ViewContainer.RemoveFromClassList(Style.k_FirstViewClass);
599 if (!m_ViewContainer.ClassListContains(Style.k_SecondViewsClass))
600 m_ViewContainer.AddToClassList(Style.k_SecondViewsClass);
601 m_Views[0].style.display = DisplayStyle.None;
602 m_Views[1].style.display = DisplayStyle.Flex;
603 break;
604 default:
605 throw new ArgumentException("Unknown Layout");
606 }
607
608 //Add flex direction here
609 if (value == Layout.VerticalSplit)
610 m_ViewContainer.AddToClassList(Style.k_VerticalViewsClass);
611 else if (m_ViewContainer.ClassListContains(Style.k_VerticalViewsClass))
612 m_ViewContainer.RemoveFromClassList(Style.k_VerticalViewsClass);
613 }
614
615 void ApplySidePanelChange(SidePanel sidePanel)
616 {
617 IStyle GetEnvironmentContenairDraggerStyle()
618 => m_EnvironmentContainer.Q(className: "unity-base-slider--vertical").Q("unity-dragger").style;
619
620 if (sidePanel == SidePanel.Environment)
621 {
622 if (!m_MainContainer.ClassListContains(Style.k_ShowEnvironmentPanelClass))
623 m_MainContainer.AddToClassList(Style.k_ShowEnvironmentPanelClass);
624
625 if (m_EnvironmentList.selectedIndex != -1)
626 m_EnvironmentContainer.Q<EnvironmentElement>().style.visibility = Visibility.Visible;
627 GetEnvironmentContenairDraggerStyle().display = DisplayStyle.Flex;
628 m_EnvironmentContainer.style.display = DisplayStyle.Flex;
629 }
630 else
631 {
632 if (m_MainContainer.ClassListContains(Style.k_ShowEnvironmentPanelClass))
633 m_MainContainer.RemoveFromClassList(Style.k_ShowEnvironmentPanelClass);
634
635 m_EnvironmentContainer.Q<EnvironmentElement>().style.visibility = Visibility.Hidden;
636 GetEnvironmentContenairDraggerStyle().display = DisplayStyle.None;
637 m_EnvironmentContainer.style.display = DisplayStyle.None;
638 }
639
640 if (sidePanel == SidePanel.Debug)
641 {
642 if (!m_MainContainer.ClassListContains(Style.k_ShowDebugPanelClass))
643 m_MainContainer.AddToClassList(Style.k_ShowDebugPanelClass);
644 UpdateSideDebugPanelProperties();
645 }
646 else
647 {
648 if (m_MainContainer.ClassListContains(Style.k_ShowDebugPanelClass))
649 m_MainContainer.RemoveFromClassList(Style.k_ShowDebugPanelClass);
650 }
651 }
652
653 void Update()
654 {
655 if (LookDev.waitingConfigure)
656 return;
657
658 // [case 1245086] Guard in case the SRP asset is set to null (or to a not supported SRP) when the lookdev window is already open
659 // Note: After an editor reload, we might get a null SRP for a couple of frames, hence the check.
660 if (!LookDev.supported)
661 {
662 // Print an error and close the Lookdev window (to avoid spamming the console)
663 if (RenderPipelineManager.currentPipeline != null)
664 Debug.LogError("LookDev is not supported by this Scriptable Render Pipeline: " + RenderPipelineManager.currentPipeline.ToString());
665 else if (GraphicsSettings.currentRenderPipeline != null)
666 Debug.LogError("LookDev is not available until a camera render occurs.");
667 else
668 Debug.LogError("LookDev is not supported: No SRP detected.");
669 LookDev.Close();
670 }
671
672 // All those states coming from the Contexts can become invalid after a domain reload so we need to update them.
673 m_FirstOrCompositeManipulator.UpdateCameraState(LookDev.currentContext);
674 m_SecondManipulator.UpdateCameraState(LookDev.currentContext, ViewIndex.Second);
675 m_GizmoManipulator.UpdateGizmoState(LookDev.currentContext.layout.gizmoState);
676 }
677
678 void OnGUI()
679 {
680 if (EditorApplication.isUpdating)
681 return;
682
683 //deal with missing style on domain reload...
684 ReloadStyleSheets();
685
686 OnUpdateRequestedInternal?.Invoke();
687 }
688 }
689}