123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text.RegularExpressions;
- using UnityEngine;
- using UnityEditor.Graphing;
- using UnityEditor.Graphing.Util;
- using UnityEditor.Rendering;
- using UnityEditor.ShaderGraph.Internal;
- using UnityEditor.ShaderGraph.Legacy;
- using UnityEditor.ShaderGraph.Serialization;
- using UnityEditor.ShaderGraph.Drawing;
- using Edge = UnityEditor.Graphing.Edge;
-
- using UnityEngine.UIElements;
- using UnityEngine.Assertions;
- using UnityEngine.Pool;
- using UnityEngine.Serialization;
-
- namespace UnityEditor.ShaderGraph
- {
- [Serializable]
- [FormerName("UnityEditor.ShaderGraph.MaterialGraph")]
- [FormerName("UnityEditor.ShaderGraph.SubGraph")]
- [FormerName("UnityEditor.ShaderGraph.AbstractMaterialGraph")]
- sealed partial class GraphData : JsonObject
- {
- public override int latestVersion => 3;
-
- public GraphObject owner { get; set; }
-
- [NonSerialized]
- internal bool graphIsConcretizing = false;
-
- #region Input data
-
- [SerializeField]
- List<JsonData<AbstractShaderProperty>> m_Properties = new List<JsonData<AbstractShaderProperty>>();
-
- public DataValueEnumerable<AbstractShaderProperty> properties => m_Properties.SelectValue();
-
- [SerializeField]
- List<JsonData<ShaderKeyword>> m_Keywords = new List<JsonData<ShaderKeyword>>();
-
- public DataValueEnumerable<ShaderKeyword> keywords => m_Keywords.SelectValue();
-
- [SerializeField]
- List<JsonData<ShaderDropdown>> m_Dropdowns = new List<JsonData<ShaderDropdown>>();
-
- public DataValueEnumerable<ShaderDropdown> dropdowns => m_Dropdowns.SelectValue();
-
- [NonSerialized]
- List<ShaderInput> m_AddedInputs = new List<ShaderInput>();
-
- public IEnumerable<ShaderInput> addedInputs
- {
- get { return m_AddedInputs; }
- }
-
- [NonSerialized]
- List<ShaderInput> m_RemovedInputs = new List<ShaderInput>();
-
- public IEnumerable<ShaderInput> removedInputs
- {
- get { return m_RemovedInputs; }
- }
-
- [NonSerialized]
- List<ShaderInput> m_MovedInputs = new List<ShaderInput>();
-
- public IEnumerable<ShaderInput> movedInputs
- {
- get { return m_MovedInputs; }
- }
-
- [NonSerialized]
- List<CategoryData> m_AddedCategories = new List<CategoryData>();
-
- public IEnumerable<CategoryData> addedCategories
- {
- get { return m_AddedCategories; }
- }
-
- [NonSerialized]
- List<CategoryData> m_RemovedCategories = new List<CategoryData>();
-
- public IEnumerable<CategoryData> removedCategories
- {
- get { return m_RemovedCategories; }
- }
-
- [NonSerialized]
- List<CategoryData> m_MovedCategories = new List<CategoryData>();
-
- public IEnumerable<CategoryData> movedCategories
- {
- get { return m_MovedCategories; }
- }
-
- [NonSerialized]
- bool m_MovedContexts = false;
- public bool movedContexts => m_MovedContexts;
-
- public string assetGuid { get; set; }
-
- #endregion
-
- #region Category Data
-
- [SerializeField]
- List<JsonData<CategoryData>> m_CategoryData = new List<JsonData<CategoryData>>();
-
- public DataValueEnumerable<CategoryData> categories => m_CategoryData.SelectValue();
-
- #endregion
-
- #region Node data
-
- [SerializeField]
- List<JsonData<AbstractMaterialNode>> m_Nodes = new List<JsonData<AbstractMaterialNode>>();
-
- [NonSerialized]
- Dictionary<string, AbstractMaterialNode> m_NodeDictionary = new Dictionary<string, AbstractMaterialNode>();
-
- [NonSerialized]
- Dictionary<string, AbstractMaterialNode> m_LegacyUpdateDictionary = new Dictionary<string, AbstractMaterialNode>();
-
- public IEnumerable<T> GetNodes<T>()
- {
- return m_Nodes.SelectValue().OfType<T>();
- }
-
- [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<JsonData<GroupData>> m_GroupDatas = new List<JsonData<GroupData>>();
-
- public DataValueEnumerable<GroupData> groups
- {
- get { return m_GroupDatas.SelectValue(); }
- }
-
- [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<JsonRef<GroupData>, List<IGroupItem>> m_GroupItems = new Dictionary<JsonRef<GroupData>, List<IGroupItem>>();
-
- public IEnumerable<IGroupItem> GetItemsInGroup(GroupData groupData)
- {
- if (m_GroupItems.TryGetValue(groupData, out var nodes))
- {
- return nodes;
- }
- return Enumerable.Empty<IGroupItem>();
- }
-
- #endregion
-
- #region StickyNote Data
- [SerializeField]
- List<JsonData<StickyNoteData>> m_StickyNoteDatas = new List<JsonData<StickyNoteData>>();
-
- public DataValueEnumerable<StickyNoteData> stickyNotes => m_StickyNoteDatas.SelectValue();
-
- [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
-
- [SerializeField]
- List<Edge> m_Edges = new List<Edge>();
-
- public IEnumerable<Edge> edges => m_Edges;
-
- [NonSerialized]
- Dictionary<string, List<IEdge>> m_NodeEdges = new Dictionary<string, 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
-
- #region Context Data
-
- [SerializeField]
- ContextData m_VertexContext;
-
- [SerializeField]
- ContextData m_FragmentContext;
-
- // We build this once and cache it as it uses reflection
- // This list is used to build the Create Node menu entries for Blocks
- // as well as when deserializing descriptor fields on serialized Blocks
- [NonSerialized]
- List<BlockFieldDescriptor> m_BlockFieldDescriptors;
-
- public ContextData vertexContext => m_VertexContext;
- public ContextData fragmentContext => m_FragmentContext;
- public List<BlockFieldDescriptor> blockFieldDescriptors => m_BlockFieldDescriptors;
-
- #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; }
-
- // we default this to Graph for subgraphs
- // but for shadergraphs, this will get replaced with Single
- [SerializeField]
- private GraphPrecision m_GraphPrecision = GraphPrecision.Graph;
-
- public GraphPrecision graphDefaultPrecision
- {
- get
- {
- // shader graphs are not allowed to have graph precision
- // we force them to Single if they somehow get set to graph
- if ((!isSubGraph) && (m_GraphPrecision == GraphPrecision.Graph))
- return GraphPrecision.Single;
-
- return m_GraphPrecision;
- }
- }
-
- public ConcretePrecision graphDefaultConcretePrecision
- {
- get
- {
- // when in "Graph switchable" mode, we choose Half as the default concrete precision
- // so you can visualize the worst-case
- return graphDefaultPrecision.ToConcrete(ConcretePrecision.Half);
- }
- }
-
- // Some state has been changed that requires checking for the auto add/removal of blocks.
- // This needs to be checked at a later point in time so actions like replace (remove + add) don't remove blocks.
- internal bool checkAutoAddRemoveBlocks { get; set; }
-
- public void SetGraphDefaultPrecision(GraphPrecision newGraphDefaultPrecision)
- {
- if ((!isSubGraph) && (newGraphDefaultPrecision == GraphPrecision.Graph))
- {
- // shader graphs can't be set to "Graph", only subgraphs can
- Debug.LogError("Cannot set ShaderGraph to a default precision of Graph");
- }
- else
- {
- m_GraphPrecision = newGraphDefaultPrecision;
- }
- }
-
- // NOTE: having preview mode default to 3D preserves the old behavior of pre-existing subgraphs
- // if we change this, we would have to introduce a versioning step if we want to maintain the old behavior
- [SerializeField]
- private PreviewMode m_PreviewMode = PreviewMode.Preview3D;
-
- public PreviewMode previewMode
- {
- get => m_PreviewMode;
- set => m_PreviewMode = value;
- }
-
- [SerializeField]
- JsonRef<AbstractMaterialNode> m_OutputNode;
-
- public AbstractMaterialNode outputNode
- {
- get => m_OutputNode;
- set => m_OutputNode = value;
- }
-
- internal delegate void SaveGraphDelegate(Shader shader, object context);
- internal static SaveGraphDelegate onSaveGraph;
-
-
- #region SubData
-
- [SerializeField]
- internal List<JsonData<AbstractShaderGraphDataExtension>> m_SubDatas = new List<JsonData<AbstractShaderGraphDataExtension>>();
- public DataValueEnumerable<AbstractShaderGraphDataExtension> SubDatas => m_SubDatas.SelectValue();
-
- #endregion
-
- #region Targets
-
- // Serialized list of user-selected active targets, sorted in displayName order (to maintain deterministic serialization order)
- // some of these may be MultiJsonInternal.UnknownTargetType if we can't recognize the type of the target
- [SerializeField]
- internal List<JsonData<Target>> m_ActiveTargets = new List<JsonData<Target>>(); // After adding to this list, you MUST call SortActiveTargets()
- public DataValueEnumerable<Target> activeTargets => m_ActiveTargets.SelectValue();
-
- // this stores all of the current possible Target types (including any unknown target types we serialized in)
- class PotentialTarget
- {
- // the potential Target
- Target m_Target;
-
- // a Target is either known (we know the Type) or unknown (can't find a matching definition of the Type)
- // Targets of unknown type are stored in an UnknownTargetType
- private Type m_KnownType;
- private MultiJsonInternal.UnknownTargetType m_UnknownTarget;
-
- public PotentialTarget(Target target)
- {
- m_Target = target;
-
- if (target is MultiJsonInternal.UnknownTargetType)
- {
- m_UnknownTarget = (MultiJsonInternal.UnknownTargetType)target;
- m_KnownType = null;
- }
- else
- {
- m_UnknownTarget = null;
- m_KnownType = target.GetType();
- }
- }
-
- public bool IsUnknown()
- {
- return m_UnknownTarget != null;
- }
-
- public MultiJsonInternal.UnknownTargetType GetUnknown()
- {
- return m_UnknownTarget;
- }
-
- public Type knownType { get { return m_KnownType; } }
-
- public bool Is(Target t)
- {
- return t == m_Target;
- }
-
- public string GetDisplayName()
- {
- return m_Target.displayName;
- }
-
- public void ReplaceStoredTarget(Target t)
- {
- if (m_KnownType != null)
- Assert.IsTrue(t.GetType() == m_KnownType);
- m_Target = t;
- }
-
- public Target GetTarget()
- {
- return m_Target;
- }
- }
- [NonSerialized]
- List<PotentialTarget> m_AllPotentialTargets = new List<PotentialTarget>();
- public IEnumerable<Target> allPotentialTargets => m_AllPotentialTargets.Select(x => x.GetTarget());
-
- public int GetTargetIndexByKnownType(Type targetType)
- {
- return m_AllPotentialTargets.FindIndex(pt => pt.knownType == targetType);
- }
-
- public int GetTargetIndex(Target t)
- {
- int result = m_AllPotentialTargets.FindIndex(pt => pt.Is(t));
- return result;
- }
-
- public IEnumerable<Target> GetValidTargets()
- {
- foreach (var potentialTarget in m_AllPotentialTargets)
- {
- var target = potentialTarget.GetTarget();
- if (target is IMayObsolete targetObsolete && targetObsolete.IsObsolete())
- continue;
- yield return potentialTarget.GetTarget();
- }
- }
-
- public void SetTargetActive(Target target, bool skipSortAndUpdate = false)
- {
- int activeIndex = m_ActiveTargets.IndexOf(target);
- if (activeIndex < 0)
- {
- activeIndex = m_ActiveTargets.Count;
- m_ActiveTargets.Add(target);
- }
-
- // active known targets should replace the stored Target in AllPotentialTargets
- if (target is MultiJsonInternal.UnknownTargetType unknownTarget)
- {
- // find any existing potential target with the same unknown jsonData
- int targetIndex = m_AllPotentialTargets.FindIndex(
- pt => pt.IsUnknown() && (pt.GetUnknown().jsonData == unknownTarget.jsonData));
-
- // replace existing target, or add it if there is none
- if (targetIndex >= 0)
- m_AllPotentialTargets[targetIndex] = new PotentialTarget(target);
- else
- m_AllPotentialTargets.Add(new PotentialTarget(target));
- }
- else
- {
- // known types should already have been registered
- Type targetType = target.GetType();
- int targetIndex = GetTargetIndexByKnownType(targetType);
- Assert.IsTrue(targetIndex >= 0);
- m_AllPotentialTargets[targetIndex].ReplaceStoredTarget(target);
- }
-
- if (!skipSortAndUpdate)
- SortAndUpdateActiveTargets();
- }
-
- public void SetTargetActive(int targetIndex, bool skipSortAndUpdate = false)
- {
- Target target = m_AllPotentialTargets[targetIndex].GetTarget();
- SetTargetActive(target, skipSortAndUpdate);
- }
-
- public void SetTargetInactive(Target target, bool skipSortAndUpdate = false)
- {
- int activeIndex = m_ActiveTargets.IndexOf(target);
- if (activeIndex < 0)
- return;
-
- int targetIndex = GetTargetIndex(target);
-
- // if a target was in the active targets, it should also have been in the potential targets list
- Assert.IsTrue(targetIndex >= 0);
-
- m_ActiveTargets.RemoveAt(activeIndex);
-
- if (!skipSortAndUpdate)
- SortAndUpdateActiveTargets();
- }
-
- // this list is populated by graph validation, and lists all of the targets that nodes did not like
- [NonSerialized]
- List<Target> m_UnsupportedTargets = new List<Target>();
- public List<Target> unsupportedTargets { get => m_UnsupportedTargets; }
-
- private Comparison<Target> targetComparison = new Comparison<Target>((a, b) => string.Compare(a.displayName, b.displayName));
- public void SortActiveTargets()
- {
- activeTargets.Sort(targetComparison);
- }
-
- // TODO: Need a better way to handle this
- public bool hasVFXCompatibleTarget => activeTargets.Any(o => o.SupportsVFX());
- #if VFX_GRAPH_10_0_0_OR_NEWER
- public bool hasVFXTarget
- {
- get
- {
- bool supports = true;
- supports &= !isSubGraph;
- supports &= activeTargets.Any();
- // Maintain support for VFXTarget and VFX compatible targets.
- supports &= activeTargets.OfType<VFXTarget>().Any() || hasVFXCompatibleTarget;
- return supports;
- }
- }
-
- public bool isOnlyVFXTarget => activeTargets.Count() == 1 &&
- activeTargets.Count(t => t is VFXTarget) == 1;
- #else
- public bool isVFXTarget => false;
- public bool isOnlyVFXTarget => false;
- #endif
- #endregion
-
- public GraphData()
- {
- m_GroupItems[null] = new List<IGroupItem>();
- GetBlockFieldDescriptors();
- AddKnownTargetsToPotentialTargets();
- }
-
- // used to initialize the graph with targets, i.e. when creating new graphs via the popup menu
- public void InitializeOutputs(Target[] targets, BlockFieldDescriptor[] blockDescriptors)
- {
- if (targets == null)
- return;
-
- foreach (var target in targets)
- {
- if (GetTargetIndexByKnownType(target.GetType()) >= 0)
- {
- SetTargetActive(target, true);
- }
- }
- SortActiveTargets();
-
- if (blockDescriptors != null)
- {
- foreach (var descriptor in blockDescriptors)
- {
- var contextData = descriptor.shaderStage == ShaderStage.Fragment ? m_FragmentContext : m_VertexContext;
- var block = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
- block.Init(descriptor);
- AddBlockNoValidate(block, contextData, contextData.blocks.Count);
- }
- }
-
- ValidateGraph();
- var activeBlocks = GetActiveBlocksForAllActiveTargets();
- UpdateActiveBlocks(activeBlocks);
- }
-
- void GetBlockFieldDescriptors()
- {
- m_BlockFieldDescriptors = new List<BlockFieldDescriptor>();
-
- var asmTypes = TypeCache.GetTypesWithAttribute<GenerateBlocksAttribute>();
- foreach (var type in asmTypes)
- {
- var attrs = type.GetCustomAttributes(typeof(GenerateBlocksAttribute), false);
- if (attrs == null || attrs.Length <= 0)
- continue;
-
- var attribute = attrs[0] as GenerateBlocksAttribute;
-
- // Get all fields that are BlockFieldDescriptor
- // If field and context stages match add to list
- foreach (var fieldInfo in type.GetFields())
- {
- if (fieldInfo.GetValue(type) is BlockFieldDescriptor blockFieldDescriptor)
- {
- blockFieldDescriptor.path = attribute.path;
- m_BlockFieldDescriptors.Add(blockFieldDescriptor);
- }
- }
- }
- }
-
- void AddKnownTargetsToPotentialTargets()
- {
- Assert.AreEqual(m_AllPotentialTargets.Count, 0);
-
- // Find all valid Targets by looking in the TypeCache
- var targetTypes = TypeCache.GetTypesDerivedFrom<Target>();
- foreach (var type in targetTypes)
- {
- if (type.IsAbstract || type.IsGenericType || !type.IsClass)
- continue;
-
- // create a new instance of the Target, to represent the potential Target
- // NOTE: this instance may be replaced later if we serialize in an Active Target of that type
- var target = (Target)Activator.CreateInstance(type);
- if (!target.isHidden)
- {
- m_AllPotentialTargets.Add(new PotentialTarget(target));
- }
- }
- }
-
- public void SortAndUpdateActiveTargets()
- {
- SortActiveTargets();
- ValidateGraph();
- NodeUtils.ReevaluateActivityOfNodeList(m_Nodes.SelectValue());
- }
-
- 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_AddedCategories.Clear();
- m_RemovedCategories.Clear();
- m_MovedCategories.Clear();
- m_AddedStickyNotes.Clear();
- m_RemovedNotes.Clear();
- m_PastedStickyNotes.Clear();
- m_MostRecentlyCreatedGroup = null;
- m_MovedContexts = 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.Any())
- {
- 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_GroupDatas.Contains(groupData))
- return false;
-
- m_GroupDatas.Add(groupData);
- m_AddedGroups.Add(groupData);
- m_GroupItems.Add(groupData, new List<IGroupItem>());
-
- return true;
- }
-
- public void RemoveGroup(GroupData groupData)
- {
- RemoveGroupNoValidate(groupData);
- ValidateGraph();
- }
-
- void RemoveGroupNoValidate(GroupData group)
- {
- if (!m_GroupDatas.Contains(group))
- throw new InvalidOperationException("Cannot remove a group that doesn't exist.");
- m_GroupDatas.Remove(group);
- m_RemovedGroups.Add(group);
-
- if (m_GroupItems.TryGetValue(group, out var items))
- {
- foreach (IGroupItem groupItem in items.ToList())
- {
- SetGroup(groupItem, null);
- }
-
- m_GroupItems.Remove(group);
- }
- }
-
- public void AddStickyNote(StickyNoteData stickyNote)
- {
- if (m_StickyNoteDatas.Contains(stickyNote))
- {
- throw new InvalidOperationException("Sticky note has already been added to the graph.");
- }
-
- if (!m_GroupItems.ContainsKey(stickyNote.group))
- {
- throw new InvalidOperationException("Trying to add sticky note with group that doesn't exist.");
- }
-
- m_StickyNoteDatas.Add(stickyNote);
- m_AddedStickyNotes.Add(stickyNote);
- m_GroupItems[stickyNote.group].Add(stickyNote);
- }
-
- void RemoveNoteNoValidate(StickyNoteData stickyNote)
- {
- if (!m_StickyNoteDatas.Contains(stickyNote))
- {
- throw new InvalidOperationException("Cannot remove a note that doesn't exist.");
- }
-
- m_StickyNoteDatas.Remove(stickyNote);
- m_RemovedNotes.Add(stickyNote);
-
- if (m_GroupItems.TryGetValue(stickyNote.group, 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,
- oldGroup = node.group,
- // 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
- newGroup = group,
- };
- node.group = groupChange.newGroup;
-
- var oldGroupNodes = m_GroupItems[groupChange.oldGroup];
- oldGroupNodes.Remove(node);
-
- m_GroupItems[groupChange.newGroup].Add(node);
- m_ParentGroupChanges.Add(groupChange);
- }
-
- public void AddContexts()
- {
- m_VertexContext = new ContextData();
- m_VertexContext.shaderStage = ShaderStage.Vertex;
- m_VertexContext.position = new Vector2(0, 0);
- m_FragmentContext = new ContextData();
- m_FragmentContext.shaderStage = ShaderStage.Fragment;
- m_FragmentContext.position = new Vector2(0, 200);
- }
-
- public void AddBlock(BlockNode blockNode, ContextData contextData, int index)
- {
- AddBlockNoValidate(blockNode, contextData, index);
- ValidateGraph();
-
- var activeBlocks = GetActiveBlocksForAllActiveTargets();
- UpdateActiveBlocks(activeBlocks);
- }
-
- void AddBlockNoValidate(BlockNode blockNode, ContextData contextData, int index)
- {
- // Regular AddNode path
- AddNodeNoValidate(blockNode);
-
- // Set BlockNode properties
- blockNode.contextData = contextData;
-
- // Add to ContextData
- if (index == -1 || index >= contextData.blocks.Count())
- {
- contextData.blocks.Add(blockNode);
- }
- else
- {
- contextData.blocks.Insert(index, blockNode);
- }
- }
-
- public List<BlockFieldDescriptor> GetActiveBlocksForAllActiveTargets()
- {
- // Get list of active Block types
- var currentBlocks = GetNodes<BlockNode>();
- var context = new TargetActiveBlockContext(currentBlocks.Select(x => x.descriptor).ToList(), null);
- foreach (var target in activeTargets)
- {
- target.GetActiveBlocks(ref context);
- }
-
- // custom blocks aren't going to exist in GetActiveBlocks, we need to ensure we grab those too.
- foreach (var cibnode in currentBlocks.Where(bn => bn.isCustomBlock))
- {
- context.AddBlock(cibnode.descriptor);
- }
- return context.activeBlocks;
- }
-
- public void UpdateActiveBlocks(List<BlockFieldDescriptor> activeBlockDescriptors)
- {
- // Set Blocks as active based on supported Block list
- //Note: we never want unknown blocks to be active, so explicitly set them to inactive always
- bool disableCI = activeTargets.All(at => at.ignoreCustomInterpolators);
- foreach (var vertexBlock in vertexContext.blocks)
- {
- if (vertexBlock.value?.isCustomBlock == true)
- {
- vertexBlock.value.SetOverrideActiveState(disableCI ? AbstractMaterialNode.ActiveState.ExplicitInactive : AbstractMaterialNode.ActiveState.ExplicitActive);
- }
- else if (vertexBlock.value?.descriptor?.isUnknown == true)
- {
- vertexBlock.value.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitInactive);
- }
- else
- {
- vertexBlock.value.SetOverrideActiveState(activeBlockDescriptors.Contains(vertexBlock.value.descriptor) ? AbstractMaterialNode.ActiveState.ExplicitActive
- : AbstractMaterialNode.ActiveState.ExplicitInactive);
- }
- }
- foreach (var fragmentBlock in fragmentContext.blocks)
- {
- if (fragmentBlock.value?.descriptor?.isUnknown == true)
- {
- fragmentBlock.value.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitInactive);
- }
- else
- {
- fragmentBlock.value.SetOverrideActiveState(activeBlockDescriptors.Contains(fragmentBlock.value.descriptor) ? AbstractMaterialNode.ActiveState.ExplicitActive
- : AbstractMaterialNode.ActiveState.ExplicitInactive);
- }
- }
- }
-
- public void AddRemoveBlocksFromActiveList(List<BlockFieldDescriptor> activeBlockDescriptors)
- {
- var blocksToRemove = ListPool<BlockNode>.Get();
-
- void GetBlocksToRemoveForContext(ContextData contextData)
- {
- for (int i = 0; i < contextData.blocks.Count; i++)
- {
- if (contextData.blocks[i].value?.isCustomBlock == true) // custom interpolators are fine.
- continue;
-
- var block = contextData.blocks[i];
- if (!activeBlockDescriptors.Contains(block.value.descriptor))
- {
- var slot = block.value.FindSlot<MaterialSlot>(0);
- //Need to check if a slot is not default value OR is an untracked unknown block type
- if (slot.IsUsingDefaultValue() || block.value.descriptor.isUnknown) // TODO: How to check default value
- {
- blocksToRemove.Add(block);
- }
- }
- }
- }
-
- void TryAddBlockToContext(BlockFieldDescriptor descriptor, ContextData contextData)
- {
- if (descriptor.shaderStage != contextData.shaderStage)
- return;
-
- if (contextData.blocks.Any(x => x.value.descriptor.Equals(descriptor)))
- return;
-
- var node = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
- node.Init(descriptor);
- AddBlockNoValidate(node, contextData, contextData.blocks.Count);
- }
-
- // Get inactive Blocks to remove
- GetBlocksToRemoveForContext(vertexContext);
- GetBlocksToRemoveForContext(fragmentContext);
-
- // Remove blocks
- foreach (var block in blocksToRemove)
- {
- RemoveNodeNoValidate(block);
- }
-
- // Add active Blocks not currently in Contexts
- foreach (var descriptor in activeBlockDescriptors)
- {
- TryAddBlockToContext(descriptor, vertexContext);
- TryAddBlockToContext(descriptor, fragmentContext);
- }
- }
-
- void AddNodeNoValidate(AbstractMaterialNode node)
- {
- if (node.group != null && !m_GroupItems.ContainsKey(node.group))
- {
- throw new InvalidOperationException("Cannot add a node whose group doesn't exist.");
- }
- node.owner = this;
- m_Nodes.Add(node);
- m_NodeDictionary.Add(node.objectId, node);
- m_AddedNodes.Add(node);
- m_GroupItems[node.group].Add(node);
- }
-
- public void RemoveNode(AbstractMaterialNode node)
- {
- if (!node.canDeleteNode)
- {
- throw new InvalidOperationException($"Node {node.name} ({node.objectId}) cannot be deleted.");
- }
- RemoveNodeNoValidate(node);
- ValidateGraph();
-
- if (node is BlockNode blockNode)
- {
- var activeBlocks = GetActiveBlocksForAllActiveTargets();
- UpdateActiveBlocks(activeBlocks);
- blockNode.Dirty(ModificationScope.Graph);
- }
- }
-
- void RemoveNodeNoValidate(AbstractMaterialNode node)
- {
- if (!m_NodeDictionary.ContainsKey(node.objectId) && node.isActive && !m_RemovedNodes.Contains(node))
- {
- throw new InvalidOperationException("Cannot remove a node that doesn't exist.");
- }
-
- m_Nodes.Remove(node);
- m_NodeDictionary.Remove(node.objectId);
- messageManager?.RemoveNode(node.objectId);
- m_RemovedNodes.Add(node);
-
- if (m_GroupItems.TryGetValue(node.group, out var groupItems))
- {
- groupItems.Remove(node);
- }
-
- if (node is BlockNode blockNode && blockNode.contextData != null)
- {
- // Remove from ContextData
- blockNode.contextData.blocks.Remove(blockNode);
- }
- }
-
- void AddEdgeToNodeEdges(IEdge edge)
- {
- List<IEdge> inputEdges;
- if (!m_NodeEdges.TryGetValue(edge.inputSlot.node.objectId, out inputEdges))
- m_NodeEdges[edge.inputSlot.node.objectId] = inputEdges = new List<IEdge>();
- inputEdges.Add(edge);
-
- List<IEdge> outputEdges;
- if (!m_NodeEdges.TryGetValue(edge.outputSlot.node.objectId, out outputEdges))
- m_NodeEdges[edge.outputSlot.node.objectId] = outputEdges = new List<IEdge>();
- outputEdges.Add(edge);
- }
-
- IEdge ConnectNoValidate(SlotReference fromSlotRef, SlotReference toSlotRef, bool assumeBatchSafety = false)
- {
- var fromNode = fromSlotRef.node;
- var toNode = toSlotRef.node;
-
- if (fromNode == null || toNode == null)
- return null;
-
- // both nodes must belong to this graph
- if ((fromNode.owner != this) || (toNode.owner != this))
- 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.
- if (!assumeBatchSafety) // This may not be that helpful.
- {
- var dependentNodes = new List<AbstractMaterialNode>();
- NodeUtils.CollectNodesNodeFeedsInto(dependentNodes, toNode);
- if (dependentNodes.Contains(fromNode))
- return null;
- }
-
- var fromSlot = fromNode.FindSlot<MaterialSlot>(fromSlotRef.slotId);
- var toSlot = toNode.FindSlot<MaterialSlot>(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);
- if (!assumeBatchSafety)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(toNode);
- }
-
- //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;
- }
-
- internal void UnnotifyAddedEdge(IEdge edge)
- {
- m_AddedEdges.Remove(edge);
- }
-
- public void RemoveEdges(IEdge[] edges)
- {
- if (edges.Length == 0)
- return;
- foreach (var edge in edges)
- {
- RemoveEdgeNoValidate(edge);
- }
- ValidateGraph();
- }
-
- public void RemoveEdge(IEdge e)
- {
- RemoveEdgeNoValidate(e);
- ValidateGraph();
- }
-
- public void RemoveElements(AbstractMaterialNode[] nodes, IEdge[] edges, GroupData[] groups, StickyNoteData[] notes, ShaderInput[] inputs = null)
- {
- foreach (var node in nodes)
- {
- if (!node.canDeleteNode)
- {
- throw new InvalidOperationException($"Node {node.name} ({node.objectId}) cannot be deleted.");
- }
- }
-
- foreach (var edge in edges.ToArray())
- {
- RemoveEdgeNoValidate(edge);
- }
-
- foreach (var serializableNode in nodes)
- {
- // Check if it is a Redirect Node
- // Get the edges and then re-create all Edges
- // This only works if it has all the edges.
- // If one edge is already deleted then we can not re-create.
- if (serializableNode is RedirectNodeData redirectNode)
- {
- redirectNode.GetOutputAndInputSlots(out SlotReference outputSlotRef, out var inputSlotRefs);
-
- foreach (SlotReference slot in inputSlotRefs)
- {
- ConnectNoValidate(outputSlotRef, slot);
- }
- }
-
- RemoveNodeNoValidate(serializableNode);
- }
-
- foreach (var noteData in notes)
- {
- RemoveNoteNoValidate(noteData);
- }
-
- foreach (var groupData in groups)
- {
- RemoveGroupNoValidate(groupData);
- }
-
- if (inputs != null)
- {
- foreach (var shaderInput in inputs)
- {
- RemoveGraphInputNoValidate(shaderInput);
- }
- }
-
- ValidateGraph();
-
- if (nodes.Any(x => x is BlockNode))
- {
- var activeBlocks = GetActiveBlocksForAllActiveTargets();
- UpdateActiveBlocks(activeBlocks);
- }
- }
-
- void RemoveEdgeNoValidate(IEdge e, bool reevaluateActivity = true)
- {
- 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 as Edge);
-
- AbstractMaterialNode input = e.inputSlot.node, output = e.outputSlot.node;
- if (input != null && ShaderGraphPreferences.autoAddRemoveBlocks)
- {
- checkAutoAddRemoveBlocks = true;
- }
-
- List<IEdge> inputNodeEdges;
- if (m_NodeEdges.TryGetValue(input.objectId, out inputNodeEdges))
- inputNodeEdges.Remove(e);
-
- List<IEdge> outputNodeEdges;
- if (m_NodeEdges.TryGetValue(output.objectId, out outputNodeEdges))
- outputNodeEdges.Remove(e);
-
- m_AddedEdges.Remove(e);
- m_RemovedEdges.Add(e);
-
- if (reevaluateActivity)
- {
- if (input != null)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(input);
- }
-
- if (output != null)
- {
- NodeUtils.ReevaluateActivityOfConnectedNodes(output);
- }
- }
- }
-
- public AbstractMaterialNode GetNodeFromId(string nodeId)
- {
- m_NodeDictionary.TryGetValue(nodeId, out var node);
- return node;
- }
-
- public T GetNodeFromId<T>(string nodeId) where T : class
- {
- m_NodeDictionary.TryGetValue(nodeId, out var node);
- return node as T;
- }
-
- internal Texture2DShaderProperty GetMainTexture()
- {
- foreach (var prop in properties)
- {
- if (prop is Texture2DShaderProperty tex)
- {
- if (tex.isMainTexture)
- {
- return tex;
- }
- }
- }
- return null;
- }
-
- internal ColorShaderProperty GetMainColor()
- {
- foreach (var prop in properties)
- {
- if (prop is ColorShaderProperty col)
- {
- if (col.isMainColor)
- {
- return col;
- }
- }
- }
- return null;
- }
-
- public bool ContainsCategory(CategoryData categoryData)
- {
- return categories.Contains(categoryData);
- }
-
- public bool ContainsInput(ShaderInput shaderInput)
- {
- if (shaderInput == null)
- return false;
-
- return properties.Contains(shaderInput) || keywords.Contains(shaderInput) || dropdowns.Contains(shaderInput);
- }
-
- public bool ContainsNode(AbstractMaterialNode node)
- {
- if (node == null)
- return false;
-
- return m_NodeDictionary.TryGetValue(node.objectId, out var foundNode) && node == foundNode;
- }
-
- public void GetEdges(MaterialSlot slot, List<IEdge> foundEdges)
- {
- List<IEdge> candidateEdges;
- if (!m_NodeEdges.TryGetValue(slot.owner.objectId, out candidateEdges))
- return;
-
- foreach (var edge in candidateEdges)
- {
- var cs = slot.isInputSlot ? edge.inputSlot : edge.outputSlot;
- if (cs.node == slot.owner && cs.slotId == slot.id)
- foundEdges.Add(edge);
- }
- }
-
- public void GetEdges(SlotReference s, List<IEdge> foundEdges)
- {
- List<IEdge> candidateEdges;
- if (!m_NodeEdges.TryGetValue(s.node.objectId, out candidateEdges))
- return;
-
- MaterialSlot slot = s.slot;
- foreach (var edge in candidateEdges)
- {
- var cs = slot.isInputSlot ? edge.inputSlot : edge.outputSlot;
- if (cs.node == s.node && 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 GetEdges(AbstractMaterialNode node, List<IEdge> foundEdges)
- {
- if (m_NodeEdges.TryGetValue(node.objectId, out var edges))
- {
- foundEdges.AddRange(edges);
- }
- }
-
- public IEnumerable<IEdge> GetEdges(AbstractMaterialNode node)
- {
- List<IEdge> edges = new List<IEdge>();
- GetEdges(node, edges);
- return edges;
- }
-
- public void ForeachHLSLProperty(Action<HLSLProperty> action)
- {
- foreach (var prop in properties)
- prop.ForeachHLSLProperty(action);
- }
-
- public void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode)
- {
- foreach (var prop in properties)
- {
- // For VFX Shader generation, we must omit exposed properties from the Material CBuffer.
- // This is because VFX computes properties on the fly in the vertex stage, and packed into interpolator.
- if (generationMode == GenerationMode.VFX && prop.isExposed)
- {
- prop.overrideHLSLDeclaration = true;
- prop.hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare;
- }
-
- // ugh, this needs to be moved to the gradient property implementation
- 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 bool IsInputAllowedInGraph(ShaderInput input)
- {
- return (isSubGraph && input.allowedInSubGraph) || (!isSubGraph && input.allowedInMainGraph);
- }
-
- public bool IsInputAllowedInGraph(AbstractMaterialNode node)
- {
- return (isSubGraph && node.allowedInSubGraph) || (!isSubGraph && node.allowedInMainGraph);
- }
-
- // adds the input to the graph, and sanitizes the names appropriately
- public void AddGraphInput(ShaderInput input, int index = -1)
- {
- if (input == null)
- return;
-
- // sanitize the display name
- input.SetDisplayNameAndSanitizeForGraph(this);
-
- // sanitize the reference name
- input.SetReferenceNameAndSanitizeForGraph(this);
-
- AddGraphInputNoSanitization(input, index);
- }
-
- // just adds the input to the graph, does not fix colliding or illegal names
- internal void AddGraphInputNoSanitization(ShaderInput input, int index = -1)
- {
- if (input == null)
- return;
-
- switch (input)
- {
- case AbstractShaderProperty property:
- if (m_Properties.Contains(property))
- return;
-
- if (index < 0)
- m_Properties.Add(property);
- else
- m_Properties.Insert(index, property);
-
- break;
- case ShaderKeyword keyword:
- if (m_Keywords.Contains(keyword))
- return;
-
- if (index < 0)
- m_Keywords.Add(keyword);
- else
- m_Keywords.Insert(index, keyword);
-
- OnKeywordChangedNoValidate();
-
- break;
- case ShaderDropdown dropdown:
- if (m_Dropdowns.Contains(dropdown))
- return;
-
- if (index < 0)
- m_Dropdowns.Add(dropdown);
- else
- m_Dropdowns.Insert(index, dropdown);
-
- OnDropdownChangedNoValidate();
-
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- m_AddedInputs.Add(input);
- }
-
- // only ignores names matching ignoreName on properties matching ignoreGuid
- public List<string> BuildPropertyDisplayNameList(AbstractShaderProperty ignoreProperty, string ignoreName)
- {
- List<String> result = new List<String>();
- foreach (var p in properties)
- {
- int before = result.Count;
- p.GetPropertyDisplayNames(result);
-
- if ((p == ignoreProperty) && (ignoreName != null))
- {
- // remove ignoreName, if it was just added
- for (int i = before; i < result.Count; i++)
- {
- if (result[i] == ignoreName)
- {
- result.RemoveAt(i);
- break;
- }
- }
- }
- }
-
- return result;
- }
-
- // only ignores names matching ignoreName on properties matching ignoreGuid
- public List<string> BuildPropertyReferenceNameList(AbstractShaderProperty ignoreProperty, string ignoreName)
- {
- List<String> result = new List<String>();
- foreach (var p in properties)
- {
- int before = result.Count;
- p.GetPropertyReferenceNames(result);
-
- if ((p == ignoreProperty) && (ignoreName != null))
- {
- // remove ignoreName, if it was just added
- for (int i = before; i < result.Count; i++)
- {
- if (result[i] == ignoreName)
- {
- result.RemoveAt(i);
- break;
- }
- }
- }
- }
- return result;
- }
-
- public string SanitizeGraphInputName(ShaderInput input, string desiredName)
- {
- string currentName = input.displayName;
- string sanitizedName = desiredName.Trim();
- switch (input)
- {
- case AbstractShaderProperty property:
- sanitizedName = GraphUtil.SanitizeName(BuildPropertyDisplayNameList(property, currentName), "{0} ({1})", sanitizedName);
- break;
- case ShaderKeyword keyword:
- sanitizedName = GraphUtil.SanitizeName(keywords.Where(p => p != input).Select(p => p.displayName), "{0} ({1})", sanitizedName);
- break;
- case ShaderDropdown dropdown:
- sanitizedName = GraphUtil.SanitizeName(dropdowns.Where(p => p != input).Select(p => p.displayName), "{0} ({1})", sanitizedName);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- return sanitizedName;
- }
-
- public string SanitizeGraphInputReferenceName(ShaderInput input, string desiredName)
- {
- var sanitizedName = NodeUtils.ConvertToValidHLSLIdentifier(desiredName, (desiredName) => (NodeUtils.IsShaderLabKeyWord(desiredName) || NodeUtils.IsShaderGraphKeyWord(desiredName)));
-
- switch (input)
- {
- case AbstractShaderProperty property:
- {
- // must deduplicate ref names against keywords, dropdowns, and properties, as they occupy the same name space
- var existingNames = properties.Where(p => p != property).Select(p => p.referenceName).Union(keywords.Select(p => p.referenceName)).Union(dropdowns.Select(p => p.referenceName));
- sanitizedName = GraphUtil.DeduplicateName(existingNames, "{0}_{1}", sanitizedName);
- }
- break;
- case ShaderKeyword keyword:
- {
- // must deduplicate ref names against keywords, dropdowns, and properties, as they occupy the same name space
- sanitizedName = sanitizedName.ToUpper();
- var existingNames = properties.Select(p => p.referenceName).Union(keywords.Where(p => p != input).Select(p => p.referenceName)).Union(dropdowns.Select(p => p.referenceName));
- sanitizedName = GraphUtil.DeduplicateName(existingNames, "{0}_{1}", sanitizedName);
- }
- break;
- case ShaderDropdown dropdown:
- {
- // must deduplicate ref names against keywords, dropdowns, and properties, as they occupy the same name space
- var existingNames = properties.Select(p => p.referenceName).Union(keywords.Select(p => p.referenceName)).Union(dropdowns.Where(p => p != input).Select(p => p.referenceName));
- sanitizedName = GraphUtil.DeduplicateName(existingNames, "{0}_{1}", sanitizedName);
- }
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- return sanitizedName;
- }
-
- // copies the ShaderInput, and adds it to the graph with proper name sanitization, returning the copy
- public ShaderInput AddCopyOfShaderInput(ShaderInput source, int insertIndex = -1)
- {
- ShaderInput copy = source.Copy();
-
- // some ShaderInputs cannot be copied (unknown types)
- if (copy == null)
- return null;
-
- // copy common properties that should always be copied over
- copy.generatePropertyBlock = source.generatePropertyBlock; // the exposed toggle
-
- if ((source is AbstractShaderProperty sourceProp) && (copy is AbstractShaderProperty copyProp))
- {
- copyProp.hidden = sourceProp.hidden;
- copyProp.precision = sourceProp.precision;
- copyProp.overrideHLSLDeclaration = sourceProp.overrideHLSLDeclaration;
- copyProp.hlslDeclarationOverride = sourceProp.hlslDeclarationOverride;
- copyProp.useCustomSlotLabel = sourceProp.useCustomSlotLabel;
- }
-
- // sanitize the display name (we let the .Copy() function actually copy the display name over)
- copy.SetDisplayNameAndSanitizeForGraph(this);
-
- // copy and sanitize the reference name (must do this after the display name, so the default is correct)
- if (source.IsUsingNewDefaultRefName())
- {
- // if source was using new default, we can just rely on the default for the copy we made.
- // the code above has already handled collisions properly for the default,
- // and it will assign the same name as the source if there are no collisions.
- // Also it will result better names chosen when there are collisions.
- }
- else
- {
- // when the source is using an old default, we set it as an override
- copy.SetReferenceNameAndSanitizeForGraph(this, source.referenceName);
- }
-
- copy.OnBeforePasteIntoGraph(this);
- AddGraphInputNoSanitization(copy, insertIndex);
-
- return copy;
- }
-
- public void RemoveGraphInput(ShaderInput input)
- {
- switch (input)
- {
- case AbstractShaderProperty property:
- var propertyNodes = GetNodes<PropertyNode>().Where(x => x.property == input).ToList();
- foreach (var propertyNode in propertyNodes)
- ReplacePropertyNodeWithConcreteNodeNoValidate(propertyNode);
- break;
- }
-
- // Also remove this input from any category it existed in
- foreach (var categoryData in categories)
- {
- if (categoryData.IsItemInCategory(input))
- {
- categoryData.RemoveItemFromCategory(input);
- break;
- }
- }
-
- RemoveGraphInputNoValidate(input);
- ValidateGraph();
- }
-
- public void MoveCategory(CategoryData category, int newIndex)
- {
- if (newIndex > m_CategoryData.Count || newIndex < 0)
- {
- AssertHelpers.Fail("New index is not within categories list.");
- return;
- }
- var currentIndex = m_CategoryData.IndexOf(category);
- if (currentIndex == -1)
- {
- AssertHelpers.Fail("Category is not in graph.");
- return;
- }
- if (newIndex == currentIndex)
- return;
- m_CategoryData.RemoveAt(currentIndex);
- if (newIndex > currentIndex)
- newIndex--;
- var isLast = newIndex == m_CategoryData.Count;
- if (isLast)
- m_CategoryData.Add(category);
- else
- m_CategoryData.Insert(newIndex, category);
- if (!m_MovedCategories.Contains(category))
- m_MovedCategories.Add(category);
- }
-
- public void MoveItemInCategory(ShaderInput itemToMove, int newIndex, string associatedCategoryGuid)
- {
- foreach (var categoryData in categories)
- {
- if (categoryData.categoryGuid == associatedCategoryGuid && categoryData.IsItemInCategory(itemToMove))
- {
- // Validate new index to move the item to
- if (newIndex < -1 || newIndex >= categoryData.childCount)
- {
- AssertHelpers.Fail("Provided invalid index input to MoveItemInCategory.");
- return;
- }
-
- categoryData.MoveItemInCategory(itemToMove, newIndex);
- break;
- }
- }
- }
-
- public int GetGraphInputIndex(ShaderInput input)
- {
- switch (input)
- {
- case AbstractShaderProperty property:
- return m_Properties.IndexOf(property);
- case ShaderKeyword keyword:
- return m_Keywords.IndexOf(keyword);
- case ShaderDropdown dropdown:
- return m_Dropdowns.IndexOf(dropdown);
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
-
- void RemoveGraphInputNoValidate(ShaderInput shaderInput)
- {
- if (shaderInput is AbstractShaderProperty property && m_Properties.Remove(property) ||
- shaderInput is ShaderKeyword keyword && m_Keywords.Remove(keyword) ||
- shaderInput is ShaderDropdown dropdown && m_Dropdowns.Remove(dropdown))
- {
- m_RemovedInputs.Add(shaderInput);
- m_AddedInputs.Remove(shaderInput);
- m_MovedInputs.Remove(shaderInput);
- }
- }
-
- static List<IEdge> s_TempEdges = new List<IEdge>();
-
- public void ReplacePropertyNodeWithConcreteNode(PropertyNode propertyNode)
- {
- ReplacePropertyNodeWithConcreteNodeNoValidate(propertyNode);
- ValidateGraph();
- }
-
- void ReplacePropertyNodeWithConcreteNodeNoValidate(PropertyNode propertyNode, bool deleteNodeIfNoConcreteFormExists = true)
- {
- var property = properties.FirstOrDefault(x => x == propertyNode.property) ?? propertyNode.property;
- if (property == null)
- return;
-
- var node = property.ToConcreteNode() as AbstractMaterialNode;
- if (node == null) // Some nodes have no concrete form
- {
- if (deleteNodeIfNoConcreteFormExists)
- RemoveNodeNoValidate(propertyNode);
- 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.group = propertyNode.group;
- AddNodeNoValidate(node);
-
- foreach (var edge in this.GetEdges(slot.slotReference))
- ConnectNoValidate(newSlot.slotReference, edge.inputSlot);
-
- RemoveNodeNoValidate(propertyNode);
- }
-
- public void AddCategory(CategoryData categoryDataReference)
- {
- m_CategoryData.Add(categoryDataReference);
- m_AddedCategories.Add(categoryDataReference);
- }
-
- public string FindCategoryForInput(ShaderInput input)
- {
- foreach (var categoryData in categories)
- {
- if (categoryData.IsItemInCategory(input))
- {
- return categoryData.categoryGuid;
- }
- }
-
- AssertHelpers.Fail("Attempted to find category for an input that doesn't exist in the graph.");
- return String.Empty;
- }
-
- public void ChangeCategoryName(string categoryGUID, string newName)
- {
- foreach (var categoryData in categories)
- {
- if (categoryData.categoryGuid == categoryGUID)
- {
- var sanitizedCategoryName = GraphUtil.SanitizeCategoryName(newName);
- categoryData.name = sanitizedCategoryName;
- return;
- }
- }
-
- AssertHelpers.Fail("Attempted to change name of a category that does not exist in the graph.");
- }
-
- public void InsertItemIntoCategory(string categoryGUID, ShaderInput itemToAdd, int insertionIndex = -1)
- {
- foreach (var categoryData in categories)
- {
- if (categoryData.categoryGuid == categoryGUID)
- {
- categoryData.InsertItemIntoCategory(itemToAdd, insertionIndex);
- }
- // Also make sure to remove this items guid from an existing category if it exists within one
- else if (categoryData.IsItemInCategory(itemToAdd))
- {
- categoryData.RemoveItemFromCategory(itemToAdd);
- }
- }
- }
-
- public void RemoveItemFromCategory(string categoryGUID, ShaderInput itemToRemove)
- {
- foreach (var categoryData in categories)
- {
- if (categoryData.categoryGuid == categoryGUID)
- {
- categoryData.RemoveItemFromCategory(itemToRemove);
- return;
- }
- }
-
- AssertHelpers.Fail("Attempted to remove item from a category that does not exist in the graph.");
- }
-
- public void RemoveCategory(string categoryGUID)
- {
- var existingCategory = categories.FirstOrDefault(category => category.categoryGuid == categoryGUID);
- if (existingCategory != null)
- {
- m_CategoryData.Remove(existingCategory);
- m_RemovedCategories.Add(existingCategory);
-
- // Whenever a category is removed, also remove any inputs within that category
- foreach (var shaderInput in existingCategory.Children)
- RemoveGraphInput(shaderInput);
- }
- else
- AssertHelpers.Fail("Attempted to remove a category that does not exist in the graph.");
- }
-
- // This differs from the rest of the category handling functions due to how categories can be copied between graphs
- // Since we have no guarantee of us owning the categories, we need a direct reference to the category to copy
- public CategoryData CopyCategory(CategoryData categoryToCopy)
- {
- var copiedCategory = new CategoryData(categoryToCopy);
- AddCategory(copiedCategory);
- // Whenever a category is copied, also copy over all the inputs within that category
- foreach (var childInputToCopy in categoryToCopy.Children)
- {
- var newShaderInput = AddCopyOfShaderInput(childInputToCopy);
- copiedCategory.InsertItemIntoCategory(newShaderInput);
- }
-
- return copiedCategory;
- }
-
- public void OnKeywordChanged()
- {
- OnKeywordChangedNoValidate();
- ValidateGraph();
- }
-
- public void OnKeywordChangedNoValidate()
- {
- DirtyAll<AbstractMaterialNode>(ModificationScope.Topological);
- }
-
- public void OnDropdownChanged()
- {
- OnDropdownChangedNoValidate();
- ValidateGraph();
- }
-
- public void OnDropdownChangedNoValidate()
- {
- DirtyAll<AbstractMaterialNode>(ModificationScope.Topological);
- }
-
- public void CleanupGraph()
- {
- //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 = edge.outputSlot.node;
- var inputNode = edge.inputSlot.node;
-
- MaterialSlot outputSlot = null;
- MaterialSlot inputSlot = null;
- if (ContainsNode(outputNode) && ContainsNode(inputNode))
- {
- 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, false);
- }
- }
- }
-
- private void DirtyAll<T>(ModificationScope modificationScope) where T : AbstractMaterialNode
- {
- graphIsConcretizing = true;
- try
- {
- var allNodes = GetNodes<T>();
- foreach (var node in allNodes)
- {
- node.Dirty(modificationScope);
- node.ValidateNode();
- }
- }
- catch (System.Exception e)
- {
- graphIsConcretizing = false;
- throw e;
- }
- graphIsConcretizing = false;
- }
-
- public void ValidateGraph()
- {
- messageManager?.ClearAllFromProvider(this);
- CleanupGraph();
- GraphSetup.SetupGraph(this);
- GraphConcretization.ConcretizeGraph(this);
- GraphValidation.ValidateGraph(this);
-
- for (int i = 0; i < m_AddedEdges.Count; ++i)
- {
- var edge = m_AddedEdges[i];
- if (!ContainsNode(edge.outputSlot.node) || !ContainsNode(edge.inputSlot.node))
- {
- Debug.LogWarningFormat("Added edge is invalid: {0} -> {1}\n{2}", edge.outputSlot.node.objectId, edge.inputSlot.node.objectId, Environment.StackTrace);
- m_AddedEdges.Remove(edge);
- }
- }
-
- for (int i = 0; i < m_ParentGroupChanges.Count; ++i)
- {
- var groupChange = m_ParentGroupChanges[i];
- switch (groupChange.groupItem)
- {
- case AbstractMaterialNode node when !ContainsNode(node):
- case StickyNoteData stickyNote when !m_StickyNoteDatas.Contains(stickyNote):
- m_ParentGroupChanges.Remove(groupChange);
- break;
- }
- }
-
- var existingDefaultCategory = categories.FirstOrDefault();
- if (existingDefaultCategory?.childCount == 0 && categories.Count() == 1 && (properties.Count() != 0 || keywords.Count() != 0 || dropdowns.Count() != 0))
- {
- // Have a graph with category data in invalid state
- // there is only one category, the default category, and all shader inputs should belong to it
- // Clear category data as it will get reconstructed in the BlackboardController constructor
- m_CategoryData.Clear();
- }
-
- ValidateCustomBlockLimit();
- ValidateContextBlocks();
- }
-
- public void AddValidationError(string id, string errorMessage,
- ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
- {
- messageManager?.AddOrAppendError(this, id, new ShaderMessage("Validation: " + errorMessage, severity));
- }
-
- public void AddSetupError(string id, string errorMessage,
- ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
- {
- messageManager?.AddOrAppendError(this, id, new ShaderMessage("Setup: " + errorMessage, severity));
- }
-
- public void AddConcretizationError(string id, string errorMessage,
- ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
- {
- messageManager?.AddOrAppendError(this, id, new ShaderMessage("Concretization: " + errorMessage, severity));
- }
-
- public void ClearErrorsForNode(AbstractMaterialNode node)
- {
- messageManager?.ClearNodesFromProvider(this, node.ToEnumerable());
- }
-
- internal bool replaceInProgress = false;
- public void ReplaceWith(GraphData other)
- {
- if (other == null)
- throw new ArgumentException("Can only replace with another AbstractMaterialGraph", "other");
-
- replaceInProgress = true;
- m_GraphPrecision = other.m_GraphPrecision;
- m_PreviewMode = other.m_PreviewMode;
- m_OutputNode = other.m_OutputNode;
-
- if ((this.vertexContext.position != other.vertexContext.position) ||
- (this.fragmentContext.position != other.fragmentContext.position))
- {
- this.vertexContext.position = other.vertexContext.position;
- this.fragmentContext.position = other.fragmentContext.position;
- m_MovedContexts = true;
- }
-
- using (var inputsToRemove = PooledList<ShaderInput>.Get())
- {
- foreach (var property in m_Properties.SelectValue())
- inputsToRemove.Add(property);
- foreach (var keyword in m_Keywords.SelectValue())
- inputsToRemove.Add(keyword);
- foreach (var dropdown in m_Dropdowns.SelectValue())
- inputsToRemove.Add(dropdown);
- foreach (var input in inputsToRemove)
- RemoveGraphInputNoValidate(input);
- }
- foreach (var otherProperty in other.properties)
- {
- AddGraphInputNoSanitization(otherProperty);
- }
- foreach (var otherKeyword in other.keywords)
- {
- AddGraphInputNoSanitization(otherKeyword);
- }
- foreach (var otherDropdown in other.dropdowns)
- {
- AddGraphInputNoSanitization(otherDropdown);
- }
-
- 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 (ListPool<GroupData>.Get(out var removedGroupDatas))
- {
- removedGroupDatas.AddRange(m_GroupDatas.SelectValue());
- foreach (var groupData in removedGroupDatas)
- {
- RemoveGroupNoValidate(groupData);
- }
- }
-
- using (ListPool<StickyNoteData>.Get(out var removedNoteDatas))
- {
- removedNoteDatas.AddRange(m_StickyNoteDatas.SelectValue());
- foreach (var groupData in removedNoteDatas)
- {
- RemoveNoteNoValidate(groupData);
- }
- }
-
- using (var pooledList = ListPool<IEdge>.Get(out var removedNodeEdges))
- {
- removedNodeEdges.AddRange(m_Edges);
- foreach (var edge in removedNodeEdges)
- RemoveEdgeNoValidate(edge);
- }
-
- using (var nodesToRemove = PooledList<AbstractMaterialNode>.Get())
- {
- nodesToRemove.AddRange(m_Nodes.SelectValue());
- foreach (var node in nodesToRemove)
- RemoveNodeNoValidate(node);
- }
-
- // Clear category data too before re-adding
- m_CategoryData.Clear();
-
- ValidateGraph();
-
- foreach (GroupData groupData in other.groups)
- AddGroup(groupData);
-
- // If categories are ever removed completely, make sure there is always one default category that exists
- if (!other.categories.Any())
- {
- AddCategory(CategoryData.DefaultCategory());
- }
- else
- {
- foreach (CategoryData categoryData in other.categories)
- {
- AddCategory(categoryData);
- }
- }
-
-
- foreach (var stickyNote in other.stickyNotes)
- {
- AddStickyNote(stickyNote);
- }
-
- foreach (var node in other.GetNodes<AbstractMaterialNode>())
- {
- if (node is BlockNode blockNode)
- {
- var contextData = blockNode.descriptor.shaderStage == ShaderStage.Vertex ? vertexContext : fragmentContext;
- AddBlockNoValidate(blockNode, contextData, blockNode.index);
- }
- else
- {
- AddNodeNoValidate(node);
- }
- }
-
- foreach (var edge in other.edges)
- {
- ConnectNoValidate(edge.outputSlot, edge.inputSlot, true);
- }
-
- outputNode = other.outputNode;
-
- // clear our local active targets and copy state from the other GraphData
-
- // NOTE: we DO NOT clear or rebuild m_AllPotentialTargets, in order to
- // retain the data from any inactive targets.
- // this allows the user can add them back and keep the old settings
-
- m_ActiveTargets.Clear();
- foreach (var target in other.activeTargets)
- {
- // Ensure target inits correctly
- var context = new TargetSetupContext();
- target.Setup(ref context);
- SetTargetActive(target, true);
- }
- SortActiveTargets();
-
- // Active blocks
- var activeBlocks = GetActiveBlocksForAllActiveTargets();
- UpdateActiveBlocks(activeBlocks);
- replaceInProgress = false;
- ValidateGraph();
- }
-
- internal void PasteGraph(CopyPasteGraph graphToPaste, List<AbstractMaterialNode> remappedNodes,
- List<Edge> remappedEdges)
- {
- var groupMap = new Dictionary<GroupData, GroupData>();
- foreach (var group in graphToPaste.groups)
- {
- var position = group.position;
- position.x += 30;
- position.y += 30;
-
- GroupData newGroup = new GroupData(group.title, position);
-
- groupMap[group] = newGroup;
-
- 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);
- pastedStickyNote.textSize = stickyNote.textSize;
- pastedStickyNote.theme = stickyNote.theme;
- if (stickyNote.group != null && groupMap.ContainsKey(stickyNote.group))
- {
- pastedStickyNote.group = groupMap[stickyNote.group];
- }
-
- AddStickyNote(pastedStickyNote);
- m_PastedStickyNotes.Add(pastedStickyNote);
- }
-
- var edges = graphToPaste.edges.ToList();
- var nodeList = graphToPaste.GetNodes<AbstractMaterialNode>();
- foreach (var node in nodeList)
- {
- // cannot paste block nodes, or unknown node types
- if ((node is BlockNode) || (node is MultiJsonInternal.UnknownNodeType))
- continue;
-
- if (!IsInputAllowedInGraph(node))
- continue;
-
- AbstractMaterialNode pastedNode = node;
-
- // 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.
- var property = m_Properties.SelectValue().FirstOrDefault(x => x.objectId == propertyNode.property.objectId
- || (x.propertyType == propertyNode.property.propertyType && x.referenceName == propertyNode.property.referenceName));
- if (property != null)
- {
- propertyNode.property = property;
- }
- else
- {
- pastedNode = propertyNode.property.ToConcreteNode();
- // some property nodes cannot be concretized.. fail to paste them
- if (pastedNode == null)
- continue;
- pastedNode.drawState = node.drawState;
- for (var i = 0; i < edges.Count; i++)
- {
- var edge = edges[i];
- if (edge.outputSlot.node == node)
- {
- edges[i] = new Edge(new SlotReference(pastedNode, edge.outputSlot.slotId), edge.inputSlot);
- }
- else if (edge.inputSlot.node == node)
- {
- edges[i] = new Edge(edge.outputSlot, new SlotReference(pastedNode, edge.inputSlot.slotId));
- }
- }
- }
- }
-
- // If the node has a group guid and no group has been copied, reset the group guid.
- // Check if the node is inside a group
- if (node.group != null)
- {
- if (groupMap.ContainsKey(node.group))
- {
- var absNode = pastedNode;
- absNode.group = groupMap[node.group];
- pastedNode = absNode;
- }
- else
- {
- pastedNode.group = null;
- }
- }
-
- 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)
- {
- var keyword = m_Keywords.SelectValue().FirstOrDefault(x => x.objectId == keywordNode.keyword.objectId
- || (x.keywordType == keywordNode.keyword.keywordType && x.referenceName == keywordNode.keyword.referenceName));
- if (keyword != null)
- {
- keywordNode.keyword = keyword;
- }
- else
- {
- owner.graphDataStore.Dispatch(new AddShaderInputAction() { shaderInputReference = keywordNode.keyword });
- }
-
- // Always update Keyword nodes to handle any collisions resolved on the Keyword
- keywordNode.UpdateNode();
- }
-
- // Check if the dropdown nodes need to have their dropdowns copied.
- if (node is DropdownNode dropdownNode)
- {
- var dropdown = m_Dropdowns.SelectValue().FirstOrDefault(x => x.objectId == dropdownNode.dropdown.objectId
- || x.referenceName == dropdownNode.dropdown.referenceName);
- if (dropdown != null)
- {
- dropdownNode.dropdown = dropdown;
- }
- else
- {
- owner.graphDataStore.Dispatch(new AddShaderInputAction() { shaderInputReference = dropdownNode.dropdown });
- }
-
- // Always update Dropdown nodes to handle any collisions resolved on the Keyword
- dropdownNode.UpdateNode();
- }
- }
-
- foreach (var edge in edges)
- {
- var newEdge = (Edge)Connect(edge.outputSlot, edge.inputSlot);
- if (newEdge != null)
- {
- remappedEdges.Add(newEdge);
- }
- }
-
- ValidateGraph();
- }
-
- public override void OnBeforeSerialize()
- {
- m_Edges.Sort();
- ChangeVersion(latestVersion);
- }
-
- static T DeserializeLegacy<T>(string typeString, string json, Guid? overrideObjectId = null) where T : JsonObject
- {
- var jsonObj = MultiJsonInternal.CreateInstanceForDeserialization(typeString);
- var value = jsonObj as T;
- if (value == null)
- {
- Debug.Log($"Cannot create instance for {typeString}");
- return null;
- }
-
- // by default, MultiJsonInternal.CreateInstance will create a new objectID randomly..
- // we need some created objects to have deterministic objectIDs, because they affect the generated shader.
- // if the generated shader is not deterministic, it can create ripple effects (i.e. causing Materials to be modified randomly as properties are renamed)
- // so we provide this path to allow the calling code to override the objectID with something deterministic
- if (overrideObjectId.HasValue)
- value.OverrideObjectId(overrideObjectId.Value.ToString("N"));
- MultiJsonInternal.Enqueue(value, json);
- return value as T;
- }
-
- static AbstractMaterialNode DeserializeLegacyNode(string typeString, string json, Guid? overrideObjectId = null)
- {
- var jsonObj = MultiJsonInternal.CreateInstanceForDeserialization(typeString);
- var value = jsonObj as AbstractMaterialNode;
- if (value == null)
- {
- //Special case - want to support nodes of unknwon type for cross pipeline compatability
- value = new LegacyUnknownTypeNode(typeString, json);
- if (overrideObjectId.HasValue)
- value.OverrideObjectId(overrideObjectId.Value.ToString("N"));
- MultiJsonInternal.Enqueue(value, json);
- return value as AbstractMaterialNode;
- }
- else
- {
- if (overrideObjectId.HasValue)
- value.OverrideObjectId(overrideObjectId.Value.ToString("N"));
- MultiJsonInternal.Enqueue(value, json);
- return value as AbstractMaterialNode;
- }
- }
-
- public override void OnAfterDeserialize(string json)
- {
- if (sgVersion == 0)
- {
- var graphData0 = JsonUtility.FromJson<GraphData0>(json);
- //If a graph was previously updated to V2, since we had to rename m_Version to m_SGVersion to avoid collision with an upgrade system from
- //HDRP, we have to handle the case that our version might not be correct -
- if (graphData0.m_Version > 0)
- {
- sgVersion = graphData0.m_Version;
- }
- else
- {
- // graphData.m_Version == 0 (matches current sgVersion)
- Guid assetGuid;
- if (!Guid.TryParse(this.assetGuid, out assetGuid))
- assetGuid = JsonObject.GenerateNamespaceUUID(Guid.Empty, json);
-
- var nodeGuidMap = new Dictionary<string, AbstractMaterialNode>();
- var propertyGuidMap = new Dictionary<string, AbstractShaderProperty>();
- var keywordGuidMap = new Dictionary<string, ShaderKeyword>();
- var groupGuidMap = new Dictionary<string, GroupData>();
- var slotsField = typeof(AbstractMaterialNode).GetField("m_Slots", BindingFlags.Instance | BindingFlags.NonPublic);
- var propertyField = typeof(PropertyNode).GetField("m_Property", BindingFlags.Instance | BindingFlags.NonPublic);
- var keywordField = typeof(KeywordNode).GetField("m_Keyword", BindingFlags.Instance | BindingFlags.NonPublic);
- var dropdownField = typeof(DropdownNode).GetField("m_Dropdown", BindingFlags.Instance | BindingFlags.NonPublic);
- var defaultReferenceNameField = typeof(ShaderInput).GetField("m_DefaultReferenceName", BindingFlags.Instance | BindingFlags.NonPublic);
-
- m_GroupDatas.Clear();
- m_StickyNoteDatas.Clear();
-
- foreach (var group0 in graphData0.m_Groups)
- {
- var group = new GroupData(group0.m_Title, group0.m_Position);
- m_GroupDatas.Add(group);
- if (!groupGuidMap.ContainsKey(group0.m_GuidSerialized))
- {
- groupGuidMap.Add(group0.m_GuidSerialized, group);
- }
- else if (!groupGuidMap[group0.m_GuidSerialized].Equals(group.objectId))
- {
- Debug.LogError("Group id mismatch");
- }
- }
-
- foreach (var serializedProperty in graphData0.m_SerializedProperties)
- {
- var propObjectId = JsonObject.GenerateNamespaceUUID(assetGuid, serializedProperty.JSONnodeData);
- var property = DeserializeLegacy<AbstractShaderProperty>(serializedProperty.typeInfo.fullName, serializedProperty.JSONnodeData, propObjectId);
- if (property == null)
- continue;
-
- m_Properties.Add(property);
-
- var input0 = JsonUtility.FromJson<ShaderInput0>(serializedProperty.JSONnodeData);
- propertyGuidMap[input0.m_Guid.m_GuidSerialized] = property;
-
- // Fix up missing reference names
- // Properties on Sub Graphs in V0 never have reference names serialized
- // To maintain Sub Graph node property mapping we force guid based reference names on upgrade
- if (string.IsNullOrEmpty((string)defaultReferenceNameField.GetValue(property)))
- {
- // ColorShaderProperty is the only Property case where `GetDefaultReferenceName` was overriden
- if (MultiJson.ParseType(serializedProperty.typeInfo.fullName) == typeof(ColorShaderProperty))
- {
- defaultReferenceNameField.SetValue(property, $"Color_{GuidEncoder.Encode(Guid.Parse(input0.m_Guid.m_GuidSerialized))}");
- }
- else
- {
- defaultReferenceNameField.SetValue(property, $"{property.concreteShaderValueType}_{GuidEncoder.Encode(Guid.Parse(input0.m_Guid.m_GuidSerialized))}");
- }
- }
- }
-
- foreach (var serializedKeyword in graphData0.m_SerializedKeywords)
- {
- var keyword = DeserializeLegacy<ShaderKeyword>(serializedKeyword.typeInfo.fullName, serializedKeyword.JSONnodeData);
- if (keyword == null)
- {
- continue;
- }
-
- m_Keywords.Add(keyword);
-
- var input0 = JsonUtility.FromJson<ShaderInput0>(serializedKeyword.JSONnodeData);
- keywordGuidMap[input0.m_Guid.m_GuidSerialized] = keyword;
- }
-
- foreach (var serializedNode in graphData0.m_SerializableNodes)
- {
- var node0 = JsonUtility.FromJson<AbstractMaterialNode0>(serializedNode.JSONnodeData);
-
- var nodeObjectId = JsonObject.GenerateNamespaceUUID(node0.m_GuidSerialized, "node");
- var node = DeserializeLegacyNode(serializedNode.typeInfo.fullName, serializedNode.JSONnodeData, nodeObjectId);
- if (node == null)
- {
- continue;
- }
-
- nodeGuidMap.Add(node0.m_GuidSerialized, node);
- m_Nodes.Add(node);
-
- if (!string.IsNullOrEmpty(node0.m_PropertyGuidSerialized) && propertyGuidMap.TryGetValue(node0.m_PropertyGuidSerialized, out var property))
- {
- propertyField.SetValue(node, (JsonRef<AbstractShaderProperty>)property);
- }
-
- if (!string.IsNullOrEmpty(node0.m_KeywordGuidSerialized) && keywordGuidMap.TryGetValue(node0.m_KeywordGuidSerialized, out var keyword))
- {
- keywordField.SetValue(node, (JsonRef<ShaderKeyword>)keyword);
- }
-
- var slots = (List<JsonData<MaterialSlot>>)slotsField.GetValue(node);
- slots.Clear();
-
- foreach (var serializedSlot in node0.m_SerializableSlots)
- {
- var slotObjectId = JsonObject.GenerateNamespaceUUID(node0.m_GuidSerialized, serializedSlot.JSONnodeData);
- var slot = DeserializeLegacy<MaterialSlot>(serializedSlot.typeInfo.fullName, serializedSlot.JSONnodeData, slotObjectId);
- if (slot == null)
- {
- continue;
- }
-
- slots.Add(slot);
- }
-
- if (!String.IsNullOrEmpty(node0.m_GroupGuidSerialized))
- {
- if (groupGuidMap.TryGetValue(node0.m_GroupGuidSerialized, out GroupData foundGroup))
- {
- node.group = foundGroup;
- }
- }
- }
-
- foreach (var stickyNote0 in graphData0.m_StickyNotes)
- {
- var stickyNote = new StickyNoteData(stickyNote0.m_Title, stickyNote0.m_Content, stickyNote0.m_Position);
- if (!String.IsNullOrEmpty(stickyNote0.m_GroupGuidSerialized))
- {
- if (groupGuidMap.TryGetValue(stickyNote0.m_GroupGuidSerialized, out GroupData foundGroup))
- {
- stickyNote.group = foundGroup;
- }
- }
- stickyNote.theme = stickyNote0.m_Theme;
- stickyNote.textSize = stickyNote0.m_TextSize;
- m_StickyNoteDatas.Add(stickyNote);
- }
-
- var subgraphOuput = GetNodes<SubGraphOutputNode>();
- isSubGraph = subgraphOuput.Any();
-
- if (isSubGraph)
- {
- m_OutputNode = subgraphOuput.FirstOrDefault();
- }
- else if (!string.IsNullOrEmpty(graphData0.m_ActiveOutputNodeGuidSerialized))
- {
- m_OutputNode = nodeGuidMap[graphData0.m_ActiveOutputNodeGuidSerialized];
- }
- else
- {
- m_OutputNode = (AbstractMaterialNode)GetNodes<IMasterNode1>().FirstOrDefault();
- }
-
- foreach (var serializedElement in graphData0.m_SerializableEdges)
- {
- var edge0 = JsonUtility.FromJson<Edge0>(serializedElement.JSONnodeData);
- m_Edges.Add(new Edge(
- new SlotReference(
- nodeGuidMap[edge0.m_OutputSlot.m_NodeGUIDSerialized],
- edge0.m_OutputSlot.m_SlotId),
- new SlotReference(
- nodeGuidMap[edge0.m_InputSlot.m_NodeGUIDSerialized],
- edge0.m_InputSlot.m_SlotId)));
- }
- }
- }
- }
-
- [Serializable]
- class OldGraphDataReadConcretePrecision
- {
- // old value just for upgrade
- [SerializeField]
- public ConcretePrecision m_ConcretePrecision = ConcretePrecision.Single;
- };
-
- public override void OnAfterMultiDeserialize(string json)
- {
- // Deferred upgrades
- if (sgVersion != latestVersion)
- {
- if (sgVersion < 2)
- {
- var addedBlocks = ListPool<BlockFieldDescriptor>.Get();
-
- void UpgradeFromBlockMap(Dictionary<BlockFieldDescriptor, int> blockMap)
- {
- // Map master node ports to blocks
- if (blockMap != null)
- {
- foreach (var blockMapping in blockMap)
- {
- // Create a new BlockNode for each unique map entry
- var descriptor = blockMapping.Key;
- if (addedBlocks.Contains(descriptor))
- continue;
-
- addedBlocks.Add(descriptor);
-
- var contextData = descriptor.shaderStage == ShaderStage.Fragment ? m_FragmentContext : m_VertexContext;
- var block = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
- block.Init(descriptor);
- AddBlockNoValidate(block, contextData, contextData.blocks.Count);
-
- // To avoid having to go around the following deserialization code
- // We simply run OnBeforeSerialization here to ensure m_SerializedDescriptor is set
- block.OnBeforeSerialize();
-
- // Now remap the incoming edges to blocks
- var slotId = blockMapping.Value;
- var oldSlot = m_OutputNode.value.FindSlot<MaterialSlot>(slotId);
- var newSlot = block.FindSlot<MaterialSlot>(0);
- if (oldSlot == null)
- continue;
-
- var oldInputSlotRef = m_OutputNode.value.GetSlotReference(slotId);
- var newInputSlotRef = block.GetSlotReference(0);
-
- // Always copy the value over for convenience
- newSlot.CopyValuesFrom(oldSlot);
-
- for (int i = 0; i < m_Edges.Count; i++)
- {
- // Find all edges connected to the master node using slot ID from the block map
- // Remove them and replace them with new edges connected to the block nodes
- var edge = m_Edges[i];
- if (edge.inputSlot.Equals(oldInputSlotRef))
- {
- var outputSlot = edge.outputSlot;
- m_Edges.Remove(edge);
- m_Edges.Add(new Edge(outputSlot, newInputSlotRef));
- }
- }
-
- // manually handle a bug where fragment normal slots could get out of sync of the master node's set fragment normal space
- if (descriptor == BlockFields.SurfaceDescription.NormalOS)
- {
- NormalMaterialSlot norm = newSlot as NormalMaterialSlot;
- if (norm.space != CoordinateSpace.Object)
- {
- norm.space = CoordinateSpace.Object;
- }
- }
- else if (descriptor == BlockFields.SurfaceDescription.NormalTS)
- {
- NormalMaterialSlot norm = newSlot as NormalMaterialSlot;
- if (norm.space != CoordinateSpace.Tangent)
- {
- norm.space = CoordinateSpace.Tangent;
- }
- }
- else if (descriptor == BlockFields.SurfaceDescription.NormalWS)
- {
- NormalMaterialSlot norm = newSlot as NormalMaterialSlot;
- if (norm.space != CoordinateSpace.World)
- {
- norm.space = CoordinateSpace.World;
- }
- }
- }
-
- // We need to call AddBlockNoValidate but this adds to m_AddedNodes resulting in duplicates
- // Therefore we need to clear this list before the view is created
- m_AddedNodes.Clear();
- }
- }
-
- var masterNode = m_OutputNode.value as IMasterNode1;
-
- // This is required for edge lookup during Target upgrade
- if (m_OutputNode.value != null)
- {
- m_OutputNode.value.owner = this;
- }
- foreach (var edge in m_Edges)
- {
- AddEdgeToNodeEdges(edge);
- }
-
- // Ensure correct initialization of Contexts
- AddContexts();
-
- // Position Contexts to the match master node
- var oldPosition = Vector2.zero;
- if (m_OutputNode.value != null)
- {
- oldPosition = m_OutputNode.value.drawState.position.position;
- }
- m_VertexContext.position = oldPosition;
- m_FragmentContext.position = new Vector2(oldPosition.x, oldPosition.y + 200);
-
- // Try to upgrade all potential targets from master node
- if (masterNode != null)
- {
- foreach (var potentialTarget in m_AllPotentialTargets)
- {
- if (potentialTarget.IsUnknown())
- continue;
-
- var target = potentialTarget.GetTarget();
- if (!(target is ILegacyTarget legacyTarget))
- continue;
-
- if (!legacyTarget.TryUpgradeFromMasterNode(masterNode, out var newBlockMap))
- continue;
-
- // upgrade succeeded! Activate it
- SetTargetActive(target, true);
- UpgradeFromBlockMap(newBlockMap);
- }
- SortActiveTargets();
- }
-
- // Clean up after upgrade
- if (!isSubGraph)
- {
- m_OutputNode = null;
- }
-
- var masterNodes = GetNodes<IMasterNode1>().ToArray();
- for (int i = 0; i < masterNodes.Length; i++)
- {
- var node = masterNodes.ElementAt(i) as AbstractMaterialNode;
- m_Nodes.Remove(node);
- }
-
- m_NodeEdges.Clear();
- }
-
- if (sgVersion < 3)
- {
- var oldGraph = JsonUtility.FromJson<OldGraphDataReadConcretePrecision>(json);
-
- // upgrade concrete precision to the new graph precision
- switch (oldGraph.m_ConcretePrecision)
- {
- case ConcretePrecision.Half:
- m_GraphPrecision = GraphPrecision.Half;
- break;
- case ConcretePrecision.Single:
- m_GraphPrecision = GraphPrecision.Single;
- break;
- }
- }
-
- ChangeVersion(latestVersion);
- }
-
- PooledList<(LegacyUnknownTypeNode, AbstractMaterialNode)> updatedNodes = PooledList<(LegacyUnknownTypeNode, AbstractMaterialNode)>.Get();
- foreach (var node in m_Nodes.SelectValue())
- {
- if (node is LegacyUnknownTypeNode lNode && lNode.foundType != null)
- {
- AbstractMaterialNode legacyNode = (AbstractMaterialNode)Activator.CreateInstance(lNode.foundType);
- JsonUtility.FromJsonOverwrite(lNode.serializedData, legacyNode);
- legacyNode.group = lNode.group;
- updatedNodes.Add((lNode, legacyNode));
- }
- }
- foreach (var nodePair in updatedNodes)
- {
- m_Nodes.Add(nodePair.Item2);
- ReplaceNodeWithNode(nodePair.Item1, nodePair.Item2);
- }
- updatedNodes.Dispose();
-
- m_NodeDictionary = new Dictionary<string, AbstractMaterialNode>(m_Nodes.Count);
-
- foreach (var group in m_GroupDatas.SelectValue())
- {
- m_GroupItems.Add(group, new List<IGroupItem>());
- }
-
- foreach (var node in m_Nodes.SelectValue())
- {
- node.owner = this;
- node.UpdateNodeAfterDeserialization();
- node.SetupSlots();
- m_NodeDictionary.Add(node.objectId, node);
- if (m_GroupItems.TryGetValue(node.group, out var groupItems))
- {
- groupItems.Add(node);
- }
- else
- {
- node.group = null;
- }
- }
-
- foreach (var stickyNote in m_StickyNoteDatas.SelectValue())
- {
- if (m_GroupItems.TryGetValue(stickyNote.group, out var groupItems))
- {
- groupItems.Add(stickyNote);
- }
- else
- {
- stickyNote.group = null;
- }
- }
-
- foreach (var edge in m_Edges)
- AddEdgeToNodeEdges(edge);
-
- // --------------------------------------------------
- // Deserialize Contexts & Blocks
-
- void DeserializeContextData(ContextData contextData, ShaderStage stage)
- {
- // Because Vertex/Fragment Contexts are serialized explicitly
- // we do not need to serialize the Stage value on the ContextData
- contextData.shaderStage = stage;
-
- var blocks = contextData.blocks.SelectValue().ToList();
- var blockCount = blocks.Count;
- for (int i = 0; i < blockCount; i++)
- {
- // Update NonSerialized data on the BlockNode
- var block = blocks[i];
- // custom interpolators fully regenerate their own descriptor on deserialization
- if (!block.isCustomBlock)
- {
- block.descriptor = m_BlockFieldDescriptors.FirstOrDefault(x => $"{x.tag}.{x.name}" == block.serializedDescriptor);
- }
- if (block.descriptor == null)
- {
- //Hit a descriptor that was not recognized from the assembly (likely from a different SRP)
- //create a new entry for it and continue on
- if (string.IsNullOrEmpty(block.serializedDescriptor))
- {
- throw new Exception($"Block {block} had no serialized descriptor");
- }
-
- var tmp = block.serializedDescriptor.Split('.');
- if (tmp.Length != 2)
- {
- throw new Exception($"Block {block}'s serialized descriptor {block.serializedDescriptor} did not match expected format {{x.tag}}.{{x.name}}");
- }
- //right thing to do?
- block.descriptor = new BlockFieldDescriptor(tmp[0], tmp[1], null, null, stage, true, true);
- m_BlockFieldDescriptors.Add(block.descriptor);
- }
- block.contextData = contextData;
- }
- }
-
- // First deserialize the ContextDatas
- DeserializeContextData(m_VertexContext, ShaderStage.Vertex);
- DeserializeContextData(m_FragmentContext, ShaderStage.Fragment);
-
- // there should be no unknown potential targets at this point
- Assert.IsFalse(m_AllPotentialTargets.Any(pt => pt.IsUnknown()));
-
- foreach (var target in m_ActiveTargets.SelectValue())
- {
- var targetType = target.GetType();
- if (targetType == typeof(MultiJsonInternal.UnknownTargetType))
- {
- // register any active UnknownTargetType as a potential target
- m_AllPotentialTargets.Add(new PotentialTarget(target));
- }
- else
- {
- // active known targets should replace the stored Target in AllPotentialTargets
- int targetIndex = m_AllPotentialTargets.FindIndex(pt => pt.knownType == targetType);
- m_AllPotentialTargets[targetIndex].ReplaceStoredTarget(target);
- }
- }
-
- SortActiveTargets();
- }
-
- private void ReplaceNodeWithNode(LegacyUnknownTypeNode nodeToReplace, AbstractMaterialNode nodeReplacement)
- {
- var oldSlots = new List<MaterialSlot>();
- nodeToReplace.GetSlots(oldSlots);
- var newSlots = new List<MaterialSlot>();
- nodeReplacement.GetSlots(newSlots);
-
- for (int i = 0; i < oldSlots.Count; i++)
- {
- newSlots[i].CopyValuesFrom(oldSlots[i]);
- var oldSlotRef = nodeToReplace.GetSlotReference(oldSlots[i].id);
- var newSlotRef = nodeReplacement.GetSlotReference(newSlots[i].id);
-
- for (int x = 0; x < m_Edges.Count; x++)
- {
- var edge = m_Edges[x];
- if (edge.inputSlot.Equals(oldSlotRef))
- {
- var outputSlot = edge.outputSlot;
- m_Edges.Remove(edge);
- m_Edges.Add(new Edge(outputSlot, newSlotRef));
- }
- else if (edge.outputSlot.Equals(oldSlotRef))
- {
- var inputSlot = edge.inputSlot;
- m_Edges.Remove(edge);
- m_Edges.Add(new Edge(newSlotRef, inputSlot));
- }
- }
- }
- }
-
- public void OnEnable()
- {
- foreach (var node in GetNodes<AbstractMaterialNode>().OfType<IOnAssetEnabled>())
- {
- node.OnEnable();
- }
-
- ShaderGraphPreferences.onVariantLimitChanged += OnKeywordChanged;
- }
-
- public void OnDisable()
- {
- ShaderGraphPreferences.onVariantLimitChanged -= OnKeywordChanged;
-
- foreach (var node in GetNodes<AbstractMaterialNode>())
- node.Dispose();
- }
-
- internal void ValidateCustomBlockLimit()
- {
- if (m_ActiveTargets.Count() == 0)
- return;
-
- int nonCustomUsage = 0;
- foreach (var bnode in vertexContext.blocks.Where(jb => !jb.value.isCustomBlock).Select(b => b.value))
- {
- if (bnode == null || bnode.descriptor == null)
- continue;
-
- if (bnode.descriptor.HasPreprocessor() || bnode.descriptor.HasSemantic() || bnode.descriptor.vectorCount == 0) // not packable.
- nonCustomUsage += 4;
- else nonCustomUsage += bnode.descriptor.vectorCount;
- }
- int maxTargetUsage = m_ActiveTargets.Select(jt => jt.value.padCustomInterpolatorLimit).Max() * 4;
-
- int padding = nonCustomUsage + maxTargetUsage;
-
- int errRange = ShaderGraphProjectSettings.instance.customInterpolatorErrorThreshold;
- int warnRange = ShaderGraphProjectSettings.instance.customInterpolatorWarningThreshold;
-
- int errorLevel = errRange * 4 - padding;
- int warnLevel = warnRange * 4 - padding;
-
- int total = 0;
-
- // warn based on the interpolator's location in the block list.
- foreach (var cib in vertexContext.blocks.Where(jb => jb.value.isCustomBlock).Select(b => b.value))
- {
- ClearErrorsForNode(cib);
- total += (int)cib.customWidth;
- if (total > errorLevel)
- {
- AddValidationError(cib.objectId, $"{cib.customName} exceeds the interpolation channel error threshold: {errRange}. See ShaderGraph project settings.");
- }
- else if (total > warnLevel)
- {
- AddValidationError(cib.objectId, $"{cib.customName} exceeds the interpolation channel warning threshold: {warnRange}. See ShaderGraph project settings.", ShaderCompilerMessageSeverity.Warning);
- }
- }
- }
-
- void ValidateContextBlocks()
- {
- void ValidateContext(ContextData contextData, ShaderStage expectedShaderStage)
- {
- if (contextData == null)
- return;
-
- foreach (var block in contextData.blocks)
- {
- var slots = block.value.GetInputSlots<MaterialSlot>();
- foreach (var slot in slots)
- FindAndReportSlotErrors(slot, expectedShaderStage);
- }
- };
-
- ValidateContext(vertexContext, ShaderStage.Vertex);
- ValidateContext(fragmentContext, ShaderStage.Fragment);
- }
-
- void FindAndReportSlotErrors(MaterialSlot initialSlot, ShaderStage expectedShaderStage)
- {
- var expectedCapability = expectedShaderStage.GetShaderStageCapability();
- var errorSourceSlots = new HashSet<MaterialSlot>();
- var visitedNodes = new HashSet<AbstractMaterialNode>();
-
- var graph = initialSlot.owner.owner;
- var slotStack = new Stack<MaterialSlot>();
- slotStack.Clear();
- slotStack.Push(initialSlot);
-
- // Trace back and find any edges that introduce an error
- while (slotStack.Any())
- {
- var slot = slotStack.Pop();
-
- // If the slot is an input, jump across the connected edge to the output it's connected to
- if (slot.isInputSlot)
- {
- foreach (var edge in graph.GetEdges(slot.slotReference))
- {
- var node = edge.outputSlot.node;
-
- var outputSlot = node.FindOutputSlot<MaterialSlot>(edge.outputSlot.slotId);
- // If the output slot this is connected to is invalid then this is a source of an error.
- // Mark the slot and stop iterating, otherwise continue the recursion
- if (!outputSlot.stageCapability.HasFlag(expectedCapability))
- errorSourceSlots.Add(outputSlot);
- else
- slotStack.Push(outputSlot);
- }
- }
- else
- {
- // No need to double visit nodes
- if (visitedNodes.Contains(slot.owner))
- continue;
- visitedNodes.Add(slot.owner);
-
- var ownerSlots = slot.owner.GetInputSlots<MaterialSlot>(slot);
- foreach (var ownerSlot in ownerSlots)
- slotStack.Push(ownerSlot);
- }
- }
-
- bool IsEntireNodeStageLocked(AbstractMaterialNode node, ShaderStageCapability expectedNodeCapability)
- {
- var slots = node.GetOutputSlots<MaterialSlot>();
- foreach (var slot in slots)
- {
- if (expectedNodeCapability != slot.stageCapability)
- return false;
- }
- return true;
- };
-
- foreach (var errorSourceSlot in errorSourceSlots)
- {
- var errorNode = errorSourceSlot.owner;
-
- // Determine if only one slot or the entire node is at fault. Currently only slots are
- // denoted with stage capabilities so deduce this by checking all outputs
- string errorSource;
- if (IsEntireNodeStageLocked(errorNode, errorSourceSlot.stageCapability))
- errorSource = $"Node {errorNode.name}";
- else
- errorSource = $"Slot {errorSourceSlot.RawDisplayName()}";
-
- // Determine what action they can take. If the stage capability is None then this can't be connected to anything.
- string actionToTake;
- if (errorSourceSlot.stageCapability != ShaderStageCapability.None)
- {
- var validStageName = errorSourceSlot.stageCapability.ToString().ToLower();
- actionToTake = $"reconnect to a {validStageName} block or delete invalid connection";
- }
- else
- actionToTake = "delete invalid connection";
-
- var invalidStageName = expectedShaderStage.ToString().ToLower();
- string message = $"{errorSource} is not compatible with {invalidStageName} block {initialSlot.RawDisplayName()}, {actionToTake}.";
- AddValidationError(errorNode.objectId, message, ShaderCompilerMessageSeverity.Error);
- }
- }
- }
-
- [Serializable]
- class InspectorPreviewData
- {
- public SerializableMesh serializedMesh = new SerializableMesh();
- public bool preventRotation;
-
- [NonSerialized]
- public Quaternion rotation = Quaternion.identity;
-
- [NonSerialized]
- public float scale = 1f;
- }
- }
|