No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GraphEditorView.cs 62KB

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