123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text.RegularExpressions;
- using UnityEngine;
- using UnityEditor.Graphing;
- using UnityEditor.Graphing.Util;
- using UnityEditor.Rendering;
- using UnityEditor.ShaderGraph.Internal;
- using Edge = UnityEditor.Graphing.Edge;
- namespace UnityEditor.ShaderGraph
- {
- [Serializable]
- [FormerName("UnityEditor.ShaderGraph.MaterialGraph")]
- [FormerName("UnityEditor.ShaderGraph.SubGraph")]
- [FormerName("UnityEditor.ShaderGraph.AbstractMaterialGraph")]
- sealed class GraphData : ISerializationCallbackReceiver
- {
- public GraphObject owner { get; set; }
- #region Input data
- [NonSerialized]
- List<AbstractShaderProperty> m_Properties = new List<AbstractShaderProperty>();
- public IEnumerable<AbstractShaderProperty> properties
- {
- get { return m_Properties; }
- }
- [SerializeField]
- List<SerializationHelper.JSONSerializedElement> m_SerializedProperties = new List<SerializationHelper.JSONSerializedElement>();
- [NonSerialized]
- List<ShaderKeyword> m_Keywords = new List<ShaderKeyword>();
- public IEnumerable<ShaderKeyword> keywords
- {
- get { return m_Keywords; }
- }
- [SerializeField]
- List<SerializationHelper.JSONSerializedElement> m_SerializedKeywords = new List<SerializationHelper.JSONSerializedElement>();
- [NonSerialized]
- List<ShaderInput> m_AddedInputs = new List<ShaderInput>();
- public IEnumerable<ShaderInput> addedInputs
- {
- get { return m_AddedInputs; }
- }
- [NonSerialized]
- List<Guid> m_RemovedInputs = new List<Guid>();
- public IEnumerable<Guid> removedInputs
- {
- get { return m_RemovedInputs; }
- }
- [NonSerialized]
- List<ShaderInput> m_MovedInputs = new List<ShaderInput>();
- public IEnumerable<ShaderInput> movedInputs
- {
- get { return m_MovedInputs; }
- }
- public string assetGuid { get; set; }
- #endregion
- #region Node data
- [NonSerialized]
- Stack<Identifier> m_FreeNodeTempIds = new Stack<Identifier>();
- [NonSerialized]
- List<AbstractMaterialNode> m_Nodes = new List<AbstractMaterialNode>();
- [NonSerialized]
- Dictionary<Guid, AbstractMaterialNode> m_NodeDictionary = new Dictionary<Guid, AbstractMaterialNode>();
- public IEnumerable<T> GetNodes<T>()
- {
- return m_Nodes.Where(x => x != null).OfType<T>();
- }
- [SerializeField]
- List<SerializationHelper.JSONSerializedElement> m_SerializableNodes = new List<SerializationHelper.JSONSerializedElement>();
- [NonSerialized]
- List<AbstractMaterialNode> m_AddedNodes = new List<AbstractMaterialNode>();
- public IEnumerable<AbstractMaterialNode> addedNodes
- {
- get { return m_AddedNodes; }
- }
- [NonSerialized]
- List<AbstractMaterialNode> m_RemovedNodes = new List<AbstractMaterialNode>();
- public IEnumerable<AbstractMaterialNode> removedNodes
- {
- get { return m_RemovedNodes; }
- }
- [NonSerialized]
- List<AbstractMaterialNode> m_PastedNodes = new List<AbstractMaterialNode>();
- public IEnumerable<AbstractMaterialNode> pastedNodes
- {
- get { return m_PastedNodes; }
- }
- #endregion
- #region Group Data
- [SerializeField]
- List<GroupData> m_Groups = new List<GroupData>();
- public IEnumerable<GroupData> groups
- {
- get { return m_Groups; }
- }
- [NonSerialized]
- List<GroupData> m_AddedGroups = new List<GroupData>();
- public IEnumerable<GroupData> addedGroups
- {
- get { return m_AddedGroups; }
- }
- [NonSerialized]
- List<GroupData> m_RemovedGroups = new List<GroupData>();
- public IEnumerable<GroupData> removedGroups
- {
- get { return m_RemovedGroups; }
- }
- [NonSerialized]
- List<GroupData> m_PastedGroups = new List<GroupData>();
- public IEnumerable<GroupData> pastedGroups
- {
- get { return m_PastedGroups; }
- }
- [NonSerialized]
- List<ParentGroupChange> m_ParentGroupChanges = new List<ParentGroupChange>();
- public IEnumerable<ParentGroupChange> parentGroupChanges
- {
- get { return m_ParentGroupChanges; }
- }
- [NonSerialized]
- GroupData m_MostRecentlyCreatedGroup;
- public GroupData mostRecentlyCreatedGroup => m_MostRecentlyCreatedGroup;
- [NonSerialized]
- Dictionary<Guid, List<IGroupItem>> m_GroupItems = new Dictionary<Guid, List<IGroupItem>>();
- public IEnumerable<IGroupItem> GetItemsInGroup(GroupData groupData)
- {
- if (m_GroupItems.TryGetValue(groupData.guid, out var nodes))
- {
- return nodes;
- }
- return Enumerable.Empty<IGroupItem>();
- }
- #endregion
- #region StickyNote Data
- [SerializeField]
- List<StickyNoteData> m_StickyNotes = new List<StickyNoteData>();
- public IEnumerable<StickyNoteData> stickyNotes => m_StickyNotes;
- [NonSerialized]
- List<StickyNoteData> m_AddedStickyNotes = new List<StickyNoteData>();
- public List<StickyNoteData> addedStickyNotes => m_AddedStickyNotes;
- [NonSerialized]
- List<StickyNoteData> m_RemovedNotes = new List<StickyNoteData>();
- public IEnumerable<StickyNoteData> removedNotes => m_RemovedNotes;
- [NonSerialized]
- List<StickyNoteData> m_PastedStickyNotes = new List<StickyNoteData>();
- public IEnumerable<StickyNoteData> pastedStickyNotes => m_PastedStickyNotes;
- #endregion
- #region Edge data
- [NonSerialized]
- List<IEdge> m_Edges = new List<IEdge>();
- public IEnumerable<IEdge> edges
- {
- get { return m_Edges; }
- }
- [SerializeField]
- List<SerializationHelper.JSONSerializedElement> m_SerializableEdges = new List<SerializationHelper.JSONSerializedElement>();
- [NonSerialized]
- Dictionary<Guid, List<IEdge>> m_NodeEdges = new Dictionary<Guid, List<IEdge>>();
- [NonSerialized]
- List<IEdge> m_AddedEdges = new List<IEdge>();
- public IEnumerable<IEdge> addedEdges
- {
- get { return m_AddedEdges; }
- }
- [NonSerialized]
- List<IEdge> m_RemovedEdges = new List<IEdge>();
- public IEnumerable<IEdge> removedEdges
- {
- get { return m_RemovedEdges; }
- }
- #endregion
- [SerializeField]
- InspectorPreviewData m_PreviewData = new InspectorPreviewData();
- public InspectorPreviewData previewData
- {
- get { return m_PreviewData; }
- set { m_PreviewData = value; }
- }
- [SerializeField]
- string m_Path;
- public string path
- {
- get { return m_Path; }
- set
- {
- if (m_Path == value)
- return;
- m_Path = value;
- if(owner != null)
- owner.RegisterCompleteObjectUndo("Change Path");
- }
- }
- public MessageManager messageManager { get; set; }
- public bool isSubGraph { get; set; }
- [SerializeField]
- private ConcretePrecision m_ConcretePrecision = ConcretePrecision.Float;
- public ConcretePrecision concretePrecision
- {
- get => m_ConcretePrecision;
- set => m_ConcretePrecision = value;
- }
- [NonSerialized]
- Guid m_ActiveOutputNodeGuid;
- public Guid activeOutputNodeGuid
- {
- get { return m_ActiveOutputNodeGuid; }
- set
- {
- if (value != m_ActiveOutputNodeGuid)
- {
- m_ActiveOutputNodeGuid = value;
- m_OutputNode = null;
- didActiveOutputNodeChange = true;
- }
- }
- }
- [SerializeField]
- string m_ActiveOutputNodeGuidSerialized;
- [NonSerialized]
- private AbstractMaterialNode m_OutputNode;
- public AbstractMaterialNode outputNode
- {
- get
- {
- // find existing node
- if (m_OutputNode == null)
- {
- if (isSubGraph)
- {
- m_OutputNode = GetNodes<SubGraphOutputNode>().FirstOrDefault();
- }
- else
- {
- m_OutputNode = GetNodeFromGuid(m_ActiveOutputNodeGuid);
- }
- }
- return m_OutputNode;
- }
- }
- public bool didActiveOutputNodeChange { get; set; }
- internal delegate void SaveGraphDelegate(Shader shader, object context);
- internal static SaveGraphDelegate onSaveGraph;
- public GraphData()
- {
- m_GroupItems[Guid.Empty] = new List<IGroupItem>();
- }
- public void ClearChanges()
- {
- m_AddedNodes.Clear();
- m_RemovedNodes.Clear();
- m_PastedNodes.Clear();
- m_ParentGroupChanges.Clear();
- m_AddedGroups.Clear();
- m_RemovedGroups.Clear();
- m_PastedGroups.Clear();
- m_AddedEdges.Clear();
- m_RemovedEdges.Clear();
- m_AddedInputs.Clear();
- m_RemovedInputs.Clear();
- m_MovedInputs.Clear();
- m_AddedStickyNotes.Clear();
- m_RemovedNotes.Clear();
- m_PastedStickyNotes.Clear();
- m_MostRecentlyCreatedGroup = null;
- didActiveOutputNodeChange = false;
- }
- public void AddNode(AbstractMaterialNode node)
- {
- if (node is AbstractMaterialNode materialNode)
- {
- if (isSubGraph && !materialNode.allowedInSubGraph)
- {
- Debug.LogWarningFormat("Attempting to add {0} to Sub Graph. This is not allowed.", materialNode.GetType());
- return;
- }
- AddNodeNoValidate(materialNode);
- // If adding a Sub Graph node whose asset contains Keywords
- // Need to restest Keywords against the variant limit
- if(node is SubGraphNode subGraphNode &&
- subGraphNode.asset != null &&
- subGraphNode.asset.keywords.Count > 0)
- {
- OnKeywordChangedNoValidate();
- }
- ValidateGraph();
- }
- else
- {
- Debug.LogWarningFormat("Trying to add node {0} to Material graph, but it is not a {1}", node, typeof(AbstractMaterialNode));
- }
- }
- public void CreateGroup(GroupData groupData)
- {
- if (AddGroup(groupData))
- {
- m_MostRecentlyCreatedGroup = groupData;
- }
- }
- bool AddGroup(GroupData groupData)
- {
- if (m_Groups.Contains(groupData))
- return false;
- m_Groups.Add(groupData);
- m_AddedGroups.Add(groupData);
- m_GroupItems.Add(groupData.guid, new List<IGroupItem>());
- return true;
- }
- public void RemoveGroup(GroupData groupData)
- {
- RemoveGroupNoValidate(groupData);
- ValidateGraph();
- }
- void RemoveGroupNoValidate(GroupData group)
- {
- if (!m_Groups.Contains(group))
- throw new InvalidOperationException("Cannot remove a group that doesn't exist.");
- m_Groups.Remove(group);
- m_RemovedGroups.Add(group);
- if (m_GroupItems.TryGetValue(group.guid, out var items))
- {
- foreach (IGroupItem groupItem in items.ToList())
- {
- SetGroup(groupItem, null);
- }
- m_GroupItems.Remove(group.guid);
- }
- }
- public void AddStickyNote(StickyNoteData stickyNote)
- {
- if (m_StickyNotes.Contains(stickyNote))
- {
- throw new InvalidOperationException("Sticky note has already been added to the graph.");
- }
- if (!m_GroupItems.ContainsKey(stickyNote.groupGuid))
- {
- throw new InvalidOperationException("Trying to add sticky note with group that doesn't exist.");
- }
- m_StickyNotes.Add(stickyNote);
- m_AddedStickyNotes.Add(stickyNote);
- m_GroupItems[stickyNote.groupGuid].Add(stickyNote);
- }
- void RemoveNoteNoValidate(StickyNoteData stickyNote)
- {
- if (!m_StickyNotes.Contains(stickyNote))
- {
- throw new InvalidOperationException("Cannot remove a note that doesn't exist.");
- }
- m_StickyNotes.Remove(stickyNote);
- m_RemovedNotes.Add(stickyNote);
- if (m_GroupItems.TryGetValue(stickyNote.groupGuid, out var groupItems))
- {
- groupItems.Remove(stickyNote);
- }
- }
- public void RemoveStickyNote(StickyNoteData stickyNote)
- {
- RemoveNoteNoValidate(stickyNote);
- ValidateGraph();
- }
- public void SetGroup(IGroupItem node, GroupData group)
- {
- var groupChange = new ParentGroupChange()
- {
- groupItem = node,
- oldGroupGuid = node.groupGuid,
- // Checking if the groupdata is null. If it is, then it means node has been removed out of a group.
- // If the group data is null, then maybe the old group id should be removed
- newGroupGuid = group?.guid ?? Guid.Empty
- };
- node.groupGuid = groupChange.newGroupGuid;
- var oldGroupNodes = m_GroupItems[groupChange.oldGroupGuid];
- oldGroupNodes.Remove(node);
- m_GroupItems[groupChange.newGroupGuid].Add(node);
- m_ParentGroupChanges.Add(groupChange);
- }
- void AddNodeNoValidate(AbstractMaterialNode node)
- {
- if (node.groupGuid != Guid.Empty && !m_GroupItems.ContainsKey(node.groupGuid))
- {
- throw new InvalidOperationException("Cannot add a node whose group doesn't exist.");
- }
- node.owner = this;
- if (m_FreeNodeTempIds.Any())
- {
- var id = m_FreeNodeTempIds.Pop();
- id.IncrementVersion();
- node.tempId = id;
- m_Nodes[id.index] = node;
- }
- else
- {
- var id = new Identifier(m_Nodes.Count);
- node.tempId = id;
- m_Nodes.Add(node);
- }
- m_NodeDictionary.Add(node.guid, node);
- m_AddedNodes.Add(node);
- m_GroupItems[node.groupGuid].Add(node);
- }
- public void RemoveNode(AbstractMaterialNode node)
- {
- if (!node.canDeleteNode)
- {
- throw new InvalidOperationException($"Node {node.name} ({node.guid}) cannot be deleted.");
- }
- RemoveNodeNoValidate(node);
- ValidateGraph();
- }
- void RemoveNodeNoValidate(AbstractMaterialNode node)
- {
- if (!m_NodeDictionary.ContainsKey(node.guid))
- {
- throw new InvalidOperationException("Cannot remove a node that doesn't exist.");
- }
- m_Nodes[node.tempId.index] = null;
- m_FreeNodeTempIds.Push(node.tempId);
- m_NodeDictionary.Remove(node.guid);
- messageManager?.RemoveNode(node.tempId);
- m_RemovedNodes.Add(node);
- if (m_GroupItems.TryGetValue(node.groupGuid, out var groupItems))
- {
- groupItems.Remove(node);
- }
- }
- void AddEdgeToNodeEdges(IEdge edge)
- {
- List<IEdge> inputEdges;
- if (!m_NodeEdges.TryGetValue(edge.inputSlot.nodeGuid, out inputEdges))
- m_NodeEdges[edge.inputSlot.nodeGuid] = inputEdges = new List<IEdge>();
- inputEdges.Add(edge);
- List<IEdge> outputEdges;
- if (!m_NodeEdges.TryGetValue(edge.outputSlot.nodeGuid, out outputEdges))
- m_NodeEdges[edge.outputSlot.nodeGuid] = outputEdges = new List<IEdge>();
- outputEdges.Add(edge);
- }
- IEdge ConnectNoValidate(SlotReference fromSlotRef, SlotReference toSlotRef)
- {
- var fromNode = GetNodeFromGuid(fromSlotRef.nodeGuid);
- var toNode = GetNodeFromGuid(toSlotRef.nodeGuid);
- if (fromNode == null || toNode == null)
- return null;
- // if fromNode is already connected to toNode
- // do now allow a connection as toNode will then
- // have an edge to fromNode creating a cycle.
- // if this is parsed it will lead to an infinite loop.
- var dependentNodes = new List<AbstractMaterialNode>();
- NodeUtils.CollectNodesNodeFeedsInto(dependentNodes, toNode);
- if (dependentNodes.Contains(fromNode))
- return null;
- var fromSlot = fromNode.FindSlot<ISlot>(fromSlotRef.slotId);
- var toSlot = toNode.FindSlot<ISlot>(toSlotRef.slotId);
- if (fromSlot == null || toSlot == null)
- return null;
- if (fromSlot.isOutputSlot == toSlot.isOutputSlot)
- return null;
- var outputSlot = fromSlot.isOutputSlot ? fromSlotRef : toSlotRef;
- var inputSlot = fromSlot.isInputSlot ? fromSlotRef : toSlotRef;
- s_TempEdges.Clear();
- GetEdges(inputSlot, s_TempEdges);
- // remove any inputs that exits before adding
- foreach (var edge in s_TempEdges)
- {
- RemoveEdgeNoValidate(edge);
- }
- var newEdge = new Edge(outputSlot, inputSlot);
- m_Edges.Add(newEdge);
- m_AddedEdges.Add(newEdge);
- AddEdgeToNodeEdges(newEdge);
- //Debug.LogFormat("Connected edge: {0} -> {1} ({2} -> {3})\n{4}", newEdge.outputSlot.nodeGuid, newEdge.inputSlot.nodeGuid, fromNode.name, toNode.name, Environment.StackTrace);
- return newEdge;
- }
- public IEdge Connect(SlotReference fromSlotRef, SlotReference toSlotRef)
- {
- var newEdge = ConnectNoValidate(fromSlotRef, toSlotRef);
- ValidateGraph();
- return newEdge;
- }
- public void RemoveEdge(IEdge e)
- {
- RemoveEdgeNoValidate(e);
- ValidateGraph();
- }
- public void RemoveElements(AbstractMaterialNode[] nodes, IEdge[] edges, GroupData[] groups, StickyNoteData[] notes)
- {
- foreach (var node in nodes)
- {
- if (!node.canDeleteNode)
- {
- throw new InvalidOperationException($"Node {node.name} ({node.guid}) cannot be deleted.");
- }
- }
- foreach (var edge in edges.ToArray())
- {
- RemoveEdgeNoValidate(edge);
- }
- foreach (var serializableNode in nodes)
- {
- RemoveNodeNoValidate(serializableNode);
- }
- foreach (var noteData in notes)
- {
- RemoveNoteNoValidate(noteData);
- }
- foreach (var groupData in groups)
- {
- RemoveGroupNoValidate(groupData);
- }
- ValidateGraph();
- }
- void RemoveEdgeNoValidate(IEdge e)
- {
- e = m_Edges.FirstOrDefault(x => x.Equals(e));
- if (e == null)
- throw new ArgumentException("Trying to remove an edge that does not exist.", "e");
- m_Edges.Remove(e);
- List<IEdge> inputNodeEdges;
- if (m_NodeEdges.TryGetValue(e.inputSlot.nodeGuid, out inputNodeEdges))
- inputNodeEdges.Remove(e);
- List<IEdge> outputNodeEdges;
- if (m_NodeEdges.TryGetValue(e.outputSlot.nodeGuid, out outputNodeEdges))
- outputNodeEdges.Remove(e);
- m_RemovedEdges.Add(e);
- }
- public AbstractMaterialNode GetNodeFromGuid(Guid guid)
- {
- AbstractMaterialNode node;
- m_NodeDictionary.TryGetValue(guid, out node);
- return node;
- }
- public AbstractMaterialNode GetNodeFromTempId(Identifier tempId)
- {
- if (tempId.index > m_Nodes.Count)
- throw new ArgumentException("Trying to retrieve a node using an identifier that does not exist.");
- var node = m_Nodes[tempId.index];
- if (node == null)
- throw new Exception("Trying to retrieve a node using an identifier that does not exist.");
- if (node.tempId.version != tempId.version)
- throw new Exception("Trying to retrieve a node that was removed from the graph.");
- return node;
- }
- public bool ContainsNodeGuid(Guid guid)
- {
- return m_NodeDictionary.ContainsKey(guid);
- }
- public T GetNodeFromGuid<T>(Guid guid) where T : AbstractMaterialNode
- {
- var node = GetNodeFromGuid(guid);
- if (node is T)
- return (T)node;
- return default(T);
- }
- public void GetEdges(SlotReference s, List<IEdge> foundEdges)
- {
- var node = GetNodeFromGuid(s.nodeGuid);
- if (node == null)
- {
- return;
- }
- ISlot slot = node.FindSlot<ISlot>(s.slotId);
- List<IEdge> candidateEdges;
- if (!m_NodeEdges.TryGetValue(s.nodeGuid, out candidateEdges))
- return;
- foreach (var edge in candidateEdges)
- {
- var cs = slot.isInputSlot ? edge.inputSlot : edge.outputSlot;
- if (cs.nodeGuid == s.nodeGuid && cs.slotId == s.slotId)
- foundEdges.Add(edge);
- }
- }
- public IEnumerable<IEdge> GetEdges(SlotReference s)
- {
- var edges = new List<IEdge>();
- GetEdges(s, edges);
- return edges;
- }
- public void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode)
- {
- foreach (var prop in properties)
- {
- if(prop is GradientShaderProperty gradientProp && generationMode == GenerationMode.Preview)
- {
- GradientUtil.GetGradientPropertiesForPreview(collector, gradientProp.referenceName, gradientProp.value);
- continue;
- }
- collector.AddShaderProperty(prop);
- }
- }
- public void CollectShaderKeywords(KeywordCollector collector, GenerationMode generationMode)
- {
- foreach (var keyword in keywords)
- {
- collector.AddShaderKeyword(keyword);
- }
- // Alwways calculate permutations when collecting
- collector.CalculateKeywordPermutations();
- }
- public void AddGraphInput(ShaderInput input)
- {
- if (input == null)
- return;
- switch(input)
- {
- case AbstractShaderProperty property:
- if (m_Properties.Contains(property))
- return;
- m_Properties.Add(property);
- break;
- case ShaderKeyword keyword:
- if (m_Keywords.Contains(keyword))
- return;
- m_Keywords.Add(keyword);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- m_AddedInputs.Add(input);
- }
- public void SanitizeGraphInputName(ShaderInput input)
- {
- input.displayName = input.displayName.Trim();
- switch(input)
- {
- case AbstractShaderProperty property:
- input.displayName = GraphUtil.SanitizeName(properties.Where(p => p.guid != input.guid).Select(p => p.displayName), "{0} ({1})", input.displayName);
- break;
- case ShaderKeyword keyword:
- input.displayName = GraphUtil.SanitizeName(keywords.Where(p => p.guid != input.guid).Select(p => p.displayName), "{0} ({1})", input.displayName);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- public void SanitizeGraphInputReferenceName(ShaderInput input, string newName)
- {
- if (string.IsNullOrEmpty(newName))
- return;
- string name = newName.Trim();
- if (string.IsNullOrEmpty(name))
- return;
- name = Regex.Replace(name, @"(?:[^A-Za-z_0-9])|(?:\s)", "_");
- switch(input)
- {
- case AbstractShaderProperty property:
- property.overrideReferenceName = GraphUtil.SanitizeName(properties.Where(p => p.guid != property.guid).Select(p => p.referenceName), "{0}_{1}", name);
- break;
- case ShaderKeyword keyword:
- keyword.overrideReferenceName = GraphUtil.SanitizeName(keywords.Where(p => p.guid != input.guid).Select(p => p.referenceName), "{0}_{1}", name).ToUpper();
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- public void RemoveGraphInput(ShaderInput input)
- {
- switch(input)
- {
- case AbstractShaderProperty property:
- var propetyNodes = GetNodes<PropertyNode>().Where(x => x.propertyGuid == input.guid).ToList();
- foreach (var propNode in propetyNodes)
- ReplacePropertyNodeWithConcreteNodeNoValidate(propNode);
- break;
- }
- RemoveGraphInputNoValidate(input.guid);
- ValidateGraph();
- }
- public void MoveProperty(AbstractShaderProperty property, int newIndex)
- {
- if (newIndex > m_Properties.Count || newIndex < 0)
- throw new ArgumentException("New index is not within properties list.");
- var currentIndex = m_Properties.IndexOf(property);
- if (currentIndex == -1)
- throw new ArgumentException("Property is not in graph.");
- if (newIndex == currentIndex)
- return;
- m_Properties.RemoveAt(currentIndex);
- if (newIndex > currentIndex)
- newIndex--;
- var isLast = newIndex == m_Properties.Count;
- if (isLast)
- m_Properties.Add(property);
- else
- m_Properties.Insert(newIndex, property);
- if (!m_MovedInputs.Contains(property))
- m_MovedInputs.Add(property);
- }
- public void MoveKeyword(ShaderKeyword keyword, int newIndex)
- {
- if (newIndex > m_Keywords.Count || newIndex < 0)
- throw new ArgumentException("New index is not within keywords list.");
- var currentIndex = m_Keywords.IndexOf(keyword);
- if (currentIndex == -1)
- throw new ArgumentException("Keyword is not in graph.");
- if (newIndex == currentIndex)
- return;
- m_Keywords.RemoveAt(currentIndex);
- if (newIndex > currentIndex)
- newIndex--;
- var isLast = newIndex == m_Keywords.Count;
- if (isLast)
- m_Keywords.Add(keyword);
- else
- m_Keywords.Insert(newIndex, keyword);
- if (!m_MovedInputs.Contains(keyword))
- m_MovedInputs.Add(keyword);
- }
- public int GetGraphInputIndex(ShaderInput input)
- {
- switch(input)
- {
- case AbstractShaderProperty property:
- return m_Properties.IndexOf(property);
- case ShaderKeyword keyword:
- return m_Keywords.IndexOf(keyword);
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- void RemoveGraphInputNoValidate(Guid guid)
- {
- if (m_Properties.RemoveAll(x => x.guid == guid) > 0 ||
- m_Keywords.RemoveAll(x => x.guid == guid) > 0)
- {
- m_RemovedInputs.Add(guid);
- m_AddedInputs.RemoveAll(x => x.guid == guid);
- m_MovedInputs.RemoveAll(x => x.guid == guid);
- }
- }
- static List<IEdge> s_TempEdges = new List<IEdge>();
- public void ReplacePropertyNodeWithConcreteNode(PropertyNode propertyNode)
- {
- ReplacePropertyNodeWithConcreteNodeNoValidate(propertyNode);
- ValidateGraph();
- }
- void ReplacePropertyNodeWithConcreteNodeNoValidate(PropertyNode propertyNode)
- {
- var property = properties.FirstOrDefault(x => x.guid == propertyNode.propertyGuid);
- if (property == null)
- return;
- var node = property.ToConcreteNode() as AbstractMaterialNode;
- if (node == null)
- return;
- var slot = propertyNode.FindOutputSlot<MaterialSlot>(PropertyNode.OutputSlotId);
- var newSlot = node.GetOutputSlots<MaterialSlot>().FirstOrDefault(s => s.valueType == slot.valueType);
- if (newSlot == null)
- return;
- node.drawState = propertyNode.drawState;
- node.groupGuid = propertyNode.groupGuid;
- AddNodeNoValidate(node);
- foreach (var edge in this.GetEdges(slot.slotReference))
- ConnectNoValidate(newSlot.slotReference, edge.inputSlot);
- RemoveNodeNoValidate(propertyNode);
- }
- public void OnKeywordChanged()
- {
- OnKeywordChangedNoValidate();
- ValidateGraph();
- }
- public void OnKeywordChangedNoValidate()
- {
- var allNodes = GetNodes<AbstractMaterialNode>();
- foreach(AbstractMaterialNode node in allNodes)
- {
- node.Dirty(ModificationScope.Topological);
- node.ValidateNode();
- }
- }
- public void ValidateGraph()
- {
- var propertyNodes = GetNodes<PropertyNode>().Where(n => !m_Properties.Any(p => p.guid == n.propertyGuid)).ToArray();
- foreach (var pNode in propertyNodes)
- ReplacePropertyNodeWithConcreteNodeNoValidate(pNode);
- messageManager?.ClearAllFromProvider(this);
- //First validate edges, remove any
- //orphans. This can happen if a user
- //manually modifies serialized data
- //of if they delete a node in the inspector
- //debug view.
- foreach (var edge in edges.ToArray())
- {
- var outputNode = GetNodeFromGuid(edge.outputSlot.nodeGuid);
- var inputNode = GetNodeFromGuid(edge.inputSlot.nodeGuid);
- MaterialSlot outputSlot = null;
- MaterialSlot inputSlot = null;
- if (outputNode != null && inputNode != null)
- {
- outputSlot = outputNode.FindOutputSlot<MaterialSlot>(edge.outputSlot.slotId);
- inputSlot = inputNode.FindInputSlot<MaterialSlot>(edge.inputSlot.slotId);
- }
- if (outputNode == null
- || inputNode == null
- || outputSlot == null
- || inputSlot == null)
- {
- //orphaned edge
- RemoveEdgeNoValidate(edge);
- }
- }
- var temporaryMarks = IndexSetPool.Get();
- var permanentMarks = IndexSetPool.Get();
- var slots = ListPool<MaterialSlot>.Get();
- // Make sure we process a node's children before the node itself.
- var stack = StackPool<AbstractMaterialNode>.Get();
- foreach (var node in GetNodes<AbstractMaterialNode>())
- {
- stack.Push(node);
- }
- while (stack.Count > 0)
- {
- var node = stack.Pop();
- if (permanentMarks.Contains(node.tempId.index))
- {
- continue;
- }
- if (temporaryMarks.Contains(node.tempId.index))
- {
- node.ValidateNode();
- permanentMarks.Add(node.tempId.index);
- }
- else
- {
- temporaryMarks.Add(node.tempId.index);
- stack.Push(node);
- node.GetInputSlots(slots);
- foreach (var inputSlot in slots)
- {
- var nodeEdges = GetEdges(inputSlot.slotReference);
- foreach (var edge in nodeEdges)
- {
- var fromSocketRef = edge.outputSlot;
- var childNode = GetNodeFromGuid(fromSocketRef.nodeGuid);
- if (childNode != null)
- {
- stack.Push(childNode);
- }
- }
- }
- slots.Clear();
- }
- }
- StackPool<AbstractMaterialNode>.Release(stack);
- ListPool<MaterialSlot>.Release(slots);
- IndexSetPool.Release(temporaryMarks);
- IndexSetPool.Release(permanentMarks);
- foreach (var edge in m_AddedEdges.ToList())
- {
- if (!ContainsNodeGuid(edge.outputSlot.nodeGuid) || !ContainsNodeGuid(edge.inputSlot.nodeGuid))
- {
- Debug.LogWarningFormat("Added edge is invalid: {0} -> {1}\n{2}", edge.outputSlot.nodeGuid, edge.inputSlot.nodeGuid, Environment.StackTrace);
- m_AddedEdges.Remove(edge);
- }
- }
- foreach (var groupChange in m_ParentGroupChanges.ToList())
- {
- if (groupChange.groupItem is AbstractMaterialNode node && !ContainsNodeGuid(node.guid))
- {
- m_ParentGroupChanges.Remove(groupChange);
- }
- if (groupChange.groupItem is StickyNoteData stickyNote && !m_StickyNotes.Contains(stickyNote))
- {
- m_ParentGroupChanges.Remove(groupChange);
- }
- }
- }
- public void AddValidationError(Identifier id, string errorMessage,
- ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
- {
- messageManager?.AddOrAppendError(this, id, new ShaderMessage(errorMessage, severity));
- }
- public void ClearErrorsForNode(AbstractMaterialNode node)
- {
- messageManager?.ClearNodesFromProvider(this, node.ToEnumerable());
- }
- public void ReplaceWith(GraphData other)
- {
- if (other == null)
- throw new ArgumentException("Can only replace with another AbstractMaterialGraph", "other");
- using (var removedInputsPooledObject = ListPool<Guid>.GetDisposable())
- {
- var removedInputGuids = removedInputsPooledObject.value;
- foreach (var property in m_Properties)
- removedInputGuids.Add(property.guid);
- foreach (var keyword in m_Keywords)
- removedInputGuids.Add(keyword.guid);
- foreach (var inputGuid in removedInputGuids)
- RemoveGraphInputNoValidate(inputGuid);
- }
- foreach (var otherProperty in other.properties)
- {
- if (!properties.Any(p => p.guid == otherProperty.guid))
- AddGraphInput(otherProperty);
- }
- foreach (var otherKeyword in other.keywords)
- {
- if (!keywords.Any(p => p.guid == otherKeyword.guid))
- AddGraphInput(otherKeyword);
- }
- other.ValidateGraph();
- ValidateGraph();
- // Current tactic is to remove all nodes and edges and then re-add them, such that depending systems
- // will re-initialize with new references.
- using (var removedGroupsPooledObject = ListPool<GroupData>.GetDisposable())
- {
- var removedGroupDatas = removedGroupsPooledObject.value;
- removedGroupDatas.AddRange(m_Groups);
- foreach (var groupData in removedGroupDatas)
- {
- RemoveGroupNoValidate(groupData);
- }
- }
- using (var removedNotesPooledObject = ListPool<StickyNoteData>.GetDisposable())
- {
- var removedNoteDatas = removedNotesPooledObject.value;
- removedNoteDatas.AddRange(m_StickyNotes);
- foreach (var groupData in removedNoteDatas)
- {
- RemoveNoteNoValidate(groupData);
- }
- }
- using (var pooledList = ListPool<IEdge>.GetDisposable())
- {
- var removedNodeEdges = pooledList.value;
- removedNodeEdges.AddRange(m_Edges);
- foreach (var edge in removedNodeEdges)
- RemoveEdgeNoValidate(edge);
- }
- using (var removedNodesPooledObject = ListPool<Guid>.GetDisposable())
- {
- var removedNodeGuids = removedNodesPooledObject.value;
- removedNodeGuids.AddRange(m_Nodes.Where(n => n != null).Select(n => n.guid));
- foreach (var nodeGuid in removedNodeGuids)
- RemoveNodeNoValidate(m_NodeDictionary[nodeGuid]);
- }
- ValidateGraph();
- foreach (GroupData groupData in other.groups)
- AddGroup(groupData);
- foreach (var stickyNote in other.stickyNotes)
- {
- AddStickyNote(stickyNote);
- }
- foreach (var node in other.GetNodes<AbstractMaterialNode>())
- AddNodeNoValidate(node);
- foreach (var edge in other.edges)
- ConnectNoValidate(edge.outputSlot, edge.inputSlot);
- ValidateGraph();
- }
- internal void PasteGraph(CopyPasteGraph graphToPaste, List<AbstractMaterialNode> remappedNodes, List<IEdge> remappedEdges)
- {
- var groupGuidMap = new Dictionary<Guid, Guid>();
- foreach (var group in graphToPaste.groups)
- {
- var position = group.position;
- position.x += 30;
- position.y += 30;
- GroupData newGroup = new GroupData(group.title, position);
- var oldGuid = group.guid;
- var newGuid = newGroup.guid;
- groupGuidMap[oldGuid] = newGuid;
- AddGroup(newGroup);
- m_PastedGroups.Add(newGroup);
- }
- foreach (var stickyNote in graphToPaste.stickyNotes)
- {
- var position = stickyNote.position;
- position.x += 30;
- position.y += 30;
- StickyNoteData pastedStickyNote = new StickyNoteData(stickyNote.title, stickyNote.content, position);
- if (groupGuidMap.ContainsKey(stickyNote.groupGuid))
- {
- pastedStickyNote.groupGuid = groupGuidMap[stickyNote.groupGuid];
- }
- AddStickyNote(pastedStickyNote);
- m_PastedStickyNotes.Add(pastedStickyNote);
- }
- var nodeGuidMap = new Dictionary<Guid, Guid>();
- foreach (var node in graphToPaste.GetNodes<AbstractMaterialNode>())
- {
- AbstractMaterialNode pastedNode = node;
- var oldGuid = node.guid;
- var newGuid = node.RewriteGuid();
- nodeGuidMap[oldGuid] = newGuid;
- // Check if the property nodes need to be made into a concrete node.
- if (node is PropertyNode propertyNode)
- {
- // If the property is not in the current graph, do check if the
- // property can be made into a concrete node.
- if (!m_Properties.Select(x => x.guid).Contains(propertyNode.propertyGuid))
- {
- // If the property is in the serialized paste graph, make the property node into a property node.
- var pastedGraphMetaProperties = graphToPaste.metaProperties.Where(x => x.guid == propertyNode.propertyGuid);
- if (pastedGraphMetaProperties.Any())
- {
- pastedNode = pastedGraphMetaProperties.FirstOrDefault().ToConcreteNode();
- pastedNode.drawState = node.drawState;
- nodeGuidMap[oldGuid] = pastedNode.guid;
- }
- }
- }
- AbstractMaterialNode abstractMaterialNode = (AbstractMaterialNode)node;
- // Check if the node is inside a group
- if (groupGuidMap.ContainsKey(abstractMaterialNode.groupGuid))
- {
- var absNode = pastedNode as AbstractMaterialNode;
- absNode.groupGuid = groupGuidMap[abstractMaterialNode.groupGuid];
- pastedNode = absNode;
- }
- var drawState = node.drawState;
- var position = drawState.position;
- position.x += 30;
- position.y += 30;
- drawState.position = position;
- node.drawState = drawState;
- remappedNodes.Add(pastedNode);
- AddNode(pastedNode);
- // add the node to the pasted node list
- m_PastedNodes.Add(pastedNode);
- // Check if the keyword nodes need to have their keywords copied.
- if (node is KeywordNode keywordNode)
- {
- // If the keyword is not in the current graph and is in the serialized paste graph copy it.
- if (!keywords.Select(x => x.guid).Contains(keywordNode.keywordGuid))
- {
- var pastedGraphMetaKeywords = graphToPaste.metaKeywords.Where(x => x.guid == keywordNode.keywordGuid);
- if (pastedGraphMetaKeywords.Any())
- {
- var keyword = pastedGraphMetaKeywords.FirstOrDefault(x => x.guid == keywordNode.keywordGuid);
- SanitizeGraphInputName(keyword);
- SanitizeGraphInputReferenceName(keyword, keyword.overrideReferenceName);
- AddGraphInput(keyword);
- }
- }
- // Always update Keyword nodes to handle any collisions resolved on the Keyword
- keywordNode.UpdateNode();
- }
- }
- // only connect edges within pasted elements, discard
- // external edges.
- foreach (var edge in graphToPaste.edges)
- {
- var outputSlot = edge.outputSlot;
- var inputSlot = edge.inputSlot;
- Guid remappedOutputNodeGuid;
- Guid remappedInputNodeGuid;
- if (nodeGuidMap.TryGetValue(outputSlot.nodeGuid, out remappedOutputNodeGuid)
- && nodeGuidMap.TryGetValue(inputSlot.nodeGuid, out remappedInputNodeGuid))
- {
- var outputSlotRef = new SlotReference(remappedOutputNodeGuid, outputSlot.slotId);
- var inputSlotRef = new SlotReference(remappedInputNodeGuid, inputSlot.slotId);
- remappedEdges.Add(Connect(outputSlotRef, inputSlotRef));
- }
- }
- ValidateGraph();
- }
- public void OnBeforeSerialize()
- {
- m_SerializableNodes = SerializationHelper.Serialize(GetNodes<AbstractMaterialNode>());
- m_SerializableEdges = SerializationHelper.Serialize<IEdge>(m_Edges);
- m_SerializedProperties = SerializationHelper.Serialize<AbstractShaderProperty>(m_Properties);
- m_SerializedKeywords = SerializationHelper.Serialize<ShaderKeyword>(m_Keywords);
- m_ActiveOutputNodeGuidSerialized = m_ActiveOutputNodeGuid == Guid.Empty ? null : m_ActiveOutputNodeGuid.ToString();
- }
- public void OnAfterDeserialize()
- {
- // have to deserialize 'globals' before nodes
- m_Properties = SerializationHelper.Deserialize<AbstractShaderProperty>(m_SerializedProperties, GraphUtil.GetLegacyTypeRemapping());
- m_Keywords = SerializationHelper.Deserialize<ShaderKeyword>(m_SerializedKeywords, GraphUtil.GetLegacyTypeRemapping());
- var nodes = SerializationHelper.Deserialize<AbstractMaterialNode>(m_SerializableNodes, GraphUtil.GetLegacyTypeRemapping());
- m_Nodes = new List<AbstractMaterialNode>(nodes.Count);
- m_NodeDictionary = new Dictionary<Guid, AbstractMaterialNode>(nodes.Count);
- foreach (var group in m_Groups)
- {
- m_GroupItems.Add(group.guid, new List<IGroupItem>());
- }
- foreach (var node in nodes)
- {
- node.owner = this;
- node.UpdateNodeAfterDeserialization();
- node.tempId = new Identifier(m_Nodes.Count);
- m_Nodes.Add(node);
- m_NodeDictionary.Add(node.guid, node);
- m_GroupItems[node.groupGuid].Add(node);
- }
- foreach (var stickyNote in m_StickyNotes)
- {
- m_GroupItems[stickyNote.groupGuid].Add(stickyNote);
- }
- m_SerializableNodes = null;
- m_Edges = SerializationHelper.Deserialize<IEdge>(m_SerializableEdges, GraphUtil.GetLegacyTypeRemapping());
- m_SerializableEdges = null;
- foreach (var edge in m_Edges)
- AddEdgeToNodeEdges(edge);
- m_OutputNode = null;
- if (!isSubGraph)
- {
- if (string.IsNullOrEmpty(m_ActiveOutputNodeGuidSerialized))
- {
- var node = (AbstractMaterialNode)GetNodes<IMasterNode>().FirstOrDefault();
- if (node != null)
- {
- m_ActiveOutputNodeGuid = node.guid;
- }
- }
- else
- {
- m_ActiveOutputNodeGuid = new Guid(m_ActiveOutputNodeGuidSerialized);
- }
- }
- }
- public void OnEnable()
- {
- foreach (var node in GetNodes<AbstractMaterialNode>().OfType<IOnAssetEnabled>())
- {
- node.OnEnable();
- }
- ShaderGraphPreferences.onVariantLimitChanged += OnKeywordChanged;
- }
- public void OnDisable()
- {
- ShaderGraphPreferences.onVariantLimitChanged -= OnKeywordChanged;
- }
- }
- [Serializable]
- class InspectorPreviewData
- {
- public SerializableMesh serializedMesh = new SerializableMesh();
- [NonSerialized]
- public Quaternion rotation = Quaternion.identity;
- [NonSerialized]
- public float scale = 1f;
- }
- }
|