MaterialNodeView.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using UnityEngine;
  6. using UnityEditor.Graphing;
  7. using UnityEditor.Graphing.Util;
  8. using UnityEditor.ShaderGraph.Drawing.Controls;
  9. using UnityEngine.Rendering;
  10. using UnityEditor.Experimental.GraphView;
  11. using UnityEditor.Rendering;
  12. using UnityEditor.ShaderGraph.Drawing.Colors;
  13. using UnityEditor.ShaderGraph.Internal;
  14. using UnityEngine.UIElements;
  15. using UnityEditor.UIElements;
  16. using Node = UnityEditor.Experimental.GraphView.Node;
  17. namespace UnityEditor.ShaderGraph.Drawing
  18. {
  19. sealed class MaterialNodeView : Node, IShaderNodeView
  20. {
  21. PreviewRenderData m_PreviewRenderData;
  22. Image m_PreviewImage;
  23. // Remove this after updated to the correct API call has landed in trunk. ------------
  24. VisualElement m_TitleContainer;
  25. new VisualElement m_ButtonContainer;
  26. VisualElement m_PreviewContainer;
  27. VisualElement m_ControlItems;
  28. VisualElement m_PreviewFiller;
  29. VisualElement m_ControlsDivider;
  30. IEdgeConnectorListener m_ConnectorListener;
  31. VisualElement m_PortInputContainer;
  32. VisualElement m_SettingsContainer;
  33. bool m_ShowSettings = false;
  34. VisualElement m_SettingsButton;
  35. VisualElement m_Settings;
  36. VisualElement m_NodeSettingsView;
  37. GraphView m_GraphView;
  38. public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener, GraphView graphView)
  39. {
  40. styleSheets.Add(Resources.Load<StyleSheet>("Styles/MaterialNodeView"));
  41. styleSheets.Add(Resources.Load<StyleSheet>($"Styles/ColorMode"));
  42. AddToClassList("MaterialNode");
  43. if (inNode == null)
  44. return;
  45. var contents = this.Q("contents");
  46. m_GraphView = graphView;
  47. m_ConnectorListener = connectorListener;
  48. node = inNode;
  49. viewDataKey = node.guid.ToString();
  50. UpdateTitle();
  51. // Add controls container
  52. var controlsContainer = new VisualElement { name = "controls" };
  53. {
  54. m_ControlsDivider = new VisualElement { name = "divider" };
  55. m_ControlsDivider.AddToClassList("horizontal");
  56. controlsContainer.Add(m_ControlsDivider);
  57. m_ControlItems = new VisualElement { name = "items" };
  58. controlsContainer.Add(m_ControlItems);
  59. // Instantiate control views from node
  60. foreach (var propertyInfo in node.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
  61. foreach (IControlAttribute attribute in propertyInfo.GetCustomAttributes(typeof(IControlAttribute), false))
  62. m_ControlItems.Add(attribute.InstantiateControl(node, propertyInfo));
  63. }
  64. if (m_ControlItems.childCount > 0)
  65. contents.Add(controlsContainer);
  66. // Node Base class toggles the 'expanded' variable already, this is on top of that call
  67. m_CollapseButton.RegisterCallback<MouseUpEvent>(SetNodeExpandedStateOnSelection);
  68. if (node.hasPreview)
  69. {
  70. // Add actual preview which floats on top of the node
  71. m_PreviewContainer = new VisualElement
  72. {
  73. name = "previewContainer",
  74. style = { overflow = Overflow.Hidden },
  75. pickingMode = PickingMode.Ignore
  76. };
  77. m_PreviewImage = new Image
  78. {
  79. name = "preview",
  80. pickingMode = PickingMode.Ignore,
  81. image = Texture2D.whiteTexture,
  82. };
  83. {
  84. // Add preview collapse button on top of preview
  85. var collapsePreviewButton = new VisualElement { name = "collapse" };
  86. collapsePreviewButton.Add(new VisualElement { name = "icon" });
  87. collapsePreviewButton.AddManipulator(new Clickable(() =>
  88. {
  89. node.owner.owner.RegisterCompleteObjectUndo("Collapse Preview");
  90. SetPreviewExpandedStateOnSelection(false);
  91. }));
  92. m_PreviewImage.Add(collapsePreviewButton);
  93. }
  94. m_PreviewContainer.Add(m_PreviewImage);
  95. // Hook up preview image to preview manager
  96. m_PreviewRenderData = previewManager.GetPreview(inNode);
  97. m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture;
  98. UpdatePreviewTexture();
  99. // Add fake preview which pads out the node to provide space for the floating preview
  100. m_PreviewFiller = new VisualElement { name = "previewFiller" };
  101. m_PreviewFiller.AddToClassList("expanded");
  102. {
  103. var previewDivider = new VisualElement { name = "divider" };
  104. previewDivider.AddToClassList("horizontal");
  105. m_PreviewFiller.Add(previewDivider);
  106. var expandPreviewButton = new VisualElement { name = "expand" };
  107. expandPreviewButton.Add(new VisualElement { name = "icon" });
  108. expandPreviewButton.AddManipulator(new Clickable(() =>
  109. {
  110. node.owner.owner.RegisterCompleteObjectUndo("Expand Preview");
  111. SetPreviewExpandedStateOnSelection(true);
  112. }));
  113. m_PreviewFiller.Add(expandPreviewButton);
  114. }
  115. contents.Add(m_PreviewFiller);
  116. UpdatePreviewExpandedState(node.previewExpanded);
  117. }
  118. // Add port input container, which acts as a pixel cache for all port inputs
  119. m_PortInputContainer = new VisualElement
  120. {
  121. name = "portInputContainer",
  122. style = { overflow = Overflow.Hidden },
  123. pickingMode = PickingMode.Ignore
  124. };
  125. Add(m_PortInputContainer);
  126. AddSlots(node.GetSlots<MaterialSlot>());
  127. UpdatePortInputs();
  128. base.expanded = node.drawState.expanded;
  129. RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here
  130. UpdatePortInputVisibilities();
  131. SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
  132. if (node is SubGraphNode)
  133. {
  134. RegisterCallback<MouseDownEvent>(OnSubGraphDoubleClick);
  135. }
  136. m_PortInputContainer.SendToBack();
  137. m_TitleContainer = this.Q("title");
  138. var masterNode = node as IMasterNode;
  139. if (masterNode != null)
  140. {
  141. AddToClassList("master");
  142. if (!masterNode.IsPipelineCompatible(GraphicsSettings.renderPipelineAsset))
  143. {
  144. AttachMessage("The current render pipeline is not compatible with this master node.", ShaderCompilerMessageSeverity.Error);
  145. }
  146. }
  147. m_NodeSettingsView = new NodeSettingsView();
  148. m_NodeSettingsView.visible = false;
  149. Add(m_NodeSettingsView);
  150. m_SettingsButton = new VisualElement {name = "settings-button"};
  151. m_SettingsButton.Add(new VisualElement { name = "icon" });
  152. m_Settings = new VisualElement();
  153. AddDefaultSettings();
  154. // Add Node type specific settings
  155. var nodeTypeSettings = node as IHasSettings;
  156. if (nodeTypeSettings != null)
  157. m_Settings.Add(nodeTypeSettings.CreateSettingsElement());
  158. // Add manipulators
  159. m_SettingsButton.AddManipulator(new Clickable(() =>
  160. {
  161. UpdateSettingsExpandedState();
  162. }));
  163. if(m_Settings.childCount > 0)
  164. {
  165. m_ButtonContainer = new VisualElement { name = "button-container" };
  166. m_ButtonContainer.style.flexDirection = FlexDirection.Row;
  167. m_ButtonContainer.Add(m_SettingsButton);
  168. m_ButtonContainer.Add(m_CollapseButton);
  169. m_TitleContainer.Add(m_ButtonContainer);
  170. }
  171. // Register OnMouseHover callbacks for node highlighting
  172. RegisterCallback<MouseEnterEvent>(OnMouseHover);
  173. RegisterCallback<MouseLeaveEvent>(OnMouseHover);
  174. }
  175. public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
  176. {
  177. ClearMessage();
  178. IconBadge badge;
  179. if (severity == ShaderCompilerMessageSeverity.Error)
  180. {
  181. badge = IconBadge.CreateError(errString);
  182. }
  183. else
  184. {
  185. badge = IconBadge.CreateComment(errString);
  186. }
  187. Add(badge);
  188. badge.AttachTo(m_TitleContainer, SpriteAlignment.RightCenter);
  189. }
  190. public void ClearMessage()
  191. {
  192. var badge = this.Q<IconBadge>();
  193. if(badge != null)
  194. {
  195. badge.Detach();
  196. badge.RemoveFromHierarchy();
  197. }
  198. }
  199. public VisualElement colorElement
  200. {
  201. get { return this; }
  202. }
  203. static readonly StyleColor noColor = new StyleColor(StyleKeyword.Null);
  204. public void SetColor(Color color)
  205. {
  206. m_TitleContainer.style.borderBottomColor = color;
  207. }
  208. public void ResetColor()
  209. {
  210. m_TitleContainer.style.borderBottomColor = noColor;
  211. }
  212. public Color GetColor()
  213. {
  214. return m_TitleContainer.resolvedStyle.borderBottomColor;
  215. }
  216. void OnGeometryChanged(GeometryChangedEvent evt)
  217. {
  218. // style.positionTop and style.positionLeft are in relation to the parent,
  219. // so we translate the layout of the settings button to be in the coordinate
  220. // space of the settings view's parent.
  221. var settingsButtonLayout = m_SettingsButton.ChangeCoordinatesTo(m_NodeSettingsView.parent, m_SettingsButton.layout);
  222. m_NodeSettingsView.style.top = settingsButtonLayout.yMax - 18f;
  223. m_NodeSettingsView.style.left = settingsButtonLayout.xMin - 16f;
  224. }
  225. void OnSubGraphDoubleClick(MouseDownEvent evt)
  226. {
  227. if (evt.clickCount == 2 && evt.button == 0)
  228. {
  229. SubGraphNode subgraphNode = node as SubGraphNode;
  230. var path = AssetDatabase.GUIDToAssetPath(subgraphNode.subGraphGuid);
  231. ShaderGraphImporterEditor.ShowGraphEditWindow(path);
  232. }
  233. }
  234. public Node gvNode => this;
  235. public AbstractMaterialNode node { get; private set; }
  236. public override bool expanded
  237. {
  238. get { return base.expanded; }
  239. set
  240. {
  241. if (base.expanded != value)
  242. base.expanded = value;
  243. if (node.drawState.expanded != value)
  244. {
  245. var ds = node.drawState;
  246. ds.expanded = value;
  247. node.drawState = ds;
  248. }
  249. RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here
  250. UpdatePortInputVisibilities();
  251. }
  252. }
  253. public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
  254. {
  255. if (evt.target is Node)
  256. {
  257. var isMaster = node is IMasterNode;
  258. var isActive = node.guid == node.owner.activeOutputNodeGuid;
  259. if (isMaster)
  260. {
  261. evt.menu.AppendAction("Set Active", SetMasterAsActive,
  262. _ => isActive ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
  263. }
  264. var canViewShader = node.hasPreview || node is IMasterNode || node is SubGraphOutputNode;
  265. evt.menu.AppendAction("Copy Shader", CopyToClipboard,
  266. _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
  267. GenerationMode.ForReals);
  268. evt.menu.AppendAction("Show Generated Code", ShowGeneratedCode,
  269. _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
  270. GenerationMode.ForReals);
  271. if (Unsupported.IsDeveloperMode())
  272. {
  273. evt.menu.AppendAction("Show Preview Code", ShowGeneratedCode,
  274. _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
  275. GenerationMode.Preview);
  276. }
  277. }
  278. base.BuildContextualMenu(evt);
  279. }
  280. void SetMasterAsActive(DropdownMenuAction action)
  281. {
  282. node.owner.activeOutputNodeGuid = node.guid;
  283. }
  284. void CopyToClipboard(DropdownMenuAction action)
  285. {
  286. GUIUtility.systemCopyBuffer = ConvertToShader((GenerationMode) action.userData);
  287. }
  288. public string SanitizeName(string name)
  289. {
  290. return new string(name.Where(c => !Char.IsWhiteSpace(c)).ToArray());
  291. }
  292. public void ShowGeneratedCode(DropdownMenuAction action)
  293. {
  294. string name = GetFirstAncestorOfType<GraphEditorView>().assetName;
  295. var mode = (GenerationMode)action.userData;
  296. string path = String.Format("Temp/GeneratedFromGraph-{0}-{1}-{2}{3}.shader", SanitizeName(name),
  297. SanitizeName(node.name), node.guid, mode == GenerationMode.Preview ? "-Preview" : "");
  298. if (GraphUtil.WriteToFile(path, ConvertToShader(mode)))
  299. GraphUtil.OpenFile(path);
  300. }
  301. string ConvertToShader(GenerationMode mode)
  302. {
  303. List<PropertyCollector.TextureInfo> textureInfo;
  304. if (node is IMasterNode masterNode)
  305. return masterNode.GetShader(mode, node.name, out textureInfo);
  306. return node.owner.GetShader(node, mode, node.name).shader;
  307. }
  308. void AddDefaultSettings()
  309. {
  310. PropertySheet ps = new PropertySheet();
  311. bool hasDefaultSettings = false;
  312. if(node.canSetPrecision)
  313. {
  314. hasDefaultSettings = true;
  315. ps.Add(new PropertyRow(new Label("Precision")), (row) =>
  316. {
  317. row.Add(new EnumField(node.precision), (field) =>
  318. {
  319. field.RegisterValueChangedCallback(evt =>
  320. {
  321. if (evt.newValue.Equals(node.precision))
  322. return;
  323. var editorView = GetFirstAncestorOfType<GraphEditorView>();
  324. var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
  325. editorView.colorManager.SetNodesDirty(nodeList);
  326. node.owner.owner.RegisterCompleteObjectUndo("Change precision");
  327. node.precision = (Precision)evt.newValue;
  328. node.owner.ValidateGraph();
  329. editorView.colorManager.UpdateNodeViews(nodeList);
  330. node.Dirty(ModificationScope.Graph);
  331. });
  332. });
  333. });
  334. }
  335. if(hasDefaultSettings)
  336. m_Settings.Add(ps);
  337. }
  338. void RecreateSettings()
  339. {
  340. m_Settings.RemoveFromHierarchy();
  341. m_Settings = new PropertySheet();
  342. // Add default settings
  343. AddDefaultSettings();
  344. // Add Node type specific settings
  345. var nodeTypeSettings = node as IHasSettings;
  346. if (nodeTypeSettings != null)
  347. m_Settings.Add(nodeTypeSettings.CreateSettingsElement());
  348. m_NodeSettingsView.Add(m_Settings);
  349. }
  350. void UpdateSettingsExpandedState()
  351. {
  352. m_ShowSettings = !m_ShowSettings;
  353. if (m_ShowSettings)
  354. {
  355. m_NodeSettingsView.Add(m_Settings);
  356. m_NodeSettingsView.visible = true;
  357. SetSelfSelected();
  358. m_SettingsButton.AddToClassList("clicked");
  359. RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
  360. OnGeometryChanged(null);
  361. }
  362. else
  363. {
  364. m_Settings.RemoveFromHierarchy();
  365. SetSelfSelected();
  366. m_NodeSettingsView.visible = false;
  367. m_SettingsButton.RemoveFromClassList("clicked");
  368. UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
  369. }
  370. }
  371. private void SetSelfSelected()
  372. {
  373. m_GraphView.ClearSelection();
  374. m_GraphView.AddToSelection(this);
  375. }
  376. void SetNodeExpandedStateOnSelection(MouseUpEvent evt)
  377. {
  378. if (!selected)
  379. SetSelfSelected();
  380. else
  381. {
  382. if (m_GraphView is MaterialGraphView)
  383. {
  384. var matGraphView = m_GraphView as MaterialGraphView;
  385. matGraphView.SetNodeExpandedOnSelection(expanded);
  386. }
  387. }
  388. }
  389. void SetPreviewExpandedStateOnSelection(bool state)
  390. {
  391. if (!selected)
  392. {
  393. SetSelfSelected();
  394. UpdatePreviewExpandedState(state);
  395. }
  396. else
  397. {
  398. if(m_GraphView is MaterialGraphView)
  399. {
  400. var matGraphView = m_GraphView as MaterialGraphView;
  401. matGraphView.SetPreviewExpandedOnSelection(state);
  402. }
  403. }
  404. }
  405. public bool CanToggleExpanded()
  406. {
  407. return m_CollapseButton.enabledInHierarchy;
  408. }
  409. void UpdatePreviewExpandedState(bool expanded)
  410. {
  411. node.previewExpanded = expanded;
  412. if (m_PreviewFiller == null)
  413. return;
  414. if (expanded)
  415. {
  416. if (m_PreviewContainer.parent != this)
  417. {
  418. Add(m_PreviewContainer);
  419. m_PreviewContainer.PlaceBehind(this.Q("selection-border"));
  420. }
  421. m_PreviewFiller.AddToClassList("expanded");
  422. m_PreviewFiller.RemoveFromClassList("collapsed");
  423. }
  424. else
  425. {
  426. if (m_PreviewContainer.parent == m_PreviewFiller)
  427. {
  428. m_PreviewContainer.RemoveFromHierarchy();
  429. }
  430. m_PreviewFiller.RemoveFromClassList("expanded");
  431. m_PreviewFiller.AddToClassList("collapsed");
  432. }
  433. UpdatePreviewTexture();
  434. }
  435. void UpdateTitle()
  436. {
  437. if (node is SubGraphNode subGraphNode && subGraphNode.asset != null)
  438. title = subGraphNode.asset.name;
  439. else
  440. title = node.name;
  441. }
  442. public void OnModified(ModificationScope scope)
  443. {
  444. UpdateTitle();
  445. if (node.hasPreview)
  446. UpdatePreviewExpandedState(node.previewExpanded);
  447. base.expanded = node.drawState.expanded;
  448. // Update slots to match node modification
  449. if (scope == ModificationScope.Topological)
  450. {
  451. RecreateSettings();
  452. var slots = node.GetSlots<MaterialSlot>().ToList();
  453. var inputPorts = inputContainer.Children().OfType<ShaderPort>().ToList();
  454. foreach (var port in inputPorts)
  455. {
  456. var currentSlot = port.slot;
  457. var newSlot = slots.FirstOrDefault(s => s.id == currentSlot.id);
  458. if (newSlot == null)
  459. {
  460. // Slot doesn't exist anymore, remove it
  461. inputContainer.Remove(port);
  462. // We also need to remove the inline input
  463. var portInputView = m_PortInputContainer.Children().OfType<PortInputView>().FirstOrDefault(v => Equals(v.slot, port.slot));
  464. if (portInputView != null)
  465. portInputView.RemoveFromHierarchy();
  466. }
  467. else
  468. {
  469. port.slot = newSlot;
  470. var portInputView = m_PortInputContainer.Children().OfType<PortInputView>().FirstOrDefault(x => x.slot.id == currentSlot.id);
  471. if (newSlot.isConnected)
  472. {
  473. portInputView?.RemoveFromHierarchy();
  474. }
  475. else
  476. {
  477. portInputView?.UpdateSlot(newSlot);
  478. }
  479. slots.Remove(newSlot);
  480. }
  481. }
  482. var outputPorts = outputContainer.Children().OfType<ShaderPort>().ToList();
  483. foreach (var port in outputPorts)
  484. {
  485. var currentSlot = port.slot;
  486. var newSlot = slots.FirstOrDefault(s => s.id == currentSlot.id);
  487. if (newSlot == null)
  488. {
  489. outputContainer.Remove(port);
  490. }
  491. else
  492. {
  493. port.slot = newSlot;
  494. slots.Remove(newSlot);
  495. }
  496. }
  497. AddSlots(slots);
  498. slots.Clear();
  499. slots.AddRange(node.GetSlots<MaterialSlot>());
  500. if (inputContainer.childCount > 0)
  501. inputContainer.Sort((x, y) => slots.IndexOf(((ShaderPort)x).slot) - slots.IndexOf(((ShaderPort)y).slot));
  502. if (outputContainer.childCount > 0)
  503. outputContainer.Sort((x, y) => slots.IndexOf(((ShaderPort)x).slot) - slots.IndexOf(((ShaderPort)y).slot));
  504. UpdatePortInputs();
  505. UpdatePortInputVisibilities();
  506. }
  507. RefreshExpandedState(); //This should not be needed. GraphView needs to improve the extension api here
  508. foreach (var listener in m_ControlItems.Children().OfType<AbstractMaterialNodeModificationListener>())
  509. {
  510. if (listener != null)
  511. listener.OnNodeModified(scope);
  512. }
  513. }
  514. void AddSlots(IEnumerable<MaterialSlot> slots)
  515. {
  516. foreach (var slot in slots)
  517. {
  518. if (slot.hidden)
  519. continue;
  520. var port = ShaderPort.Create(slot, m_ConnectorListener);
  521. if (slot.isOutputSlot)
  522. outputContainer.Add(port);
  523. else
  524. inputContainer.Add(port);
  525. }
  526. }
  527. void UpdatePortInputs()
  528. {
  529. foreach (var port in inputContainer.Children().OfType<ShaderPort>())
  530. {
  531. if (port.slot.isConnected)
  532. {
  533. continue;
  534. }
  535. var portInputView = m_PortInputContainer.Children().OfType<PortInputView>().FirstOrDefault(a => Equals(a.slot, port.slot));
  536. if (portInputView == null)
  537. {
  538. portInputView = new PortInputView(port.slot) { style = { position = Position.Absolute } };
  539. m_PortInputContainer.Add(portInputView);
  540. SetPortInputPosition(port, portInputView);
  541. }
  542. port.RegisterCallback<GeometryChangedEvent>(UpdatePortInput);
  543. }
  544. }
  545. void UpdatePortInput(GeometryChangedEvent evt)
  546. {
  547. var port = (ShaderPort)evt.target;
  548. var inputViews = m_PortInputContainer.Children().OfType<PortInputView>().Where(x => Equals(x.slot, port.slot));
  549. // Ensure PortInputViews are initialized correctly
  550. // Dynamic port lists require one update to validate before init
  551. if(inputViews.Count() != 0)
  552. {
  553. var inputView = inputViews.First();
  554. SetPortInputPosition(port, inputView);
  555. }
  556. port.UnregisterCallback<GeometryChangedEvent>(UpdatePortInput);
  557. }
  558. void SetPortInputPosition(ShaderPort port, PortInputView inputView)
  559. {
  560. inputView.style.top = port.layout.y;
  561. inputView.parent.style.height = inputContainer.layout.height;
  562. }
  563. void UpdatePortInputVisibilities()
  564. {
  565. if (expanded)
  566. {
  567. m_PortInputContainer.style.display = StyleKeyword.Null;
  568. }
  569. else
  570. {
  571. m_PortInputContainer.style.display = DisplayStyle.None;
  572. }
  573. }
  574. public void UpdatePortInputTypes()
  575. {
  576. foreach (var anchor in inputContainer.Children().Concat(outputContainer.Children()).OfType<ShaderPort>())
  577. {
  578. var slot = anchor.slot;
  579. anchor.portName = slot.displayName;
  580. anchor.visualClass = slot.concreteValueType.ToClassName();
  581. }
  582. foreach (var portInputView in m_PortInputContainer.Children().OfType<PortInputView>())
  583. portInputView.UpdateSlotType();
  584. foreach (var control in m_ControlItems.Children())
  585. {
  586. var listener = control as AbstractMaterialNodeModificationListener;
  587. if (listener != null)
  588. listener.OnNodeModified(ModificationScope.Graph);
  589. }
  590. }
  591. void OnResize(Vector2 deltaSize)
  592. {
  593. var updatedWidth = topContainer.layout.width + deltaSize.x;
  594. var updatedHeight = m_PreviewImage.layout.height + deltaSize.y;
  595. var previewNode = node as PreviewNode;
  596. if (previewNode != null)
  597. {
  598. previewNode.SetDimensions(updatedWidth, updatedHeight);
  599. UpdateSize();
  600. }
  601. }
  602. void OnMouseHover(EventBase evt)
  603. {
  604. var graphView = GetFirstAncestorOfType<GraphEditorView>();
  605. if (graphView == null)
  606. return;
  607. var blackboardProvider = graphView.blackboardProvider;
  608. if (blackboardProvider == null)
  609. return;
  610. // Keyword nodes should be highlighted when Blackboard entry is hovered
  611. // TODO: Move to new NodeView type when keyword node has unique style
  612. if(node is KeywordNode keywordNode)
  613. {
  614. var keywordRow = blackboardProvider.GetBlackboardRow(keywordNode.keywordGuid);
  615. if (keywordRow != null)
  616. {
  617. if (evt.eventTypeId == MouseEnterEvent.TypeId())
  618. {
  619. keywordRow.AddToClassList("hovered");
  620. }
  621. else
  622. {
  623. keywordRow.RemoveFromClassList("hovered");
  624. }
  625. }
  626. }
  627. }
  628. void UpdatePreviewTexture()
  629. {
  630. if (m_PreviewRenderData.texture == null || !node.previewExpanded)
  631. {
  632. m_PreviewImage.visible = false;
  633. m_PreviewImage.image = Texture2D.blackTexture;
  634. }
  635. else
  636. {
  637. m_PreviewImage.visible = true;
  638. m_PreviewImage.AddToClassList("visible");
  639. m_PreviewImage.RemoveFromClassList("hidden");
  640. if (m_PreviewImage.image != m_PreviewRenderData.texture)
  641. m_PreviewImage.image = m_PreviewRenderData.texture;
  642. else
  643. m_PreviewImage.MarkDirtyRepaint();
  644. if (m_PreviewRenderData.shaderData.isCompiling)
  645. m_PreviewImage.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
  646. else
  647. m_PreviewImage.tintColor = Color.white;
  648. }
  649. }
  650. void UpdateSize()
  651. {
  652. var previewNode = node as PreviewNode;
  653. if (previewNode == null)
  654. return;
  655. var width = previewNode.width;
  656. var height = previewNode.height;
  657. m_PreviewImage.style.height = height;
  658. m_PreviewImage.style.width = width;
  659. }
  660. public void Dispose()
  661. {
  662. foreach (var portInputView in m_PortInputContainer.Children().OfType<PortInputView>())
  663. portInputView.Dispose();
  664. node = null;
  665. ((VisualElement)this).userData = null;
  666. if (m_PreviewRenderData != null)
  667. {
  668. m_PreviewRenderData.onPreviewChanged -= UpdatePreviewTexture;
  669. m_PreviewRenderData = null;
  670. }
  671. }
  672. }
  673. }