AbstractMaterialNode.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  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. namespace UnityEditor.ShaderGraph
  9. {
  10. [Serializable]
  11. abstract class AbstractMaterialNode : ISerializationCallbackReceiver, IGroupItem
  12. {
  13. [NonSerialized]
  14. private Guid m_Guid;
  15. [SerializeField]
  16. private string m_GuidSerialized;
  17. [NonSerialized]
  18. Guid m_GroupGuid;
  19. [SerializeField]
  20. string m_GroupGuidSerialized;
  21. [SerializeField]
  22. private string m_Name;
  23. [SerializeField]
  24. protected int m_NodeVersion;
  25. [SerializeField]
  26. private DrawState m_DrawState;
  27. [NonSerialized]
  28. bool m_HasError;
  29. [NonSerialized]
  30. private List<ISlot> m_Slots = new List<ISlot>();
  31. [SerializeField]
  32. List<SerializationHelper.JSONSerializedElement> m_SerializableSlots = new List<SerializationHelper.JSONSerializedElement>();
  33. public Identifier tempId { get; set; }
  34. public GraphData owner { get; set; }
  35. OnNodeModified m_OnModified;
  36. public void RegisterCallback(OnNodeModified callback)
  37. {
  38. m_OnModified += callback;
  39. }
  40. public void UnregisterCallback(OnNodeModified callback)
  41. {
  42. m_OnModified -= callback;
  43. }
  44. public void Dirty(ModificationScope scope)
  45. {
  46. if (m_OnModified != null)
  47. m_OnModified(this, scope);
  48. }
  49. public Guid guid
  50. {
  51. get { return m_Guid; }
  52. }
  53. public Guid groupGuid
  54. {
  55. get { return m_GroupGuid; }
  56. set { m_GroupGuid = value; }
  57. }
  58. public string name
  59. {
  60. get { return m_Name; }
  61. set { m_Name = value; }
  62. }
  63. protected virtual string documentationPage => name;
  64. public virtual string documentationURL => NodeUtils.GetDocumentationString(documentationPage);
  65. public virtual bool canDeleteNode
  66. {
  67. get { return owner != null && guid != owner.activeOutputNodeGuid; }
  68. }
  69. public DrawState drawState
  70. {
  71. get { return m_DrawState; }
  72. set
  73. {
  74. m_DrawState = value;
  75. Dirty(ModificationScope.Layout);
  76. }
  77. }
  78. public virtual bool canSetPrecision
  79. {
  80. get { return true; }
  81. }
  82. private ConcretePrecision m_ConcretePrecision = ConcretePrecision.Float;
  83. public ConcretePrecision concretePrecision
  84. {
  85. get => m_ConcretePrecision;
  86. set => m_ConcretePrecision = value;
  87. }
  88. [SerializeField]
  89. private Precision m_Precision = Precision.Inherit;
  90. public Precision precision
  91. {
  92. get => m_Precision;
  93. set => m_Precision = value;
  94. }
  95. [SerializeField]
  96. bool m_PreviewExpanded = true;
  97. public bool previewExpanded
  98. {
  99. get { return m_PreviewExpanded; }
  100. set
  101. {
  102. if (previewExpanded == value)
  103. return;
  104. m_PreviewExpanded = value;
  105. Dirty(ModificationScope.Node);
  106. }
  107. }
  108. // Nodes that want to have a preview area can override this and return true
  109. public virtual bool hasPreview
  110. {
  111. get { return false; }
  112. }
  113. public virtual PreviewMode previewMode
  114. {
  115. get { return PreviewMode.Preview2D; }
  116. }
  117. public virtual bool allowedInSubGraph
  118. {
  119. get { return true; }
  120. }
  121. public virtual bool allowedInMainGraph
  122. {
  123. get { return true; }
  124. }
  125. public virtual bool allowedInLayerGraph
  126. {
  127. get { return true; }
  128. }
  129. public virtual bool hasError
  130. {
  131. get { return m_HasError; }
  132. protected set { m_HasError = value; }
  133. }
  134. string m_DefaultVariableName;
  135. string m_NameForDefaultVariableName;
  136. Guid m_GuidForDefaultVariableName;
  137. string defaultVariableName
  138. {
  139. get
  140. {
  141. if (m_NameForDefaultVariableName != name || m_GuidForDefaultVariableName != guid)
  142. {
  143. m_DefaultVariableName = string.Format("{0}_{1}", NodeUtils.GetHLSLSafeName(name ?? "node"), GuidEncoder.Encode(guid));
  144. m_NameForDefaultVariableName = name;
  145. m_GuidForDefaultVariableName = guid;
  146. }
  147. return m_DefaultVariableName;
  148. }
  149. }
  150. #region Custom Colors
  151. [SerializeField]
  152. CustomColorData m_CustomColors = new CustomColorData();
  153. public bool TryGetColor(string provider, ref Color color)
  154. {
  155. return m_CustomColors.TryGetColor(provider, out color);
  156. }
  157. public void ResetColor(string provider)
  158. {
  159. m_CustomColors.Remove(provider);
  160. }
  161. public void SetColor(string provider, Color color)
  162. {
  163. m_CustomColors.Set(provider, color);
  164. }
  165. #endregion
  166. protected AbstractMaterialNode()
  167. {
  168. m_DrawState.expanded = true;
  169. m_Guid = Guid.NewGuid();
  170. version = 0;
  171. }
  172. public Guid RewriteGuid()
  173. {
  174. m_Guid = Guid.NewGuid();
  175. return m_Guid;
  176. }
  177. public void GetInputSlots<T>(List<T> foundSlots) where T : ISlot
  178. {
  179. foreach (var slot in m_Slots)
  180. {
  181. if (slot.isInputSlot && slot is T)
  182. foundSlots.Add((T)slot);
  183. }
  184. }
  185. public void GetOutputSlots<T>(List<T> foundSlots) where T : ISlot
  186. {
  187. foreach (var slot in m_Slots)
  188. {
  189. if (slot.isOutputSlot && slot is T)
  190. foundSlots.Add((T)slot);
  191. }
  192. }
  193. public void GetSlots<T>(List<T> foundSlots) where T : ISlot
  194. {
  195. foreach (var slot in m_Slots)
  196. {
  197. if (slot is T)
  198. foundSlots.Add((T)slot);
  199. }
  200. }
  201. public virtual void CollectShaderProperties(PropertyCollector properties, GenerationMode generationMode)
  202. {
  203. foreach (var inputSlot in this.GetInputSlots<MaterialSlot>())
  204. {
  205. var edges = owner.GetEdges(inputSlot.slotReference);
  206. if (edges.Any())
  207. continue;
  208. inputSlot.AddDefaultProperty(properties, generationMode);
  209. }
  210. }
  211. public string GetSlotValue(int inputSlotId, GenerationMode generationMode, ConcretePrecision concretePrecision)
  212. {
  213. string slotValue = GetSlotValue(inputSlotId, generationMode);
  214. return slotValue.Replace(PrecisionUtil.Token, concretePrecision.ToShaderString());
  215. }
  216. public string GetSlotValue(int inputSlotId, GenerationMode generationMode)
  217. {
  218. var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
  219. if (inputSlot == null)
  220. return string.Empty;
  221. var edges = owner.GetEdges(inputSlot.slotReference).ToArray();
  222. if (edges.Any())
  223. {
  224. var fromSocketRef = edges[0].outputSlot;
  225. var fromNode = owner.GetNodeFromGuid<AbstractMaterialNode>(fromSocketRef.nodeGuid);
  226. if (fromNode == null)
  227. return string.Empty;
  228. var slot = fromNode.FindOutputSlot<MaterialSlot>(fromSocketRef.slotId);
  229. if (slot == null)
  230. return string.Empty;
  231. return ShaderGenerator.AdaptNodeOutput(fromNode, slot.id, inputSlot.concreteValueType);
  232. }
  233. return inputSlot.GetDefaultValue(generationMode);
  234. }
  235. public static ConcreteSlotValueType ConvertDynamicVectorInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
  236. {
  237. var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
  238. var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
  239. switch (inputTypesDistinct.Count)
  240. {
  241. case 0:
  242. return ConcreteSlotValueType.Vector1;
  243. case 1:
  244. if(SlotValueHelper.AreCompatible(SlotValueType.DynamicVector, inputTypesDistinct.First()))
  245. return inputTypesDistinct.First();
  246. break;
  247. default:
  248. // find the 'minumum' channel width excluding 1 as it can promote
  249. inputTypesDistinct.RemoveAll(x => x == ConcreteSlotValueType.Vector1);
  250. var ordered = inputTypesDistinct.OrderByDescending(x => x);
  251. if (ordered.Any())
  252. return ordered.FirstOrDefault();
  253. break;
  254. }
  255. return ConcreteSlotValueType.Vector1;
  256. }
  257. public static ConcreteSlotValueType ConvertDynamicMatrixInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
  258. {
  259. var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
  260. var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
  261. switch (inputTypesDistinct.Count)
  262. {
  263. case 0:
  264. return ConcreteSlotValueType.Matrix2;
  265. case 1:
  266. return inputTypesDistinct.FirstOrDefault();
  267. default:
  268. var ordered = inputTypesDistinct.OrderByDescending(x => x);
  269. if (ordered.Any())
  270. return ordered.FirstOrDefault();
  271. break;
  272. }
  273. return ConcreteSlotValueType.Matrix2;
  274. }
  275. protected const string k_validationErrorMessage = "Error found during node validation";
  276. public virtual bool ValidateConcretePrecision(ref string errorMessage)
  277. {
  278. // If Node has a precision override use that
  279. if (precision != Precision.Inherit)
  280. {
  281. m_ConcretePrecision = precision.ToConcrete();
  282. return false;
  283. }
  284. // Get inputs
  285. using(var tempSlots = PooledList<MaterialSlot>.Get())
  286. {
  287. GetInputSlots(tempSlots);
  288. // If no inputs were found use the precision of the Graph
  289. // This can be removed when parameters are considered as true inputs
  290. if (tempSlots.Count == 0)
  291. {
  292. m_ConcretePrecision = owner.concretePrecision;
  293. return false;
  294. }
  295. // Otherwise compare precisions from inputs
  296. var precisionsToCompare = new List<int>();
  297. bool isInError = false;
  298. foreach (var inputSlot in tempSlots)
  299. {
  300. // If input port doesnt have an edge use the Graph's precision for that input
  301. var edges = owner.GetEdges(inputSlot.slotReference).ToList();
  302. if (!edges.Any())
  303. {
  304. precisionsToCompare.Add((int)owner.concretePrecision);
  305. continue;
  306. }
  307. // Get output node from edge
  308. var outputSlotRef = edges[0].outputSlot;
  309. var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
  310. if (outputNode == null)
  311. {
  312. errorMessage = string.Format("Failed to find Node with Guid {0}", outputSlotRef.nodeGuid);
  313. isInError = true;
  314. continue;
  315. }
  316. // Use precision from connected Node
  317. precisionsToCompare.Add((int)outputNode.concretePrecision);
  318. }
  319. // Use highest precision from all input sources
  320. m_ConcretePrecision = (ConcretePrecision)precisionsToCompare.OrderBy(x => x).First();
  321. // Clean up
  322. return isInError;
  323. }
  324. }
  325. public virtual void ValidateNode()
  326. {
  327. var isInError = false;
  328. var errorMessage = k_validationErrorMessage;
  329. var dynamicInputSlotsToCompare = DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Get();
  330. var skippedDynamicSlots = ListPool<DynamicVectorMaterialSlot>.Get();
  331. var dynamicMatrixInputSlotsToCompare = DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Get();
  332. var skippedDynamicMatrixSlots = ListPool<DynamicMatrixMaterialSlot>.Get();
  333. // iterate the input slots
  334. using (var tempSlots = PooledList<MaterialSlot>.Get())
  335. {
  336. GetInputSlots(tempSlots);
  337. foreach (var inputSlot in tempSlots)
  338. {
  339. inputSlot.hasError = false;
  340. // if there is a connection
  341. var edges = owner.GetEdges(inputSlot.slotReference).ToList();
  342. if (!edges.Any())
  343. {
  344. if (inputSlot is DynamicVectorMaterialSlot)
  345. skippedDynamicSlots.Add(inputSlot as DynamicVectorMaterialSlot);
  346. if (inputSlot is DynamicMatrixMaterialSlot)
  347. skippedDynamicMatrixSlots.Add(inputSlot as DynamicMatrixMaterialSlot);
  348. continue;
  349. }
  350. // get the output details
  351. var outputSlotRef = edges[0].outputSlot;
  352. var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
  353. if (outputNode == null)
  354. continue;
  355. var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
  356. if (outputSlot == null)
  357. continue;
  358. if (outputSlot.hasError)
  359. {
  360. inputSlot.hasError = true;
  361. continue;
  362. }
  363. var outputConcreteType = outputSlot.concreteValueType;
  364. // dynamic input... depends on output from other node.
  365. // we need to compare ALL dynamic inputs to make sure they
  366. // are compatible.
  367. if (inputSlot is DynamicVectorMaterialSlot)
  368. {
  369. dynamicInputSlotsToCompare.Add((DynamicVectorMaterialSlot) inputSlot, outputConcreteType);
  370. continue;
  371. }
  372. else if (inputSlot is DynamicMatrixMaterialSlot)
  373. {
  374. dynamicMatrixInputSlotsToCompare.Add((DynamicMatrixMaterialSlot) inputSlot, outputConcreteType);
  375. continue;
  376. }
  377. }
  378. // we can now figure out the dynamic slotType
  379. // from here set all the
  380. var dynamicType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
  381. foreach (var dynamicKvP in dynamicInputSlotsToCompare)
  382. dynamicKvP.Key.SetConcreteType(dynamicType);
  383. foreach (var skippedSlot in skippedDynamicSlots)
  384. skippedSlot.SetConcreteType(dynamicType);
  385. // and now dynamic matrices
  386. var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicMatrixInputSlotsToCompare.Values);
  387. foreach (var dynamicKvP in dynamicMatrixInputSlotsToCompare)
  388. dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
  389. foreach (var skippedSlot in skippedDynamicMatrixSlots)
  390. skippedSlot.SetConcreteType(dynamicMatrixType);
  391. tempSlots.Clear();
  392. GetInputSlots(tempSlots);
  393. var inputError = tempSlots.Any(x => x.hasError);
  394. // configure the output slots now
  395. // their slotType will either be the default output slotType
  396. // or the above dynamic slotType for dynamic nodes
  397. // or error if there is an input error
  398. tempSlots.Clear();
  399. GetOutputSlots(tempSlots);
  400. foreach (var outputSlot in tempSlots)
  401. {
  402. outputSlot.hasError = false;
  403. if (inputError)
  404. {
  405. outputSlot.hasError = true;
  406. continue;
  407. }
  408. if (outputSlot is DynamicVectorMaterialSlot)
  409. {
  410. (outputSlot as DynamicVectorMaterialSlot).SetConcreteType(dynamicType);
  411. continue;
  412. }
  413. else if (outputSlot is DynamicMatrixMaterialSlot)
  414. {
  415. (outputSlot as DynamicMatrixMaterialSlot).SetConcreteType(dynamicMatrixType);
  416. continue;
  417. }
  418. }
  419. isInError |= inputError;
  420. tempSlots.Clear();
  421. GetOutputSlots(tempSlots);
  422. isInError |= tempSlots.Any(x => x.hasError);
  423. isInError |= CalculateNodeHasError(ref errorMessage);
  424. isInError |= ValidateConcretePrecision(ref errorMessage);
  425. hasError = isInError;
  426. if (isInError)
  427. {
  428. ((GraphData) owner).AddValidationError(tempId, errorMessage);
  429. }
  430. else
  431. {
  432. ++version;
  433. }
  434. ListPool<DynamicVectorMaterialSlot>.Release(skippedDynamicSlots);
  435. DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
  436. ListPool<DynamicMatrixMaterialSlot>.Release(skippedDynamicMatrixSlots);
  437. DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Release(dynamicMatrixInputSlotsToCompare);
  438. }
  439. }
  440. public int version { get; set; }
  441. public virtual bool canCopyNode => true;
  442. //True if error
  443. protected virtual bool CalculateNodeHasError(ref string errorMessage)
  444. {
  445. foreach (var slot in this.GetInputSlots<MaterialSlot>())
  446. {
  447. if (slot.isConnected)
  448. {
  449. var edge = owner.GetEdges(slot.slotReference).First();
  450. var outputNode = owner.GetNodeFromGuid(edge.outputSlot.nodeGuid);
  451. var outputSlot = outputNode.GetOutputSlots<MaterialSlot>().First(s => s.id == edge.outputSlot.slotId);
  452. if (!slot.IsCompatibleWith(outputSlot))
  453. {
  454. errorMessage = $"Slot {slot.RawDisplayName()} cannot accept input of type {outputSlot.concreteValueType}.";
  455. return true;
  456. }
  457. }
  458. }
  459. return false;
  460. }
  461. public virtual void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
  462. {
  463. using (var tempSlots = PooledList<MaterialSlot>.Get())
  464. using (var tempPreviewProperties = PooledList<PreviewProperty>.Get())
  465. using (var tempEdges = PooledList<IEdge>.Get())
  466. {
  467. GetInputSlots(tempSlots);
  468. foreach (var s in tempSlots)
  469. {
  470. tempPreviewProperties.Clear();
  471. tempEdges.Clear();
  472. owner.GetEdges(s.slotReference, tempEdges);
  473. if (tempEdges.Any())
  474. continue;
  475. s.GetPreviewProperties(tempPreviewProperties, GetVariableNameForSlot(s.id));
  476. for (int i = 0; i < tempPreviewProperties.Count; i++)
  477. {
  478. if (tempPreviewProperties[i].name == null)
  479. continue;
  480. properties.Add(tempPreviewProperties[i]);
  481. }
  482. }
  483. }
  484. }
  485. public virtual string GetVariableNameForSlot(int slotId)
  486. {
  487. var slot = FindSlot<MaterialSlot>(slotId);
  488. if (slot == null)
  489. 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");
  490. return string.Format("_{0}_{1}_{2}", GetVariableNameForNode(), NodeUtils.GetHLSLSafeName(slot.shaderOutputName), unchecked((uint)slotId));
  491. }
  492. public virtual string GetVariableNameForNode()
  493. {
  494. return defaultVariableName;
  495. }
  496. public void AddSlot(ISlot slot)
  497. {
  498. if (!(slot is MaterialSlot))
  499. throw new ArgumentException(string.Format("Trying to add slot {0} to Material node {1}, but it is not a {2}", slot, this, typeof(MaterialSlot)));
  500. var addingSlot = (MaterialSlot)slot;
  501. var foundSlot = FindSlot<MaterialSlot>(slot.id);
  502. // this will remove the old slot and add a new one
  503. // if an old one was found. This allows updating values
  504. m_Slots.RemoveAll(x => x.id == slot.id);
  505. m_Slots.Add(slot);
  506. slot.owner = this;
  507. OnSlotsChanged();
  508. if (foundSlot == null)
  509. return;
  510. addingSlot.CopyValuesFrom(foundSlot);
  511. }
  512. public void RemoveSlot(int slotId)
  513. {
  514. // Remove edges that use this slot
  515. // no owner can happen after creation
  516. // but before added to graph
  517. if (owner != null)
  518. {
  519. var edges = owner.GetEdges(GetSlotReference(slotId));
  520. foreach (var edge in edges.ToArray())
  521. owner.RemoveEdge(edge);
  522. }
  523. //remove slots
  524. m_Slots.RemoveAll(x => x.id == slotId);
  525. OnSlotsChanged();
  526. }
  527. protected virtual void OnSlotsChanged()
  528. {
  529. Dirty(ModificationScope.Topological);
  530. owner?.ClearErrorsForNode(this);
  531. }
  532. public void RemoveSlotsNameNotMatching(IEnumerable<int> slotIds, bool supressWarnings = false)
  533. {
  534. var invalidSlots = m_Slots.Select(x => x.id).Except(slotIds);
  535. foreach (var invalidSlot in invalidSlots.ToArray())
  536. {
  537. if (!supressWarnings)
  538. Debug.LogWarningFormat("Removing Invalid MaterialSlot: {0}", invalidSlot);
  539. RemoveSlot(invalidSlot);
  540. }
  541. }
  542. public SlotReference GetSlotReference(int slotId)
  543. {
  544. var slot = FindSlot<ISlot>(slotId);
  545. if (slot == null)
  546. throw new ArgumentException("Slot could not be found", "slotId");
  547. return new SlotReference(guid, slotId);
  548. }
  549. public T FindSlot<T>(int slotId) where T : ISlot
  550. {
  551. foreach (var slot in m_Slots)
  552. {
  553. if (slot.id == slotId && slot is T)
  554. return (T)slot;
  555. }
  556. return default(T);
  557. }
  558. public T FindInputSlot<T>(int slotId) where T : ISlot
  559. {
  560. foreach (var slot in m_Slots)
  561. {
  562. if (slot.isInputSlot && slot.id == slotId && slot is T)
  563. return (T)slot;
  564. }
  565. return default(T);
  566. }
  567. public T FindOutputSlot<T>(int slotId) where T : ISlot
  568. {
  569. foreach (var slot in m_Slots)
  570. {
  571. if (slot.isOutputSlot && slot.id == slotId && slot is T)
  572. return (T)slot;
  573. }
  574. return default(T);
  575. }
  576. public virtual IEnumerable<ISlot> GetInputsWithNoConnection()
  577. {
  578. return this.GetInputSlots<ISlot>().Where(x => !owner.GetEdges(GetSlotReference(x.id)).Any());
  579. }
  580. public virtual void OnBeforeSerialize()
  581. {
  582. m_GuidSerialized = m_Guid.ToString();
  583. m_GroupGuidSerialized = m_GroupGuid.ToString();
  584. m_SerializableSlots = SerializationHelper.Serialize<ISlot>(m_Slots);
  585. }
  586. public virtual void OnAfterDeserialize()
  587. {
  588. if (!string.IsNullOrEmpty(m_GuidSerialized))
  589. m_Guid = new Guid(m_GuidSerialized);
  590. else
  591. m_Guid = Guid.NewGuid();
  592. if (m_NodeVersion != GetCompiledNodeVersion())
  593. {
  594. UpgradeNodeWithVersion(m_NodeVersion, GetCompiledNodeVersion());
  595. m_NodeVersion = GetCompiledNodeVersion();
  596. }
  597. if (!string.IsNullOrEmpty(m_GroupGuidSerialized))
  598. m_GroupGuid = new Guid(m_GroupGuidSerialized);
  599. else
  600. m_GroupGuid = Guid.Empty;
  601. m_Slots = SerializationHelper.Deserialize<ISlot>(m_SerializableSlots, GraphUtil.GetLegacyTypeRemapping());
  602. m_SerializableSlots = null;
  603. foreach (var s in m_Slots)
  604. s.owner = this;
  605. UpdateNodeAfterDeserialization();
  606. }
  607. public virtual void UpdateNodeAfterDeserialization()
  608. {}
  609. public virtual int GetCompiledNodeVersion() => 0;
  610. public virtual void UpgradeNodeWithVersion(int from, int to)
  611. {}
  612. public bool IsSlotConnected(int slotId)
  613. {
  614. var slot = FindSlot<MaterialSlot>(slotId);
  615. return slot != null && owner.GetEdges(slot.slotReference).Any();
  616. }
  617. }
  618. }