A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.IO; 4using System.Linq; 5using UnityEngine; 6using UnityEditor.Graphing; 7using UnityEditor.Graphing.Util; 8using UnityEditor.ShaderGraph.Drawing.Inspector; 9using Object = UnityEngine.Object; 10 11using UnityEditor.Experimental.GraphView; 12using UnityEditor.ShaderGraph.Drawing.Colors; 13using UnityEngine.UIElements; 14using Edge = UnityEditor.Experimental.GraphView.Edge; 15using UnityEditor.VersionControl; 16using UnityEditor.Searcher; 17 18using Unity.Profiling; 19using UnityEditor.ShaderGraph.Internal; 20using UnityEditor.Experimental; 21using UnityEditor.PackageManager.UI; 22 23namespace UnityEditor.ShaderGraph.Drawing 24{ 25 [Serializable] 26 class FloatingWindowsLayout 27 { 28 public WindowDockingLayout previewLayout = new WindowDockingLayout 29 { 30 dockingTop = false, 31 dockingLeft = false, 32 verticalOffset = 8, 33 horizontalOffset = 8 34 }; 35 } 36 37 [Serializable] 38 class UserViewSettings 39 { 40 public bool isBlackboardVisible = true; 41 public bool isPreviewVisible = true; 42 public bool isInspectorVisible = true; 43 public string colorProvider = NoColors.Title; 44 } 45 46 class GraphEditorView : VisualElement, IDisposable 47 { 48 MaterialGraphView m_GraphView; 49 MasterPreviewView m_MasterPreviewView; 50 InspectorView m_InspectorView; 51 52 GraphData m_Graph; 53 PreviewManager m_PreviewManager; 54 MessageManager m_MessageManager; 55 SearchWindowProvider m_SearchWindowProvider; 56 EdgeConnectorListener m_EdgeConnectorListener; 57 VisualElement m_HoveredContextView; 58 59 BlackboardController m_BlackboardController; 60 61 internal BlackboardController blackboardController 62 { 63 get => m_BlackboardController; 64 set 65 { 66 if (value != null) 67 m_BlackboardController = value; 68 } 69 } 70 71 ColorManager m_ColorManager; 72 EditorWindow m_EditorWindow; 73 74 const string k_UserViewSettings = "UnityEditor.ShaderGraph.ToggleSettings"; 75 UserViewSettings m_UserViewSettings; 76 77 internal UserViewSettings viewSettings { get => m_UserViewSettings; } 78 79 const string k_FloatingWindowsLayoutKey = "UnityEditor.ShaderGraph.FloatingWindowsLayout2"; 80 FloatingWindowsLayout m_FloatingWindowsLayout = new FloatingWindowsLayout(); 81 82 public Action saveRequested { get; set; } 83 84 public Action saveAsRequested { get; set; } 85 86 public Func<bool> isCheckedOut { get; set; } 87 88 public Action checkOut { get; set; } 89 90 public Action convertToSubgraphRequested 91 { 92 get { return m_GraphView.onConvertToSubgraphClick; } 93 set { m_GraphView.onConvertToSubgraphClick = value; } 94 } 95 96 public Action showInProjectRequested { get; set; } 97 98 public MaterialGraphView graphView 99 { 100 get { return m_GraphView; } 101 } 102 103 public InspectorView inspectorView 104 { 105 get { return m_InspectorView; } 106 } 107 108 internal PreviewManager previewManager 109 { 110 get { return m_PreviewManager; } 111 set { m_PreviewManager = value; } 112 } 113 114 public string assetName 115 { 116 get => m_AssetName; 117 set 118 { 119 m_AssetName = value; 120 // Also update blackboard title 121 m_BlackboardController.UpdateBlackboardTitle(m_AssetName); 122 } 123 } 124 125 public ColorManager colorManager 126 { 127 get => m_ColorManager; 128 } 129 130 void InstallSample(string sampleName) 131 { 132 var sample = Sample.FindByPackage("com.unity.shadergraph", null).SingleOrDefault(x => x.displayName == sampleName); 133 if (!string.IsNullOrEmpty(sample.displayName)) 134 { 135 if (!sample.isImported) 136 { 137 sample.Import(); 138 } 139 else 140 { 141 var reinstall = EditorUtility.DisplayDialog("Warning", "This sample package is already installed.\nDo you want to reinstall it?", "Yes", "No"); 142 if (reinstall) 143 { 144 sample.Import(Sample.ImportOptions.OverridePreviousImports); 145 } 146 } 147 } 148 else 149 { 150 Debug.LogWarning($"Could not find sample package {sampleName}"); 151 } 152 } 153 154 private static readonly ProfilerMarker AddGroupsMarker = new ProfilerMarker("AddGroups"); 155 private static readonly ProfilerMarker AddStickyNotesMarker = new ProfilerMarker("AddStickyNotes"); 156 public GraphEditorView(EditorWindow editorWindow, GraphData graph, MessageManager messageManager, string graphName) 157 { 158 m_GraphViewGroupTitleChanged = OnGroupTitleChanged; 159 m_GraphViewElementsAddedToGroup = OnElementsAddedToGroup; 160 m_GraphViewElementsRemovedFromGroup = OnElementsRemovedFromGroup; 161 ShaderGraphPreferences.onZoomStepSizeChanged += ResetZoom; 162 163 m_EditorWindow = editorWindow; 164 m_Graph = graph; 165 m_AssetName = graphName; 166 m_MessageManager = messageManager; 167 previewManager = new PreviewManager(graph, messageManager); 168 previewManager.RenderPreviews(m_EditorWindow, false); 169 170 styleSheets.Add(Resources.Load<StyleSheet>("Styles/GraphEditorView")); 171 var serializedSettings = EditorUserSettings.GetConfigValue(k_UserViewSettings); 172 m_UserViewSettings = JsonUtility.FromJson<UserViewSettings>(serializedSettings) ?? new UserViewSettings(); 173 m_ColorManager = new ColorManager(m_UserViewSettings.colorProvider); 174 175 176 List<IShaderGraphToolbarExtension> toolbarExtensions = new(); 177 foreach (var type in TypeCache.GetTypesDerivedFrom(typeof(IShaderGraphToolbarExtension)).Where(e => !e.IsGenericType)) 178 { 179 toolbarExtensions.Add((IShaderGraphToolbarExtension)Activator.CreateInstance(type)); 180 } 181 182 var colorProviders = m_ColorManager.providerNames.ToArray(); 183 var toolbar = new IMGUIContainer(() => 184 { 185 GUILayout.BeginHorizontal(EditorStyles.toolbar); 186 if (GUILayout.Button(new GUIContent(EditorGUIUtility.FindTexture("SaveActive"), "Save"), EditorStyles.toolbarButton)) 187 { 188 if (saveRequested != null) 189 saveRequested(); 190 } 191 if (GUILayout.Button(EditorResources.Load<Texture>("d_dropdown"), EditorStyles.toolbarButton)) 192 { 193 GenericMenu menu = new GenericMenu(); 194 menu.AddItem(new GUIContent("Save As..."), false, () => saveAsRequested()); 195 menu.AddItem(new GUIContent("Show In Project"), false, () => showInProjectRequested()); 196 if (!isCheckedOut() && Provider.enabled && Provider.isActive) 197 { 198 menu.AddItem(new GUIContent("Check Out"), false, () => 199 { 200 if (checkOut != null) 201 checkOut(); 202 }); 203 } 204 else 205 { 206 menu.AddDisabledItem(new GUIContent("Check Out"), false); 207 } 208 menu.ShowAsContext(); 209 } 210 211 if (graphView != null) 212 foreach (var ext in toolbarExtensions) 213 ext.OnGUI(graphView); 214 215 GUILayout.FlexibleSpace(); 216 217 EditorGUI.BeginChangeCheck(); 218 GUILayout.Label("Color Mode"); 219 var newColorIndex = EditorGUILayout.Popup(m_ColorManager.activeIndex, colorProviders, GUILayout.Width(100f)); 220 GUILayout.Space(4); 221 m_UserViewSettings.isBlackboardVisible = GUILayout.Toggle(m_UserViewSettings.isBlackboardVisible, new GUIContent(Resources.Load<Texture2D>("Icons/blackboard"), "Blackboard"), EditorStyles.toolbarButton); 222 223 GUILayout.Space(6); 224 225 m_UserViewSettings.isInspectorVisible = GUILayout.Toggle(m_UserViewSettings.isInspectorVisible, new GUIContent(EditorGUIUtility.TrIconContent("d_UnityEditor.InspectorWindow").image, "Graph Inspector"), EditorStyles.toolbarButton); 226 227 GUILayout.Space(6); 228 229 m_UserViewSettings.isPreviewVisible = GUILayout.Toggle(m_UserViewSettings.isPreviewVisible, new GUIContent(EditorGUIUtility.FindTexture("PreMatSphere"), "Main Preview"), EditorStyles.toolbarButton); 230 231 if (GUILayout.Button(new GUIContent(EditorGUIUtility.TrIconContent("_Help").image, "Open Shader Graph User Manual"), EditorStyles.toolbarButton)) 232 { 233 Application.OpenURL(UnityEngine.Rendering.ShaderGraph.Documentation.GetPageLink("index")); 234 //Application.OpenURL("https://docs.unity3d.com/Packages/com.unity.shadergraph@17.0/manual/index.html"); // TODO : point to latest? 235 } 236 if (GUILayout.Button(EditorResources.Load<Texture>("d_dropdown"), EditorStyles.toolbarButton)) 237 { 238 GenericMenu menu = new GenericMenu(); 239 menu.AddItem(new GUIContent("Shader Graph Samples"), false, () => 240 { 241 PackageManager.UI.Window.Open("com.unity.shadergraph"); 242 }); 243 menu.AddItem(new GUIContent("Install Node Reference Sample"), false, () => 244 { 245 InstallSample("Node Reference"); 246 }); 247 menu.AddItem(new GUIContent("Install Procedural Patterns Sample"), false, () => 248 { 249 InstallSample("Procedural Patterns"); 250 }); 251 menu.AddSeparator(""); 252 menu.AddItem(new GUIContent("Shader Graph Feature Page"), false, () => 253 { 254 Application.OpenURL("https://unity.com/features/shader-graph"); 255 }); 256 menu.AddItem(new GUIContent("Shader Graph Forums"), false, () => 257 { 258 Application.OpenURL("https://forum.unity.com/forums/shader-graph.346/"); 259 }); 260 menu.AddItem(new GUIContent("Shader Graph Roadmap"), false, () => 261 { 262 Application.OpenURL("https://portal.productboard.com/unity/1-unity-platform-rendering-visual-effects/tabs/7-shader-graph"); 263 }); 264 menu.ShowAsContext(); 265 } 266 267 if (EditorGUI.EndChangeCheck()) 268 { 269 UserViewSettingsChangeCheck(newColorIndex); 270 } 271 GUILayout.EndHorizontal(); 272 }); 273 Add(toolbar); 274 275 var content = new VisualElement { name = "content" }; 276 { 277 m_GraphView = new MaterialGraphView(graph, () => m_PreviewManager.UpdateMasterPreview(ModificationScope.Topological)) 278 { name = "GraphView", viewDataKey = "MaterialGraphView" }; 279 ResetZoom(); 280 m_GraphView.AddManipulator(new ContentDragger()); 281 m_GraphView.AddManipulator(new SelectionDragger()); 282 m_GraphView.AddManipulator(new RectangleSelector()); 283 m_GraphView.AddManipulator(new ClickSelector()); 284 285 // Bugfix 1312222. Running 'ResetSelectedBlockNodes' on all mouse up interactions will break selection 286 // after changing tabs. This was originally added to fix a bug with middle-mouse clicking while dragging a block node. 287 m_GraphView.RegisterCallback<MouseUpEvent>(evt => { if (evt.button == (int)MouseButton.MiddleMouse) m_GraphView.ResetSelectedBlockNodes(); }); 288 // This takes care of when a property is dragged from BB and then the drag is ended by the Escape key, hides the scroll boundary regions and drag indicator if so 289 m_GraphView.RegisterCallback<DragExitedEvent>(evt => 290 { 291 blackboardController.blackboard.OnDragExitedEvent(evt); 292 blackboardController.blackboard.hideDragIndicatorAction?.Invoke(); 293 }); 294 295 RegisterGraphViewCallbacks(); 296 content.Add(m_GraphView); 297 298 string serializedWindowLayout = EditorUserSettings.GetConfigValue(k_FloatingWindowsLayoutKey); 299 if (!string.IsNullOrEmpty(serializedWindowLayout)) 300 { 301 m_FloatingWindowsLayout = JsonUtility.FromJson<FloatingWindowsLayout>(serializedWindowLayout); 302 } 303 304 CreateMasterPreview(); 305 CreateInspector(); 306 CreateBlackboard(); 307 308 UpdateSubWindowsVisibility(); 309 310 m_GraphView.graphViewChanged = GraphViewChanged; 311 312 RegisterCallback<GeometryChangedEvent>(ApplySerializedWindowLayouts); 313 if (m_Graph.isSubGraph) 314 { 315 m_GraphView.AddToClassList("subgraph"); 316 } 317 } 318 319 m_SearchWindowProvider = new SearcherProvider(); 320 m_SearchWindowProvider.Initialize(editorWindow, m_Graph, m_GraphView); 321 m_GraphView.nodeCreationRequest = NodeCreationRequest; 322 //regenerate entries when graph view is refocused, to propogate subgraph changes 323 m_GraphView.RegisterCallback<FocusInEvent>(evt => { m_SearchWindowProvider.regenerateEntries = true; }); 324 325 m_EdgeConnectorListener = new EdgeConnectorListener(m_Graph, m_SearchWindowProvider, editorWindow); 326 327 if (!m_Graph.isSubGraph) 328 { 329 AddContexts(); 330 } 331 332 using (AddGroupsMarker.Auto()) 333 { 334 foreach (var graphGroup in graph.groups) 335 AddGroup(graphGroup); 336 } 337 338 using (AddStickyNotesMarker.Auto()) 339 { 340 foreach (var stickyNote in graph.stickyNotes) 341 AddStickyNote(stickyNote); 342 } 343 344 AddNodes(graph.GetNodes<AbstractMaterialNode>()); 345 AddBlocks(graph.GetNodes<BlockNode>()); 346 AddEdges(graph.edges); 347 Add(content); 348 349 // Active block lists need to be initialized on window start up 350 // Do this here to as we cant do this inside GraphData 351 // This is due to targets not being deserialized yet 352 var context = new TargetSetupContext(); 353 foreach (var target in m_Graph.activeTargets) 354 { 355 target.Setup(ref context); 356 } 357 var activeBlocks = m_Graph.GetActiveBlocksForAllActiveTargets(); 358 m_Graph.UpdateActiveBlocks(activeBlocks); 359 360 // Graph settings need to be initialized after the target setup 361 m_InspectorView.InitializeGraphSettings(); 362 } 363 364 private void CreateBlackboard() 365 { 366 var blackboardViewModel = new BlackboardViewModel() { parentView = graphView, model = m_Graph, title = assetName }; 367 m_BlackboardController = new BlackboardController(m_Graph, blackboardViewModel, m_Graph.owner.graphDataStore); 368 } 369 370 void AddContexts() 371 { 372 ContextView AddContext(string name, ContextData contextData, Direction portDirection) 373 { 374 //need to eventually remove this reference to editor window in context views 375 var contextView = new ContextView(name, contextData, m_EditorWindow); 376 377 // GraphView marks ContextViews' stacks, but not the actual root elements, as insertable. We want the 378 // contextual searcher menu to come up when *any* part of the ContextView is hovered. As a workaround, 379 // we keep track of the hovered ContextView and offer it if no targets are found. 380 contextView.RegisterCallback((MouseOverEvent _) => m_HoveredContextView = contextView); 381 contextView.RegisterCallback((MouseOutEvent _) => 382 { 383 if (m_HoveredContextView == contextView) m_HoveredContextView = null; 384 }); 385 386 contextView.SetPosition(new Rect(contextData.position, Vector2.zero)); 387 contextView.AddPort(portDirection); 388 m_GraphView.AddElement(contextView); 389 return contextView; 390 } 391 392 // Add Contexts 393 // As Contexts are hardcoded and contain a single port we can just give the direction 394 var vertexContext = AddContext("Vertex", m_Graph.vertexContext, Direction.Output); 395 var fragmentContext = AddContext("Fragment", m_Graph.fragmentContext, Direction.Input); 396 397 // Connect Contexts 398 // Vertical Edges have no representation in Model 399 // Therefore just draw it and dont allow interaction 400 var contextEdge = new Edge() 401 { 402 output = vertexContext.port, 403 input = fragmentContext.port, 404 pickingMode = PickingMode.Ignore, 405 }; 406 m_GraphView.AddElement(contextEdge); 407 408 // Update the Context list on MaterialGraphView 409 m_GraphView.UpdateContextList(); 410 } 411 412 internal void UserViewSettingsChangeCheck(int newColorIndex) 413 { 414 if (newColorIndex != m_ColorManager.activeIndex) 415 { 416 m_ColorManager.SetActiveProvider(newColorIndex, m_GraphView.Query<MaterialNodeView>().ToList()); 417 m_UserViewSettings.colorProvider = m_ColorManager.activeProviderName; 418 } 419 420 var serializedViewSettings = JsonUtility.ToJson(m_UserViewSettings); 421 EditorUserSettings.SetConfigValue(k_UserViewSettings, serializedViewSettings); 422 423 UpdateSubWindowsVisibility(); 424 } 425 426 void NodeCreationRequest(NodeCreationContext c) 427 { 428 if (EditorWindow.focusedWindow == m_EditorWindow) //only display the search window when current graph view is focused 429 { 430 m_SearchWindowProvider.connectedPort = null; 431 m_SearchWindowProvider.target = c.target ?? m_HoveredContextView; 432 var displayPosition = graphView.cachedMousePosition; 433 434 SearcherWindow.Show(m_EditorWindow, (m_SearchWindowProvider as SearcherProvider).LoadSearchWindow(), 435 item => (m_SearchWindowProvider as SearcherProvider).OnSearcherSelectEntry(item, displayPosition), 436 displayPosition, null, new SearcherWindow.Alignment(SearcherWindow.Alignment.Vertical.Center, SearcherWindow.Alignment.Horizontal.Left)); 437 } 438 } 439 440 // Master Preview, Inspector and Blackboard all need to keep their layouts when hidden in order to restore user preferences. 441 // Because of their differences we do this is different ways, for now. 442 void UpdateSubWindowsVisibility() 443 { 444 // Blackboard needs to be effectively removed when hidden to avoid bugs. 445 if (m_UserViewSettings.isBlackboardVisible) 446 blackboardController.blackboard.ShowWindow(); 447 else 448 blackboardController.blackboard.HideWindow(); 449 450 // Same for the inspector 451 if (m_UserViewSettings.isInspectorVisible) 452 m_InspectorView.ShowWindow(); 453 else 454 m_InspectorView.HideWindow(); 455 456 m_MasterPreviewView.visible = m_UserViewSettings.isPreviewVisible; 457 } 458 459 Action<Group, string> m_GraphViewGroupTitleChanged; 460 Action<Group, IEnumerable<GraphElement>> m_GraphViewElementsAddedToGroup; 461 Action<Group, IEnumerable<GraphElement>> m_GraphViewElementsRemovedFromGroup; 462 463 void RegisterGraphViewCallbacks() 464 { 465 m_GraphView.groupTitleChanged = m_GraphViewGroupTitleChanged; 466 m_GraphView.elementsAddedToGroup = m_GraphViewElementsAddedToGroup; 467 m_GraphView.elementsRemovedFromGroup = m_GraphViewElementsRemovedFromGroup; 468 } 469 470 void UnregisterGraphViewCallbacks() 471 { 472 m_GraphView.groupTitleChanged = null; 473 m_GraphView.elementsAddedToGroup = null; 474 m_GraphView.elementsRemovedFromGroup = null; 475 } 476 477 void CreateMasterPreview() 478 { 479 m_MasterPreviewView = new MasterPreviewView(previewManager, m_Graph) { name = "masterPreview" }; 480 481 var masterPreviewViewDraggable = new WindowDraggable(null, this); 482 m_MasterPreviewView.AddManipulator(masterPreviewViewDraggable); 483 m_GraphView.Add(m_MasterPreviewView); 484 485 masterPreviewViewDraggable.OnDragFinished += UpdateSerializedWindowLayout; 486 m_MasterPreviewView.previewResizeBorderFrame.OnResizeFinished += UpdateSerializedWindowLayout; 487 } 488 489 void CreateInspector() 490 { 491 var inspectorViewModel = new InspectorViewModel() { parentView = this.graphView }; 492 m_InspectorView = new InspectorView(inspectorViewModel); 493 graphView.OnSelectionChange += m_InspectorView.TriggerInspectorUpdate; 494 // Undo/redo actions that only affect selection don't trigger the above callback for some reason, so we also have to do this 495 Undo.undoRedoPerformed += (() => { m_InspectorView?.TriggerInspectorUpdate(graphView?.selection); }); 496 } 497 498 // a nice curve that scales well for various HID (touchpad and mice). 499 static float WeightStepSize(float x) => Mathf.Clamp(2 * Mathf.Pow(x, 7f / 2f), 0.001f, 2.0f); 500 void ResetZoom() 501 { 502 var weightedStepSize = WeightStepSize(ShaderGraphPreferences.zoomStepSize); 503 m_GraphView?.SetupZoom(0.05f, 8.0f, weightedStepSize, 1.0f); 504 } 505 506 GraphViewChange GraphViewChanged(GraphViewChange graphViewChange) 507 { 508 if (graphViewChange.edgesToCreate != null) 509 { 510 foreach (var edge in graphViewChange.edgesToCreate) 511 { 512 var leftSlot = edge.output.GetSlot(); 513 var rightSlot = edge.input.GetSlot(); 514 if (leftSlot != null && rightSlot != null) 515 { 516 m_Graph.owner.RegisterCompleteObjectUndo("Connect Edge"); 517 m_Graph.Connect(leftSlot.slotReference, rightSlot.slotReference); 518 } 519 } 520 graphViewChange.edgesToCreate.Clear(); 521 } 522 523 if (graphViewChange.movedElements != null) 524 { 525 m_Graph.owner.RegisterCompleteObjectUndo("Move Elements"); 526 527 List<GraphElement> nodesInsideGroup = new List<GraphElement>(); 528 foreach (var element in graphViewChange.movedElements) 529 { 530 var groupNode = element as ShaderGroup; 531 if (groupNode == null) 532 continue; 533 534 foreach (GraphElement graphElement in groupNode.containedElements) 535 { 536 nodesInsideGroup.Add(graphElement); 537 } 538 539 SetGroupPosition(groupNode); 540 } 541 542 if (nodesInsideGroup.Any()) 543 graphViewChange.movedElements.AddRange(nodesInsideGroup); 544 545 foreach (var element in graphViewChange.movedElements) 546 { 547 if (element.userData is AbstractMaterialNode node) 548 { 549 var drawState = node.drawState; 550 drawState.position = element.parent.ChangeCoordinatesTo(m_GraphView.contentViewContainer, element.GetPosition()); 551 node.drawState = drawState; 552 553 // BlockNode moved outside a Context 554 // This isnt allowed but there is no way to disallow it on the GraphView 555 if (node is BlockNode blockNode && 556 element.GetFirstAncestorOfType<ContextView>() == null) 557 { 558 var context = graphView.GetContext(blockNode.contextData); 559 560 // isDragging ensures we arent calling this when moving 561 // the BlockNode into the GraphView during dragging 562 if (context.isDragging) 563 continue; 564 565 // Remove from GraphView and add back to Context 566 m_GraphView.RemoveElement(element); 567 context.InsertBlock(element as MaterialNodeView); 568 } 569 } 570 571 if (element is StickyNote stickyNote) 572 { 573 SetStickyNotePosition(stickyNote); 574 } 575 576 if (element is ContextView contextView) 577 { 578 var rect = element.parent.ChangeCoordinatesTo(m_GraphView.contentViewContainer, element.GetPosition()); 579 contextView.contextData.position = rect.position; 580 } 581 } 582 } 583 584 var nodesToUpdate = m_NodeViewHashSet; 585 nodesToUpdate.Clear(); 586 587 if (graphViewChange.elementsToRemove != null) 588 { 589 m_Graph.owner.RegisterCompleteObjectUndo("Remove Elements"); 590 m_Graph.RemoveElements( 591 graphViewChange.elementsToRemove.OfType<IShaderNodeView>().Select(v => v.node).ToArray(), 592 graphViewChange.elementsToRemove.OfType<Edge>().Select(e => (IEdge)e.userData).ToArray(), 593 graphViewChange.elementsToRemove.OfType<ShaderGroup>().Select(g => g.userData).ToArray(), 594 graphViewChange.elementsToRemove.OfType<StickyNote>().Select(n => n.userData).ToArray(), 595 graphViewChange.elementsToRemove.OfType<SGBlackboardField>().Select(f => (ShaderInput)f.userData).ToArray() 596 ); 597 foreach (var edge in graphViewChange.elementsToRemove.OfType<Edge>()) 598 { 599 if (edge.input != null) 600 { 601 if (edge.input.node is IShaderNodeView materialNodeView) 602 nodesToUpdate.Add(materialNodeView); 603 } 604 if (edge.output != null) 605 { 606 if (edge.output.node is IShaderNodeView materialNodeView) 607 nodesToUpdate.Add(materialNodeView); 608 } 609 } 610 } 611 612 foreach (var node in nodesToUpdate) 613 { 614 node.OnModified(ModificationScope.Topological); 615 } 616 617 UpdateEdgeColors(nodesToUpdate); 618 return graphViewChange; 619 } 620 621 void SetGroupPosition(ShaderGroup groupNode) 622 { 623 var pos = groupNode.GetPosition(); 624 groupNode.userData.position = new Vector2(pos.x, pos.y); 625 } 626 627 void SetStickyNotePosition(StickyNote stickyNote) 628 { 629 var pos = stickyNote.GetPosition(); 630 stickyNote.userData.position = new Rect(pos); 631 } 632 633 void OnGroupTitleChanged(Group graphGroup, string title) 634 { 635 var groupData = graphGroup.userData as GroupData; 636 if (groupData != null) 637 { 638 groupData.title = graphGroup.title; 639 } 640 } 641 642 void OnElementsAddedToGroup(Group graphGroup, IEnumerable<GraphElement> elements) 643 { 644 if (graphGroup.userData is GroupData groupData) 645 { 646 var anyChanged = false; 647 foreach (var element in elements) 648 { 649 if (element.userData is IGroupItem groupItem && groupItem.group != groupData) 650 { 651 anyChanged = true; 652 break; 653 } 654 } 655 656 if (!anyChanged) 657 return; 658 659 m_Graph.owner.RegisterCompleteObjectUndo(groupData.title); 660 661 foreach (var element in elements) 662 { 663 if (element.userData is IGroupItem groupItem) 664 { 665 m_Graph.SetGroup(groupItem, groupData); 666 } 667 } 668 } 669 } 670 671 void OnElementsRemovedFromGroup(Group graphGroup, IEnumerable<GraphElement> elements) 672 { 673 if (graphGroup.userData is GroupData groupData) 674 { 675 var anyChanged = false; 676 foreach (var element in elements) 677 { 678 if (element.userData is IGroupItem groupItem && groupItem.group == groupData) 679 { 680 anyChanged = true; 681 break; 682 } 683 } 684 685 if (!anyChanged) 686 return; 687 688 m_Graph.owner.RegisterCompleteObjectUndo("Ungroup Node(s)"); 689 690 foreach (var element in elements) 691 { 692 if (element.userData is IGroupItem groupItem) 693 { 694 m_Graph.SetGroup(groupItem, null); 695 SetGroupPosition((ShaderGroup)graphGroup); //, (GraphElement)nodeView); 696 } 697 } 698 } 699 } 700 701 702 703 704 void OnNodeChanged(AbstractMaterialNode inNode, ModificationScope scope) 705 { 706 if (m_GraphView == null) 707 return; 708 709 var dependentNodes = new List<AbstractMaterialNode>(); 710 if (!inNode.owner.graphIsConcretizing && !inNode.owner.replaceInProgress) 711 NodeUtils.CollectNodesNodeFeedsInto(dependentNodes, inNode); 712 else dependentNodes.Add(inNode); 713 714 foreach (var node in dependentNodes) 715 { 716 var nodeView = m_GraphView.GetNodeByGuid(node.objectId) as IShaderNodeView; 717 if (nodeView != null) 718 nodeView.OnModified(scope); 719 } 720 } 721 722 723 HashSet<IShaderNodeView> m_NodeViewHashSet = new HashSet<IShaderNodeView>(); 724 HashSet<ShaderGroup> m_GroupHashSet = new HashSet<ShaderGroup>(); 725 float lastUpdate = 0f; 726 public void HandleGraphChanges(bool wasUndoRedoPerformed) 727 { 728 UnregisterGraphViewCallbacks(); 729 // anything that gets a new view needs to be updated. throughout this call. 730 // while it's a little expensive to build this every graph change, it's a huge 731 // improvement in overall performance to do so. 732 Dictionary<object, GraphElement> lookupTable = new(); 733 m_GraphView.graphElements.ForEach(e => { 734 if (e.userData != null) 735 lookupTable.Add(e.userData, e); 736 }); 737 738 previewManager.HandleGraphChanges(); 739 740 var windowReceivesUpdates = (m_EditorWindow as MaterialGraphEditWindow)?.isVisible ?? false; 741 if (Time.realtimeSinceStartup - lastUpdate >= 0.03f && windowReceivesUpdates && m_UserViewSettings.isPreviewVisible) 742 { 743 lastUpdate = Time.realtimeSinceStartup; 744 previewManager.UpdateMasterPreview(ModificationScope.Node); 745 } 746 m_InspectorView.HandleGraphChanges(); 747 748 if (m_Graph.addedEdges.Any() || m_Graph.removedEdges.Any()) 749 { 750 // Precision color provider is the only one that needs to update node colors on connection. 751 if (m_ColorManager.activeProviderName == "Precision") 752 { 753 var nodeList = m_GraphView.Query<MaterialNodeView>().ToList(); 754 m_ColorManager.SetNodesDirty(nodeList); 755 m_ColorManager.UpdateNodeViews(nodeList); 756 } 757 } 758 759 previewManager.RenderPreviews(m_EditorWindow); 760 761 762 m_GraphView.wasUndoRedoPerformed = wasUndoRedoPerformed; 763 764 if (wasUndoRedoPerformed || m_InspectorView.doesInspectorNeedUpdate) 765 m_InspectorView.Update(); 766 767 if (wasUndoRedoPerformed) 768 m_GraphView.RestorePersistentSelectionAfterUndoRedo(); 769 770 m_GroupHashSet.Clear(); 771 772 HandleRemovedNodes(lookupTable); 773 774 foreach (var noteData in m_Graph.removedNotes) 775 { 776 if (lookupTable.TryGetValue(noteData, out var note)) 777 m_GraphView.RemoveElement(note); 778 } 779 780 foreach (GroupData groupData in m_Graph.removedGroups) 781 { 782 if (lookupTable.TryGetValue(groupData, out var group)) 783 m_GraphView.RemoveElement(group); 784 } 785 786 foreach (var groupData in m_Graph.addedGroups) 787 { 788 AddGroup(groupData, lookupTable: lookupTable); 789 } 790 791 foreach (var stickyNote in m_Graph.addedStickyNotes) 792 { 793 AddStickyNote(stickyNote, lookupTable: lookupTable); 794 } 795 796 foreach (var node in m_Graph.addedNodes) 797 { 798 AddNode(node, lookupTable: lookupTable); 799 } 800 801 foreach (var groupChange in m_Graph.parentGroupChanges) 802 { 803 GraphElement graphElement = null; 804 if (groupChange.groupItem is AbstractMaterialNode node) 805 { 806 lookupTable.TryGetValue(node, out graphElement); 807 } 808 else if (groupChange.groupItem is StickyNoteData stickyNote) 809 { 810 lookupTable.TryGetValue(stickyNote, out graphElement); 811 } 812 else 813 { 814 throw new InvalidOperationException("Unknown group item type."); 815 } 816 817 if (graphElement != null) 818 { 819 var groupView = graphElement.GetContainingScope() as ShaderGroup; 820 if (groupView?.userData != groupChange.newGroup) 821 { 822 groupView?.RemoveElement(graphElement); 823 if (groupChange.newGroup != null) 824 { 825 lookupTable.TryGetValue(groupChange.newGroup, out var newGroupView); 826 ((ShaderGroup)newGroupView).AddElement(graphElement); 827 } 828 } 829 } 830 } 831 832 foreach (var groupData in m_Graph.pastedGroups) 833 { 834 if (lookupTable.TryGetValue(groupData, out var group)) 835 m_GraphView.AddToSelection(group); 836 } 837 838 foreach (var stickyNoteData in m_Graph.pastedStickyNotes) 839 { 840 if (lookupTable.TryGetValue(stickyNoteData, out var stickyNote)) 841 m_GraphView.AddToSelection(stickyNote); 842 } 843 844 foreach (var node in m_Graph.pastedNodes) 845 { 846 if (lookupTable.TryGetValue(node.objectId, out var nodeView) && nodeView is IShaderNodeView) 847 m_GraphView.AddToSelection((Node)nodeView); 848 } 849 850 foreach (var shaderGroup in m_GroupHashSet) 851 { 852 SetGroupPosition(shaderGroup); 853 } 854 855 var nodesToUpdate = m_NodeViewHashSet; 856 nodesToUpdate.Clear(); 857 858 foreach (var edge in m_Graph.removedEdges) 859 { 860 if (lookupTable.TryGetValue(edge, out var obj) && obj is Edge edgeView) 861 { 862 var nodeView = (IShaderNodeView)edgeView.input.node; 863 if (nodeView?.node != null) 864 { 865 nodesToUpdate.Add(nodeView); 866 } 867 868 edgeView.output.Disconnect(edgeView); 869 edgeView.input.Disconnect(edgeView); 870 871 edgeView.output = null; 872 edgeView.input = null; 873 874 m_GraphView.RemoveElement(edgeView); 875 } 876 } 877 878 foreach (var edge in m_Graph.addedEdges) 879 { 880 var edgeView = AddEdge(edge, lookupTable: lookupTable); 881 if (edgeView != null) 882 nodesToUpdate.Add((IShaderNodeView)edgeView.input.node); 883 } 884 885 foreach (var node in nodesToUpdate) 886 { 887 node.OnModified(ModificationScope.Topological); 888 } 889 890 UpdateEdgeColors(nodesToUpdate); 891 892 if (m_Graph.movedContexts) 893 { 894 foreach (var context in m_GraphView.contexts) 895 { 896 context.SetPosition(new Rect(context.contextData.position, Vector2.zero)); 897 } 898 } 899 900 // Checking if any new Group Nodes just got added 901 if (m_Graph.mostRecentlyCreatedGroup != null) 902 { 903 var groups = m_GraphView.graphElements.ToList().OfType<ShaderGroup>(); 904 foreach (ShaderGroup shaderGroup in groups) 905 { 906 if (shaderGroup.userData == m_Graph.mostRecentlyCreatedGroup) 907 { 908 shaderGroup.FocusTitleTextField(); 909 break; 910 } 911 } 912 } 913 914 // If we auto-remove blocks and something has happened to trigger a check (don't re-check constantly) 915 if (m_Graph.checkAutoAddRemoveBlocks && ShaderGraphPreferences.autoAddRemoveBlocks) 916 { 917 var activeBlocks = m_Graph.GetActiveBlocksForAllActiveTargets(); 918 m_Graph.AddRemoveBlocksFromActiveList(activeBlocks); 919 m_Graph.checkAutoAddRemoveBlocks = false; 920 // We have to re-check any nodes views that need to be removed since we already handled this above. After leaving this function the states on m_Graph will be cleared so we'll lose track of removed blocks. 921 HandleRemovedNodes(lookupTable); 922 } 923 924 UpdateBadges(); 925 926 RegisterGraphViewCallbacks(); 927 } 928 929 void HandleRemovedNodes(Dictionary<object, GraphElement> lookupTable = null) 930 { 931 foreach (var node in m_Graph.removedNodes) 932 { 933 node.UnregisterCallback(OnNodeChanged); 934 IShaderNodeView nodeView = null; 935 if (lookupTable != null && lookupTable.TryGetValue(node, out var nodeElement)) 936 { 937 nodeView = nodeElement as IShaderNodeView; 938 } 939 else 940 { 941 nodeView = m_GraphView.nodes.ToList().OfType<IShaderNodeView>().FirstOrDefault(p => p.node != null && p.node == node); 942 } 943 944 // When deleting a node make sure to clear any input observers 945 switch (node) 946 { 947 case PropertyNode propertyNode: 948 propertyNode.property.RemoveObserver(propertyNode); 949 propertyNode.property.RemoveObserver(nodeView as IShaderInputObserver); 950 break; 951 case KeywordNode keywordNode: 952 keywordNode.keyword.RemoveObserver(keywordNode); 953 break; 954 case DropdownNode dropdownNode: 955 dropdownNode.dropdown.RemoveObserver(dropdownNode); 956 break; 957 } 958 959 if (nodeView != null) 960 { 961 nodeView.Dispose(); 962 963 if (node is BlockNode blockNode) 964 { 965 var context = m_GraphView.GetContext(blockNode.contextData); 966 // blocknode may be floating and not actually in the stacknode's visual hierarchy. 967 if (context.Contains(nodeView as Node)) 968 { 969 context.RemoveElement(nodeView as Node); 970 } 971 else 972 { 973 m_GraphView.RemoveElement((Node)nodeView); 974 } 975 } 976 else 977 { 978 m_GraphView.RemoveElement((Node)nodeView); 979 } 980 981 if (node.group != null) 982 { 983 if (lookupTable.TryGetValue(node.group, out var shaderGroup)) 984 m_GroupHashSet.Add((ShaderGroup)shaderGroup); 985 } 986 } 987 } 988 } 989 990 void UpdateBadges() 991 { 992 if (!m_MessageManager.nodeMessagesChanged) 993 return; 994 995 foreach (var messageData in m_MessageManager.GetNodeMessages()) 996 { 997 var node = m_Graph.GetNodeFromId(messageData.Key); 998 999 if (node == null || !(m_GraphView.GetNodeByGuid(node.objectId) is IShaderNodeView nodeView)) 1000 continue; 1001 1002 if (messageData.Value.Count == 0) 1003 { 1004 nodeView.ClearMessage(); 1005 } 1006 else 1007 { 1008 var foundMessage = messageData.Value.First(); 1009 string messageString; 1010 if (foundMessage.line > 0) 1011 messageString = foundMessage.message + " at line " + foundMessage.line; 1012 else 1013 messageString = foundMessage.message; 1014 nodeView.AttachMessage(messageString, foundMessage.severity); 1015 } 1016 } 1017 } 1018 1019 List<GraphElement> m_GraphElementsTemp = new List<GraphElement>(); 1020 1021 void AddNode(AbstractMaterialNode node, bool usePrebuiltVisualGroupMap = false, Dictionary<object, GraphElement> lookupTable = null) 1022 { 1023 var materialNode = node; 1024 Node nodeView; 1025 if (node is PropertyNode propertyNode) 1026 { 1027 var tokenNode = new PropertyNodeView(propertyNode, m_EdgeConnectorListener); 1028 m_GraphView.AddElement(tokenNode); 1029 nodeView = tokenNode; 1030 1031 // Register node model and node view as observer of property 1032 propertyNode.property.AddObserver(propertyNode); 1033 propertyNode.property.AddObserver(tokenNode); 1034 } 1035 else if (node is BlockNode blockNode) 1036 { 1037 var blockNodeView = new MaterialNodeView { userData = blockNode }; 1038 blockNodeView.Initialize(blockNode, m_PreviewManager, m_EdgeConnectorListener, graphView); 1039 blockNodeView.MarkDirtyRepaint(); 1040 nodeView = blockNodeView; 1041 1042 var context = m_GraphView.GetContext(blockNode.contextData); 1043 context.InsertBlock(blockNodeView); 1044 } 1045 else if (node is RedirectNodeData redirectNodeData) 1046 { 1047 var redirectNodeView = new RedirectNodeView { userData = redirectNodeData }; 1048 m_GraphView.AddElement(redirectNodeView); 1049 redirectNodeView.ConnectToData(materialNode, m_EdgeConnectorListener); 1050 nodeView = redirectNodeView; 1051 } 1052 else 1053 { 1054 var materialNodeView = new MaterialNodeView { userData = materialNode }; 1055 1056 // For keywords and dropdowns, we only register the node model itself as an observer, 1057 // the material node view redraws completely on changes so it doesn't need to be an observer 1058 switch (node) 1059 { 1060 case KeywordNode keywordNode: 1061 keywordNode.keyword.AddObserver(keywordNode); 1062 break; 1063 case DropdownNode dropdownNode: 1064 dropdownNode.dropdown.AddObserver(dropdownNode); 1065 break; 1066 } 1067 1068 m_GraphView.AddElement(materialNodeView); 1069 materialNodeView.Initialize(materialNode, m_PreviewManager, m_EdgeConnectorListener, graphView); 1070 m_ColorManager.UpdateNodeView(materialNodeView); 1071 nodeView = materialNodeView; 1072 } 1073 1074 node.RegisterCallback(OnNodeChanged); 1075 nodeView.MarkDirtyRepaint(); 1076 1077 if (m_SearchWindowProvider.nodeNeedsRepositioning && 1078 m_SearchWindowProvider.targetSlotReference.node == node) 1079 { 1080 m_SearchWindowProvider.nodeNeedsRepositioning = false; 1081 if (nodeView is IShaderNodeView shaderView && 1082 shaderView.FindPort(m_SearchWindowProvider.targetSlotReference, out var port)) 1083 { 1084 port.RegisterCallback<GeometryChangedEvent>(RepositionNode); 1085 return; 1086 } 1087 } 1088 1089 if (materialNode.group != null) 1090 { 1091 if (usePrebuiltVisualGroupMap) 1092 { 1093 // cheaper way to add the node to groups it is in 1094 ShaderGroup groupView; 1095 visualGroupMap.TryGetValue(materialNode.group, out groupView); 1096 if (groupView != null) 1097 groupView.AddElement(nodeView); 1098 } 1099 else 1100 { 1101 // This should also work for sticky notes 1102 m_GraphElementsTemp.Clear(); 1103 m_GraphView.graphElements.ToList(m_GraphElementsTemp); 1104 1105 foreach (var element in m_GraphElementsTemp) 1106 { 1107 if (element is ShaderGroup groupView && groupView.userData == materialNode.group) 1108 { 1109 groupView.AddElement(nodeView); 1110 } 1111 } 1112 } 1113 } 1114 1115 lookupTable?.Add(node, nodeView); 1116 } 1117 1118 private static Dictionary<GroupData, ShaderGroup> visualGroupMap = new Dictionary<GroupData, ShaderGroup>(); 1119 private static void AddToVisualGroupMap(GraphElement e) 1120 { 1121 if (e is ShaderGroup sg) 1122 { 1123 visualGroupMap.Add(sg.userData, sg); 1124 } 1125 } 1126 1127 private static Action<GraphElement> AddToVisualGroupMapAction = AddToVisualGroupMap; 1128 void BuildVisualGroupMap() 1129 { 1130 visualGroupMap.Clear(); 1131 m_GraphView.graphElements.ForEach(AddToVisualGroupMapAction); 1132 } 1133 1134 private static readonly ProfilerMarker AddNodesMarker = new ProfilerMarker("AddNodes"); 1135 void AddNodes(IEnumerable<AbstractMaterialNode> nodes) 1136 { 1137 using (AddNodesMarker.Auto()) 1138 { 1139 BuildVisualGroupMap(); 1140 foreach (var node in nodes) 1141 { 1142 // Skip BlockNodes as we need to order them 1143 if (node is BlockNode) 1144 continue; 1145 1146 AddNode(node, true); 1147 } 1148 visualGroupMap.Clear(); 1149 } 1150 } 1151 1152 private static readonly ProfilerMarker AddBlocksMarker = new ProfilerMarker("AddBlocks"); 1153 void AddBlocks(IEnumerable<BlockNode> blocks) 1154 { 1155 using (AddBlocksMarker.Auto()) 1156 { 1157 // As they can be reordered, we cannot be sure BlockNodes are deserialized in the same order as their stack position 1158 // To handle this we reorder the BlockNodes here to avoid having to reorder them on the fly as they are added 1159 foreach (var node in blocks.OrderBy(s => s.index)) 1160 { 1161 AddNode(node); 1162 } 1163 } 1164 } 1165 1166 void AddGroup(GroupData groupData, Dictionary<object, GraphElement> lookupTable = null) 1167 { 1168 ShaderGroup graphGroup = new ShaderGroup(); 1169 1170 graphGroup.userData = groupData; 1171 graphGroup.title = groupData.title; 1172 graphGroup.SetPosition(new Rect(graphGroup.userData.position, Vector2.zero)); 1173 1174 m_GraphView.AddElement(graphGroup); 1175 lookupTable?.Add(groupData, graphGroup); 1176 } 1177 1178 void AddStickyNote(StickyNoteData stickyNoteData, Dictionary<object, GraphElement> lookupTable = null) 1179 { 1180 var stickyNote = new StickyNote(stickyNoteData.position, m_Graph); 1181 1182 stickyNote.userData = stickyNoteData; 1183 stickyNote.viewDataKey = stickyNoteData.objectId; 1184 stickyNote.title = stickyNoteData.title; 1185 stickyNote.contents = stickyNoteData.content; 1186 stickyNote.textSize = (StickyNote.TextSize)stickyNoteData.textSize; 1187 stickyNote.theme = (StickyNote.Theme)stickyNoteData.theme; 1188 stickyNote.userData.group = stickyNoteData.group; 1189 stickyNote.SetPosition(new Rect(stickyNote.userData.position)); 1190 1191 m_GraphView.AddElement(stickyNote); 1192 lookupTable?.Add(stickyNoteData, stickyNote); 1193 1194 // Add Sticky Note to group 1195 m_GraphElementsTemp.Clear(); 1196 m_GraphView.graphElements.ToList(m_GraphElementsTemp); 1197 1198 if (stickyNoteData.group != null) 1199 { 1200 foreach (var element in m_GraphElementsTemp) 1201 { 1202 if (element is ShaderGroup groupView && groupView.userData == stickyNoteData.group) 1203 { 1204 groupView.AddElement(stickyNote); 1205 } 1206 } 1207 } 1208 } 1209 1210 static void RepositionNode(GeometryChangedEvent evt) 1211 { 1212 var port = evt.target as ShaderPort; 1213 if (port == null) 1214 return; 1215 port.UnregisterCallback<GeometryChangedEvent>(RepositionNode); 1216 var nodeView = port.node as IShaderNodeView; 1217 if (nodeView == null) 1218 return; 1219 var offset = nodeView.gvNode.mainContainer.WorldToLocal(port.GetGlobalCenter() + new Vector3(3f, 3f, 0f)); 1220 var position = nodeView.gvNode.GetPosition(); 1221 position.position -= offset; 1222 nodeView.gvNode.SetPosition(position); 1223 var drawState = nodeView.node.drawState; 1224 drawState.position = position; 1225 nodeView.node.drawState = drawState; 1226 nodeView.gvNode.MarkDirtyRepaint(); 1227 port.MarkDirtyRepaint(); 1228 } 1229 1230 private static Dictionary<AbstractMaterialNode, IShaderNodeView> visualNodeMap = new Dictionary<AbstractMaterialNode, IShaderNodeView>(); 1231 private static void AddToVisualNodeMap(Node n) 1232 { 1233 IShaderNodeView snv = n as IShaderNodeView; 1234 if (snv != null) 1235 visualNodeMap.Add(snv.node, snv); 1236 } 1237 1238 private static Action<Node> AddToVisualNodeMapAction = AddToVisualNodeMap; 1239 void BuildVisualNodeMap() 1240 { 1241 visualNodeMap.Clear(); 1242 m_GraphView.nodes.ForEach(AddToVisualNodeMapAction); 1243 } 1244 1245 private static readonly ProfilerMarker AddEdgesMarker = new ProfilerMarker("AddEdges"); 1246 void AddEdges(IEnumerable<IEdge> edges) 1247 { 1248 using (AddEdgesMarker.Auto()) 1249 { 1250 // fast way 1251 BuildVisualNodeMap(); 1252 foreach (IEdge edge in edges) 1253 { 1254 AddEdge(edge, true, false); 1255 } 1256 1257 // apply the port update on every node 1258 foreach (IShaderNodeView nodeView in visualNodeMap.Values) 1259 { 1260 nodeView.gvNode.RefreshPorts(); 1261 nodeView.UpdatePortInputTypes(); 1262 } 1263 1264 // cleanup temp data 1265 visualNodeMap.Clear(); 1266 } 1267 } 1268 1269 Edge AddEdge(IEdge edge, bool useVisualNodeMap = false, bool updateNodePorts = true, Dictionary<object, GraphElement> lookupTable = null) 1270 { 1271 var sourceNode = edge.outputSlot.node; 1272 if (sourceNode == null) 1273 { 1274 Debug.LogWarning("Source node is null"); 1275 return null; 1276 } 1277 var sourceSlot = sourceNode.FindOutputSlot<MaterialSlot>(edge.outputSlot.slotId); 1278 1279 var targetNode = edge.inputSlot.node; 1280 if (targetNode == null) 1281 { 1282 Debug.LogWarning("Target node is null"); 1283 return null; 1284 } 1285 var targetSlot = targetNode.FindInputSlot<MaterialSlot>(edge.inputSlot.slotId); 1286 1287 IShaderNodeView sourceNodeView = null; 1288 if (lookupTable != null) 1289 { 1290 lookupTable.TryGetValue(sourceNode, out var graphElement); 1291 sourceNodeView = (IShaderNodeView)graphElement; 1292 } 1293 else if (useVisualNodeMap) 1294 visualNodeMap.TryGetValue(sourceNode, out sourceNodeView); 1295 1296 if (sourceNodeView == null) 1297 sourceNodeView = m_GraphView.nodes.ToList().OfType<IShaderNodeView>().FirstOrDefault(x => x.node == sourceNode); 1298 1299 if (sourceNodeView != null) 1300 { 1301 sourceNodeView.FindPort(sourceSlot.slotReference, out var sourceAnchor); 1302 1303 IShaderNodeView targetNodeView = null; 1304 if (lookupTable != null) 1305 { 1306 lookupTable.TryGetValue(targetNode, out var graphElement); 1307 targetNodeView = (IShaderNodeView)graphElement; 1308 } 1309 else if (useVisualNodeMap) 1310 visualNodeMap.TryGetValue(targetNode, out targetNodeView); 1311 1312 if (targetNodeView == null) 1313 targetNodeView = m_GraphView.nodes.ToList().OfType<IShaderNodeView>().First(x => x.node == targetNode); 1314 1315 targetNodeView.FindPort(targetSlot.slotReference, out var targetAnchor); 1316 1317 var edgeView = new Edge 1318 { 1319 userData = edge, 1320 output = sourceAnchor, 1321 input = targetAnchor 1322 }; 1323 1324 edgeView.RegisterCallback<MouseDownEvent>(OnMouseDown); 1325 edgeView.output.Connect(edgeView); 1326 edgeView.input.Connect(edgeView); 1327 m_GraphView.AddElement(edgeView); 1328 1329 if (updateNodePorts) 1330 { 1331 sourceNodeView.gvNode.RefreshPorts(); 1332 targetNodeView.gvNode.RefreshPorts(); 1333 sourceNodeView.UpdatePortInputTypes(); 1334 targetNodeView.UpdatePortInputTypes(); 1335 } 1336 1337 return edgeView; 1338 } 1339 1340 return null; 1341 } 1342 1343 void OnMouseDown(MouseDownEvent evt) 1344 { 1345 if (evt.button == (int)MouseButton.LeftMouse && evt.clickCount == 2) 1346 { 1347 if (evt.target is Edge edgeTarget) 1348 { 1349 Vector2 pos = evt.mousePosition; 1350 m_GraphView.CreateRedirectNode(pos, edgeTarget); 1351 } 1352 } 1353 } 1354 1355 Stack<Node> m_NodeStack = new Stack<Node>(); 1356 string m_AssetName; 1357 1358 void UpdateEdgeColors(HashSet<IShaderNodeView> nodeViews) 1359 { 1360 var nodeStack = m_NodeStack; 1361 nodeStack.Clear(); 1362 foreach (var nodeView in nodeViews) 1363 nodeStack.Push((Node)nodeView); 1364 PooledList<Edge> edgesToUpdate = PooledList<Edge>.Get(); 1365 while (nodeStack.Any()) 1366 { 1367 var nodeView = nodeStack.Pop(); 1368 if (nodeView is IShaderNodeView shaderNodeView) 1369 { 1370 shaderNodeView.UpdatePortInputTypes(); 1371 } 1372 1373 foreach (var anchorView in nodeView.outputContainer.Children().OfType<Port>()) 1374 { 1375 foreach (var edgeView in anchorView.connections) 1376 { 1377 //update edges based on the active state of any modified nodes 1378 if (edgeView.input.node is MaterialNodeView inputNode && edgeView.output.node is MaterialNodeView outputNode) 1379 { 1380 //force redraw on update to prevent visual lag in the graph 1381 //Now has to be delayed a frame because setting port styles wont update colors till next frame 1382 edgesToUpdate.Add(edgeView); 1383 } 1384 //update edges based on dynamic vector length of any modified nodes 1385 var targetSlot = edgeView.input.GetSlot(); 1386 if (targetSlot.valueType == SlotValueType.DynamicVector || targetSlot.valueType == SlotValueType.DynamicMatrix || targetSlot.valueType == SlotValueType.Dynamic) 1387 { 1388 var connectedNodeView = edgeView.input.node; 1389 if (connectedNodeView != null && !nodeViews.Contains((IShaderNodeView)connectedNodeView)) 1390 { 1391 nodeStack.Push(connectedNodeView); 1392 nodeViews.Add((IShaderNodeView)connectedNodeView); 1393 } 1394 } 1395 } 1396 } 1397 1398 foreach (var anchorView in nodeView.inputContainer.Query<Port>().ToList()) 1399 { 1400 var targetSlot = anchorView.GetSlot(); 1401 if (targetSlot.valueType != SlotValueType.DynamicVector) 1402 continue; 1403 foreach (var edgeView in anchorView.connections) 1404 { 1405 //update edges based on the active state of any modified nodes 1406 if (edgeView.input.node is MaterialNodeView inputNode && edgeView.output.node is MaterialNodeView outputNode) 1407 { 1408 //force redraw on update to prevent visual lag in the graph 1409 //Now has to be delayed a frame because setting port styles wont update colors till next frame 1410 edgesToUpdate.Add(edgeView); 1411 } 1412 //update edge color for upstream dynamic vector types 1413 var connectedNodeView = edgeView.output.node; 1414 if (connectedNodeView != null && !nodeViews.Contains((IShaderNodeView)connectedNodeView)) 1415 { 1416 nodeStack.Push(connectedNodeView); 1417 nodeViews.Add((IShaderNodeView)connectedNodeView); 1418 } 1419 } 1420 } 1421 } 1422 schedule.Execute(() => 1423 { 1424 foreach (Edge e in edgesToUpdate) 1425 { 1426 e.UpdateEdgeControl(); 1427 } 1428 edgesToUpdate.Dispose(); 1429 }).StartingIn(0); 1430 } 1431 1432 void ApplySerializedWindowLayouts(GeometryChangedEvent evt) 1433 { 1434 UnregisterCallback<GeometryChangedEvent>(ApplySerializedWindowLayouts); 1435 1436 ApplyMasterPreviewLayout(); 1437 1438 m_BlackboardController.blackboard.DeserializeLayout(); 1439 1440 m_InspectorView.DeserializeLayout(); 1441 } 1442 1443 void ApplyMasterPreviewLayout() 1444 { 1445 // If a preview size was loaded in from saved user settings use that 1446 if (m_FloatingWindowsLayout.previewLayout.size.x > 0f && m_FloatingWindowsLayout.previewLayout.size.y > 0f) 1447 { 1448 previewManager.ResizeMasterPreview(m_FloatingWindowsLayout.previewLayout.size); 1449 } 1450 else // Use default specified in the stylesheet for master preview 1451 { 1452 m_FloatingWindowsLayout.previewLayout.size = m_MasterPreviewView.layout.size; 1453 } 1454 1455 m_FloatingWindowsLayout.previewLayout.ApplyPosition(m_MasterPreviewView); 1456 1457 m_MasterPreviewView.style.width = m_FloatingWindowsLayout.previewLayout.size.x; 1458 m_MasterPreviewView.style.height = m_FloatingWindowsLayout.previewLayout.size.y; 1459 m_MasterPreviewView.RegisterCallback<GeometryChangedEvent>(SerializeMasterPreviewLayout); 1460 } 1461 1462 void SerializeMasterPreviewLayout(GeometryChangedEvent evt) 1463 { 1464 UpdateSerializedWindowLayout(); 1465 } 1466 1467 void UpdateSerializedWindowLayout() 1468 { 1469 m_FloatingWindowsLayout.previewLayout.CalculateDockingCornerAndOffset(m_MasterPreviewView.layout, m_GraphView.layout); 1470 m_FloatingWindowsLayout.previewLayout.ClampToParentWindow(); 1471 1472 blackboardController.blackboard.ClampToParentLayout(m_GraphView.layout); 1473 1474 m_InspectorView.ClampToParentLayout(m_GraphView.layout); 1475 1476 if (m_MasterPreviewView.visible) 1477 { 1478 m_FloatingWindowsLayout.previewLayout.size = m_MasterPreviewView.layout.size; 1479 } 1480 1481 string serializedWindowLayout = JsonUtility.ToJson(m_FloatingWindowsLayout); 1482 EditorUserSettings.SetConfigValue(k_FloatingWindowsLayoutKey, serializedWindowLayout); 1483 } 1484 1485 public void Dispose() 1486 { 1487 ShaderGraphPreferences.onZoomStepSizeChanged -= ResetZoom; 1488 if (m_GraphView != null) 1489 { 1490 saveRequested = null; 1491 saveAsRequested = null; 1492 convertToSubgraphRequested = null; 1493 showInProjectRequested = null; 1494 isCheckedOut = null; 1495 checkOut = null; 1496 foreach (var materialNodeView in m_GraphView.Query<MaterialNodeView>().ToList()) 1497 materialNodeView.Dispose(); 1498 foreach (var propertyNodeView in m_GraphView.Query<PropertyNodeView>().ToList()) 1499 propertyNodeView.Dispose(); 1500 foreach (var redirectNodeView in m_GraphView.Query<RedirectNodeView>().ToList()) 1501 redirectNodeView.Dispose(); 1502 foreach (var contextView in m_GraphView.Query<ContextView>().ToList()) 1503 contextView.Dispose(); 1504 foreach (var edge in m_GraphView.Query<Edge>().ToList()) 1505 { 1506 edge.output = null; 1507 edge.input = null; 1508 } 1509 1510 m_GraphView.nodeCreationRequest = null; 1511 m_GraphView = null; 1512 } 1513 1514 m_BlackboardController?.Dispose(); 1515 m_BlackboardController = null; 1516 1517 m_InspectorView?.Dispose(); 1518 m_InspectorView = null; 1519 1520 if (previewManager != null) 1521 { 1522 previewManager.Dispose(); 1523 previewManager = null; 1524 } 1525 1526 // Unload any static resources here 1527 Resources.UnloadAsset(ShaderPort.styleSheet); 1528 1529 if (m_SearchWindowProvider != null) 1530 { 1531 m_SearchWindowProvider.Dispose(); 1532 m_SearchWindowProvider = null; 1533 } 1534 } 1535 } 1536}