123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEditor.Graphing;
- using UnityEditor.ShaderGraph.Drawing.Colors;
- using UnityEditor.ShaderGraph.Internal;
- using UnityEditor.ShaderGraph.Drawing;
- using UnityEditor.ShaderGraph.Serialization;
- using UnityEngine.Assertions;
- using UnityEngine.Pool;
-
- namespace UnityEditor.ShaderGraph
- {
- [Serializable]
- abstract class AbstractMaterialNode : JsonObject, IGroupItem, IRectInterface
- {
- [SerializeField]
- JsonRef<GroupData> m_Group = null;
-
- [SerializeField]
- private string m_Name;
-
- [SerializeField]
- private DrawState m_DrawState;
-
- [NonSerialized]
- bool m_HasError;
-
- [NonSerialized]
- bool m_IsValid = true;
-
- [NonSerialized]
- bool m_IsActive = true;
-
- [NonSerialized]
- bool m_WasUsedByGenerator = false;
-
- [SerializeField]
- List<JsonData<MaterialSlot>> m_Slots = new List<JsonData<MaterialSlot>>();
-
- public GraphData owner { get; set; }
-
- internal virtual bool ExposeToSearcher => true;
-
- OnNodeModified m_OnModified;
-
- Action m_UnregisterAll;
-
- public GroupData group
- {
- get => m_Group;
- set
- {
- if (m_Group == value)
- return;
-
- m_Group = value;
- Dirty(ModificationScope.Topological);
- }
- }
-
- public void RegisterCallback(OnNodeModified callback)
- {
- m_OnModified += callback;
-
- // Setup so we can unregister this callback later at teardown time
- m_UnregisterAll += () => m_OnModified -= callback;
- }
-
- public void UnregisterCallback(OnNodeModified callback)
- {
- m_OnModified -= callback;
- }
-
- public void Dirty(ModificationScope scope)
- {
- // Calling m_OnModified immediately upon dirtying the node can result in a lot of churn. For example,
- // nodes can cause cascading view updates *multiple times* per operation.
- // If this call causes future performance issues, we should investigate some kind of deferral or early out
- // until all of the dirty nodes have been identified.
- if (m_OnModified != null && !owner.replaceInProgress)
- m_OnModified(this, scope);
- NodeValidation.HandleValidationExtensions(this);
- }
-
- public string name
- {
- get { return m_Name; }
- set { m_Name = value; }
- }
-
- public string[] synonyms;
-
- protected virtual string documentationPage => name;
- public virtual string documentationURL => NodeUtils.GetDocumentationString(documentationPage);
-
- public virtual bool canDeleteNode => owner != null && owner.outputNode != this;
-
- public DrawState drawState
- {
- get { return m_DrawState; }
- set
- {
- m_DrawState = value;
- Dirty(ModificationScope.Layout);
- }
- }
-
- Rect IRectInterface.rect
- {
- get => drawState.position;
- set
- {
- var state = drawState;
- state.position = value;
- drawState = state;
- }
- }
-
- public virtual bool canSetPrecision
- {
- get { return true; }
- }
-
- // this is the precision after the inherit/automatic behavior has been calculated
- // it does NOT include fallback to any graph default precision
- public GraphPrecision graphPrecision { get; set; } = GraphPrecision.Single;
-
- private ConcretePrecision m_ConcretePrecision = ConcretePrecision.Single;
-
- public ConcretePrecision concretePrecision
- {
- get => m_ConcretePrecision;
- set => m_ConcretePrecision = value;
- }
-
- [SerializeField]
- private Precision m_Precision = Precision.Inherit;
-
- public Precision precision
- {
- get => m_Precision;
- set => m_Precision = value;
- }
-
- [SerializeField]
- bool m_PreviewExpanded = true;
-
- public bool previewExpanded
- {
- get { return m_PreviewExpanded; }
- set
- {
- if (previewExpanded == value)
- return;
- m_PreviewExpanded = value;
- Dirty(ModificationScope.Node);
- }
- }
-
- [SerializeField]
- protected int m_DismissedVersion = 0;
- public int dismissedUpdateVersion { get => m_DismissedVersion; set => m_DismissedVersion = value; }
-
- // by default, if this returns null, the system will allow creation of any previous version
- public virtual IEnumerable<int> allowedNodeVersions => null;
-
- // Nodes that want to have a preview area can override this and return true
- public virtual bool hasPreview
- {
- get { return false; }
- }
-
- [SerializeField]
- internal PreviewMode m_PreviewMode = PreviewMode.Inherit;
- public virtual PreviewMode previewMode
- {
- get { return m_PreviewMode; }
- }
-
- public virtual bool allowedInSubGraph
- {
- get { return !(this is BlockNode); }
- }
-
- public virtual bool allowedInMainGraph
- {
- get { return true; }
- }
-
- public virtual bool allowedInLayerGraph
- {
- get { return true; }
- }
-
- public virtual bool hasError
- {
- get { return m_HasError; }
- protected set { m_HasError = value; }
- }
-
- public virtual bool isActive
- {
- get { return m_IsActive; }
- }
-
- internal virtual bool wasUsedByGenerator
- {
- get { return m_WasUsedByGenerator; }
- }
-
- internal void SetUsedByGenerator()
- {
- m_WasUsedByGenerator = true;
- }
-
- //There are times when isActive needs to be set to a value explicitly, and
- //not be changed by active forest parsing (what we do when we need to figure out
- //what nodes should or should not be active, usually from an edit; see NodeUtils).
- //In this case, we allow for explicit setting of an active value that cant be overriden.
- //Implicit implies that active forest parsing can edit the nodes isActive property
- public enum ActiveState
- {
- Implicit = 0,
- ExplicitInactive = 1,
- ExplicitActive = 2
- }
-
- private ActiveState m_ActiveState = ActiveState.Implicit;
- public ActiveState activeState
- {
- get => m_ActiveState;
- }
-
- public void SetOverrideActiveState(ActiveState overrideState, bool updateConnections = true)
- {
- if (m_ActiveState == overrideState)
- {
- return;
- }
-
- m_ActiveState = overrideState;
- switch (m_ActiveState)
- {
- case ActiveState.Implicit:
- if (updateConnections)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(this);
- }
- break;
- case ActiveState.ExplicitInactive:
- if (m_IsActive == false)
- {
- break;
- }
- else
- {
- m_IsActive = false;
- Dirty(ModificationScope.Node);
- if (updateConnections)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(this);
- }
- break;
- }
- case ActiveState.ExplicitActive:
- if (m_IsActive == true)
- {
- break;
- }
- else
- {
- m_IsActive = true;
- Dirty(ModificationScope.Node);
- if (updateConnections)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(this);
- }
- break;
- }
- }
- }
-
- public void SetActive(bool value, bool updateConnections = true)
- {
- if (m_IsActive == value)
- return;
-
- if (m_ActiveState != ActiveState.Implicit)
- {
- Debug.LogError($"Cannot set IsActive on Node {this} when value is explicitly overriden by ActiveState {m_ActiveState}");
- return;
- }
-
- // Update this node
- m_IsActive = value;
- Dirty(ModificationScope.Node);
-
- if (updateConnections)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(this);
- }
- }
-
- public virtual bool isValid
- {
- get { return m_IsValid; }
- set
- {
- if (m_IsValid == value)
- return;
-
- m_IsValid = value;
- }
- }
-
-
- string m_DefaultVariableName;
- string m_NameForDefaultVariableName;
-
- string defaultVariableName
- {
- get
- {
- if (m_NameForDefaultVariableName != name)
- {
- m_DefaultVariableName = string.Format("{0}_{1}", NodeUtils.GetHLSLSafeName(name ?? "node"), objectId);
- m_NameForDefaultVariableName = name;
- }
- return m_DefaultVariableName;
- }
- }
-
- #region Custom Colors
-
- [SerializeField]
- CustomColorData m_CustomColors = new CustomColorData();
-
- public bool TryGetColor(string provider, ref Color color)
- {
- return m_CustomColors.TryGetColor(provider, out color);
- }
-
- public void ResetColor(string provider)
- {
- m_CustomColors.Remove(provider);
- }
-
- public void SetColor(string provider, Color color)
- {
- m_CustomColors.Set(provider, color);
- }
-
- #endregion
-
- protected AbstractMaterialNode()
- {
- m_DrawState.expanded = true;
- }
-
- public void GetInputSlots<T>(List<T> foundSlots) where T : MaterialSlot
- {
- foreach (var slot in m_Slots.SelectValue())
- {
- if (slot.isInputSlot && slot is T)
- foundSlots.Add((T)slot);
- }
- }
-
- public virtual void GetInputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots) where T : MaterialSlot
- {
- GetInputSlots(foundSlots);
- }
-
- public void GetOutputSlots<T>(List<T> foundSlots) where T : MaterialSlot
- {
- foreach (var slot in m_Slots.SelectValue())
- {
- if (slot.isOutputSlot && slot is T materialSlot)
- {
- foundSlots.Add(materialSlot);
- }
- }
- }
-
- public virtual void GetOutputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots) where T : MaterialSlot
- {
- GetOutputSlots(foundSlots);
- }
-
- public void GetSlots<T>(List<T> foundSlots) where T : MaterialSlot
- {
- foreach (var slot in m_Slots.SelectValue())
- {
- if (slot is T materialSlot)
- {
- foundSlots.Add(materialSlot);
- }
- }
- }
-
- public virtual void CollectShaderProperties(PropertyCollector properties, GenerationMode generationMode)
- {
- foreach (var inputSlot in this.GetInputSlots<MaterialSlot>())
- {
- var edges = owner.GetEdges(inputSlot.slotReference);
- if (edges.Any(e => e.outputSlot.node.isActive))
- continue;
-
- inputSlot.AddDefaultProperty(properties, generationMode);
- }
- }
-
- public string GetSlotValue(int inputSlotId, GenerationMode generationMode, ConcretePrecision concretePrecision)
- {
- string slotValue = GetSlotValue(inputSlotId, generationMode);
- return slotValue.Replace(PrecisionUtil.Token, concretePrecision.ToShaderString());
- }
-
- public string GetSlotValue(int inputSlotId, GenerationMode generationMode)
- {
- var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
- if (inputSlot == null)
- return string.Empty;
-
- var edges = owner.GetEdges(inputSlot.slotReference);
-
- if (edges.Any())
- {
- var fromSocketRef = edges.First().outputSlot;
- var fromNode = fromSocketRef.node;
- return fromNode.GetOutputForSlot(fromSocketRef, inputSlot.concreteValueType, generationMode);
- }
-
- return inputSlot.GetDefaultValue(generationMode);
- }
-
- public AbstractShaderProperty GetSlotProperty(int inputSlotId)
- {
- if (owner == null)
- return null;
-
- var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
- if (inputSlot?.slotReference.node == null)
- return null;
-
- var edges = owner.GetEdges(inputSlot.slotReference);
- if (edges.Any())
- {
- var fromSocketRef = edges.First().outputSlot;
- var fromNode = fromSocketRef.node;
- if (fromNode == null)
- return null; // this is an error condition... we have an edge that connects to a non-existant node?
-
- if (fromNode is PropertyNode propNode)
- {
- return propNode.property;
- }
-
- if (fromNode is RedirectNodeData redirectNode)
- {
- return redirectNode.GetSlotProperty(RedirectNodeData.kInputSlotID);
- }
-
- #if PROCEDURAL_VT_IN_GRAPH
- if (fromNode is ProceduralVirtualTextureNode pvtNode)
- {
- return pvtNode.AsShaderProperty();
- }
- #endif // PROCEDURAL_VT_IN_GRAPH
-
- return null;
- }
-
- return null;
- }
-
- protected internal virtual string GetOutputForSlot(SlotReference fromSocketRef, ConcreteSlotValueType valueType, GenerationMode generationMode)
- {
- var slot = FindOutputSlot<MaterialSlot>(fromSocketRef.slotId);
- if (slot == null)
- return string.Empty;
-
- if (fromSocketRef.node.isActive)
- return GenerationUtils.AdaptNodeOutput(this, slot.id, valueType);
- else
- return slot.GetDefaultValue(generationMode);
- }
-
- public AbstractMaterialNode GetInputNodeFromSlot(int inputSlotId)
- {
- var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
- if (inputSlot == null)
- return null;
-
- var edges = owner.GetEdges(inputSlot.slotReference).ToArray();
- AbstractMaterialNode fromNode = null;
- if (edges.Count() > 0)
- {
- var fromSocketRef = edges[0].outputSlot;
- fromNode = fromSocketRef.node;
- }
- return fromNode;
- }
-
- public static ConcreteSlotValueType ConvertDynamicVectorInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
- {
- var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
-
- var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
- switch (inputTypesDistinct.Count)
- {
- case 0:
- // nothing connected -- use Vec1 by default
- return ConcreteSlotValueType.Vector1;
- case 1:
- if (SlotValueHelper.AreCompatible(SlotValueType.DynamicVector, inputTypesDistinct.First()))
- {
- if (inputTypesDistinct.First() == ConcreteSlotValueType.Boolean)
- return ConcreteSlotValueType.Vector1;
- return inputTypesDistinct.First();
- }
- break;
- default:
- // find the 'minumum' channel width excluding 1 as it can promote
- inputTypesDistinct.RemoveAll(x => (x == ConcreteSlotValueType.Vector1) || (x == ConcreteSlotValueType.Boolean));
- var ordered = inputTypesDistinct.OrderByDescending(x => x);
- if (ordered.Any())
- {
- var first = ordered.FirstOrDefault();
- return first;
- }
- break;
- }
- return ConcreteSlotValueType.Vector1;
- }
-
- public static ConcreteSlotValueType ConvertDynamicMatrixInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
- {
- var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
-
- var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
- switch (inputTypesDistinct.Count)
- {
- case 0:
- return ConcreteSlotValueType.Matrix2;
- case 1:
- return inputTypesDistinct.FirstOrDefault();
- default:
- var ordered = inputTypesDistinct.OrderByDescending(x => x);
- if (ordered.Any())
- return ordered.FirstOrDefault();
- break;
- }
- return ConcreteSlotValueType.Matrix2;
- }
-
- protected const string k_validationErrorMessage = "Error found during node validation";
-
- // evaluate ALL the precisions...
- public virtual void UpdatePrecision(List<MaterialSlot> inputSlots)
- {
- // first let's reduce from precision ==> graph precision
- if (precision == Precision.Inherit)
- {
- // inherit means calculate it automatically based on inputs
-
- // If no inputs were found use the precision of the Graph
- if (inputSlots.Count == 0)
- {
- graphPrecision = GraphPrecision.Graph;
- }
- else
- {
- int curGraphPrecision = (int)GraphPrecision.Half;
- foreach (var inputSlot in inputSlots)
- {
- // If input port doesn't have an edge use the Graph's precision for that input
- var edges = owner?.GetEdges(inputSlot.slotReference).ToList();
- if (!edges.Any())
- {
- // disconnected inputs use graph precision
- curGraphPrecision = Math.Min(curGraphPrecision, (int)GraphPrecision.Graph);
- }
- else
- {
- var outputSlotRef = edges[0].outputSlot;
- var outputNode = outputSlotRef.node;
- curGraphPrecision = Math.Min(curGraphPrecision, (int)outputNode.graphPrecision);
- }
- }
- graphPrecision = (GraphPrecision)curGraphPrecision;
- }
- }
- else
- {
- // not inherited, just use the node's selected precision
- graphPrecision = precision.ToGraphPrecision(GraphPrecision.Graph);
- }
-
- // calculate the concrete precision, with fall-back to the graph concrete precision
- concretePrecision = graphPrecision.ToConcrete(owner.graphDefaultConcretePrecision);
- }
-
- public virtual void EvaluateDynamicMaterialSlots(List<MaterialSlot> inputSlots, List<MaterialSlot> outputSlots)
- {
- var dynamicInputSlotsToCompare = DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Get();
- var skippedDynamicSlots = ListPool<DynamicVectorMaterialSlot>.Get();
-
- var dynamicMatrixInputSlotsToCompare = DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Get();
- var skippedDynamicMatrixSlots = ListPool<DynamicMatrixMaterialSlot>.Get();
-
- // iterate the input slots
- {
- foreach (var inputSlot in inputSlots)
- {
- inputSlot.hasError = false;
- // if there is a connection
- var edges = owner.GetEdges(inputSlot.slotReference).ToList();
- if (!edges.Any())
- {
- if (inputSlot is DynamicVectorMaterialSlot)
- skippedDynamicSlots.Add(inputSlot as DynamicVectorMaterialSlot);
- if (inputSlot is DynamicMatrixMaterialSlot)
- skippedDynamicMatrixSlots.Add(inputSlot as DynamicMatrixMaterialSlot);
- continue;
- }
-
- // get the output details
- var outputSlotRef = edges[0].outputSlot;
- var outputNode = outputSlotRef.node;
- if (outputNode == null)
- continue;
-
- var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
- if (outputSlot == null)
- continue;
-
- if (outputSlot.hasError)
- {
- inputSlot.hasError = true;
- continue;
- }
-
- var outputConcreteType = outputSlot.concreteValueType;
- // dynamic input... depends on output from other node.
- // we need to compare ALL dynamic inputs to make sure they
- // are compatible.
- if (inputSlot is DynamicVectorMaterialSlot)
- {
- dynamicInputSlotsToCompare.Add((DynamicVectorMaterialSlot)inputSlot, outputConcreteType);
- continue;
- }
- else if (inputSlot is DynamicMatrixMaterialSlot)
- {
- dynamicMatrixInputSlotsToCompare.Add((DynamicMatrixMaterialSlot)inputSlot, outputConcreteType);
- continue;
- }
- }
-
- // we can now figure out the dynamic slotType
- // from here set all the
- var dynamicType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
- foreach (var dynamicKvP in dynamicInputSlotsToCompare)
- dynamicKvP.Key.SetConcreteType(dynamicType);
- foreach (var skippedSlot in skippedDynamicSlots)
- skippedSlot.SetConcreteType(dynamicType);
-
- // and now dynamic matrices
- var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicMatrixInputSlotsToCompare.Values);
- foreach (var dynamicKvP in dynamicMatrixInputSlotsToCompare)
- dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
- foreach (var skippedSlot in skippedDynamicMatrixSlots)
- skippedSlot.SetConcreteType(dynamicMatrixType);
-
- bool inputError = inputSlots.Any(x => x.hasError);
- if (inputError)
- {
- owner.AddConcretizationError(objectId, string.Format("Node {0} had input error", objectId));
- hasError = true;
- }
-
- // configure the output slots now
- // their slotType will either be the default output slotType
- // or the above dynamic slotType for dynamic nodes
- // or error if there is an input error
- foreach (var outputSlot in outputSlots)
- {
- outputSlot.hasError = false;
-
- if (inputError)
- {
- outputSlot.hasError = true;
- continue;
- }
-
- if (outputSlot is DynamicVectorMaterialSlot dynamicVectorMaterialSlot)
- {
- dynamicVectorMaterialSlot.SetConcreteType(dynamicType);
- continue;
- }
- else if (outputSlot is DynamicMatrixMaterialSlot dynamicMatrixMaterialSlot)
- {
- dynamicMatrixMaterialSlot.SetConcreteType(dynamicMatrixType);
- continue;
- }
- }
-
- if (outputSlots.Any(x => x.hasError))
- {
- owner.AddConcretizationError(objectId, string.Format("Node {0} had output error", objectId));
- hasError = true;
- }
- CalculateNodeHasError();
-
- ListPool<DynamicVectorMaterialSlot>.Release(skippedDynamicSlots);
- DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
-
- ListPool<DynamicMatrixMaterialSlot>.Release(skippedDynamicMatrixSlots);
- DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Release(dynamicMatrixInputSlotsToCompare);
- }
- }
-
- public virtual void Concretize()
- {
- hasError = false;
- owner?.ClearErrorsForNode(this);
-
- using (var inputSlots = PooledList<MaterialSlot>.Get())
- using (var outputSlots = PooledList<MaterialSlot>.Get())
- {
- GetInputSlots(inputSlots);
- GetOutputSlots(outputSlots);
-
- UpdatePrecision(inputSlots);
- EvaluateDynamicMaterialSlots(inputSlots, outputSlots);
- }
- }
-
- public virtual void ValidateNode()
- {
- if ((sgVersion < latestVersion) && (dismissedUpdateVersion < latestVersion))
- owner.messageManager?.AddOrAppendError(owner, objectId, new ShaderMessage("There is a newer version of this node available. Inspect node for details.", Rendering.ShaderCompilerMessageSeverity.Warning));
- }
-
- public virtual bool canCutNode => true;
- public virtual bool canCopyNode => true;
-
- protected virtual void CalculateNodeHasError()
- {
- foreach (var slot in this.GetInputSlots<MaterialSlot>())
- {
- if (slot.isConnected)
- {
- var edge = owner.GetEdges(slot.slotReference).First();
- var outputNode = edge.outputSlot.node;
- var outputSlot = outputNode.GetOutputSlots<MaterialSlot>().First(s => s.id == edge.outputSlot.slotId);
- if (!slot.IsCompatibleWith(outputSlot))
- {
- owner.AddConcretizationError(objectId, $"Slot {slot.RawDisplayName()} cannot accept input of type {outputSlot.concreteValueType}.");
- hasError = true;
- return;
- }
- }
- }
- }
-
- protected string GetRayTracingError() => $@"
- #if defined(SHADER_STAGE_RAY_TRACING) && defined(RAYTRACING_SHADER_GRAPH_DEFAULT)
- #error '{name}' node is not supported in ray tracing, please provide an alternate implementation, relying for instance on the 'Raytracing Quality' keyword
- #endif";
-
- public virtual void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
- {
- using (var tempSlots = PooledList<MaterialSlot>.Get())
- using (var tempPreviewProperties = PooledList<PreviewProperty>.Get())
- using (var tempEdges = PooledList<IEdge>.Get())
- {
- GetInputSlots(tempSlots);
- foreach (var s in tempSlots)
- {
- tempPreviewProperties.Clear();
- tempEdges.Clear();
- if (owner != null)
- {
- owner.GetEdges(s.slotReference, tempEdges);
- if (tempEdges.Any())
- continue;
- }
-
- s.GetPreviewProperties(tempPreviewProperties, GetVariableNameForSlot(s.id));
- for (int i = 0; i < tempPreviewProperties.Count; i++)
- {
- if (tempPreviewProperties[i].name == null)
- continue;
-
- properties.Add(tempPreviewProperties[i]);
- }
- }
- }
- }
-
- public virtual string GetVariableNameForSlot(int slotId)
- {
- var slot = FindSlot<MaterialSlot>(slotId);
- if (slot == null)
- 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");
- return string.Format("_{0}_{1}_{2}_{3}", GetVariableNameForNode(), NodeUtils.GetHLSLSafeName(slot.shaderOutputName), unchecked((uint)slotId), slot.concreteValueType.ToPropertyType().ToString());
- }
-
- public string GetConnnectionStateVariableNameForSlot(int slotId)
- {
- return ShaderInput.GetConnectionStateVariableName(GetVariableNameForSlot(slotId));
- }
-
- public virtual string GetVariableNameForNode()
- {
- return defaultVariableName;
- }
-
- public MaterialSlot AddSlot(MaterialSlot slot, bool attemptToModifyExistingInstance = true)
- {
- if (slot == null)
- {
- throw new ArgumentException($"Trying to add null slot to node {this}");
- }
- MaterialSlot foundSlot = FindSlot<MaterialSlot>(slot.id);
-
- if (slot == foundSlot)
- return foundSlot;
-
- // Try to keep the existing instance to avoid unnecessary changes to file
- if (attemptToModifyExistingInstance && foundSlot != null && slot.GetType() == foundSlot.GetType())
- {
- foundSlot.displayName = slot.RawDisplayName();
- foundSlot.CopyDefaultValue(slot);
- return foundSlot;
- }
-
- // keep the same ordering by replacing the first match, if it exists
- int firstIndex = m_Slots.FindIndex(s => s.value.id == slot.id);
- if (firstIndex >= 0)
- {
- m_Slots[firstIndex] = slot;
-
- // remove additional matches to get rid of unused duplicates
- m_Slots.RemoveAllFromRange(s => s.value.id == slot.id, firstIndex + 1, m_Slots.Count - (firstIndex + 1));
- }
- else
- m_Slots.Add(slot);
-
- slot.owner = this;
-
- OnSlotsChanged();
-
- if (foundSlot == null)
- return slot;
-
- // foundSlot is of a different type; try to copy values
- // I think this is to support casting if implemented in CopyValuesFrom ?
- slot.CopyValuesFrom(foundSlot);
- foundSlot.owner = null;
-
- return slot;
- }
-
- public void RemoveSlot(int slotId)
- {
- // Remove edges that use this slot
- // no owner can happen after creation
- // but before added to graph
- if (owner != null)
- {
- var edges = owner.GetEdges(GetSlotReference(slotId));
- owner.RemoveEdges(edges.ToArray());
- }
-
- //remove slots
- m_Slots.RemoveAll(x => x.value.id == slotId);
-
- OnSlotsChanged();
- }
-
- protected virtual void OnSlotsChanged()
- {
- Dirty(ModificationScope.Topological);
- owner?.ClearErrorsForNode(this);
- }
-
- public void RemoveSlotsNameNotMatching(IEnumerable<int> slotIds, bool supressWarnings = false)
- {
- var invalidSlots = m_Slots.Select(x => x.value.id).Except(slotIds);
-
- foreach (var invalidSlot in invalidSlots.ToArray())
- {
- if (!supressWarnings)
- Debug.LogWarningFormat("Removing Invalid MaterialSlot: {0}", invalidSlot);
- RemoveSlot(invalidSlot);
- }
- }
-
- public bool SetSlotOrder(List<int> desiredOrderSlotIds)
- {
- bool changed = false;
- int writeIndex = 0;
- for (int orderIndex = 0; orderIndex < desiredOrderSlotIds.Count; orderIndex++)
- {
- var id = desiredOrderSlotIds[orderIndex];
- var matchIndex = m_Slots.FindIndex(s => s.value.id == id);
- if (matchIndex < 0)
- {
- // no matching slot with that id.. skip it
- }
- else
- {
- if (writeIndex != matchIndex)
- {
- // swap the matching slot into position
- var slot = m_Slots[matchIndex];
- m_Slots[matchIndex] = m_Slots[writeIndex];
- m_Slots[writeIndex] = slot;
- changed = true;
- }
- writeIndex++;
- }
- }
- return changed;
- }
-
- public SlotReference GetSlotReference(int slotId)
- {
- var slot = FindSlot<MaterialSlot>(slotId);
- if (slot == null)
- throw new ArgumentException("Slot could not be found", "slotId");
- return new SlotReference(this, slotId);
- }
-
- public T FindSlot<T>(int slotId) where T : MaterialSlot
- {
- foreach (var slot in m_Slots.SelectValue())
- {
- if (slot.id == slotId && slot is T)
- return (T)slot;
- }
- return default(T);
- }
-
- public T FindInputSlot<T>(int slotId) where T : MaterialSlot
- {
- foreach (var slot in m_Slots.SelectValue())
- {
- if (slot.isInputSlot && slot.id == slotId && slot is T)
- return (T)slot;
- }
- return default(T);
- }
-
- public T FindOutputSlot<T>(int slotId) where T : MaterialSlot
- {
- foreach (var slot in m_Slots.SelectValue())
- {
- if (slot.isOutputSlot && slot.id == slotId && slot is T)
- return (T)slot;
- }
- return default(T);
- }
-
- public virtual IEnumerable<MaterialSlot> GetInputsWithNoConnection()
- {
- return this.GetInputSlots<MaterialSlot>().Where(x => !owner.GetEdges(GetSlotReference(x.id)).Any());
- }
-
- public void SetupSlots()
- {
- foreach (var s in m_Slots.SelectValue())
- s.owner = this;
- }
-
- public virtual void UpdateNodeAfterDeserialization()
- { }
-
- public bool IsSlotConnected(int slotId)
- {
- var slot = FindSlot<MaterialSlot>(slotId);
- return slot != null && owner.GetEdges(slot.slotReference).Any();
- }
-
- public virtual void Setup() { }
-
- protected void EnqueSlotsForSerialization()
- {
- foreach (var slot in m_Slots)
- {
- slot.OnBeforeSerialize();
- }
- }
-
- public virtual void Dispose()
- {
- foreach (var slot in m_Slots)
- slot.value.Dispose();
-
- m_UnregisterAll?.Invoke();
- m_UnregisterAll = null;
- }
- }
- }
|