Keine Beschreibung
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

AbstractMaterialNode.cs 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEngine;
  5. using UnityEditor.Graphing;
  6. using UnityEditor.ShaderGraph.Drawing.Colors;
  7. using UnityEditor.ShaderGraph.Internal;
  8. using UnityEditor.ShaderGraph.Drawing;
  9. using UnityEditor.ShaderGraph.Serialization;
  10. using UnityEngine.Assertions;
  11. using UnityEngine.Pool;
  12. namespace UnityEditor.ShaderGraph
  13. {
  14. [Serializable]
  15. abstract class AbstractMaterialNode : JsonObject, IGroupItem, IRectInterface
  16. {
  17. [SerializeField]
  18. JsonRef<GroupData> m_Group = null;
  19. [SerializeField]
  20. private string m_Name;
  21. [SerializeField]
  22. private DrawState m_DrawState;
  23. [NonSerialized]
  24. bool m_HasError;
  25. [NonSerialized]
  26. bool m_IsValid = true;
  27. [NonSerialized]
  28. bool m_IsActive = true;
  29. [NonSerialized]
  30. bool m_WasUsedByGenerator = false;
  31. [SerializeField]
  32. List<JsonData<MaterialSlot>> m_Slots = new List<JsonData<MaterialSlot>>();
  33. public GraphData owner { get; set; }
  34. internal virtual bool ExposeToSearcher => true;
  35. OnNodeModified m_OnModified;
  36. Action m_UnregisterAll;
  37. public GroupData group
  38. {
  39. get => m_Group;
  40. set
  41. {
  42. if (m_Group == value)
  43. return;
  44. m_Group = value;
  45. Dirty(ModificationScope.Topological);
  46. }
  47. }
  48. public void RegisterCallback(OnNodeModified callback)
  49. {
  50. m_OnModified += callback;
  51. // Setup so we can unregister this callback later at teardown time
  52. m_UnregisterAll += () => m_OnModified -= callback;
  53. }
  54. public void UnregisterCallback(OnNodeModified callback)
  55. {
  56. m_OnModified -= callback;
  57. }
  58. public void Dirty(ModificationScope scope)
  59. {
  60. // Calling m_OnModified immediately upon dirtying the node can result in a lot of churn. For example,
  61. // nodes can cause cascading view updates *multiple times* per operation.
  62. // If this call causes future performance issues, we should investigate some kind of deferral or early out
  63. // until all of the dirty nodes have been identified.
  64. if (m_OnModified != null && !owner.replaceInProgress)
  65. m_OnModified(this, scope);
  66. NodeValidation.HandleValidationExtensions(this);
  67. }
  68. public string name
  69. {
  70. get { return m_Name; }
  71. set { m_Name = value; }
  72. }
  73. public string[] synonyms;
  74. protected virtual string documentationPage => name;
  75. public virtual string documentationURL => NodeUtils.GetDocumentationString(documentationPage);
  76. public virtual bool canDeleteNode => owner != null && owner.outputNode != this;
  77. public DrawState drawState
  78. {
  79. get { return m_DrawState; }
  80. set
  81. {
  82. m_DrawState = value;
  83. Dirty(ModificationScope.Layout);
  84. }
  85. }
  86. Rect IRectInterface.rect
  87. {
  88. get => drawState.position;
  89. set
  90. {
  91. var state = drawState;
  92. state.position = value;
  93. drawState = state;
  94. }
  95. }
  96. public virtual bool canSetPrecision
  97. {
  98. get { return true; }
  99. }
  100. // this is the precision after the inherit/automatic behavior has been calculated
  101. // it does NOT include fallback to any graph default precision
  102. public GraphPrecision graphPrecision { get; set; } = GraphPrecision.Single;
  103. private ConcretePrecision m_ConcretePrecision = ConcretePrecision.Single;
  104. public ConcretePrecision concretePrecision
  105. {
  106. get => m_ConcretePrecision;
  107. set => m_ConcretePrecision = value;
  108. }
  109. [SerializeField]
  110. private Precision m_Precision = Precision.Inherit;
  111. public Precision precision
  112. {
  113. get => m_Precision;
  114. set => m_Precision = value;
  115. }
  116. [SerializeField]
  117. bool m_PreviewExpanded = true;
  118. public bool previewExpanded
  119. {
  120. get { return m_PreviewExpanded; }
  121. set
  122. {
  123. if (previewExpanded == value)
  124. return;
  125. m_PreviewExpanded = value;
  126. Dirty(ModificationScope.Node);
  127. }
  128. }
  129. [SerializeField]
  130. protected int m_DismissedVersion = 0;
  131. public int dismissedUpdateVersion { get => m_DismissedVersion; set => m_DismissedVersion = value; }
  132. // by default, if this returns null, the system will allow creation of any previous version
  133. public virtual IEnumerable<int> allowedNodeVersions => null;
  134. // Nodes that want to have a preview area can override this and return true
  135. public virtual bool hasPreview
  136. {
  137. get { return false; }
  138. }
  139. [SerializeField]
  140. internal PreviewMode m_PreviewMode = PreviewMode.Inherit;
  141. public virtual PreviewMode previewMode
  142. {
  143. get { return m_PreviewMode; }
  144. }
  145. public virtual bool allowedInSubGraph
  146. {
  147. get { return !(this is BlockNode); }
  148. }
  149. public virtual bool allowedInMainGraph
  150. {
  151. get { return true; }
  152. }
  153. public virtual bool allowedInLayerGraph
  154. {
  155. get { return true; }
  156. }
  157. public virtual bool hasError
  158. {
  159. get { return m_HasError; }
  160. protected set { m_HasError = value; }
  161. }
  162. public virtual bool isActive
  163. {
  164. get { return m_IsActive; }
  165. }
  166. internal virtual bool wasUsedByGenerator
  167. {
  168. get { return m_WasUsedByGenerator; }
  169. }
  170. internal void SetUsedByGenerator()
  171. {
  172. m_WasUsedByGenerator = true;
  173. }
  174. //There are times when isActive needs to be set to a value explicitly, and
  175. //not be changed by active forest parsing (what we do when we need to figure out
  176. //what nodes should or should not be active, usually from an edit; see NodeUtils).
  177. //In this case, we allow for explicit setting of an active value that cant be overriden.
  178. //Implicit implies that active forest parsing can edit the nodes isActive property
  179. public enum ActiveState
  180. {
  181. Implicit = 0,
  182. ExplicitInactive = 1,
  183. ExplicitActive = 2
  184. }
  185. private ActiveState m_ActiveState = ActiveState.Implicit;
  186. public ActiveState activeState
  187. {
  188. get => m_ActiveState;
  189. }
  190. public void SetOverrideActiveState(ActiveState overrideState, bool updateConnections = true)
  191. {
  192. if (m_ActiveState == overrideState)
  193. {
  194. return;
  195. }
  196. m_ActiveState = overrideState;
  197. switch (m_ActiveState)
  198. {
  199. case ActiveState.Implicit:
  200. if (updateConnections)
  201. {
  202. NodeUtils.ReevaluateActivityOfConnectedNodes(this);
  203. }
  204. break;
  205. case ActiveState.ExplicitInactive:
  206. if (m_IsActive == false)
  207. {
  208. break;
  209. }
  210. else
  211. {
  212. m_IsActive = false;
  213. Dirty(ModificationScope.Node);
  214. if (updateConnections)
  215. {
  216. NodeUtils.ReevaluateActivityOfConnectedNodes(this);
  217. }
  218. break;
  219. }
  220. case ActiveState.ExplicitActive:
  221. if (m_IsActive == true)
  222. {
  223. break;
  224. }
  225. else
  226. {
  227. m_IsActive = true;
  228. Dirty(ModificationScope.Node);
  229. if (updateConnections)
  230. {
  231. NodeUtils.ReevaluateActivityOfConnectedNodes(this);
  232. }
  233. break;
  234. }
  235. }
  236. }
  237. public void SetActive(bool value, bool updateConnections = true)
  238. {
  239. if (m_IsActive == value)
  240. return;
  241. if (m_ActiveState != ActiveState.Implicit)
  242. {
  243. Debug.LogError($"Cannot set IsActive on Node {this} when value is explicitly overriden by ActiveState {m_ActiveState}");
  244. return;
  245. }
  246. // Update this node
  247. m_IsActive = value;
  248. Dirty(ModificationScope.Node);
  249. if (updateConnections)
  250. {
  251. NodeUtils.ReevaluateActivityOfConnectedNodes(this);
  252. }
  253. }
  254. public virtual bool isValid
  255. {
  256. get { return m_IsValid; }
  257. set
  258. {
  259. if (m_IsValid == value)
  260. return;
  261. m_IsValid = value;
  262. }
  263. }
  264. string m_DefaultVariableName;
  265. string m_NameForDefaultVariableName;
  266. string defaultVariableName
  267. {
  268. get
  269. {
  270. if (m_NameForDefaultVariableName != name)
  271. {
  272. m_DefaultVariableName = string.Format("{0}_{1}", NodeUtils.GetHLSLSafeName(name ?? "node"), objectId);
  273. m_NameForDefaultVariableName = name;
  274. }
  275. return m_DefaultVariableName;
  276. }
  277. }
  278. #region Custom Colors
  279. [SerializeField]
  280. CustomColorData m_CustomColors = new CustomColorData();
  281. public bool TryGetColor(string provider, ref Color color)
  282. {
  283. return m_CustomColors.TryGetColor(provider, out color);
  284. }
  285. public void ResetColor(string provider)
  286. {
  287. m_CustomColors.Remove(provider);
  288. }
  289. public void SetColor(string provider, Color color)
  290. {
  291. m_CustomColors.Set(provider, color);
  292. }
  293. #endregion
  294. protected AbstractMaterialNode()
  295. {
  296. m_DrawState.expanded = true;
  297. }
  298. public void GetInputSlots<T>(List<T> foundSlots) where T : MaterialSlot
  299. {
  300. foreach (var slot in m_Slots.SelectValue())
  301. {
  302. if (slot.isInputSlot && slot is T)
  303. foundSlots.Add((T)slot);
  304. }
  305. }
  306. public virtual void GetInputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots) where T : MaterialSlot
  307. {
  308. GetInputSlots(foundSlots);
  309. }
  310. public void GetOutputSlots<T>(List<T> foundSlots) where T : MaterialSlot
  311. {
  312. foreach (var slot in m_Slots.SelectValue())
  313. {
  314. if (slot.isOutputSlot && slot is T materialSlot)
  315. {
  316. foundSlots.Add(materialSlot);
  317. }
  318. }
  319. }
  320. public virtual void GetOutputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots) where T : MaterialSlot
  321. {
  322. GetOutputSlots(foundSlots);
  323. }
  324. public void GetSlots<T>(List<T> foundSlots) where T : MaterialSlot
  325. {
  326. foreach (var slot in m_Slots.SelectValue())
  327. {
  328. if (slot is T materialSlot)
  329. {
  330. foundSlots.Add(materialSlot);
  331. }
  332. }
  333. }
  334. public virtual void CollectShaderProperties(PropertyCollector properties, GenerationMode generationMode)
  335. {
  336. foreach (var inputSlot in this.GetInputSlots<MaterialSlot>())
  337. {
  338. var edges = owner.GetEdges(inputSlot.slotReference);
  339. if (edges.Any(e => e.outputSlot.node.isActive))
  340. continue;
  341. inputSlot.AddDefaultProperty(properties, generationMode);
  342. }
  343. }
  344. public string GetSlotValue(int inputSlotId, GenerationMode generationMode, ConcretePrecision concretePrecision)
  345. {
  346. string slotValue = GetSlotValue(inputSlotId, generationMode);
  347. return slotValue.Replace(PrecisionUtil.Token, concretePrecision.ToShaderString());
  348. }
  349. public string GetSlotValue(int inputSlotId, GenerationMode generationMode)
  350. {
  351. var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
  352. if (inputSlot == null)
  353. return string.Empty;
  354. var edges = owner.GetEdges(inputSlot.slotReference);
  355. if (edges.Any())
  356. {
  357. var fromSocketRef = edges.First().outputSlot;
  358. var fromNode = fromSocketRef.node;
  359. return fromNode.GetOutputForSlot(fromSocketRef, inputSlot.concreteValueType, generationMode);
  360. }
  361. return inputSlot.GetDefaultValue(generationMode);
  362. }
  363. public AbstractShaderProperty GetSlotProperty(int inputSlotId)
  364. {
  365. if (owner == null)
  366. return null;
  367. var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
  368. if (inputSlot?.slotReference.node == null)
  369. return null;
  370. var edges = owner.GetEdges(inputSlot.slotReference);
  371. if (edges.Any())
  372. {
  373. var fromSocketRef = edges.First().outputSlot;
  374. var fromNode = fromSocketRef.node;
  375. if (fromNode == null)
  376. return null; // this is an error condition... we have an edge that connects to a non-existant node?
  377. if (fromNode is PropertyNode propNode)
  378. {
  379. return propNode.property;
  380. }
  381. if (fromNode is RedirectNodeData redirectNode)
  382. {
  383. return redirectNode.GetSlotProperty(RedirectNodeData.kInputSlotID);
  384. }
  385. #if PROCEDURAL_VT_IN_GRAPH
  386. if (fromNode is ProceduralVirtualTextureNode pvtNode)
  387. {
  388. return pvtNode.AsShaderProperty();
  389. }
  390. #endif // PROCEDURAL_VT_IN_GRAPH
  391. return null;
  392. }
  393. return null;
  394. }
  395. protected internal virtual string GetOutputForSlot(SlotReference fromSocketRef, ConcreteSlotValueType valueType, GenerationMode generationMode)
  396. {
  397. var slot = FindOutputSlot<MaterialSlot>(fromSocketRef.slotId);
  398. if (slot == null)
  399. return string.Empty;
  400. if (fromSocketRef.node.isActive)
  401. return GenerationUtils.AdaptNodeOutput(this, slot.id, valueType);
  402. else
  403. return slot.GetDefaultValue(generationMode);
  404. }
  405. public AbstractMaterialNode GetInputNodeFromSlot(int inputSlotId)
  406. {
  407. var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
  408. if (inputSlot == null)
  409. return null;
  410. var edges = owner.GetEdges(inputSlot.slotReference).ToArray();
  411. AbstractMaterialNode fromNode = null;
  412. if (edges.Count() > 0)
  413. {
  414. var fromSocketRef = edges[0].outputSlot;
  415. fromNode = fromSocketRef.node;
  416. }
  417. return fromNode;
  418. }
  419. public static ConcreteSlotValueType ConvertDynamicVectorInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
  420. {
  421. var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
  422. var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
  423. switch (inputTypesDistinct.Count)
  424. {
  425. case 0:
  426. // nothing connected -- use Vec1 by default
  427. return ConcreteSlotValueType.Vector1;
  428. case 1:
  429. if (SlotValueHelper.AreCompatible(SlotValueType.DynamicVector, inputTypesDistinct.First()))
  430. {
  431. if (inputTypesDistinct.First() == ConcreteSlotValueType.Boolean)
  432. return ConcreteSlotValueType.Vector1;
  433. return inputTypesDistinct.First();
  434. }
  435. break;
  436. default:
  437. // find the 'minumum' channel width excluding 1 as it can promote
  438. inputTypesDistinct.RemoveAll(x => (x == ConcreteSlotValueType.Vector1) || (x == ConcreteSlotValueType.Boolean));
  439. var ordered = inputTypesDistinct.OrderByDescending(x => x);
  440. if (ordered.Any())
  441. {
  442. var first = ordered.FirstOrDefault();
  443. return first;
  444. }
  445. break;
  446. }
  447. return ConcreteSlotValueType.Vector1;
  448. }
  449. public static ConcreteSlotValueType ConvertDynamicMatrixInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
  450. {
  451. var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
  452. var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
  453. switch (inputTypesDistinct.Count)
  454. {
  455. case 0:
  456. return ConcreteSlotValueType.Matrix2;
  457. case 1:
  458. return inputTypesDistinct.FirstOrDefault();
  459. default:
  460. var ordered = inputTypesDistinct.OrderByDescending(x => x);
  461. if (ordered.Any())
  462. return ordered.FirstOrDefault();
  463. break;
  464. }
  465. return ConcreteSlotValueType.Matrix2;
  466. }
  467. protected const string k_validationErrorMessage = "Error found during node validation";
  468. // evaluate ALL the precisions...
  469. public virtual void UpdatePrecision(List<MaterialSlot> inputSlots)
  470. {
  471. // first let's reduce from precision ==> graph precision
  472. if (precision == Precision.Inherit)
  473. {
  474. // inherit means calculate it automatically based on inputs
  475. // If no inputs were found use the precision of the Graph
  476. if (inputSlots.Count == 0)
  477. {
  478. graphPrecision = GraphPrecision.Graph;
  479. }
  480. else
  481. {
  482. int curGraphPrecision = (int)GraphPrecision.Half;
  483. foreach (var inputSlot in inputSlots)
  484. {
  485. // If input port doesn't have an edge use the Graph's precision for that input
  486. var edges = owner?.GetEdges(inputSlot.slotReference).ToList();
  487. if (!edges.Any())
  488. {
  489. // disconnected inputs use graph precision
  490. curGraphPrecision = Math.Min(curGraphPrecision, (int)GraphPrecision.Graph);
  491. }
  492. else
  493. {
  494. var outputSlotRef = edges[0].outputSlot;
  495. var outputNode = outputSlotRef.node;
  496. curGraphPrecision = Math.Min(curGraphPrecision, (int)outputNode.graphPrecision);
  497. }
  498. }
  499. graphPrecision = (GraphPrecision)curGraphPrecision;
  500. }
  501. }
  502. else
  503. {
  504. // not inherited, just use the node's selected precision
  505. graphPrecision = precision.ToGraphPrecision(GraphPrecision.Graph);
  506. }
  507. // calculate the concrete precision, with fall-back to the graph concrete precision
  508. concretePrecision = graphPrecision.ToConcrete(owner.graphDefaultConcretePrecision);
  509. }
  510. public virtual void EvaluateDynamicMaterialSlots(List<MaterialSlot> inputSlots, List<MaterialSlot> outputSlots)
  511. {
  512. var dynamicInputSlotsToCompare = DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Get();
  513. var skippedDynamicSlots = ListPool<DynamicVectorMaterialSlot>.Get();
  514. var dynamicMatrixInputSlotsToCompare = DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Get();
  515. var skippedDynamicMatrixSlots = ListPool<DynamicMatrixMaterialSlot>.Get();
  516. // iterate the input slots
  517. {
  518. foreach (var inputSlot in inputSlots)
  519. {
  520. inputSlot.hasError = false;
  521. // if there is a connection
  522. var edges = owner.GetEdges(inputSlot.slotReference).ToList();
  523. if (!edges.Any())
  524. {
  525. if (inputSlot is DynamicVectorMaterialSlot)
  526. skippedDynamicSlots.Add(inputSlot as DynamicVectorMaterialSlot);
  527. if (inputSlot is DynamicMatrixMaterialSlot)
  528. skippedDynamicMatrixSlots.Add(inputSlot as DynamicMatrixMaterialSlot);
  529. continue;
  530. }
  531. // get the output details
  532. var outputSlotRef = edges[0].outputSlot;
  533. var outputNode = outputSlotRef.node;
  534. if (outputNode == null)
  535. continue;
  536. var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
  537. if (outputSlot == null)
  538. continue;
  539. if (outputSlot.hasError)
  540. {
  541. inputSlot.hasError = true;
  542. continue;
  543. }
  544. var outputConcreteType = outputSlot.concreteValueType;
  545. // dynamic input... depends on output from other node.
  546. // we need to compare ALL dynamic inputs to make sure they
  547. // are compatible.
  548. if (inputSlot is DynamicVectorMaterialSlot)
  549. {
  550. dynamicInputSlotsToCompare.Add((DynamicVectorMaterialSlot)inputSlot, outputConcreteType);
  551. continue;
  552. }
  553. else if (inputSlot is DynamicMatrixMaterialSlot)
  554. {
  555. dynamicMatrixInputSlotsToCompare.Add((DynamicMatrixMaterialSlot)inputSlot, outputConcreteType);
  556. continue;
  557. }
  558. }
  559. // we can now figure out the dynamic slotType
  560. // from here set all the
  561. var dynamicType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
  562. foreach (var dynamicKvP in dynamicInputSlotsToCompare)
  563. dynamicKvP.Key.SetConcreteType(dynamicType);
  564. foreach (var skippedSlot in skippedDynamicSlots)
  565. skippedSlot.SetConcreteType(dynamicType);
  566. // and now dynamic matrices
  567. var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicMatrixInputSlotsToCompare.Values);
  568. foreach (var dynamicKvP in dynamicMatrixInputSlotsToCompare)
  569. dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
  570. foreach (var skippedSlot in skippedDynamicMatrixSlots)
  571. skippedSlot.SetConcreteType(dynamicMatrixType);
  572. bool inputError = inputSlots.Any(x => x.hasError);
  573. if (inputError)
  574. {
  575. owner.AddConcretizationError(objectId, string.Format("Node {0} had input error", objectId));
  576. hasError = true;
  577. }
  578. // configure the output slots now
  579. // their slotType will either be the default output slotType
  580. // or the above dynamic slotType for dynamic nodes
  581. // or error if there is an input error
  582. foreach (var outputSlot in outputSlots)
  583. {
  584. outputSlot.hasError = false;
  585. if (inputError)
  586. {
  587. outputSlot.hasError = true;
  588. continue;
  589. }
  590. if (outputSlot is DynamicVectorMaterialSlot dynamicVectorMaterialSlot)
  591. {
  592. dynamicVectorMaterialSlot.SetConcreteType(dynamicType);
  593. continue;
  594. }
  595. else if (outputSlot is DynamicMatrixMaterialSlot dynamicMatrixMaterialSlot)
  596. {
  597. dynamicMatrixMaterialSlot.SetConcreteType(dynamicMatrixType);
  598. continue;
  599. }
  600. }
  601. if (outputSlots.Any(x => x.hasError))
  602. {
  603. owner.AddConcretizationError(objectId, string.Format("Node {0} had output error", objectId));
  604. hasError = true;
  605. }
  606. CalculateNodeHasError();
  607. ListPool<DynamicVectorMaterialSlot>.Release(skippedDynamicSlots);
  608. DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
  609. ListPool<DynamicMatrixMaterialSlot>.Release(skippedDynamicMatrixSlots);
  610. DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Release(dynamicMatrixInputSlotsToCompare);
  611. }
  612. }
  613. public virtual void Concretize()
  614. {
  615. hasError = false;
  616. owner?.ClearErrorsForNode(this);
  617. using (var inputSlots = PooledList<MaterialSlot>.Get())
  618. using (var outputSlots = PooledList<MaterialSlot>.Get())
  619. {
  620. GetInputSlots(inputSlots);
  621. GetOutputSlots(outputSlots);
  622. UpdatePrecision(inputSlots);
  623. EvaluateDynamicMaterialSlots(inputSlots, outputSlots);
  624. }
  625. }
  626. public virtual void ValidateNode()
  627. {
  628. if ((sgVersion < latestVersion) && (dismissedUpdateVersion < latestVersion))
  629. owner.messageManager?.AddOrAppendError(owner, objectId, new ShaderMessage("There is a newer version of this node available. Inspect node for details.", Rendering.ShaderCompilerMessageSeverity.Warning));
  630. }
  631. public virtual bool canCutNode => true;
  632. public virtual bool canCopyNode => true;
  633. protected virtual void CalculateNodeHasError()
  634. {
  635. foreach (var slot in this.GetInputSlots<MaterialSlot>())
  636. {
  637. if (slot.isConnected)
  638. {
  639. var edge = owner.GetEdges(slot.slotReference).First();
  640. var outputNode = edge.outputSlot.node;
  641. var outputSlot = outputNode.GetOutputSlots<MaterialSlot>().First(s => s.id == edge.outputSlot.slotId);
  642. if (!slot.IsCompatibleWith(outputSlot))
  643. {
  644. owner.AddConcretizationError(objectId, $"Slot {slot.RawDisplayName()} cannot accept input of type {outputSlot.concreteValueType}.");
  645. hasError = true;
  646. return;
  647. }
  648. }
  649. }
  650. }
  651. protected string GetRayTracingError() => $@"
  652. #if defined(SHADER_STAGE_RAY_TRACING) && defined(RAYTRACING_SHADER_GRAPH_DEFAULT)
  653. #error '{name}' node is not supported in ray tracing, please provide an alternate implementation, relying for instance on the 'Raytracing Quality' keyword
  654. #endif";
  655. public virtual void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
  656. {
  657. using (var tempSlots = PooledList<MaterialSlot>.Get())
  658. using (var tempPreviewProperties = PooledList<PreviewProperty>.Get())
  659. using (var tempEdges = PooledList<IEdge>.Get())
  660. {
  661. GetInputSlots(tempSlots);
  662. foreach (var s in tempSlots)
  663. {
  664. tempPreviewProperties.Clear();
  665. tempEdges.Clear();
  666. if (owner != null)
  667. {
  668. owner.GetEdges(s.slotReference, tempEdges);
  669. if (tempEdges.Any())
  670. continue;
  671. }
  672. s.GetPreviewProperties(tempPreviewProperties, GetVariableNameForSlot(s.id));
  673. for (int i = 0; i < tempPreviewProperties.Count; i++)
  674. {
  675. if (tempPreviewProperties[i].name == null)
  676. continue;
  677. properties.Add(tempPreviewProperties[i]);
  678. }
  679. }
  680. }
  681. }
  682. public virtual string GetVariableNameForSlot(int slotId)
  683. {
  684. var slot = FindSlot<MaterialSlot>(slotId);
  685. if (slot == null)
  686. throw new ArgumentException(string.Format("Attempting to use MaterialSlot({0}) on node of type {1} where this slot can not be found", slotId, this), "slotId");
  687. return string.Format("_{0}_{1}_{2}_{3}", GetVariableNameForNode(), NodeUtils.GetHLSLSafeName(slot.shaderOutputName), unchecked((uint)slotId), slot.concreteValueType.ToPropertyType().ToString());
  688. }
  689. public string GetConnnectionStateVariableNameForSlot(int slotId)
  690. {
  691. return ShaderInput.GetConnectionStateVariableName(GetVariableNameForSlot(slotId));
  692. }
  693. public virtual string GetVariableNameForNode()
  694. {
  695. return defaultVariableName;
  696. }
  697. public MaterialSlot AddSlot(MaterialSlot slot, bool attemptToModifyExistingInstance = true)
  698. {
  699. if (slot == null)
  700. {
  701. throw new ArgumentException($"Trying to add null slot to node {this}");
  702. }
  703. MaterialSlot foundSlot = FindSlot<MaterialSlot>(slot.id);
  704. if (slot == foundSlot)
  705. return foundSlot;
  706. // Try to keep the existing instance to avoid unnecessary changes to file
  707. if (attemptToModifyExistingInstance && foundSlot != null && slot.GetType() == foundSlot.GetType())
  708. {
  709. foundSlot.displayName = slot.RawDisplayName();
  710. foundSlot.CopyDefaultValue(slot);
  711. return foundSlot;
  712. }
  713. // keep the same ordering by replacing the first match, if it exists
  714. int firstIndex = m_Slots.FindIndex(s => s.value.id == slot.id);
  715. if (firstIndex >= 0)
  716. {
  717. m_Slots[firstIndex] = slot;
  718. // remove additional matches to get rid of unused duplicates
  719. m_Slots.RemoveAllFromRange(s => s.value.id == slot.id, firstIndex + 1, m_Slots.Count - (firstIndex + 1));
  720. }
  721. else
  722. m_Slots.Add(slot);
  723. slot.owner = this;
  724. OnSlotsChanged();
  725. if (foundSlot == null)
  726. return slot;
  727. // foundSlot is of a different type; try to copy values
  728. // I think this is to support casting if implemented in CopyValuesFrom ?
  729. slot.CopyValuesFrom(foundSlot);
  730. foundSlot.owner = null;
  731. return slot;
  732. }
  733. public void RemoveSlot(int slotId)
  734. {
  735. // Remove edges that use this slot
  736. // no owner can happen after creation
  737. // but before added to graph
  738. if (owner != null)
  739. {
  740. var edges = owner.GetEdges(GetSlotReference(slotId));
  741. owner.RemoveEdges(edges.ToArray());
  742. }
  743. //remove slots
  744. m_Slots.RemoveAll(x => x.value.id == slotId);
  745. OnSlotsChanged();
  746. }
  747. protected virtual void OnSlotsChanged()
  748. {
  749. Dirty(ModificationScope.Topological);
  750. owner?.ClearErrorsForNode(this);
  751. }
  752. public void RemoveSlotsNameNotMatching(IEnumerable<int> slotIds, bool supressWarnings = false)
  753. {
  754. var invalidSlots = m_Slots.Select(x => x.value.id).Except(slotIds);
  755. foreach (var invalidSlot in invalidSlots.ToArray())
  756. {
  757. if (!supressWarnings)
  758. Debug.LogWarningFormat("Removing Invalid MaterialSlot: {0}", invalidSlot);
  759. RemoveSlot(invalidSlot);
  760. }
  761. }
  762. public bool SetSlotOrder(List<int> desiredOrderSlotIds)
  763. {
  764. bool changed = false;
  765. int writeIndex = 0;
  766. for (int orderIndex = 0; orderIndex < desiredOrderSlotIds.Count; orderIndex++)
  767. {
  768. var id = desiredOrderSlotIds[orderIndex];
  769. var matchIndex = m_Slots.FindIndex(s => s.value.id == id);
  770. if (matchIndex < 0)
  771. {
  772. // no matching slot with that id.. skip it
  773. }
  774. else
  775. {
  776. if (writeIndex != matchIndex)
  777. {
  778. // swap the matching slot into position
  779. var slot = m_Slots[matchIndex];
  780. m_Slots[matchIndex] = m_Slots[writeIndex];
  781. m_Slots[writeIndex] = slot;
  782. changed = true;
  783. }
  784. writeIndex++;
  785. }
  786. }
  787. return changed;
  788. }
  789. public SlotReference GetSlotReference(int slotId)
  790. {
  791. var slot = FindSlot<MaterialSlot>(slotId);
  792. if (slot == null)
  793. throw new ArgumentException("Slot could not be found", "slotId");
  794. return new SlotReference(this, slotId);
  795. }
  796. public T FindSlot<T>(int slotId) where T : MaterialSlot
  797. {
  798. foreach (var slot in m_Slots.SelectValue())
  799. {
  800. if (slot.id == slotId && slot is T)
  801. return (T)slot;
  802. }
  803. return default(T);
  804. }
  805. public T FindInputSlot<T>(int slotId) where T : MaterialSlot
  806. {
  807. foreach (var slot in m_Slots.SelectValue())
  808. {
  809. if (slot.isInputSlot && slot.id == slotId && slot is T)
  810. return (T)slot;
  811. }
  812. return default(T);
  813. }
  814. public T FindOutputSlot<T>(int slotId) where T : MaterialSlot
  815. {
  816. foreach (var slot in m_Slots.SelectValue())
  817. {
  818. if (slot.isOutputSlot && slot.id == slotId && slot is T)
  819. return (T)slot;
  820. }
  821. return default(T);
  822. }
  823. public virtual IEnumerable<MaterialSlot> GetInputsWithNoConnection()
  824. {
  825. return this.GetInputSlots<MaterialSlot>().Where(x => !owner.GetEdges(GetSlotReference(x.id)).Any());
  826. }
  827. public void SetupSlots()
  828. {
  829. foreach (var s in m_Slots.SelectValue())
  830. s.owner = this;
  831. }
  832. public virtual void UpdateNodeAfterDeserialization()
  833. { }
  834. public bool IsSlotConnected(int slotId)
  835. {
  836. var slot = FindSlot<MaterialSlot>(slotId);
  837. return slot != null && owner.GetEdges(slot.slotReference).Any();
  838. }
  839. public virtual void Setup() { }
  840. protected void EnqueSlotsForSerialization()
  841. {
  842. foreach (var slot in m_Slots)
  843. {
  844. slot.OnBeforeSerialize();
  845. }
  846. }
  847. public virtual void Dispose()
  848. {
  849. foreach (var slot in m_Slots)
  850. slot.value.Dispose();
  851. m_UnregisterAll?.Invoke();
  852. m_UnregisterAll = null;
  853. }
  854. }
  855. }