説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

MaterialNodeView.cs 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  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.Inspector.PropertyDrawers;
  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, IInspectable
  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. VisualElement m_PreviewContainer;
  26. VisualElement m_PreviewFiller;
  27. VisualElement m_ControlItems;
  28. VisualElement m_ControlsDivider;
  29. VisualElement m_DropdownItems;
  30. VisualElement m_DropdownsDivider;
  31. Action m_UnregisterAll;
  32. IEdgeConnectorListener m_ConnectorListener;
  33. MaterialGraphView m_GraphView;
  34. public string inspectorTitle => $"{node.name} (Node)";
  35. public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener, MaterialGraphView graphView)
  36. {
  37. styleSheets.Add(Resources.Load<StyleSheet>("Styles/MaterialNodeView"));
  38. styleSheets.Add(Resources.Load<StyleSheet>($"Styles/ColorMode"));
  39. AddToClassList("MaterialNode");
  40. if (inNode == null)
  41. return;
  42. var contents = this.Q("contents");
  43. m_GraphView = graphView;
  44. mainContainer.style.overflow = StyleKeyword.None; // Override explicit style set in base class
  45. m_ConnectorListener = connectorListener;
  46. node = inNode;
  47. viewDataKey = node.objectId;
  48. UpdateTitle();
  49. // Add disabled overlay
  50. Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
  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. // Add dropdowns container
  67. if (inNode is SubGraphNode)
  68. {
  69. var dropdownContainer = new VisualElement { name = "dropdowns" };
  70. {
  71. m_DropdownsDivider = new VisualElement { name = "divider" };
  72. m_DropdownsDivider.AddToClassList("horizontal");
  73. dropdownContainer.Add(m_DropdownsDivider);
  74. m_DropdownItems = new VisualElement { name = "items" };
  75. dropdownContainer.Add(m_DropdownItems);
  76. UpdateDropdownEntries();
  77. }
  78. contents.Add(dropdownContainer);
  79. }
  80. if (node.hasPreview)
  81. {
  82. // Add actual preview which floats on top of the node
  83. m_PreviewContainer = new VisualElement
  84. {
  85. name = "previewContainer",
  86. style = { overflow = Overflow.Hidden },
  87. pickingMode = PickingMode.Ignore
  88. };
  89. m_PreviewImage = new Image
  90. {
  91. name = "preview",
  92. pickingMode = PickingMode.Ignore,
  93. image = Texture2D.whiteTexture,
  94. };
  95. {
  96. // Add preview collapse button on top of preview
  97. var collapsePreviewButton = new VisualElement { name = "collapse" };
  98. collapsePreviewButton.Add(new VisualElement { name = "icon" });
  99. collapsePreviewButton.AddManipulator(new Clickable(() =>
  100. {
  101. SetPreviewExpandedStateOnSelection(false);
  102. }));
  103. m_PreviewImage.Add(collapsePreviewButton);
  104. }
  105. m_PreviewContainer.Add(m_PreviewImage);
  106. // Hook up preview image to preview manager
  107. m_PreviewRenderData = previewManager.GetPreviewRenderData(inNode);
  108. m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture;
  109. UpdatePreviewTexture();
  110. // Add fake preview which pads out the node to provide space for the floating preview
  111. m_PreviewFiller = new VisualElement { name = "previewFiller" };
  112. m_PreviewFiller.AddToClassList("expanded");
  113. {
  114. var previewDivider = new VisualElement { name = "divider" };
  115. previewDivider.AddToClassList("horizontal");
  116. m_PreviewFiller.Add(previewDivider);
  117. var expandPreviewButton = new VisualElement { name = "expand" };
  118. expandPreviewButton.Add(new VisualElement { name = "icon" });
  119. expandPreviewButton.AddManipulator(new Clickable(() =>
  120. {
  121. SetPreviewExpandedStateOnSelection(true);
  122. }));
  123. m_PreviewFiller.Add(expandPreviewButton);
  124. }
  125. contents.Add(m_PreviewFiller);
  126. UpdatePreviewExpandedState(node.previewExpanded);
  127. }
  128. base.expanded = node.drawState.expanded;
  129. AddSlots(node.GetSlots<MaterialSlot>());
  130. switch (node)
  131. {
  132. case SubGraphNode:
  133. RegisterCallback<MouseDownEvent>(OnSubGraphDoubleClick);
  134. m_UnregisterAll += () => { UnregisterCallback<MouseDownEvent>(OnSubGraphDoubleClick); };
  135. break;
  136. }
  137. m_TitleContainer = this.Q("title");
  138. if (node is BlockNode blockData)
  139. {
  140. AddToClassList("blockData");
  141. m_TitleContainer.RemoveFromHierarchy();
  142. }
  143. else
  144. {
  145. SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
  146. }
  147. // Update active state
  148. SetActive(node.isActive);
  149. // Register OnMouseHover callbacks for node highlighting
  150. RegisterCallback<MouseEnterEvent>(OnMouseHover);
  151. m_UnregisterAll += () => { UnregisterCallback<MouseEnterEvent>(OnMouseHover); };
  152. RegisterCallback<MouseLeaveEvent>(OnMouseHover);
  153. m_UnregisterAll += () => { UnregisterCallback<MouseLeaveEvent>(OnMouseHover); };
  154. ShaderGraphPreferences.onAllowDeprecatedChanged += UpdateTitle;
  155. }
  156. public bool FindPort(SlotReference slotRef, out ShaderPort port)
  157. {
  158. port = inputContainer.Query<ShaderPort>().ToList()
  159. .Concat(outputContainer.Query<ShaderPort>().ToList())
  160. .First(p => p.slot.slotReference.Equals(slotRef));
  161. return port != null;
  162. }
  163. public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
  164. {
  165. ClearMessage();
  166. IconBadge badge;
  167. if (severity == ShaderCompilerMessageSeverity.Error)
  168. {
  169. badge = IconBadge.CreateError(errString);
  170. }
  171. else
  172. {
  173. badge = IconBadge.CreateComment(errString);
  174. }
  175. Add(badge);
  176. if (node is BlockNode)
  177. {
  178. FindPort(node.GetSlotReference(0), out var port);
  179. badge.AttachTo(port.parent, SpriteAlignment.RightCenter);
  180. }
  181. else
  182. {
  183. badge.AttachTo(m_TitleContainer, SpriteAlignment.RightCenter);
  184. }
  185. }
  186. public void SetActive(bool state)
  187. {
  188. // Setup
  189. var disabledString = "disabled";
  190. var portDisabledString = "inactive";
  191. if (!state)
  192. {
  193. // Add elements to disabled class list
  194. AddToClassList(disabledString);
  195. var inputPorts = inputContainer.Query<ShaderPort>().ToList();
  196. foreach (var port in inputPorts)
  197. {
  198. port.AddToClassList(portDisabledString);
  199. }
  200. var outputPorts = outputContainer.Query<ShaderPort>().ToList();
  201. foreach (var port in outputPorts)
  202. {
  203. port.AddToClassList(portDisabledString);
  204. }
  205. }
  206. else
  207. {
  208. // Remove elements from disabled class list
  209. RemoveFromClassList(disabledString);
  210. var inputPorts = inputContainer.Query<ShaderPort>().ToList();
  211. foreach (var port in inputPorts)
  212. {
  213. port.RemoveFromClassList(portDisabledString);
  214. }
  215. var outputPorts = outputContainer.Query<ShaderPort>().ToList();
  216. foreach (var port in outputPorts)
  217. {
  218. port.RemoveFromClassList(portDisabledString);
  219. }
  220. }
  221. }
  222. public void ClearMessage()
  223. {
  224. var badge = this.Q<IconBadge>();
  225. badge?.Detach();
  226. badge?.RemoveFromHierarchy();
  227. }
  228. public void UpdateDropdownEntries()
  229. {
  230. if (node is SubGraphNode subGraphNode && subGraphNode.asset != null)
  231. {
  232. m_DropdownItems.Clear();
  233. var dropdowns = subGraphNode.asset.dropdowns;
  234. foreach (var dropdown in dropdowns)
  235. {
  236. if (dropdown.isExposed)
  237. {
  238. var name = subGraphNode.GetDropdownEntryName(dropdown.referenceName);
  239. if (!dropdown.ContainsEntry(name))
  240. {
  241. name = dropdown.entryName;
  242. subGraphNode.SetDropdownEntryName(dropdown.referenceName, name);
  243. }
  244. var field = new PopupField<string>(dropdown.entries.Select(x => x.displayName).ToList(), name);
  245. // Create anonymous lambda
  246. EventCallback<ChangeEvent<string>> eventCallback = (evt) =>
  247. {
  248. subGraphNode.owner.owner.RegisterCompleteObjectUndo("Change Dropdown Value");
  249. subGraphNode.SetDropdownEntryName(dropdown.referenceName, field.value);
  250. subGraphNode.Dirty(ModificationScope.Topological);
  251. };
  252. field.RegisterValueChangedCallback(eventCallback);
  253. // Setup so we can unregister this callback later
  254. m_UnregisterAll += () => field.UnregisterValueChangedCallback(eventCallback);
  255. m_DropdownItems.Add(new PropertyRow(new Label(dropdown.displayName)), (row) =>
  256. {
  257. row.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
  258. row.Add(field);
  259. });
  260. }
  261. }
  262. }
  263. }
  264. public VisualElement colorElement
  265. {
  266. get { return this; }
  267. }
  268. static readonly StyleColor noColor = new StyleColor(StyleKeyword.Null);
  269. public void SetColor(Color color)
  270. {
  271. m_TitleContainer.style.borderBottomColor = color;
  272. }
  273. public void ResetColor()
  274. {
  275. m_TitleContainer.style.borderBottomColor = noColor;
  276. }
  277. public Color GetColor()
  278. {
  279. return m_TitleContainer.resolvedStyle.borderBottomColor;
  280. }
  281. void OnSubGraphDoubleClick(MouseDownEvent evt)
  282. {
  283. if (evt.clickCount == 2 && evt.button == 0)
  284. {
  285. SubGraphNode subgraphNode = node as SubGraphNode;
  286. var path = AssetDatabase.GUIDToAssetPath(subgraphNode.subGraphGuid);
  287. ShaderGraphImporterEditor.ShowGraphEditWindow(path);
  288. }
  289. }
  290. public Node gvNode => this;
  291. [Inspectable("Node", null)]
  292. public AbstractMaterialNode node { get; private set; }
  293. public override bool expanded
  294. {
  295. get => base.expanded;
  296. set
  297. {
  298. if (base.expanded == value)
  299. return;
  300. base.expanded = value;
  301. if (node.drawState.expanded != value)
  302. {
  303. var ds = node.drawState;
  304. ds.expanded = value;
  305. node.drawState = ds;
  306. }
  307. foreach (var inputPort in inputContainer.Query<ShaderPort>().ToList())
  308. {
  309. inputPort.parent.style.visibility = inputPort.style.visibility;
  310. }
  311. RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed
  312. }
  313. }
  314. public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
  315. {
  316. if (evt.target is Node)
  317. {
  318. var canViewShader = node.hasPreview || node is SubGraphOutputNode;
  319. evt.menu.AppendAction("Copy Shader", CopyToClipboard,
  320. _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
  321. GenerationMode.ForReals);
  322. evt.menu.AppendAction("Show Generated Code", ShowGeneratedCode,
  323. _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
  324. GenerationMode.ForReals);
  325. if (Unsupported.IsDeveloperMode())
  326. {
  327. evt.menu.AppendAction("Show Preview Code", ShowGeneratedCode,
  328. _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
  329. GenerationMode.Preview);
  330. }
  331. }
  332. base.BuildContextualMenu(evt);
  333. }
  334. void CopyToClipboard(DropdownMenuAction action)
  335. {
  336. GUIUtility.systemCopyBuffer = ConvertToShader((GenerationMode)action.userData);
  337. }
  338. public string SanitizeName(string name)
  339. {
  340. return new string(name.Where(c => !Char.IsWhiteSpace(c)).ToArray());
  341. }
  342. public void ShowGeneratedCode(DropdownMenuAction action)
  343. {
  344. string name = GetFirstAncestorOfType<GraphEditorView>().assetName;
  345. var mode = (GenerationMode)action.userData;
  346. string path = String.Format("Temp/GeneratedFromGraph-{0}-{1}-{2}{3}.shader", SanitizeName(name),
  347. SanitizeName(node.name), node.objectId, mode == GenerationMode.Preview ? "-Preview" : "");
  348. if (GraphUtil.WriteToFile(path, ConvertToShader(mode)))
  349. GraphUtil.OpenFile(path);
  350. }
  351. string ConvertToShader(GenerationMode mode)
  352. {
  353. var generator = new Generator(node.owner, node, mode, node.name);
  354. return generator.generatedShader;
  355. }
  356. void SetNodesAsDirty()
  357. {
  358. var editorView = GetFirstAncestorOfType<GraphEditorView>();
  359. var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
  360. editorView.colorManager.SetNodesDirty(nodeList);
  361. }
  362. void UpdateNodeViews()
  363. {
  364. var editorView = GetFirstAncestorOfType<GraphEditorView>();
  365. var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
  366. editorView.colorManager.UpdateNodeViews(nodeList);
  367. }
  368. public object GetObjectToInspect()
  369. {
  370. return node;
  371. }
  372. public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
  373. {
  374. if (propertyDrawer is IGetNodePropertyDrawerPropertyData nodePropertyDrawer)
  375. {
  376. nodePropertyDrawer.GetPropertyData(SetNodesAsDirty, UpdateNodeViews);
  377. }
  378. }
  379. private void SetSelfSelected()
  380. {
  381. m_GraphView.ClearSelection();
  382. m_GraphView.AddToSelection(this);
  383. }
  384. protected override void ToggleCollapse()
  385. {
  386. node.owner.owner.RegisterCompleteObjectUndo(!expanded ? "Expand Nodes" : "Collapse Nodes");
  387. expanded = !expanded;
  388. // If selected, expand/collapse the other applicable nodes that are also selected
  389. if (selected)
  390. {
  391. m_GraphView.SetNodeExpandedForSelectedNodes(expanded, false);
  392. }
  393. }
  394. void SetPreviewExpandedStateOnSelection(bool state)
  395. {
  396. // If selected, expand/collapse the other applicable nodes that are also selected
  397. if (selected)
  398. {
  399. m_GraphView.SetPreviewExpandedForSelectedNodes(state);
  400. }
  401. else
  402. {
  403. node.owner.owner.RegisterCompleteObjectUndo(state ? "Expand Previews" : "Collapse Previews");
  404. node.previewExpanded = state;
  405. }
  406. }
  407. public bool CanToggleNodeExpanded()
  408. {
  409. return !(node is BlockNode) && m_CollapseButton.enabledInHierarchy;
  410. }
  411. void UpdatePreviewExpandedState(bool expanded)
  412. {
  413. node.previewExpanded = expanded;
  414. if (m_PreviewFiller == null)
  415. return;
  416. if (expanded)
  417. {
  418. if (m_PreviewContainer.parent != this)
  419. {
  420. Add(m_PreviewContainer);
  421. m_PreviewContainer.PlaceBehind(this.Q("selection-border"));
  422. }
  423. m_PreviewFiller.AddToClassList("expanded");
  424. m_PreviewFiller.RemoveFromClassList("collapsed");
  425. }
  426. else
  427. {
  428. if (m_PreviewContainer.parent == m_PreviewFiller)
  429. {
  430. m_PreviewContainer.RemoveFromHierarchy();
  431. }
  432. m_PreviewFiller.RemoveFromClassList("expanded");
  433. m_PreviewFiller.AddToClassList("collapsed");
  434. }
  435. UpdatePreviewTexture();
  436. }
  437. void UpdateTitle()
  438. {
  439. if (node is SubGraphNode subGraphNode && subGraphNode.asset != null)
  440. title = subGraphNode.asset.name;
  441. else
  442. {
  443. if (node.sgVersion < node.latestVersion)
  444. {
  445. if (node is IHasCustomDeprecationMessage customDeprecationMessage)
  446. {
  447. title = customDeprecationMessage.GetCustomDeprecationLabel();
  448. }
  449. else
  450. {
  451. title = node.name + $" (Legacy v{node.sgVersion})";
  452. }
  453. }
  454. else
  455. {
  456. title = node.name;
  457. }
  458. }
  459. }
  460. void UpdateShaderPortsForSlots(bool inputSlots, List<MaterialSlot> allSlots, ShaderPort[] slotShaderPorts)
  461. {
  462. VisualElement portContainer = inputSlots ? inputContainer : outputContainer;
  463. var existingPorts = portContainer.Query<ShaderPort>().ToList();
  464. foreach (ShaderPort shaderPort in existingPorts)
  465. {
  466. var currentSlotId = shaderPort.slot.id;
  467. int newSlotIndex = allSlots.FindIndex(s => s.id == currentSlotId);
  468. if (newSlotIndex < 0)
  469. {
  470. // slot doesn't exist anymore, remove it
  471. if (inputSlots)
  472. portContainer.Remove(shaderPort.parent); // remove parent (includes the InputView)
  473. else
  474. portContainer.Remove(shaderPort);
  475. }
  476. else
  477. {
  478. var newSlot = allSlots[newSlotIndex];
  479. slotShaderPorts[newSlotIndex] = shaderPort;
  480. // these should probably be in an UpdateShaderPort(shaderPort, newSlot) function
  481. shaderPort.slot = newSlot;
  482. shaderPort.portName = newSlot.displayName;
  483. if (inputSlots) // input slots also have to update the InputView
  484. UpdatePortInputView(shaderPort);
  485. }
  486. }
  487. }
  488. public void OnModified(ModificationScope scope)
  489. {
  490. UpdateTitle();
  491. SetActive(node.isActive);
  492. if (node.hasPreview)
  493. UpdatePreviewExpandedState(node.previewExpanded);
  494. base.expanded = node.drawState.expanded;
  495. switch (scope)
  496. {
  497. // Update slots to match node modification
  498. case ModificationScope.Topological:
  499. {
  500. var slots = node.GetSlots<MaterialSlot>().ToList();
  501. // going to record the corresponding ShaderPort to each slot, so we can order them later
  502. ShaderPort[] slotShaderPorts = new ShaderPort[slots.Count];
  503. // update existing input and output ports
  504. UpdateShaderPortsForSlots(true, slots, slotShaderPorts);
  505. UpdateShaderPortsForSlots(false, slots, slotShaderPorts);
  506. // check if there are any new slots that must create new ports
  507. for (int i = 0; i < slots.Count; i++)
  508. {
  509. if (slotShaderPorts[i] == null)
  510. slotShaderPorts[i] = AddShaderPortForSlot(slots[i]);
  511. }
  512. // make sure they are in the right order
  513. // by bringing each port to front in declaration order
  514. // note that this sorts input and output containers at the same time
  515. foreach (var shaderPort in slotShaderPorts)
  516. {
  517. if (shaderPort != null)
  518. {
  519. if (shaderPort.slot.isInputSlot)
  520. shaderPort.parent.BringToFront();
  521. else
  522. shaderPort.BringToFront();
  523. }
  524. }
  525. break;
  526. }
  527. }
  528. RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed
  529. foreach (var listener in m_ControlItems.Children().OfType<AbstractMaterialNodeModificationListener>())
  530. {
  531. if (listener != null)
  532. listener.OnNodeModified(scope);
  533. }
  534. }
  535. ShaderPort AddShaderPortForSlot(MaterialSlot slot)
  536. {
  537. if (slot.hidden)
  538. return null;
  539. ShaderPort port = ShaderPort.Create(slot, m_ConnectorListener);
  540. if (slot.isOutputSlot)
  541. {
  542. outputContainer.Add(port);
  543. }
  544. else
  545. {
  546. var portContainer = new VisualElement();
  547. portContainer.style.flexDirection = FlexDirection.Row;
  548. var portInputView = new PortInputView(slot) { style = { position = Position.Absolute } };
  549. portContainer.Add(portInputView);
  550. portContainer.Add(port);
  551. inputContainer.Add(portContainer);
  552. // Update active state
  553. if (node.isActive)
  554. {
  555. portInputView.RemoveFromClassList("disabled");
  556. }
  557. else
  558. {
  559. portInputView.AddToClassList("disabled");
  560. }
  561. }
  562. port.OnDisconnect = OnEdgeDisconnected;
  563. return port;
  564. }
  565. void AddSlots(IEnumerable<MaterialSlot> slots)
  566. {
  567. foreach (var slot in slots)
  568. AddShaderPortForSlot(slot);
  569. // Make sure the visuals are properly updated to reflect port list
  570. RefreshPorts();
  571. }
  572. void OnEdgeDisconnected(Port obj)
  573. {
  574. RefreshExpandedState();
  575. }
  576. static bool GetPortInputView(ShaderPort port, out PortInputView view)
  577. {
  578. view = port.parent.Q<PortInputView>();
  579. return view != null;
  580. }
  581. public void UpdatePortInputTypes()
  582. {
  583. var portList = inputContainer.Query<ShaderPort>().ToList();
  584. portList.AddRange(outputContainer.Query<ShaderPort>().ToList());
  585. foreach (var anchor in portList)
  586. {
  587. var slot = anchor.slot;
  588. anchor.portName = slot.displayName;
  589. anchor.visualClass = slot.concreteValueType.ToClassName();
  590. if (GetPortInputView(anchor, out var portInputView))
  591. {
  592. portInputView.UpdateSlotType();
  593. UpdatePortInputVisibility(portInputView, anchor);
  594. }
  595. }
  596. foreach (var control in m_ControlItems.Children())
  597. {
  598. if (control is AbstractMaterialNodeModificationListener listener)
  599. listener.OnNodeModified(ModificationScope.Graph);
  600. }
  601. }
  602. void UpdatePortInputView(ShaderPort port)
  603. {
  604. if (GetPortInputView(port, out var portInputView))
  605. {
  606. portInputView.UpdateSlot(port.slot);
  607. UpdatePortInputVisibility(portInputView, port);
  608. }
  609. }
  610. void UpdatePortInputVisibility(PortInputView portInputView, ShaderPort port)
  611. {
  612. SetElementVisible(portInputView, !port.slot.isConnected);
  613. port.parent.style.visibility = port.style.visibility;
  614. portInputView.MarkDirtyRepaint();
  615. }
  616. void SetElementVisible(VisualElement element, bool isVisible)
  617. {
  618. const string k_HiddenClassList = "hidden";
  619. if (isVisible)
  620. {
  621. // Restore default value for visibility by setting it to StyleKeyword.Null.
  622. // Setting it to Visibility.Visible would make it visible even if parent is hidden.
  623. element.style.visibility = StyleKeyword.Null;
  624. element.RemoveFromClassList(k_HiddenClassList);
  625. }
  626. else
  627. {
  628. element.style.visibility = Visibility.Hidden;
  629. element.AddToClassList(k_HiddenClassList);
  630. }
  631. }
  632. SGBlackboardRow GetAssociatedBlackboardRow()
  633. {
  634. var graphEditorView = GetFirstAncestorOfType<GraphEditorView>();
  635. if (graphEditorView == null)
  636. return null;
  637. var blackboardController = graphEditorView.blackboardController;
  638. if (blackboardController == null)
  639. return null;
  640. if (node is KeywordNode keywordNode)
  641. {
  642. return blackboardController.GetBlackboardRow(keywordNode.keyword);
  643. }
  644. if (node is DropdownNode dropdownNode)
  645. {
  646. return blackboardController.GetBlackboardRow(dropdownNode.dropdown);
  647. }
  648. return null;
  649. }
  650. void OnMouseHover(EventBase evt)
  651. {
  652. // Keyword/Dropdown nodes should be highlighted when Blackboard entry is hovered
  653. // TODO: Move to new NodeView type when keyword node has unique style
  654. var blackboardRow = GetAssociatedBlackboardRow();
  655. if (blackboardRow != null)
  656. {
  657. if (evt.eventTypeId == MouseEnterEvent.TypeId())
  658. {
  659. blackboardRow.AddToClassList("hovered");
  660. }
  661. else
  662. {
  663. blackboardRow.RemoveFromClassList("hovered");
  664. }
  665. }
  666. }
  667. void UpdatePreviewTexture()
  668. {
  669. if (m_PreviewRenderData.texture == null || !node.previewExpanded)
  670. {
  671. m_PreviewImage.visible = false;
  672. m_PreviewImage.image = Texture2D.blackTexture;
  673. }
  674. else
  675. {
  676. m_PreviewImage.visible = true;
  677. m_PreviewImage.AddToClassList("visible");
  678. m_PreviewImage.RemoveFromClassList("hidden");
  679. if (m_PreviewImage.image != m_PreviewRenderData.texture)
  680. m_PreviewImage.image = m_PreviewRenderData.texture;
  681. else
  682. m_PreviewImage.MarkDirtyRepaint();
  683. if (m_PreviewRenderData.shaderData.isOutOfDate)
  684. m_PreviewImage.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
  685. else
  686. m_PreviewImage.tintColor = Color.white;
  687. }
  688. }
  689. public void Dispose()
  690. {
  691. ClearMessage();
  692. foreach (var portInputView in inputContainer.Query<PortInputView>().ToList())
  693. portInputView.Dispose();
  694. foreach (var shaderPort in outputContainer.Query<ShaderPort>().ToList())
  695. shaderPort.Dispose();
  696. var propRow = GetAssociatedBlackboardRow();
  697. // If this node view is deleted, remove highlighting from associated blackboard row
  698. if (propRow != null)
  699. {
  700. propRow.RemoveFromClassList("hovered");
  701. }
  702. styleSheets.Clear();
  703. inputContainer?.Clear();
  704. outputContainer?.Clear();
  705. m_DropdownsDivider?.Clear();
  706. m_ControlsDivider?.Clear();
  707. m_PreviewContainer?.Clear();
  708. m_ControlItems?.Clear();
  709. m_ConnectorListener = null;
  710. m_GraphView = null;
  711. m_DropdownItems = null;
  712. m_ControlItems = null;
  713. m_ControlsDivider = null;
  714. m_PreviewContainer = null;
  715. m_PreviewFiller = null;
  716. m_PreviewImage = null;
  717. m_DropdownsDivider = null;
  718. m_TitleContainer = null;
  719. node = null;
  720. userData = null;
  721. // Unregister callback
  722. m_UnregisterAll?.Invoke();
  723. m_UnregisterAll = null;
  724. if (m_PreviewRenderData != null)
  725. {
  726. m_PreviewRenderData.onPreviewChanged -= UpdatePreviewTexture;
  727. m_PreviewRenderData = null;
  728. }
  729. ShaderGraphPreferences.onAllowDeprecatedChanged -= UpdateTitle;
  730. Clear();
  731. }
  732. }
  733. }