暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using System.Text.RegularExpressions;
  7. using UnityEngine;
  8. using UnityEditor.Graphing;
  9. using UnityEditor.Graphing.Util;
  10. using UnityEditor.Rendering;
  11. using UnityEditor.ShaderGraph.Internal;
  12. using UnityEditor.ShaderGraph.Legacy;
  13. using UnityEditor.ShaderGraph.Serialization;
  14. using UnityEditor.ShaderGraph.Drawing;
  15. using Edge = UnityEditor.Graphing.Edge;
  16. using UnityEngine.UIElements;
  17. using UnityEngine.Assertions;
  18. using UnityEngine.Pool;
  19. using UnityEngine.Serialization;
  20. namespace UnityEditor.ShaderGraph
  21. {
  22. [Serializable]
  23. [FormerName("UnityEditor.ShaderGraph.MaterialGraph")]
  24. [FormerName("UnityEditor.ShaderGraph.SubGraph")]
  25. [FormerName("UnityEditor.ShaderGraph.AbstractMaterialGraph")]
  26. sealed partial class GraphData : JsonObject
  27. {
  28. public override int latestVersion => 3;
  29. public GraphObject owner { get; set; }
  30. [NonSerialized]
  31. internal bool graphIsConcretizing = false;
  32. #region Input data
  33. [SerializeField]
  34. List<JsonData<AbstractShaderProperty>> m_Properties = new List<JsonData<AbstractShaderProperty>>();
  35. public DataValueEnumerable<AbstractShaderProperty> properties => m_Properties.SelectValue();
  36. [SerializeField]
  37. List<JsonData<ShaderKeyword>> m_Keywords = new List<JsonData<ShaderKeyword>>();
  38. public DataValueEnumerable<ShaderKeyword> keywords => m_Keywords.SelectValue();
  39. [SerializeField]
  40. List<JsonData<ShaderDropdown>> m_Dropdowns = new List<JsonData<ShaderDropdown>>();
  41. public DataValueEnumerable<ShaderDropdown> dropdowns => m_Dropdowns.SelectValue();
  42. [NonSerialized]
  43. List<ShaderInput> m_AddedInputs = new List<ShaderInput>();
  44. public IEnumerable<ShaderInput> addedInputs
  45. {
  46. get { return m_AddedInputs; }
  47. }
  48. [NonSerialized]
  49. List<ShaderInput> m_RemovedInputs = new List<ShaderInput>();
  50. public IEnumerable<ShaderInput> removedInputs
  51. {
  52. get { return m_RemovedInputs; }
  53. }
  54. [NonSerialized]
  55. List<ShaderInput> m_MovedInputs = new List<ShaderInput>();
  56. public IEnumerable<ShaderInput> movedInputs
  57. {
  58. get { return m_MovedInputs; }
  59. }
  60. [NonSerialized]
  61. List<CategoryData> m_AddedCategories = new List<CategoryData>();
  62. public IEnumerable<CategoryData> addedCategories
  63. {
  64. get { return m_AddedCategories; }
  65. }
  66. [NonSerialized]
  67. List<CategoryData> m_RemovedCategories = new List<CategoryData>();
  68. public IEnumerable<CategoryData> removedCategories
  69. {
  70. get { return m_RemovedCategories; }
  71. }
  72. [NonSerialized]
  73. List<CategoryData> m_MovedCategories = new List<CategoryData>();
  74. public IEnumerable<CategoryData> movedCategories
  75. {
  76. get { return m_MovedCategories; }
  77. }
  78. [NonSerialized]
  79. bool m_MovedContexts = false;
  80. public bool movedContexts => m_MovedContexts;
  81. public string assetGuid { get; set; }
  82. #endregion
  83. #region Category Data
  84. [SerializeField]
  85. List<JsonData<CategoryData>> m_CategoryData = new List<JsonData<CategoryData>>();
  86. public DataValueEnumerable<CategoryData> categories => m_CategoryData.SelectValue();
  87. #endregion
  88. #region Node data
  89. [SerializeField]
  90. List<JsonData<AbstractMaterialNode>> m_Nodes = new List<JsonData<AbstractMaterialNode>>();
  91. [NonSerialized]
  92. Dictionary<string, AbstractMaterialNode> m_NodeDictionary = new Dictionary<string, AbstractMaterialNode>();
  93. [NonSerialized]
  94. Dictionary<string, AbstractMaterialNode> m_LegacyUpdateDictionary = new Dictionary<string, AbstractMaterialNode>();
  95. public IEnumerable<T> GetNodes<T>()
  96. {
  97. return m_Nodes.SelectValue().OfType<T>();
  98. }
  99. [NonSerialized]
  100. List<AbstractMaterialNode> m_AddedNodes = new List<AbstractMaterialNode>();
  101. public IEnumerable<AbstractMaterialNode> addedNodes
  102. {
  103. get { return m_AddedNodes; }
  104. }
  105. [NonSerialized]
  106. List<AbstractMaterialNode> m_RemovedNodes = new List<AbstractMaterialNode>();
  107. public IEnumerable<AbstractMaterialNode> removedNodes
  108. {
  109. get { return m_RemovedNodes; }
  110. }
  111. [NonSerialized]
  112. List<AbstractMaterialNode> m_PastedNodes = new List<AbstractMaterialNode>();
  113. public IEnumerable<AbstractMaterialNode> pastedNodes
  114. {
  115. get { return m_PastedNodes; }
  116. }
  117. #endregion
  118. #region Group Data
  119. [SerializeField]
  120. List<JsonData<GroupData>> m_GroupDatas = new List<JsonData<GroupData>>();
  121. public DataValueEnumerable<GroupData> groups
  122. {
  123. get { return m_GroupDatas.SelectValue(); }
  124. }
  125. [NonSerialized]
  126. List<GroupData> m_AddedGroups = new List<GroupData>();
  127. public IEnumerable<GroupData> addedGroups
  128. {
  129. get { return m_AddedGroups; }
  130. }
  131. [NonSerialized]
  132. List<GroupData> m_RemovedGroups = new List<GroupData>();
  133. public IEnumerable<GroupData> removedGroups
  134. {
  135. get { return m_RemovedGroups; }
  136. }
  137. [NonSerialized]
  138. List<GroupData> m_PastedGroups = new List<GroupData>();
  139. public IEnumerable<GroupData> pastedGroups
  140. {
  141. get { return m_PastedGroups; }
  142. }
  143. [NonSerialized]
  144. List<ParentGroupChange> m_ParentGroupChanges = new List<ParentGroupChange>();
  145. public IEnumerable<ParentGroupChange> parentGroupChanges
  146. {
  147. get { return m_ParentGroupChanges; }
  148. }
  149. [NonSerialized]
  150. GroupData m_MostRecentlyCreatedGroup;
  151. public GroupData mostRecentlyCreatedGroup => m_MostRecentlyCreatedGroup;
  152. [NonSerialized]
  153. Dictionary<JsonRef<GroupData>, List<IGroupItem>> m_GroupItems = new Dictionary<JsonRef<GroupData>, List<IGroupItem>>();
  154. public IEnumerable<IGroupItem> GetItemsInGroup(GroupData groupData)
  155. {
  156. if (m_GroupItems.TryGetValue(groupData, out var nodes))
  157. {
  158. return nodes;
  159. }
  160. return Enumerable.Empty<IGroupItem>();
  161. }
  162. #endregion
  163. #region StickyNote Data
  164. [SerializeField]
  165. List<JsonData<StickyNoteData>> m_StickyNoteDatas = new List<JsonData<StickyNoteData>>();
  166. public DataValueEnumerable<StickyNoteData> stickyNotes => m_StickyNoteDatas.SelectValue();
  167. [NonSerialized]
  168. List<StickyNoteData> m_AddedStickyNotes = new List<StickyNoteData>();
  169. public List<StickyNoteData> addedStickyNotes => m_AddedStickyNotes;
  170. [NonSerialized]
  171. List<StickyNoteData> m_RemovedNotes = new List<StickyNoteData>();
  172. public IEnumerable<StickyNoteData> removedNotes => m_RemovedNotes;
  173. [NonSerialized]
  174. List<StickyNoteData> m_PastedStickyNotes = new List<StickyNoteData>();
  175. public IEnumerable<StickyNoteData> pastedStickyNotes => m_PastedStickyNotes;
  176. #endregion
  177. #region Edge data
  178. [SerializeField]
  179. List<Edge> m_Edges = new List<Edge>();
  180. public IEnumerable<Edge> edges => m_Edges;
  181. [NonSerialized]
  182. Dictionary<string, List<IEdge>> m_NodeEdges = new Dictionary<string, List<IEdge>>();
  183. [NonSerialized]
  184. List<IEdge> m_AddedEdges = new List<IEdge>();
  185. public IEnumerable<IEdge> addedEdges
  186. {
  187. get { return m_AddedEdges; }
  188. }
  189. [NonSerialized]
  190. List<IEdge> m_RemovedEdges = new List<IEdge>();
  191. public IEnumerable<IEdge> removedEdges
  192. {
  193. get { return m_RemovedEdges; }
  194. }
  195. #endregion
  196. #region Context Data
  197. [SerializeField]
  198. ContextData m_VertexContext;
  199. [SerializeField]
  200. ContextData m_FragmentContext;
  201. // We build this once and cache it as it uses reflection
  202. // This list is used to build the Create Node menu entries for Blocks
  203. // as well as when deserializing descriptor fields on serialized Blocks
  204. [NonSerialized]
  205. List<BlockFieldDescriptor> m_BlockFieldDescriptors;
  206. public ContextData vertexContext => m_VertexContext;
  207. public ContextData fragmentContext => m_FragmentContext;
  208. public List<BlockFieldDescriptor> blockFieldDescriptors => m_BlockFieldDescriptors;
  209. #endregion
  210. [SerializeField]
  211. InspectorPreviewData m_PreviewData = new InspectorPreviewData();
  212. public InspectorPreviewData previewData
  213. {
  214. get { return m_PreviewData; }
  215. set { m_PreviewData = value; }
  216. }
  217. [SerializeField]
  218. string m_Path;
  219. public string path
  220. {
  221. get { return m_Path; }
  222. set
  223. {
  224. if (m_Path == value)
  225. return;
  226. m_Path = value;
  227. if (owner != null)
  228. owner.RegisterCompleteObjectUndo("Change Path");
  229. }
  230. }
  231. public MessageManager messageManager { get; set; }
  232. public bool isSubGraph { get; set; }
  233. // we default this to Graph for subgraphs
  234. // but for shadergraphs, this will get replaced with Single
  235. [SerializeField]
  236. private GraphPrecision m_GraphPrecision = GraphPrecision.Graph;
  237. public GraphPrecision graphDefaultPrecision
  238. {
  239. get
  240. {
  241. // shader graphs are not allowed to have graph precision
  242. // we force them to Single if they somehow get set to graph
  243. if ((!isSubGraph) && (m_GraphPrecision == GraphPrecision.Graph))
  244. return GraphPrecision.Single;
  245. return m_GraphPrecision;
  246. }
  247. }
  248. public ConcretePrecision graphDefaultConcretePrecision
  249. {
  250. get
  251. {
  252. // when in "Graph switchable" mode, we choose Half as the default concrete precision
  253. // so you can visualize the worst-case
  254. return graphDefaultPrecision.ToConcrete(ConcretePrecision.Half);
  255. }
  256. }
  257. // Some state has been changed that requires checking for the auto add/removal of blocks.
  258. // This needs to be checked at a later point in time so actions like replace (remove + add) don't remove blocks.
  259. internal bool checkAutoAddRemoveBlocks { get; set; }
  260. public void SetGraphDefaultPrecision(GraphPrecision newGraphDefaultPrecision)
  261. {
  262. if ((!isSubGraph) && (newGraphDefaultPrecision == GraphPrecision.Graph))
  263. {
  264. // shader graphs can't be set to "Graph", only subgraphs can
  265. Debug.LogError("Cannot set ShaderGraph to a default precision of Graph");
  266. }
  267. else
  268. {
  269. m_GraphPrecision = newGraphDefaultPrecision;
  270. }
  271. }
  272. // NOTE: having preview mode default to 3D preserves the old behavior of pre-existing subgraphs
  273. // if we change this, we would have to introduce a versioning step if we want to maintain the old behavior
  274. [SerializeField]
  275. private PreviewMode m_PreviewMode = PreviewMode.Preview3D;
  276. public PreviewMode previewMode
  277. {
  278. get => m_PreviewMode;
  279. set => m_PreviewMode = value;
  280. }
  281. [SerializeField]
  282. JsonRef<AbstractMaterialNode> m_OutputNode;
  283. public AbstractMaterialNode outputNode
  284. {
  285. get => m_OutputNode;
  286. set => m_OutputNode = value;
  287. }
  288. internal delegate void SaveGraphDelegate(Shader shader, object context);
  289. internal static SaveGraphDelegate onSaveGraph;
  290. #region SubData
  291. [SerializeField]
  292. internal List<JsonData<AbstractShaderGraphDataExtension>> m_SubDatas = new List<JsonData<AbstractShaderGraphDataExtension>>();
  293. public DataValueEnumerable<AbstractShaderGraphDataExtension> SubDatas => m_SubDatas.SelectValue();
  294. #endregion
  295. #region Targets
  296. // Serialized list of user-selected active targets, sorted in displayName order (to maintain deterministic serialization order)
  297. // some of these may be MultiJsonInternal.UnknownTargetType if we can't recognize the type of the target
  298. [SerializeField]
  299. internal List<JsonData<Target>> m_ActiveTargets = new List<JsonData<Target>>(); // After adding to this list, you MUST call SortActiveTargets()
  300. public DataValueEnumerable<Target> activeTargets => m_ActiveTargets.SelectValue();
  301. // this stores all of the current possible Target types (including any unknown target types we serialized in)
  302. class PotentialTarget
  303. {
  304. // the potential Target
  305. Target m_Target;
  306. // a Target is either known (we know the Type) or unknown (can't find a matching definition of the Type)
  307. // Targets of unknown type are stored in an UnknownTargetType
  308. private Type m_KnownType;
  309. private MultiJsonInternal.UnknownTargetType m_UnknownTarget;
  310. public PotentialTarget(Target target)
  311. {
  312. m_Target = target;
  313. if (target is MultiJsonInternal.UnknownTargetType)
  314. {
  315. m_UnknownTarget = (MultiJsonInternal.UnknownTargetType)target;
  316. m_KnownType = null;
  317. }
  318. else
  319. {
  320. m_UnknownTarget = null;
  321. m_KnownType = target.GetType();
  322. }
  323. }
  324. public bool IsUnknown()
  325. {
  326. return m_UnknownTarget != null;
  327. }
  328. public MultiJsonInternal.UnknownTargetType GetUnknown()
  329. {
  330. return m_UnknownTarget;
  331. }
  332. public Type knownType { get { return m_KnownType; } }
  333. public bool Is(Target t)
  334. {
  335. return t == m_Target;
  336. }
  337. public string GetDisplayName()
  338. {
  339. return m_Target.displayName;
  340. }
  341. public void ReplaceStoredTarget(Target t)
  342. {
  343. if (m_KnownType != null)
  344. Assert.IsTrue(t.GetType() == m_KnownType);
  345. m_Target = t;
  346. }
  347. public Target GetTarget()
  348. {
  349. return m_Target;
  350. }
  351. }
  352. [NonSerialized]
  353. List<PotentialTarget> m_AllPotentialTargets = new List<PotentialTarget>();
  354. public IEnumerable<Target> allPotentialTargets => m_AllPotentialTargets.Select(x => x.GetTarget());
  355. public int GetTargetIndexByKnownType(Type targetType)
  356. {
  357. return m_AllPotentialTargets.FindIndex(pt => pt.knownType == targetType);
  358. }
  359. public int GetTargetIndex(Target t)
  360. {
  361. int result = m_AllPotentialTargets.FindIndex(pt => pt.Is(t));
  362. return result;
  363. }
  364. public IEnumerable<Target> GetValidTargets()
  365. {
  366. foreach (var potentialTarget in m_AllPotentialTargets)
  367. {
  368. var target = potentialTarget.GetTarget();
  369. if (target is IMayObsolete targetObsolete && targetObsolete.IsObsolete())
  370. continue;
  371. yield return potentialTarget.GetTarget();
  372. }
  373. }
  374. public void SetTargetActive(Target target, bool skipSortAndUpdate = false)
  375. {
  376. int activeIndex = m_ActiveTargets.IndexOf(target);
  377. if (activeIndex < 0)
  378. {
  379. activeIndex = m_ActiveTargets.Count;
  380. m_ActiveTargets.Add(target);
  381. }
  382. // active known targets should replace the stored Target in AllPotentialTargets
  383. if (target is MultiJsonInternal.UnknownTargetType unknownTarget)
  384. {
  385. // find any existing potential target with the same unknown jsonData
  386. int targetIndex = m_AllPotentialTargets.FindIndex(
  387. pt => pt.IsUnknown() && (pt.GetUnknown().jsonData == unknownTarget.jsonData));
  388. // replace existing target, or add it if there is none
  389. if (targetIndex >= 0)
  390. m_AllPotentialTargets[targetIndex] = new PotentialTarget(target);
  391. else
  392. m_AllPotentialTargets.Add(new PotentialTarget(target));
  393. }
  394. else
  395. {
  396. // known types should already have been registered
  397. Type targetType = target.GetType();
  398. int targetIndex = GetTargetIndexByKnownType(targetType);
  399. Assert.IsTrue(targetIndex >= 0);
  400. m_AllPotentialTargets[targetIndex].ReplaceStoredTarget(target);
  401. }
  402. if (!skipSortAndUpdate)
  403. SortAndUpdateActiveTargets();
  404. }
  405. public void SetTargetActive(int targetIndex, bool skipSortAndUpdate = false)
  406. {
  407. Target target = m_AllPotentialTargets[targetIndex].GetTarget();
  408. SetTargetActive(target, skipSortAndUpdate);
  409. }
  410. public void SetTargetInactive(Target target, bool skipSortAndUpdate = false)
  411. {
  412. int activeIndex = m_ActiveTargets.IndexOf(target);
  413. if (activeIndex < 0)
  414. return;
  415. int targetIndex = GetTargetIndex(target);
  416. // if a target was in the active targets, it should also have been in the potential targets list
  417. Assert.IsTrue(targetIndex >= 0);
  418. m_ActiveTargets.RemoveAt(activeIndex);
  419. if (!skipSortAndUpdate)
  420. SortAndUpdateActiveTargets();
  421. }
  422. // this list is populated by graph validation, and lists all of the targets that nodes did not like
  423. [NonSerialized]
  424. List<Target> m_UnsupportedTargets = new List<Target>();
  425. public List<Target> unsupportedTargets { get => m_UnsupportedTargets; }
  426. private Comparison<Target> targetComparison = new Comparison<Target>((a, b) => string.Compare(a.displayName, b.displayName));
  427. public void SortActiveTargets()
  428. {
  429. activeTargets.Sort(targetComparison);
  430. }
  431. // TODO: Need a better way to handle this
  432. public bool hasVFXCompatibleTarget => activeTargets.Any(o => o.SupportsVFX());
  433. #if VFX_GRAPH_10_0_0_OR_NEWER
  434. public bool hasVFXTarget
  435. {
  436. get
  437. {
  438. bool supports = true;
  439. supports &= !isSubGraph;
  440. supports &= activeTargets.Any();
  441. // Maintain support for VFXTarget and VFX compatible targets.
  442. supports &= activeTargets.OfType<VFXTarget>().Any() || hasVFXCompatibleTarget;
  443. return supports;
  444. }
  445. }
  446. public bool isOnlyVFXTarget => activeTargets.Count() == 1 &&
  447. activeTargets.Count(t => t is VFXTarget) == 1;
  448. #else
  449. public bool isVFXTarget => false;
  450. public bool isOnlyVFXTarget => false;
  451. #endif
  452. #endregion
  453. public GraphData()
  454. {
  455. m_GroupItems[null] = new List<IGroupItem>();
  456. GetBlockFieldDescriptors();
  457. AddKnownTargetsToPotentialTargets();
  458. }
  459. // used to initialize the graph with targets, i.e. when creating new graphs via the popup menu
  460. public void InitializeOutputs(Target[] targets, BlockFieldDescriptor[] blockDescriptors)
  461. {
  462. if (targets == null)
  463. return;
  464. foreach (var target in targets)
  465. {
  466. if (GetTargetIndexByKnownType(target.GetType()) >= 0)
  467. {
  468. SetTargetActive(target, true);
  469. }
  470. }
  471. SortActiveTargets();
  472. if (blockDescriptors != null)
  473. {
  474. foreach (var descriptor in blockDescriptors)
  475. {
  476. var contextData = descriptor.shaderStage == ShaderStage.Fragment ? m_FragmentContext : m_VertexContext;
  477. var block = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
  478. block.Init(descriptor);
  479. AddBlockNoValidate(block, contextData, contextData.blocks.Count);
  480. }
  481. }
  482. ValidateGraph();
  483. var activeBlocks = GetActiveBlocksForAllActiveTargets();
  484. UpdateActiveBlocks(activeBlocks);
  485. }
  486. void GetBlockFieldDescriptors()
  487. {
  488. m_BlockFieldDescriptors = new List<BlockFieldDescriptor>();
  489. var asmTypes = TypeCache.GetTypesWithAttribute<GenerateBlocksAttribute>();
  490. foreach (var type in asmTypes)
  491. {
  492. var attrs = type.GetCustomAttributes(typeof(GenerateBlocksAttribute), false);
  493. if (attrs == null || attrs.Length <= 0)
  494. continue;
  495. var attribute = attrs[0] as GenerateBlocksAttribute;
  496. // Get all fields that are BlockFieldDescriptor
  497. // If field and context stages match add to list
  498. foreach (var fieldInfo in type.GetFields())
  499. {
  500. if (fieldInfo.GetValue(type) is BlockFieldDescriptor blockFieldDescriptor)
  501. {
  502. blockFieldDescriptor.path = attribute.path;
  503. m_BlockFieldDescriptors.Add(blockFieldDescriptor);
  504. }
  505. }
  506. }
  507. }
  508. void AddKnownTargetsToPotentialTargets()
  509. {
  510. Assert.AreEqual(m_AllPotentialTargets.Count, 0);
  511. // Find all valid Targets by looking in the TypeCache
  512. var targetTypes = TypeCache.GetTypesDerivedFrom<Target>();
  513. foreach (var type in targetTypes)
  514. {
  515. if (type.IsAbstract || type.IsGenericType || !type.IsClass)
  516. continue;
  517. // create a new instance of the Target, to represent the potential Target
  518. // NOTE: this instance may be replaced later if we serialize in an Active Target of that type
  519. var target = (Target)Activator.CreateInstance(type);
  520. if (!target.isHidden)
  521. {
  522. m_AllPotentialTargets.Add(new PotentialTarget(target));
  523. }
  524. }
  525. }
  526. public void SortAndUpdateActiveTargets()
  527. {
  528. SortActiveTargets();
  529. ValidateGraph();
  530. NodeUtils.ReevaluateActivityOfNodeList(m_Nodes.SelectValue());
  531. }
  532. public void ClearChanges()
  533. {
  534. m_AddedNodes.Clear();
  535. m_RemovedNodes.Clear();
  536. m_PastedNodes.Clear();
  537. m_ParentGroupChanges.Clear();
  538. m_AddedGroups.Clear();
  539. m_RemovedGroups.Clear();
  540. m_PastedGroups.Clear();
  541. m_AddedEdges.Clear();
  542. m_RemovedEdges.Clear();
  543. m_AddedInputs.Clear();
  544. m_RemovedInputs.Clear();
  545. m_MovedInputs.Clear();
  546. m_AddedCategories.Clear();
  547. m_RemovedCategories.Clear();
  548. m_MovedCategories.Clear();
  549. m_AddedStickyNotes.Clear();
  550. m_RemovedNotes.Clear();
  551. m_PastedStickyNotes.Clear();
  552. m_MostRecentlyCreatedGroup = null;
  553. m_MovedContexts = false;
  554. }
  555. public void AddNode(AbstractMaterialNode node)
  556. {
  557. if (node is AbstractMaterialNode materialNode)
  558. {
  559. if (isSubGraph && !materialNode.allowedInSubGraph)
  560. {
  561. Debug.LogWarningFormat("Attempting to add {0} to Sub Graph. This is not allowed.", materialNode.GetType());
  562. return;
  563. }
  564. AddNodeNoValidate(materialNode);
  565. // If adding a Sub Graph node whose asset contains Keywords
  566. // Need to restest Keywords against the variant limit
  567. if (node is SubGraphNode subGraphNode &&
  568. subGraphNode.asset != null &&
  569. subGraphNode.asset.keywords.Any())
  570. {
  571. OnKeywordChangedNoValidate();
  572. }
  573. ValidateGraph();
  574. }
  575. else
  576. {
  577. Debug.LogWarningFormat("Trying to add node {0} to Material graph, but it is not a {1}", node, typeof(AbstractMaterialNode));
  578. }
  579. }
  580. public void CreateGroup(GroupData groupData)
  581. {
  582. if (AddGroup(groupData))
  583. {
  584. m_MostRecentlyCreatedGroup = groupData;
  585. }
  586. }
  587. bool AddGroup(GroupData groupData)
  588. {
  589. if (m_GroupDatas.Contains(groupData))
  590. return false;
  591. m_GroupDatas.Add(groupData);
  592. m_AddedGroups.Add(groupData);
  593. m_GroupItems.Add(groupData, new List<IGroupItem>());
  594. return true;
  595. }
  596. public void RemoveGroup(GroupData groupData)
  597. {
  598. RemoveGroupNoValidate(groupData);
  599. ValidateGraph();
  600. }
  601. void RemoveGroupNoValidate(GroupData group)
  602. {
  603. if (!m_GroupDatas.Contains(group))
  604. throw new InvalidOperationException("Cannot remove a group that doesn't exist.");
  605. m_GroupDatas.Remove(group);
  606. m_RemovedGroups.Add(group);
  607. if (m_GroupItems.TryGetValue(group, out var items))
  608. {
  609. foreach (IGroupItem groupItem in items.ToList())
  610. {
  611. SetGroup(groupItem, null);
  612. }
  613. m_GroupItems.Remove(group);
  614. }
  615. }
  616. public void AddStickyNote(StickyNoteData stickyNote)
  617. {
  618. if (m_StickyNoteDatas.Contains(stickyNote))
  619. {
  620. throw new InvalidOperationException("Sticky note has already been added to the graph.");
  621. }
  622. if (!m_GroupItems.ContainsKey(stickyNote.group))
  623. {
  624. throw new InvalidOperationException("Trying to add sticky note with group that doesn't exist.");
  625. }
  626. m_StickyNoteDatas.Add(stickyNote);
  627. m_AddedStickyNotes.Add(stickyNote);
  628. m_GroupItems[stickyNote.group].Add(stickyNote);
  629. }
  630. void RemoveNoteNoValidate(StickyNoteData stickyNote)
  631. {
  632. if (!m_StickyNoteDatas.Contains(stickyNote))
  633. {
  634. throw new InvalidOperationException("Cannot remove a note that doesn't exist.");
  635. }
  636. m_StickyNoteDatas.Remove(stickyNote);
  637. m_RemovedNotes.Add(stickyNote);
  638. if (m_GroupItems.TryGetValue(stickyNote.group, out var groupItems))
  639. {
  640. groupItems.Remove(stickyNote);
  641. }
  642. }
  643. public void RemoveStickyNote(StickyNoteData stickyNote)
  644. {
  645. RemoveNoteNoValidate(stickyNote);
  646. ValidateGraph();
  647. }
  648. public void SetGroup(IGroupItem node, GroupData group)
  649. {
  650. var groupChange = new ParentGroupChange()
  651. {
  652. groupItem = node,
  653. oldGroup = node.group,
  654. // Checking if the groupdata is null. If it is, then it means node has been removed out of a group.
  655. // If the group data is null, then maybe the old group id should be removed
  656. newGroup = group,
  657. };
  658. node.group = groupChange.newGroup;
  659. var oldGroupNodes = m_GroupItems[groupChange.oldGroup];
  660. oldGroupNodes.Remove(node);
  661. m_GroupItems[groupChange.newGroup].Add(node);
  662. m_ParentGroupChanges.Add(groupChange);
  663. }
  664. public void AddContexts()
  665. {
  666. m_VertexContext = new ContextData();
  667. m_VertexContext.shaderStage = ShaderStage.Vertex;
  668. m_VertexContext.position = new Vector2(0, 0);
  669. m_FragmentContext = new ContextData();
  670. m_FragmentContext.shaderStage = ShaderStage.Fragment;
  671. m_FragmentContext.position = new Vector2(0, 200);
  672. }
  673. public void AddBlock(BlockNode blockNode, ContextData contextData, int index)
  674. {
  675. AddBlockNoValidate(blockNode, contextData, index);
  676. ValidateGraph();
  677. var activeBlocks = GetActiveBlocksForAllActiveTargets();
  678. UpdateActiveBlocks(activeBlocks);
  679. }
  680. void AddBlockNoValidate(BlockNode blockNode, ContextData contextData, int index)
  681. {
  682. // Regular AddNode path
  683. AddNodeNoValidate(blockNode);
  684. // Set BlockNode properties
  685. blockNode.contextData = contextData;
  686. // Add to ContextData
  687. if (index == -1 || index >= contextData.blocks.Count())
  688. {
  689. contextData.blocks.Add(blockNode);
  690. }
  691. else
  692. {
  693. contextData.blocks.Insert(index, blockNode);
  694. }
  695. }
  696. public List<BlockFieldDescriptor> GetActiveBlocksForAllActiveTargets()
  697. {
  698. // Get list of active Block types
  699. var currentBlocks = GetNodes<BlockNode>();
  700. var context = new TargetActiveBlockContext(currentBlocks.Select(x => x.descriptor).ToList(), null);
  701. foreach (var target in activeTargets)
  702. {
  703. target.GetActiveBlocks(ref context);
  704. }
  705. // custom blocks aren't going to exist in GetActiveBlocks, we need to ensure we grab those too.
  706. foreach (var cibnode in currentBlocks.Where(bn => bn.isCustomBlock))
  707. {
  708. context.AddBlock(cibnode.descriptor);
  709. }
  710. return context.activeBlocks;
  711. }
  712. public void UpdateActiveBlocks(List<BlockFieldDescriptor> activeBlockDescriptors)
  713. {
  714. // Set Blocks as active based on supported Block list
  715. //Note: we never want unknown blocks to be active, so explicitly set them to inactive always
  716. bool disableCI = activeTargets.All(at => at.ignoreCustomInterpolators);
  717. foreach (var vertexBlock in vertexContext.blocks)
  718. {
  719. if (vertexBlock.value?.isCustomBlock == true)
  720. {
  721. vertexBlock.value.SetOverrideActiveState(disableCI ? AbstractMaterialNode.ActiveState.ExplicitInactive : AbstractMaterialNode.ActiveState.ExplicitActive);
  722. }
  723. else if (vertexBlock.value?.descriptor?.isUnknown == true)
  724. {
  725. vertexBlock.value.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitInactive);
  726. }
  727. else
  728. {
  729. vertexBlock.value.SetOverrideActiveState(activeBlockDescriptors.Contains(vertexBlock.value.descriptor) ? AbstractMaterialNode.ActiveState.ExplicitActive
  730. : AbstractMaterialNode.ActiveState.ExplicitInactive);
  731. }
  732. }
  733. foreach (var fragmentBlock in fragmentContext.blocks)
  734. {
  735. if (fragmentBlock.value?.descriptor?.isUnknown == true)
  736. {
  737. fragmentBlock.value.SetOverrideActiveState(AbstractMaterialNode.ActiveState.ExplicitInactive);
  738. }
  739. else
  740. {
  741. fragmentBlock.value.SetOverrideActiveState(activeBlockDescriptors.Contains(fragmentBlock.value.descriptor) ? AbstractMaterialNode.ActiveState.ExplicitActive
  742. : AbstractMaterialNode.ActiveState.ExplicitInactive);
  743. }
  744. }
  745. }
  746. public void AddRemoveBlocksFromActiveList(List<BlockFieldDescriptor> activeBlockDescriptors)
  747. {
  748. var blocksToRemove = ListPool<BlockNode>.Get();
  749. void GetBlocksToRemoveForContext(ContextData contextData)
  750. {
  751. for (int i = 0; i < contextData.blocks.Count; i++)
  752. {
  753. if (contextData.blocks[i].value?.isCustomBlock == true) // custom interpolators are fine.
  754. continue;
  755. var block = contextData.blocks[i];
  756. if (!activeBlockDescriptors.Contains(block.value.descriptor))
  757. {
  758. var slot = block.value.FindSlot<MaterialSlot>(0);
  759. //Need to check if a slot is not default value OR is an untracked unknown block type
  760. if (slot.IsUsingDefaultValue() || block.value.descriptor.isUnknown) // TODO: How to check default value
  761. {
  762. blocksToRemove.Add(block);
  763. }
  764. }
  765. }
  766. }
  767. void TryAddBlockToContext(BlockFieldDescriptor descriptor, ContextData contextData)
  768. {
  769. if (descriptor.shaderStage != contextData.shaderStage)
  770. return;
  771. if (contextData.blocks.Any(x => x.value.descriptor.Equals(descriptor)))
  772. return;
  773. var node = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
  774. node.Init(descriptor);
  775. AddBlockNoValidate(node, contextData, contextData.blocks.Count);
  776. }
  777. // Get inactive Blocks to remove
  778. GetBlocksToRemoveForContext(vertexContext);
  779. GetBlocksToRemoveForContext(fragmentContext);
  780. // Remove blocks
  781. foreach (var block in blocksToRemove)
  782. {
  783. RemoveNodeNoValidate(block);
  784. }
  785. // Add active Blocks not currently in Contexts
  786. foreach (var descriptor in activeBlockDescriptors)
  787. {
  788. TryAddBlockToContext(descriptor, vertexContext);
  789. TryAddBlockToContext(descriptor, fragmentContext);
  790. }
  791. }
  792. void AddNodeNoValidate(AbstractMaterialNode node)
  793. {
  794. if (node.group != null && !m_GroupItems.ContainsKey(node.group))
  795. {
  796. throw new InvalidOperationException("Cannot add a node whose group doesn't exist.");
  797. }
  798. node.owner = this;
  799. m_Nodes.Add(node);
  800. m_NodeDictionary.Add(node.objectId, node);
  801. m_AddedNodes.Add(node);
  802. m_GroupItems[node.group].Add(node);
  803. }
  804. public void RemoveNode(AbstractMaterialNode node)
  805. {
  806. if (!node.canDeleteNode)
  807. {
  808. throw new InvalidOperationException($"Node {node.name} ({node.objectId}) cannot be deleted.");
  809. }
  810. RemoveNodeNoValidate(node);
  811. ValidateGraph();
  812. if (node is BlockNode blockNode)
  813. {
  814. var activeBlocks = GetActiveBlocksForAllActiveTargets();
  815. UpdateActiveBlocks(activeBlocks);
  816. blockNode.Dirty(ModificationScope.Graph);
  817. }
  818. }
  819. void RemoveNodeNoValidate(AbstractMaterialNode node)
  820. {
  821. if (!m_NodeDictionary.ContainsKey(node.objectId) && node.isActive && !m_RemovedNodes.Contains(node))
  822. {
  823. throw new InvalidOperationException("Cannot remove a node that doesn't exist.");
  824. }
  825. m_Nodes.Remove(node);
  826. m_NodeDictionary.Remove(node.objectId);
  827. messageManager?.RemoveNode(node.objectId);
  828. m_RemovedNodes.Add(node);
  829. if (m_GroupItems.TryGetValue(node.group, out var groupItems))
  830. {
  831. groupItems.Remove(node);
  832. }
  833. if (node is BlockNode blockNode && blockNode.contextData != null)
  834. {
  835. // Remove from ContextData
  836. blockNode.contextData.blocks.Remove(blockNode);
  837. }
  838. }
  839. void AddEdgeToNodeEdges(IEdge edge)
  840. {
  841. List<IEdge> inputEdges;
  842. if (!m_NodeEdges.TryGetValue(edge.inputSlot.node.objectId, out inputEdges))
  843. m_NodeEdges[edge.inputSlot.node.objectId] = inputEdges = new List<IEdge>();
  844. inputEdges.Add(edge);
  845. List<IEdge> outputEdges;
  846. if (!m_NodeEdges.TryGetValue(edge.outputSlot.node.objectId, out outputEdges))
  847. m_NodeEdges[edge.outputSlot.node.objectId] = outputEdges = new List<IEdge>();
  848. outputEdges.Add(edge);
  849. }
  850. IEdge ConnectNoValidate(SlotReference fromSlotRef, SlotReference toSlotRef, bool assumeBatchSafety = false)
  851. {
  852. var fromNode = fromSlotRef.node;
  853. var toNode = toSlotRef.node;
  854. if (fromNode == null || toNode == null)
  855. return null;
  856. // both nodes must belong to this graph
  857. if ((fromNode.owner != this) || (toNode.owner != this))
  858. return null;
  859. // if fromNode is already connected to toNode
  860. // do now allow a connection as toNode will then
  861. // have an edge to fromNode creating a cycle.
  862. // if this is parsed it will lead to an infinite loop.
  863. if (!assumeBatchSafety) // This may not be that helpful.
  864. {
  865. var dependentNodes = new List<AbstractMaterialNode>();
  866. NodeUtils.CollectNodesNodeFeedsInto(dependentNodes, toNode);
  867. if (dependentNodes.Contains(fromNode))
  868. return null;
  869. }
  870. var fromSlot = fromNode.FindSlot<MaterialSlot>(fromSlotRef.slotId);
  871. var toSlot = toNode.FindSlot<MaterialSlot>(toSlotRef.slotId);
  872. if (fromSlot == null || toSlot == null)
  873. return null;
  874. if (fromSlot.isOutputSlot == toSlot.isOutputSlot)
  875. return null;
  876. var outputSlot = fromSlot.isOutputSlot ? fromSlotRef : toSlotRef;
  877. var inputSlot = fromSlot.isInputSlot ? fromSlotRef : toSlotRef;
  878. s_TempEdges.Clear();
  879. GetEdges(inputSlot, s_TempEdges);
  880. // remove any inputs that exits before adding
  881. foreach (var edge in s_TempEdges)
  882. {
  883. RemoveEdgeNoValidate(edge);
  884. }
  885. var newEdge = new Edge(outputSlot, inputSlot);
  886. m_Edges.Add(newEdge);
  887. m_AddedEdges.Add(newEdge);
  888. AddEdgeToNodeEdges(newEdge);
  889. if (!assumeBatchSafety)
  890. {
  891. NodeUtils.ReevaluateActivityOfConnectedNodes(toNode);
  892. }
  893. //Debug.LogFormat("Connected edge: {0} -> {1} ({2} -> {3})\n{4}", newEdge.outputSlot.nodeGuid, newEdge.inputSlot.nodeGuid, fromNode.name, toNode.name, Environment.StackTrace);
  894. return newEdge;
  895. }
  896. public IEdge Connect(SlotReference fromSlotRef, SlotReference toSlotRef)
  897. {
  898. var newEdge = ConnectNoValidate(fromSlotRef, toSlotRef);
  899. ValidateGraph();
  900. return newEdge;
  901. }
  902. internal void UnnotifyAddedEdge(IEdge edge)
  903. {
  904. m_AddedEdges.Remove(edge);
  905. }
  906. public void RemoveEdges(IEdge[] edges)
  907. {
  908. if (edges.Length == 0)
  909. return;
  910. foreach (var edge in edges)
  911. {
  912. RemoveEdgeNoValidate(edge);
  913. }
  914. ValidateGraph();
  915. }
  916. public void RemoveEdge(IEdge e)
  917. {
  918. RemoveEdgeNoValidate(e);
  919. ValidateGraph();
  920. }
  921. public void RemoveElements(AbstractMaterialNode[] nodes, IEdge[] edges, GroupData[] groups, StickyNoteData[] notes, ShaderInput[] inputs = null)
  922. {
  923. foreach (var node in nodes)
  924. {
  925. if (!node.canDeleteNode)
  926. {
  927. throw new InvalidOperationException($"Node {node.name} ({node.objectId}) cannot be deleted.");
  928. }
  929. }
  930. foreach (var edge in edges.ToArray())
  931. {
  932. RemoveEdgeNoValidate(edge);
  933. }
  934. foreach (var serializableNode in nodes)
  935. {
  936. // Check if it is a Redirect Node
  937. // Get the edges and then re-create all Edges
  938. // This only works if it has all the edges.
  939. // If one edge is already deleted then we can not re-create.
  940. if (serializableNode is RedirectNodeData redirectNode)
  941. {
  942. redirectNode.GetOutputAndInputSlots(out SlotReference outputSlotRef, out var inputSlotRefs);
  943. foreach (SlotReference slot in inputSlotRefs)
  944. {
  945. ConnectNoValidate(outputSlotRef, slot);
  946. }
  947. }
  948. RemoveNodeNoValidate(serializableNode);
  949. }
  950. foreach (var noteData in notes)
  951. {
  952. RemoveNoteNoValidate(noteData);
  953. }
  954. foreach (var groupData in groups)
  955. {
  956. RemoveGroupNoValidate(groupData);
  957. }
  958. if (inputs != null)
  959. {
  960. foreach (var shaderInput in inputs)
  961. {
  962. RemoveGraphInputNoValidate(shaderInput);
  963. }
  964. }
  965. ValidateGraph();
  966. if (nodes.Any(x => x is BlockNode))
  967. {
  968. var activeBlocks = GetActiveBlocksForAllActiveTargets();
  969. UpdateActiveBlocks(activeBlocks);
  970. }
  971. }
  972. void RemoveEdgeNoValidate(IEdge e, bool reevaluateActivity = true)
  973. {
  974. e = m_Edges.FirstOrDefault(x => x.Equals(e));
  975. if (e == null)
  976. throw new ArgumentException("Trying to remove an edge that does not exist.", "e");
  977. m_Edges.Remove(e as Edge);
  978. AbstractMaterialNode input = e.inputSlot.node, output = e.outputSlot.node;
  979. if (input != null && ShaderGraphPreferences.autoAddRemoveBlocks)
  980. {
  981. checkAutoAddRemoveBlocks = true;
  982. }
  983. List<IEdge> inputNodeEdges;
  984. if (m_NodeEdges.TryGetValue(input.objectId, out inputNodeEdges))
  985. inputNodeEdges.Remove(e);
  986. List<IEdge> outputNodeEdges;
  987. if (m_NodeEdges.TryGetValue(output.objectId, out outputNodeEdges))
  988. outputNodeEdges.Remove(e);
  989. m_AddedEdges.Remove(e);
  990. m_RemovedEdges.Add(e);
  991. if (reevaluateActivity)
  992. {
  993. if (input != null)
  994. {
  995. NodeUtils.ReevaluateActivityOfConnectedNodes(input);
  996. }
  997. if (output != null)
  998. {
  999. NodeUtils.ReevaluateActivityOfConnectedNodes(output);
  1000. }
  1001. }
  1002. }
  1003. public AbstractMaterialNode GetNodeFromId(string nodeId)
  1004. {
  1005. m_NodeDictionary.TryGetValue(nodeId, out var node);
  1006. return node;
  1007. }
  1008. public T GetNodeFromId<T>(string nodeId) where T : class
  1009. {
  1010. m_NodeDictionary.TryGetValue(nodeId, out var node);
  1011. return node as T;
  1012. }
  1013. internal Texture2DShaderProperty GetMainTexture()
  1014. {
  1015. foreach (var prop in properties)
  1016. {
  1017. if (prop is Texture2DShaderProperty tex)
  1018. {
  1019. if (tex.isMainTexture)
  1020. {
  1021. return tex;
  1022. }
  1023. }
  1024. }
  1025. return null;
  1026. }
  1027. internal ColorShaderProperty GetMainColor()
  1028. {
  1029. foreach (var prop in properties)
  1030. {
  1031. if (prop is ColorShaderProperty col)
  1032. {
  1033. if (col.isMainColor)
  1034. {
  1035. return col;
  1036. }
  1037. }
  1038. }
  1039. return null;
  1040. }
  1041. public bool ContainsCategory(CategoryData categoryData)
  1042. {
  1043. return categories.Contains(categoryData);
  1044. }
  1045. public bool ContainsInput(ShaderInput shaderInput)
  1046. {
  1047. if (shaderInput == null)
  1048. return false;
  1049. return properties.Contains(shaderInput) || keywords.Contains(shaderInput) || dropdowns.Contains(shaderInput);
  1050. }
  1051. public bool ContainsNode(AbstractMaterialNode node)
  1052. {
  1053. if (node == null)
  1054. return false;
  1055. return m_NodeDictionary.TryGetValue(node.objectId, out var foundNode) && node == foundNode;
  1056. }
  1057. public void GetEdges(MaterialSlot slot, List<IEdge> foundEdges)
  1058. {
  1059. List<IEdge> candidateEdges;
  1060. if (!m_NodeEdges.TryGetValue(slot.owner.objectId, out candidateEdges))
  1061. return;
  1062. foreach (var edge in candidateEdges)
  1063. {
  1064. var cs = slot.isInputSlot ? edge.inputSlot : edge.outputSlot;
  1065. if (cs.node == slot.owner && cs.slotId == slot.id)
  1066. foundEdges.Add(edge);
  1067. }
  1068. }
  1069. public void GetEdges(SlotReference s, List<IEdge> foundEdges)
  1070. {
  1071. List<IEdge> candidateEdges;
  1072. if (!m_NodeEdges.TryGetValue(s.node.objectId, out candidateEdges))
  1073. return;
  1074. MaterialSlot slot = s.slot;
  1075. foreach (var edge in candidateEdges)
  1076. {
  1077. var cs = slot.isInputSlot ? edge.inputSlot : edge.outputSlot;
  1078. if (cs.node == s.node && cs.slotId == s.slotId)
  1079. foundEdges.Add(edge);
  1080. }
  1081. }
  1082. public IEnumerable<IEdge> GetEdges(SlotReference s)
  1083. {
  1084. var edges = new List<IEdge>();
  1085. GetEdges(s, edges);
  1086. return edges;
  1087. }
  1088. public void GetEdges(AbstractMaterialNode node, List<IEdge> foundEdges)
  1089. {
  1090. if (m_NodeEdges.TryGetValue(node.objectId, out var edges))
  1091. {
  1092. foundEdges.AddRange(edges);
  1093. }
  1094. }
  1095. public IEnumerable<IEdge> GetEdges(AbstractMaterialNode node)
  1096. {
  1097. List<IEdge> edges = new List<IEdge>();
  1098. GetEdges(node, edges);
  1099. return edges;
  1100. }
  1101. public void ForeachHLSLProperty(Action<HLSLProperty> action)
  1102. {
  1103. foreach (var prop in properties)
  1104. prop.ForeachHLSLProperty(action);
  1105. }
  1106. public void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode)
  1107. {
  1108. foreach (var prop in properties)
  1109. {
  1110. // For VFX Shader generation, we must omit exposed properties from the Material CBuffer.
  1111. // This is because VFX computes properties on the fly in the vertex stage, and packed into interpolator.
  1112. if (generationMode == GenerationMode.VFX && prop.isExposed)
  1113. {
  1114. prop.overrideHLSLDeclaration = true;
  1115. prop.hlslDeclarationOverride = HLSLDeclaration.DoNotDeclare;
  1116. }
  1117. // ugh, this needs to be moved to the gradient property implementation
  1118. if (prop is GradientShaderProperty gradientProp && generationMode == GenerationMode.Preview)
  1119. {
  1120. GradientUtil.GetGradientPropertiesForPreview(collector, gradientProp.referenceName, gradientProp.value);
  1121. continue;
  1122. }
  1123. collector.AddShaderProperty(prop);
  1124. }
  1125. }
  1126. public void CollectShaderKeywords(KeywordCollector collector, GenerationMode generationMode)
  1127. {
  1128. foreach (var keyword in keywords)
  1129. {
  1130. collector.AddShaderKeyword(keyword);
  1131. }
  1132. // Alwways calculate permutations when collecting
  1133. collector.CalculateKeywordPermutations();
  1134. }
  1135. public bool IsInputAllowedInGraph(ShaderInput input)
  1136. {
  1137. return (isSubGraph && input.allowedInSubGraph) || (!isSubGraph && input.allowedInMainGraph);
  1138. }
  1139. public bool IsInputAllowedInGraph(AbstractMaterialNode node)
  1140. {
  1141. return (isSubGraph && node.allowedInSubGraph) || (!isSubGraph && node.allowedInMainGraph);
  1142. }
  1143. // adds the input to the graph, and sanitizes the names appropriately
  1144. public void AddGraphInput(ShaderInput input, int index = -1)
  1145. {
  1146. if (input == null)
  1147. return;
  1148. // sanitize the display name
  1149. input.SetDisplayNameAndSanitizeForGraph(this);
  1150. // sanitize the reference name
  1151. input.SetReferenceNameAndSanitizeForGraph(this);
  1152. AddGraphInputNoSanitization(input, index);
  1153. }
  1154. // just adds the input to the graph, does not fix colliding or illegal names
  1155. internal void AddGraphInputNoSanitization(ShaderInput input, int index = -1)
  1156. {
  1157. if (input == null)
  1158. return;
  1159. switch (input)
  1160. {
  1161. case AbstractShaderProperty property:
  1162. if (m_Properties.Contains(property))
  1163. return;
  1164. if (index < 0)
  1165. m_Properties.Add(property);
  1166. else
  1167. m_Properties.Insert(index, property);
  1168. break;
  1169. case ShaderKeyword keyword:
  1170. if (m_Keywords.Contains(keyword))
  1171. return;
  1172. if (index < 0)
  1173. m_Keywords.Add(keyword);
  1174. else
  1175. m_Keywords.Insert(index, keyword);
  1176. OnKeywordChangedNoValidate();
  1177. break;
  1178. case ShaderDropdown dropdown:
  1179. if (m_Dropdowns.Contains(dropdown))
  1180. return;
  1181. if (index < 0)
  1182. m_Dropdowns.Add(dropdown);
  1183. else
  1184. m_Dropdowns.Insert(index, dropdown);
  1185. OnDropdownChangedNoValidate();
  1186. break;
  1187. default:
  1188. throw new ArgumentOutOfRangeException();
  1189. }
  1190. m_AddedInputs.Add(input);
  1191. }
  1192. // only ignores names matching ignoreName on properties matching ignoreGuid
  1193. public List<string> BuildPropertyDisplayNameList(AbstractShaderProperty ignoreProperty, string ignoreName)
  1194. {
  1195. List<String> result = new List<String>();
  1196. foreach (var p in properties)
  1197. {
  1198. int before = result.Count;
  1199. p.GetPropertyDisplayNames(result);
  1200. if ((p == ignoreProperty) && (ignoreName != null))
  1201. {
  1202. // remove ignoreName, if it was just added
  1203. for (int i = before; i < result.Count; i++)
  1204. {
  1205. if (result[i] == ignoreName)
  1206. {
  1207. result.RemoveAt(i);
  1208. break;
  1209. }
  1210. }
  1211. }
  1212. }
  1213. return result;
  1214. }
  1215. // only ignores names matching ignoreName on properties matching ignoreGuid
  1216. public List<string> BuildPropertyReferenceNameList(AbstractShaderProperty ignoreProperty, string ignoreName)
  1217. {
  1218. List<String> result = new List<String>();
  1219. foreach (var p in properties)
  1220. {
  1221. int before = result.Count;
  1222. p.GetPropertyReferenceNames(result);
  1223. if ((p == ignoreProperty) && (ignoreName != null))
  1224. {
  1225. // remove ignoreName, if it was just added
  1226. for (int i = before; i < result.Count; i++)
  1227. {
  1228. if (result[i] == ignoreName)
  1229. {
  1230. result.RemoveAt(i);
  1231. break;
  1232. }
  1233. }
  1234. }
  1235. }
  1236. return result;
  1237. }
  1238. public string SanitizeGraphInputName(ShaderInput input, string desiredName)
  1239. {
  1240. string currentName = input.displayName;
  1241. string sanitizedName = desiredName.Trim();
  1242. switch (input)
  1243. {
  1244. case AbstractShaderProperty property:
  1245. sanitizedName = GraphUtil.SanitizeName(BuildPropertyDisplayNameList(property, currentName), "{0} ({1})", sanitizedName);
  1246. break;
  1247. case ShaderKeyword keyword:
  1248. sanitizedName = GraphUtil.SanitizeName(keywords.Where(p => p != input).Select(p => p.displayName), "{0} ({1})", sanitizedName);
  1249. break;
  1250. case ShaderDropdown dropdown:
  1251. sanitizedName = GraphUtil.SanitizeName(dropdowns.Where(p => p != input).Select(p => p.displayName), "{0} ({1})", sanitizedName);
  1252. break;
  1253. default:
  1254. throw new ArgumentOutOfRangeException();
  1255. }
  1256. return sanitizedName;
  1257. }
  1258. public string SanitizeGraphInputReferenceName(ShaderInput input, string desiredName)
  1259. {
  1260. var sanitizedName = NodeUtils.ConvertToValidHLSLIdentifier(desiredName, (desiredName) => (NodeUtils.IsShaderLabKeyWord(desiredName) || NodeUtils.IsShaderGraphKeyWord(desiredName)));
  1261. switch (input)
  1262. {
  1263. case AbstractShaderProperty property:
  1264. {
  1265. // must deduplicate ref names against keywords, dropdowns, and properties, as they occupy the same name space
  1266. var existingNames = properties.Where(p => p != property).Select(p => p.referenceName).Union(keywords.Select(p => p.referenceName)).Union(dropdowns.Select(p => p.referenceName));
  1267. sanitizedName = GraphUtil.DeduplicateName(existingNames, "{0}_{1}", sanitizedName);
  1268. }
  1269. break;
  1270. case ShaderKeyword keyword:
  1271. {
  1272. // must deduplicate ref names against keywords, dropdowns, and properties, as they occupy the same name space
  1273. sanitizedName = sanitizedName.ToUpper();
  1274. var existingNames = properties.Select(p => p.referenceName).Union(keywords.Where(p => p != input).Select(p => p.referenceName)).Union(dropdowns.Select(p => p.referenceName));
  1275. sanitizedName = GraphUtil.DeduplicateName(existingNames, "{0}_{1}", sanitizedName);
  1276. }
  1277. break;
  1278. case ShaderDropdown dropdown:
  1279. {
  1280. // must deduplicate ref names against keywords, dropdowns, and properties, as they occupy the same name space
  1281. var existingNames = properties.Select(p => p.referenceName).Union(keywords.Select(p => p.referenceName)).Union(dropdowns.Where(p => p != input).Select(p => p.referenceName));
  1282. sanitizedName = GraphUtil.DeduplicateName(existingNames, "{0}_{1}", sanitizedName);
  1283. }
  1284. break;
  1285. default:
  1286. throw new ArgumentOutOfRangeException();
  1287. }
  1288. return sanitizedName;
  1289. }
  1290. // copies the ShaderInput, and adds it to the graph with proper name sanitization, returning the copy
  1291. public ShaderInput AddCopyOfShaderInput(ShaderInput source, int insertIndex = -1)
  1292. {
  1293. ShaderInput copy = source.Copy();
  1294. // some ShaderInputs cannot be copied (unknown types)
  1295. if (copy == null)
  1296. return null;
  1297. // copy common properties that should always be copied over
  1298. copy.generatePropertyBlock = source.generatePropertyBlock; // the exposed toggle
  1299. if ((source is AbstractShaderProperty sourceProp) && (copy is AbstractShaderProperty copyProp))
  1300. {
  1301. copyProp.hidden = sourceProp.hidden;
  1302. copyProp.precision = sourceProp.precision;
  1303. copyProp.overrideHLSLDeclaration = sourceProp.overrideHLSLDeclaration;
  1304. copyProp.hlslDeclarationOverride = sourceProp.hlslDeclarationOverride;
  1305. copyProp.useCustomSlotLabel = sourceProp.useCustomSlotLabel;
  1306. }
  1307. // sanitize the display name (we let the .Copy() function actually copy the display name over)
  1308. copy.SetDisplayNameAndSanitizeForGraph(this);
  1309. // copy and sanitize the reference name (must do this after the display name, so the default is correct)
  1310. if (source.IsUsingNewDefaultRefName())
  1311. {
  1312. // if source was using new default, we can just rely on the default for the copy we made.
  1313. // the code above has already handled collisions properly for the default,
  1314. // and it will assign the same name as the source if there are no collisions.
  1315. // Also it will result better names chosen when there are collisions.
  1316. }
  1317. else
  1318. {
  1319. // when the source is using an old default, we set it as an override
  1320. copy.SetReferenceNameAndSanitizeForGraph(this, source.referenceName);
  1321. }
  1322. copy.OnBeforePasteIntoGraph(this);
  1323. AddGraphInputNoSanitization(copy, insertIndex);
  1324. return copy;
  1325. }
  1326. public void RemoveGraphInput(ShaderInput input)
  1327. {
  1328. switch (input)
  1329. {
  1330. case AbstractShaderProperty property:
  1331. var propertyNodes = GetNodes<PropertyNode>().Where(x => x.property == input).ToList();
  1332. foreach (var propertyNode in propertyNodes)
  1333. ReplacePropertyNodeWithConcreteNodeNoValidate(propertyNode);
  1334. break;
  1335. }
  1336. // Also remove this input from any category it existed in
  1337. foreach (var categoryData in categories)
  1338. {
  1339. if (categoryData.IsItemInCategory(input))
  1340. {
  1341. categoryData.RemoveItemFromCategory(input);
  1342. break;
  1343. }
  1344. }
  1345. RemoveGraphInputNoValidate(input);
  1346. ValidateGraph();
  1347. }
  1348. public void MoveCategory(CategoryData category, int newIndex)
  1349. {
  1350. if (newIndex > m_CategoryData.Count || newIndex < 0)
  1351. {
  1352. AssertHelpers.Fail("New index is not within categories list.");
  1353. return;
  1354. }
  1355. var currentIndex = m_CategoryData.IndexOf(category);
  1356. if (currentIndex == -1)
  1357. {
  1358. AssertHelpers.Fail("Category is not in graph.");
  1359. return;
  1360. }
  1361. if (newIndex == currentIndex)
  1362. return;
  1363. m_CategoryData.RemoveAt(currentIndex);
  1364. if (newIndex > currentIndex)
  1365. newIndex--;
  1366. var isLast = newIndex == m_CategoryData.Count;
  1367. if (isLast)
  1368. m_CategoryData.Add(category);
  1369. else
  1370. m_CategoryData.Insert(newIndex, category);
  1371. if (!m_MovedCategories.Contains(category))
  1372. m_MovedCategories.Add(category);
  1373. }
  1374. public void MoveItemInCategory(ShaderInput itemToMove, int newIndex, string associatedCategoryGuid)
  1375. {
  1376. foreach (var categoryData in categories)
  1377. {
  1378. if (categoryData.categoryGuid == associatedCategoryGuid && categoryData.IsItemInCategory(itemToMove))
  1379. {
  1380. // Validate new index to move the item to
  1381. if (newIndex < -1 || newIndex >= categoryData.childCount)
  1382. {
  1383. AssertHelpers.Fail("Provided invalid index input to MoveItemInCategory.");
  1384. return;
  1385. }
  1386. categoryData.MoveItemInCategory(itemToMove, newIndex);
  1387. break;
  1388. }
  1389. }
  1390. }
  1391. public int GetGraphInputIndex(ShaderInput input)
  1392. {
  1393. switch (input)
  1394. {
  1395. case AbstractShaderProperty property:
  1396. return m_Properties.IndexOf(property);
  1397. case ShaderKeyword keyword:
  1398. return m_Keywords.IndexOf(keyword);
  1399. case ShaderDropdown dropdown:
  1400. return m_Dropdowns.IndexOf(dropdown);
  1401. default:
  1402. throw new ArgumentOutOfRangeException();
  1403. }
  1404. }
  1405. void RemoveGraphInputNoValidate(ShaderInput shaderInput)
  1406. {
  1407. if (shaderInput is AbstractShaderProperty property && m_Properties.Remove(property) ||
  1408. shaderInput is ShaderKeyword keyword && m_Keywords.Remove(keyword) ||
  1409. shaderInput is ShaderDropdown dropdown && m_Dropdowns.Remove(dropdown))
  1410. {
  1411. m_RemovedInputs.Add(shaderInput);
  1412. m_AddedInputs.Remove(shaderInput);
  1413. m_MovedInputs.Remove(shaderInput);
  1414. }
  1415. }
  1416. static List<IEdge> s_TempEdges = new List<IEdge>();
  1417. public void ReplacePropertyNodeWithConcreteNode(PropertyNode propertyNode)
  1418. {
  1419. ReplacePropertyNodeWithConcreteNodeNoValidate(propertyNode);
  1420. ValidateGraph();
  1421. }
  1422. void ReplacePropertyNodeWithConcreteNodeNoValidate(PropertyNode propertyNode, bool deleteNodeIfNoConcreteFormExists = true)
  1423. {
  1424. var property = properties.FirstOrDefault(x => x == propertyNode.property) ?? propertyNode.property;
  1425. if (property == null)
  1426. return;
  1427. var node = property.ToConcreteNode() as AbstractMaterialNode;
  1428. if (node == null) // Some nodes have no concrete form
  1429. {
  1430. if (deleteNodeIfNoConcreteFormExists)
  1431. RemoveNodeNoValidate(propertyNode);
  1432. return;
  1433. }
  1434. var slot = propertyNode.FindOutputSlot<MaterialSlot>(PropertyNode.OutputSlotId);
  1435. var newSlot = node.GetOutputSlots<MaterialSlot>().FirstOrDefault(s => s.valueType == slot.valueType);
  1436. if (newSlot == null)
  1437. return;
  1438. node.drawState = propertyNode.drawState;
  1439. node.group = propertyNode.group;
  1440. AddNodeNoValidate(node);
  1441. foreach (var edge in this.GetEdges(slot.slotReference))
  1442. ConnectNoValidate(newSlot.slotReference, edge.inputSlot);
  1443. RemoveNodeNoValidate(propertyNode);
  1444. }
  1445. public void AddCategory(CategoryData categoryDataReference)
  1446. {
  1447. m_CategoryData.Add(categoryDataReference);
  1448. m_AddedCategories.Add(categoryDataReference);
  1449. }
  1450. public string FindCategoryForInput(ShaderInput input)
  1451. {
  1452. foreach (var categoryData in categories)
  1453. {
  1454. if (categoryData.IsItemInCategory(input))
  1455. {
  1456. return categoryData.categoryGuid;
  1457. }
  1458. }
  1459. AssertHelpers.Fail("Attempted to find category for an input that doesn't exist in the graph.");
  1460. return String.Empty;
  1461. }
  1462. public void ChangeCategoryName(string categoryGUID, string newName)
  1463. {
  1464. foreach (var categoryData in categories)
  1465. {
  1466. if (categoryData.categoryGuid == categoryGUID)
  1467. {
  1468. var sanitizedCategoryName = GraphUtil.SanitizeCategoryName(newName);
  1469. categoryData.name = sanitizedCategoryName;
  1470. return;
  1471. }
  1472. }
  1473. AssertHelpers.Fail("Attempted to change name of a category that does not exist in the graph.");
  1474. }
  1475. public void InsertItemIntoCategory(string categoryGUID, ShaderInput itemToAdd, int insertionIndex = -1)
  1476. {
  1477. foreach (var categoryData in categories)
  1478. {
  1479. if (categoryData.categoryGuid == categoryGUID)
  1480. {
  1481. categoryData.InsertItemIntoCategory(itemToAdd, insertionIndex);
  1482. }
  1483. // Also make sure to remove this items guid from an existing category if it exists within one
  1484. else if (categoryData.IsItemInCategory(itemToAdd))
  1485. {
  1486. categoryData.RemoveItemFromCategory(itemToAdd);
  1487. }
  1488. }
  1489. }
  1490. public void RemoveItemFromCategory(string categoryGUID, ShaderInput itemToRemove)
  1491. {
  1492. foreach (var categoryData in categories)
  1493. {
  1494. if (categoryData.categoryGuid == categoryGUID)
  1495. {
  1496. categoryData.RemoveItemFromCategory(itemToRemove);
  1497. return;
  1498. }
  1499. }
  1500. AssertHelpers.Fail("Attempted to remove item from a category that does not exist in the graph.");
  1501. }
  1502. public void RemoveCategory(string categoryGUID)
  1503. {
  1504. var existingCategory = categories.FirstOrDefault(category => category.categoryGuid == categoryGUID);
  1505. if (existingCategory != null)
  1506. {
  1507. m_CategoryData.Remove(existingCategory);
  1508. m_RemovedCategories.Add(existingCategory);
  1509. // Whenever a category is removed, also remove any inputs within that category
  1510. foreach (var shaderInput in existingCategory.Children)
  1511. RemoveGraphInput(shaderInput);
  1512. }
  1513. else
  1514. AssertHelpers.Fail("Attempted to remove a category that does not exist in the graph.");
  1515. }
  1516. // This differs from the rest of the category handling functions due to how categories can be copied between graphs
  1517. // Since we have no guarantee of us owning the categories, we need a direct reference to the category to copy
  1518. public CategoryData CopyCategory(CategoryData categoryToCopy)
  1519. {
  1520. var copiedCategory = new CategoryData(categoryToCopy);
  1521. AddCategory(copiedCategory);
  1522. // Whenever a category is copied, also copy over all the inputs within that category
  1523. foreach (var childInputToCopy in categoryToCopy.Children)
  1524. {
  1525. var newShaderInput = AddCopyOfShaderInput(childInputToCopy);
  1526. copiedCategory.InsertItemIntoCategory(newShaderInput);
  1527. }
  1528. return copiedCategory;
  1529. }
  1530. public void OnKeywordChanged()
  1531. {
  1532. OnKeywordChangedNoValidate();
  1533. ValidateGraph();
  1534. }
  1535. public void OnKeywordChangedNoValidate()
  1536. {
  1537. DirtyAll<AbstractMaterialNode>(ModificationScope.Topological);
  1538. }
  1539. public void OnDropdownChanged()
  1540. {
  1541. OnDropdownChangedNoValidate();
  1542. ValidateGraph();
  1543. }
  1544. public void OnDropdownChangedNoValidate()
  1545. {
  1546. DirtyAll<AbstractMaterialNode>(ModificationScope.Topological);
  1547. }
  1548. public void CleanupGraph()
  1549. {
  1550. //First validate edges, remove any
  1551. //orphans. This can happen if a user
  1552. //manually modifies serialized data
  1553. //of if they delete a node in the inspector
  1554. //debug view.
  1555. foreach (var edge in edges.ToArray())
  1556. {
  1557. var outputNode = edge.outputSlot.node;
  1558. var inputNode = edge.inputSlot.node;
  1559. MaterialSlot outputSlot = null;
  1560. MaterialSlot inputSlot = null;
  1561. if (ContainsNode(outputNode) && ContainsNode(inputNode))
  1562. {
  1563. outputSlot = outputNode.FindOutputSlot<MaterialSlot>(edge.outputSlot.slotId);
  1564. inputSlot = inputNode.FindInputSlot<MaterialSlot>(edge.inputSlot.slotId);
  1565. }
  1566. if (outputNode == null
  1567. || inputNode == null
  1568. || outputSlot == null
  1569. || inputSlot == null)
  1570. {
  1571. //orphaned edge
  1572. RemoveEdgeNoValidate(edge, false);
  1573. }
  1574. }
  1575. }
  1576. private void DirtyAll<T>(ModificationScope modificationScope) where T : AbstractMaterialNode
  1577. {
  1578. graphIsConcretizing = true;
  1579. try
  1580. {
  1581. var allNodes = GetNodes<T>();
  1582. foreach (var node in allNodes)
  1583. {
  1584. node.Dirty(modificationScope);
  1585. node.ValidateNode();
  1586. }
  1587. }
  1588. catch (System.Exception e)
  1589. {
  1590. graphIsConcretizing = false;
  1591. throw e;
  1592. }
  1593. graphIsConcretizing = false;
  1594. }
  1595. public void ValidateGraph()
  1596. {
  1597. messageManager?.ClearAllFromProvider(this);
  1598. CleanupGraph();
  1599. GraphSetup.SetupGraph(this);
  1600. GraphConcretization.ConcretizeGraph(this);
  1601. GraphValidation.ValidateGraph(this);
  1602. for (int i = 0; i < m_AddedEdges.Count; ++i)
  1603. {
  1604. var edge = m_AddedEdges[i];
  1605. if (!ContainsNode(edge.outputSlot.node) || !ContainsNode(edge.inputSlot.node))
  1606. {
  1607. Debug.LogWarningFormat("Added edge is invalid: {0} -> {1}\n{2}", edge.outputSlot.node.objectId, edge.inputSlot.node.objectId, Environment.StackTrace);
  1608. m_AddedEdges.Remove(edge);
  1609. }
  1610. }
  1611. for (int i = 0; i < m_ParentGroupChanges.Count; ++i)
  1612. {
  1613. var groupChange = m_ParentGroupChanges[i];
  1614. switch (groupChange.groupItem)
  1615. {
  1616. case AbstractMaterialNode node when !ContainsNode(node):
  1617. case StickyNoteData stickyNote when !m_StickyNoteDatas.Contains(stickyNote):
  1618. m_ParentGroupChanges.Remove(groupChange);
  1619. break;
  1620. }
  1621. }
  1622. var existingDefaultCategory = categories.FirstOrDefault();
  1623. if (existingDefaultCategory?.childCount == 0 && categories.Count() == 1 && (properties.Count() != 0 || keywords.Count() != 0 || dropdowns.Count() != 0))
  1624. {
  1625. // Have a graph with category data in invalid state
  1626. // there is only one category, the default category, and all shader inputs should belong to it
  1627. // Clear category data as it will get reconstructed in the BlackboardController constructor
  1628. m_CategoryData.Clear();
  1629. }
  1630. ValidateCustomBlockLimit();
  1631. ValidateContextBlocks();
  1632. }
  1633. public void AddValidationError(string id, string errorMessage,
  1634. ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
  1635. {
  1636. messageManager?.AddOrAppendError(this, id, new ShaderMessage("Validation: " + errorMessage, severity));
  1637. }
  1638. public void AddSetupError(string id, string errorMessage,
  1639. ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
  1640. {
  1641. messageManager?.AddOrAppendError(this, id, new ShaderMessage("Setup: " + errorMessage, severity));
  1642. }
  1643. public void AddConcretizationError(string id, string errorMessage,
  1644. ShaderCompilerMessageSeverity severity = ShaderCompilerMessageSeverity.Error)
  1645. {
  1646. messageManager?.AddOrAppendError(this, id, new ShaderMessage("Concretization: " + errorMessage, severity));
  1647. }
  1648. public void ClearErrorsForNode(AbstractMaterialNode node)
  1649. {
  1650. messageManager?.ClearNodesFromProvider(this, node.ToEnumerable());
  1651. }
  1652. internal bool replaceInProgress = false;
  1653. public void ReplaceWith(GraphData other)
  1654. {
  1655. if (other == null)
  1656. throw new ArgumentException("Can only replace with another AbstractMaterialGraph", "other");
  1657. replaceInProgress = true;
  1658. m_GraphPrecision = other.m_GraphPrecision;
  1659. m_PreviewMode = other.m_PreviewMode;
  1660. m_OutputNode = other.m_OutputNode;
  1661. if ((this.vertexContext.position != other.vertexContext.position) ||
  1662. (this.fragmentContext.position != other.fragmentContext.position))
  1663. {
  1664. this.vertexContext.position = other.vertexContext.position;
  1665. this.fragmentContext.position = other.fragmentContext.position;
  1666. m_MovedContexts = true;
  1667. }
  1668. using (var inputsToRemove = PooledList<ShaderInput>.Get())
  1669. {
  1670. foreach (var property in m_Properties.SelectValue())
  1671. inputsToRemove.Add(property);
  1672. foreach (var keyword in m_Keywords.SelectValue())
  1673. inputsToRemove.Add(keyword);
  1674. foreach (var dropdown in m_Dropdowns.SelectValue())
  1675. inputsToRemove.Add(dropdown);
  1676. foreach (var input in inputsToRemove)
  1677. RemoveGraphInputNoValidate(input);
  1678. }
  1679. foreach (var otherProperty in other.properties)
  1680. {
  1681. AddGraphInputNoSanitization(otherProperty);
  1682. }
  1683. foreach (var otherKeyword in other.keywords)
  1684. {
  1685. AddGraphInputNoSanitization(otherKeyword);
  1686. }
  1687. foreach (var otherDropdown in other.dropdowns)
  1688. {
  1689. AddGraphInputNoSanitization(otherDropdown);
  1690. }
  1691. other.ValidateGraph();
  1692. ValidateGraph();
  1693. // Current tactic is to remove all nodes and edges and then re-add them, such that depending systems
  1694. // will re-initialize with new references.
  1695. using (ListPool<GroupData>.Get(out var removedGroupDatas))
  1696. {
  1697. removedGroupDatas.AddRange(m_GroupDatas.SelectValue());
  1698. foreach (var groupData in removedGroupDatas)
  1699. {
  1700. RemoveGroupNoValidate(groupData);
  1701. }
  1702. }
  1703. using (ListPool<StickyNoteData>.Get(out var removedNoteDatas))
  1704. {
  1705. removedNoteDatas.AddRange(m_StickyNoteDatas.SelectValue());
  1706. foreach (var groupData in removedNoteDatas)
  1707. {
  1708. RemoveNoteNoValidate(groupData);
  1709. }
  1710. }
  1711. using (var pooledList = ListPool<IEdge>.Get(out var removedNodeEdges))
  1712. {
  1713. removedNodeEdges.AddRange(m_Edges);
  1714. foreach (var edge in removedNodeEdges)
  1715. RemoveEdgeNoValidate(edge);
  1716. }
  1717. using (var nodesToRemove = PooledList<AbstractMaterialNode>.Get())
  1718. {
  1719. nodesToRemove.AddRange(m_Nodes.SelectValue());
  1720. foreach (var node in nodesToRemove)
  1721. RemoveNodeNoValidate(node);
  1722. }
  1723. // Clear category data too before re-adding
  1724. m_CategoryData.Clear();
  1725. ValidateGraph();
  1726. foreach (GroupData groupData in other.groups)
  1727. AddGroup(groupData);
  1728. // If categories are ever removed completely, make sure there is always one default category that exists
  1729. if (!other.categories.Any())
  1730. {
  1731. AddCategory(CategoryData.DefaultCategory());
  1732. }
  1733. else
  1734. {
  1735. foreach (CategoryData categoryData in other.categories)
  1736. {
  1737. AddCategory(categoryData);
  1738. }
  1739. }
  1740. foreach (var stickyNote in other.stickyNotes)
  1741. {
  1742. AddStickyNote(stickyNote);
  1743. }
  1744. foreach (var node in other.GetNodes<AbstractMaterialNode>())
  1745. {
  1746. if (node is BlockNode blockNode)
  1747. {
  1748. var contextData = blockNode.descriptor.shaderStage == ShaderStage.Vertex ? vertexContext : fragmentContext;
  1749. AddBlockNoValidate(blockNode, contextData, blockNode.index);
  1750. }
  1751. else
  1752. {
  1753. AddNodeNoValidate(node);
  1754. }
  1755. }
  1756. foreach (var edge in other.edges)
  1757. {
  1758. ConnectNoValidate(edge.outputSlot, edge.inputSlot, true);
  1759. }
  1760. outputNode = other.outputNode;
  1761. // clear our local active targets and copy state from the other GraphData
  1762. // NOTE: we DO NOT clear or rebuild m_AllPotentialTargets, in order to
  1763. // retain the data from any inactive targets.
  1764. // this allows the user can add them back and keep the old settings
  1765. m_ActiveTargets.Clear();
  1766. foreach (var target in other.activeTargets)
  1767. {
  1768. // Ensure target inits correctly
  1769. var context = new TargetSetupContext();
  1770. target.Setup(ref context);
  1771. SetTargetActive(target, true);
  1772. }
  1773. SortActiveTargets();
  1774. // Active blocks
  1775. var activeBlocks = GetActiveBlocksForAllActiveTargets();
  1776. UpdateActiveBlocks(activeBlocks);
  1777. replaceInProgress = false;
  1778. ValidateGraph();
  1779. }
  1780. internal void PasteGraph(CopyPasteGraph graphToPaste, List<AbstractMaterialNode> remappedNodes,
  1781. List<Edge> remappedEdges)
  1782. {
  1783. var groupMap = new Dictionary<GroupData, GroupData>();
  1784. foreach (var group in graphToPaste.groups)
  1785. {
  1786. var position = group.position;
  1787. position.x += 30;
  1788. position.y += 30;
  1789. GroupData newGroup = new GroupData(group.title, position);
  1790. groupMap[group] = newGroup;
  1791. AddGroup(newGroup);
  1792. m_PastedGroups.Add(newGroup);
  1793. }
  1794. foreach (var stickyNote in graphToPaste.stickyNotes)
  1795. {
  1796. var position = stickyNote.position;
  1797. position.x += 30;
  1798. position.y += 30;
  1799. StickyNoteData pastedStickyNote = new StickyNoteData(stickyNote.title, stickyNote.content, position);
  1800. pastedStickyNote.textSize = stickyNote.textSize;
  1801. pastedStickyNote.theme = stickyNote.theme;
  1802. if (stickyNote.group != null && groupMap.ContainsKey(stickyNote.group))
  1803. {
  1804. pastedStickyNote.group = groupMap[stickyNote.group];
  1805. }
  1806. AddStickyNote(pastedStickyNote);
  1807. m_PastedStickyNotes.Add(pastedStickyNote);
  1808. }
  1809. var edges = graphToPaste.edges.ToList();
  1810. var nodeList = graphToPaste.GetNodes<AbstractMaterialNode>();
  1811. foreach (var node in nodeList)
  1812. {
  1813. // cannot paste block nodes, or unknown node types
  1814. if ((node is BlockNode) || (node is MultiJsonInternal.UnknownNodeType))
  1815. continue;
  1816. if (!IsInputAllowedInGraph(node))
  1817. continue;
  1818. AbstractMaterialNode pastedNode = node;
  1819. // Check if the property nodes need to be made into a concrete node.
  1820. if (node is PropertyNode propertyNode)
  1821. {
  1822. // If the property is not in the current graph, do check if the
  1823. // property can be made into a concrete node.
  1824. var property = m_Properties.SelectValue().FirstOrDefault(x => x.objectId == propertyNode.property.objectId
  1825. || (x.propertyType == propertyNode.property.propertyType && x.referenceName == propertyNode.property.referenceName));
  1826. if (property != null)
  1827. {
  1828. propertyNode.property = property;
  1829. }
  1830. else
  1831. {
  1832. pastedNode = propertyNode.property.ToConcreteNode();
  1833. // some property nodes cannot be concretized.. fail to paste them
  1834. if (pastedNode == null)
  1835. continue;
  1836. pastedNode.drawState = node.drawState;
  1837. for (var i = 0; i < edges.Count; i++)
  1838. {
  1839. var edge = edges[i];
  1840. if (edge.outputSlot.node == node)
  1841. {
  1842. edges[i] = new Edge(new SlotReference(pastedNode, edge.outputSlot.slotId), edge.inputSlot);
  1843. }
  1844. else if (edge.inputSlot.node == node)
  1845. {
  1846. edges[i] = new Edge(edge.outputSlot, new SlotReference(pastedNode, edge.inputSlot.slotId));
  1847. }
  1848. }
  1849. }
  1850. }
  1851. // If the node has a group guid and no group has been copied, reset the group guid.
  1852. // Check if the node is inside a group
  1853. if (node.group != null)
  1854. {
  1855. if (groupMap.ContainsKey(node.group))
  1856. {
  1857. var absNode = pastedNode;
  1858. absNode.group = groupMap[node.group];
  1859. pastedNode = absNode;
  1860. }
  1861. else
  1862. {
  1863. pastedNode.group = null;
  1864. }
  1865. }
  1866. remappedNodes.Add(pastedNode);
  1867. AddNode(pastedNode);
  1868. // add the node to the pasted node list
  1869. m_PastedNodes.Add(pastedNode);
  1870. // Check if the keyword nodes need to have their keywords copied.
  1871. if (node is KeywordNode keywordNode)
  1872. {
  1873. var keyword = m_Keywords.SelectValue().FirstOrDefault(x => x.objectId == keywordNode.keyword.objectId
  1874. || (x.keywordType == keywordNode.keyword.keywordType && x.referenceName == keywordNode.keyword.referenceName));
  1875. if (keyword != null)
  1876. {
  1877. keywordNode.keyword = keyword;
  1878. }
  1879. else
  1880. {
  1881. owner.graphDataStore.Dispatch(new AddShaderInputAction() { shaderInputReference = keywordNode.keyword });
  1882. }
  1883. // Always update Keyword nodes to handle any collisions resolved on the Keyword
  1884. keywordNode.UpdateNode();
  1885. }
  1886. // Check if the dropdown nodes need to have their dropdowns copied.
  1887. if (node is DropdownNode dropdownNode)
  1888. {
  1889. var dropdown = m_Dropdowns.SelectValue().FirstOrDefault(x => x.objectId == dropdownNode.dropdown.objectId
  1890. || x.referenceName == dropdownNode.dropdown.referenceName);
  1891. if (dropdown != null)
  1892. {
  1893. dropdownNode.dropdown = dropdown;
  1894. }
  1895. else
  1896. {
  1897. owner.graphDataStore.Dispatch(new AddShaderInputAction() { shaderInputReference = dropdownNode.dropdown });
  1898. }
  1899. // Always update Dropdown nodes to handle any collisions resolved on the Keyword
  1900. dropdownNode.UpdateNode();
  1901. }
  1902. }
  1903. foreach (var edge in edges)
  1904. {
  1905. var newEdge = (Edge)Connect(edge.outputSlot, edge.inputSlot);
  1906. if (newEdge != null)
  1907. {
  1908. remappedEdges.Add(newEdge);
  1909. }
  1910. }
  1911. ValidateGraph();
  1912. }
  1913. public override void OnBeforeSerialize()
  1914. {
  1915. m_Edges.Sort();
  1916. ChangeVersion(latestVersion);
  1917. }
  1918. static T DeserializeLegacy<T>(string typeString, string json, Guid? overrideObjectId = null) where T : JsonObject
  1919. {
  1920. var jsonObj = MultiJsonInternal.CreateInstanceForDeserialization(typeString);
  1921. var value = jsonObj as T;
  1922. if (value == null)
  1923. {
  1924. Debug.Log($"Cannot create instance for {typeString}");
  1925. return null;
  1926. }
  1927. // by default, MultiJsonInternal.CreateInstance will create a new objectID randomly..
  1928. // we need some created objects to have deterministic objectIDs, because they affect the generated shader.
  1929. // if the generated shader is not deterministic, it can create ripple effects (i.e. causing Materials to be modified randomly as properties are renamed)
  1930. // so we provide this path to allow the calling code to override the objectID with something deterministic
  1931. if (overrideObjectId.HasValue)
  1932. value.OverrideObjectId(overrideObjectId.Value.ToString("N"));
  1933. MultiJsonInternal.Enqueue(value, json);
  1934. return value as T;
  1935. }
  1936. static AbstractMaterialNode DeserializeLegacyNode(string typeString, string json, Guid? overrideObjectId = null)
  1937. {
  1938. var jsonObj = MultiJsonInternal.CreateInstanceForDeserialization(typeString);
  1939. var value = jsonObj as AbstractMaterialNode;
  1940. if (value == null)
  1941. {
  1942. //Special case - want to support nodes of unknwon type for cross pipeline compatability
  1943. value = new LegacyUnknownTypeNode(typeString, json);
  1944. if (overrideObjectId.HasValue)
  1945. value.OverrideObjectId(overrideObjectId.Value.ToString("N"));
  1946. MultiJsonInternal.Enqueue(value, json);
  1947. return value as AbstractMaterialNode;
  1948. }
  1949. else
  1950. {
  1951. if (overrideObjectId.HasValue)
  1952. value.OverrideObjectId(overrideObjectId.Value.ToString("N"));
  1953. MultiJsonInternal.Enqueue(value, json);
  1954. return value as AbstractMaterialNode;
  1955. }
  1956. }
  1957. public override void OnAfterDeserialize(string json)
  1958. {
  1959. if (sgVersion == 0)
  1960. {
  1961. var graphData0 = JsonUtility.FromJson<GraphData0>(json);
  1962. //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
  1963. //HDRP, we have to handle the case that our version might not be correct -
  1964. if (graphData0.m_Version > 0)
  1965. {
  1966. sgVersion = graphData0.m_Version;
  1967. }
  1968. else
  1969. {
  1970. // graphData.m_Version == 0 (matches current sgVersion)
  1971. Guid assetGuid;
  1972. if (!Guid.TryParse(this.assetGuid, out assetGuid))
  1973. assetGuid = JsonObject.GenerateNamespaceUUID(Guid.Empty, json);
  1974. var nodeGuidMap = new Dictionary<string, AbstractMaterialNode>();
  1975. var propertyGuidMap = new Dictionary<string, AbstractShaderProperty>();
  1976. var keywordGuidMap = new Dictionary<string, ShaderKeyword>();
  1977. var groupGuidMap = new Dictionary<string, GroupData>();
  1978. var slotsField = typeof(AbstractMaterialNode).GetField("m_Slots", BindingFlags.Instance | BindingFlags.NonPublic);
  1979. var propertyField = typeof(PropertyNode).GetField("m_Property", BindingFlags.Instance | BindingFlags.NonPublic);
  1980. var keywordField = typeof(KeywordNode).GetField("m_Keyword", BindingFlags.Instance | BindingFlags.NonPublic);
  1981. var dropdownField = typeof(DropdownNode).GetField("m_Dropdown", BindingFlags.Instance | BindingFlags.NonPublic);
  1982. var defaultReferenceNameField = typeof(ShaderInput).GetField("m_DefaultReferenceName", BindingFlags.Instance | BindingFlags.NonPublic);
  1983. m_GroupDatas.Clear();
  1984. m_StickyNoteDatas.Clear();
  1985. foreach (var group0 in graphData0.m_Groups)
  1986. {
  1987. var group = new GroupData(group0.m_Title, group0.m_Position);
  1988. m_GroupDatas.Add(group);
  1989. if (!groupGuidMap.ContainsKey(group0.m_GuidSerialized))
  1990. {
  1991. groupGuidMap.Add(group0.m_GuidSerialized, group);
  1992. }
  1993. else if (!groupGuidMap[group0.m_GuidSerialized].Equals(group.objectId))
  1994. {
  1995. Debug.LogError("Group id mismatch");
  1996. }
  1997. }
  1998. foreach (var serializedProperty in graphData0.m_SerializedProperties)
  1999. {
  2000. var propObjectId = JsonObject.GenerateNamespaceUUID(assetGuid, serializedProperty.JSONnodeData);
  2001. var property = DeserializeLegacy<AbstractShaderProperty>(serializedProperty.typeInfo.fullName, serializedProperty.JSONnodeData, propObjectId);
  2002. if (property == null)
  2003. continue;
  2004. m_Properties.Add(property);
  2005. var input0 = JsonUtility.FromJson<ShaderInput0>(serializedProperty.JSONnodeData);
  2006. propertyGuidMap[input0.m_Guid.m_GuidSerialized] = property;
  2007. // Fix up missing reference names
  2008. // Properties on Sub Graphs in V0 never have reference names serialized
  2009. // To maintain Sub Graph node property mapping we force guid based reference names on upgrade
  2010. if (string.IsNullOrEmpty((string)defaultReferenceNameField.GetValue(property)))
  2011. {
  2012. // ColorShaderProperty is the only Property case where `GetDefaultReferenceName` was overriden
  2013. if (MultiJson.ParseType(serializedProperty.typeInfo.fullName) == typeof(ColorShaderProperty))
  2014. {
  2015. defaultReferenceNameField.SetValue(property, $"Color_{GuidEncoder.Encode(Guid.Parse(input0.m_Guid.m_GuidSerialized))}");
  2016. }
  2017. else
  2018. {
  2019. defaultReferenceNameField.SetValue(property, $"{property.concreteShaderValueType}_{GuidEncoder.Encode(Guid.Parse(input0.m_Guid.m_GuidSerialized))}");
  2020. }
  2021. }
  2022. }
  2023. foreach (var serializedKeyword in graphData0.m_SerializedKeywords)
  2024. {
  2025. var keyword = DeserializeLegacy<ShaderKeyword>(serializedKeyword.typeInfo.fullName, serializedKeyword.JSONnodeData);
  2026. if (keyword == null)
  2027. {
  2028. continue;
  2029. }
  2030. m_Keywords.Add(keyword);
  2031. var input0 = JsonUtility.FromJson<ShaderInput0>(serializedKeyword.JSONnodeData);
  2032. keywordGuidMap[input0.m_Guid.m_GuidSerialized] = keyword;
  2033. }
  2034. foreach (var serializedNode in graphData0.m_SerializableNodes)
  2035. {
  2036. var node0 = JsonUtility.FromJson<AbstractMaterialNode0>(serializedNode.JSONnodeData);
  2037. var nodeObjectId = JsonObject.GenerateNamespaceUUID(node0.m_GuidSerialized, "node");
  2038. var node = DeserializeLegacyNode(serializedNode.typeInfo.fullName, serializedNode.JSONnodeData, nodeObjectId);
  2039. if (node == null)
  2040. {
  2041. continue;
  2042. }
  2043. nodeGuidMap.Add(node0.m_GuidSerialized, node);
  2044. m_Nodes.Add(node);
  2045. if (!string.IsNullOrEmpty(node0.m_PropertyGuidSerialized) && propertyGuidMap.TryGetValue(node0.m_PropertyGuidSerialized, out var property))
  2046. {
  2047. propertyField.SetValue(node, (JsonRef<AbstractShaderProperty>)property);
  2048. }
  2049. if (!string.IsNullOrEmpty(node0.m_KeywordGuidSerialized) && keywordGuidMap.TryGetValue(node0.m_KeywordGuidSerialized, out var keyword))
  2050. {
  2051. keywordField.SetValue(node, (JsonRef<ShaderKeyword>)keyword);
  2052. }
  2053. var slots = (List<JsonData<MaterialSlot>>)slotsField.GetValue(node);
  2054. slots.Clear();
  2055. foreach (var serializedSlot in node0.m_SerializableSlots)
  2056. {
  2057. var slotObjectId = JsonObject.GenerateNamespaceUUID(node0.m_GuidSerialized, serializedSlot.JSONnodeData);
  2058. var slot = DeserializeLegacy<MaterialSlot>(serializedSlot.typeInfo.fullName, serializedSlot.JSONnodeData, slotObjectId);
  2059. if (slot == null)
  2060. {
  2061. continue;
  2062. }
  2063. slots.Add(slot);
  2064. }
  2065. if (!String.IsNullOrEmpty(node0.m_GroupGuidSerialized))
  2066. {
  2067. if (groupGuidMap.TryGetValue(node0.m_GroupGuidSerialized, out GroupData foundGroup))
  2068. {
  2069. node.group = foundGroup;
  2070. }
  2071. }
  2072. }
  2073. foreach (var stickyNote0 in graphData0.m_StickyNotes)
  2074. {
  2075. var stickyNote = new StickyNoteData(stickyNote0.m_Title, stickyNote0.m_Content, stickyNote0.m_Position);
  2076. if (!String.IsNullOrEmpty(stickyNote0.m_GroupGuidSerialized))
  2077. {
  2078. if (groupGuidMap.TryGetValue(stickyNote0.m_GroupGuidSerialized, out GroupData foundGroup))
  2079. {
  2080. stickyNote.group = foundGroup;
  2081. }
  2082. }
  2083. stickyNote.theme = stickyNote0.m_Theme;
  2084. stickyNote.textSize = stickyNote0.m_TextSize;
  2085. m_StickyNoteDatas.Add(stickyNote);
  2086. }
  2087. var subgraphOuput = GetNodes<SubGraphOutputNode>();
  2088. isSubGraph = subgraphOuput.Any();
  2089. if (isSubGraph)
  2090. {
  2091. m_OutputNode = subgraphOuput.FirstOrDefault();
  2092. }
  2093. else if (!string.IsNullOrEmpty(graphData0.m_ActiveOutputNodeGuidSerialized))
  2094. {
  2095. m_OutputNode = nodeGuidMap[graphData0.m_ActiveOutputNodeGuidSerialized];
  2096. }
  2097. else
  2098. {
  2099. m_OutputNode = (AbstractMaterialNode)GetNodes<IMasterNode1>().FirstOrDefault();
  2100. }
  2101. foreach (var serializedElement in graphData0.m_SerializableEdges)
  2102. {
  2103. var edge0 = JsonUtility.FromJson<Edge0>(serializedElement.JSONnodeData);
  2104. m_Edges.Add(new Edge(
  2105. new SlotReference(
  2106. nodeGuidMap[edge0.m_OutputSlot.m_NodeGUIDSerialized],
  2107. edge0.m_OutputSlot.m_SlotId),
  2108. new SlotReference(
  2109. nodeGuidMap[edge0.m_InputSlot.m_NodeGUIDSerialized],
  2110. edge0.m_InputSlot.m_SlotId)));
  2111. }
  2112. }
  2113. }
  2114. }
  2115. [Serializable]
  2116. class OldGraphDataReadConcretePrecision
  2117. {
  2118. // old value just for upgrade
  2119. [SerializeField]
  2120. public ConcretePrecision m_ConcretePrecision = ConcretePrecision.Single;
  2121. };
  2122. public override void OnAfterMultiDeserialize(string json)
  2123. {
  2124. // Deferred upgrades
  2125. if (sgVersion != latestVersion)
  2126. {
  2127. if (sgVersion < 2)
  2128. {
  2129. var addedBlocks = ListPool<BlockFieldDescriptor>.Get();
  2130. void UpgradeFromBlockMap(Dictionary<BlockFieldDescriptor, int> blockMap)
  2131. {
  2132. // Map master node ports to blocks
  2133. if (blockMap != null)
  2134. {
  2135. foreach (var blockMapping in blockMap)
  2136. {
  2137. // Create a new BlockNode for each unique map entry
  2138. var descriptor = blockMapping.Key;
  2139. if (addedBlocks.Contains(descriptor))
  2140. continue;
  2141. addedBlocks.Add(descriptor);
  2142. var contextData = descriptor.shaderStage == ShaderStage.Fragment ? m_FragmentContext : m_VertexContext;
  2143. var block = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
  2144. block.Init(descriptor);
  2145. AddBlockNoValidate(block, contextData, contextData.blocks.Count);
  2146. // To avoid having to go around the following deserialization code
  2147. // We simply run OnBeforeSerialization here to ensure m_SerializedDescriptor is set
  2148. block.OnBeforeSerialize();
  2149. // Now remap the incoming edges to blocks
  2150. var slotId = blockMapping.Value;
  2151. var oldSlot = m_OutputNode.value.FindSlot<MaterialSlot>(slotId);
  2152. var newSlot = block.FindSlot<MaterialSlot>(0);
  2153. if (oldSlot == null)
  2154. continue;
  2155. var oldInputSlotRef = m_OutputNode.value.GetSlotReference(slotId);
  2156. var newInputSlotRef = block.GetSlotReference(0);
  2157. // Always copy the value over for convenience
  2158. newSlot.CopyValuesFrom(oldSlot);
  2159. for (int i = 0; i < m_Edges.Count; i++)
  2160. {
  2161. // Find all edges connected to the master node using slot ID from the block map
  2162. // Remove them and replace them with new edges connected to the block nodes
  2163. var edge = m_Edges[i];
  2164. if (edge.inputSlot.Equals(oldInputSlotRef))
  2165. {
  2166. var outputSlot = edge.outputSlot;
  2167. m_Edges.Remove(edge);
  2168. m_Edges.Add(new Edge(outputSlot, newInputSlotRef));
  2169. }
  2170. }
  2171. // manually handle a bug where fragment normal slots could get out of sync of the master node's set fragment normal space
  2172. if (descriptor == BlockFields.SurfaceDescription.NormalOS)
  2173. {
  2174. NormalMaterialSlot norm = newSlot as NormalMaterialSlot;
  2175. if (norm.space != CoordinateSpace.Object)
  2176. {
  2177. norm.space = CoordinateSpace.Object;
  2178. }
  2179. }
  2180. else if (descriptor == BlockFields.SurfaceDescription.NormalTS)
  2181. {
  2182. NormalMaterialSlot norm = newSlot as NormalMaterialSlot;
  2183. if (norm.space != CoordinateSpace.Tangent)
  2184. {
  2185. norm.space = CoordinateSpace.Tangent;
  2186. }
  2187. }
  2188. else if (descriptor == BlockFields.SurfaceDescription.NormalWS)
  2189. {
  2190. NormalMaterialSlot norm = newSlot as NormalMaterialSlot;
  2191. if (norm.space != CoordinateSpace.World)
  2192. {
  2193. norm.space = CoordinateSpace.World;
  2194. }
  2195. }
  2196. }
  2197. // We need to call AddBlockNoValidate but this adds to m_AddedNodes resulting in duplicates
  2198. // Therefore we need to clear this list before the view is created
  2199. m_AddedNodes.Clear();
  2200. }
  2201. }
  2202. var masterNode = m_OutputNode.value as IMasterNode1;
  2203. // This is required for edge lookup during Target upgrade
  2204. if (m_OutputNode.value != null)
  2205. {
  2206. m_OutputNode.value.owner = this;
  2207. }
  2208. foreach (var edge in m_Edges)
  2209. {
  2210. AddEdgeToNodeEdges(edge);
  2211. }
  2212. // Ensure correct initialization of Contexts
  2213. AddContexts();
  2214. // Position Contexts to the match master node
  2215. var oldPosition = Vector2.zero;
  2216. if (m_OutputNode.value != null)
  2217. {
  2218. oldPosition = m_OutputNode.value.drawState.position.position;
  2219. }
  2220. m_VertexContext.position = oldPosition;
  2221. m_FragmentContext.position = new Vector2(oldPosition.x, oldPosition.y + 200);
  2222. // Try to upgrade all potential targets from master node
  2223. if (masterNode != null)
  2224. {
  2225. foreach (var potentialTarget in m_AllPotentialTargets)
  2226. {
  2227. if (potentialTarget.IsUnknown())
  2228. continue;
  2229. var target = potentialTarget.GetTarget();
  2230. if (!(target is ILegacyTarget legacyTarget))
  2231. continue;
  2232. if (!legacyTarget.TryUpgradeFromMasterNode(masterNode, out var newBlockMap))
  2233. continue;
  2234. // upgrade succeeded! Activate it
  2235. SetTargetActive(target, true);
  2236. UpgradeFromBlockMap(newBlockMap);
  2237. }
  2238. SortActiveTargets();
  2239. }
  2240. // Clean up after upgrade
  2241. if (!isSubGraph)
  2242. {
  2243. m_OutputNode = null;
  2244. }
  2245. var masterNodes = GetNodes<IMasterNode1>().ToArray();
  2246. for (int i = 0; i < masterNodes.Length; i++)
  2247. {
  2248. var node = masterNodes.ElementAt(i) as AbstractMaterialNode;
  2249. m_Nodes.Remove(node);
  2250. }
  2251. m_NodeEdges.Clear();
  2252. }
  2253. if (sgVersion < 3)
  2254. {
  2255. var oldGraph = JsonUtility.FromJson<OldGraphDataReadConcretePrecision>(json);
  2256. // upgrade concrete precision to the new graph precision
  2257. switch (oldGraph.m_ConcretePrecision)
  2258. {
  2259. case ConcretePrecision.Half:
  2260. m_GraphPrecision = GraphPrecision.Half;
  2261. break;
  2262. case ConcretePrecision.Single:
  2263. m_GraphPrecision = GraphPrecision.Single;
  2264. break;
  2265. }
  2266. }
  2267. ChangeVersion(latestVersion);
  2268. }
  2269. PooledList<(LegacyUnknownTypeNode, AbstractMaterialNode)> updatedNodes = PooledList<(LegacyUnknownTypeNode, AbstractMaterialNode)>.Get();
  2270. foreach (var node in m_Nodes.SelectValue())
  2271. {
  2272. if (node is LegacyUnknownTypeNode lNode && lNode.foundType != null)
  2273. {
  2274. AbstractMaterialNode legacyNode = (AbstractMaterialNode)Activator.CreateInstance(lNode.foundType);
  2275. JsonUtility.FromJsonOverwrite(lNode.serializedData, legacyNode);
  2276. legacyNode.group = lNode.group;
  2277. updatedNodes.Add((lNode, legacyNode));
  2278. }
  2279. }
  2280. foreach (var nodePair in updatedNodes)
  2281. {
  2282. m_Nodes.Add(nodePair.Item2);
  2283. ReplaceNodeWithNode(nodePair.Item1, nodePair.Item2);
  2284. }
  2285. updatedNodes.Dispose();
  2286. m_NodeDictionary = new Dictionary<string, AbstractMaterialNode>(m_Nodes.Count);
  2287. foreach (var group in m_GroupDatas.SelectValue())
  2288. {
  2289. m_GroupItems.Add(group, new List<IGroupItem>());
  2290. }
  2291. foreach (var node in m_Nodes.SelectValue())
  2292. {
  2293. node.owner = this;
  2294. node.UpdateNodeAfterDeserialization();
  2295. node.SetupSlots();
  2296. m_NodeDictionary.Add(node.objectId, node);
  2297. if (m_GroupItems.TryGetValue(node.group, out var groupItems))
  2298. {
  2299. groupItems.Add(node);
  2300. }
  2301. else
  2302. {
  2303. node.group = null;
  2304. }
  2305. }
  2306. foreach (var stickyNote in m_StickyNoteDatas.SelectValue())
  2307. {
  2308. if (m_GroupItems.TryGetValue(stickyNote.group, out var groupItems))
  2309. {
  2310. groupItems.Add(stickyNote);
  2311. }
  2312. else
  2313. {
  2314. stickyNote.group = null;
  2315. }
  2316. }
  2317. foreach (var edge in m_Edges)
  2318. AddEdgeToNodeEdges(edge);
  2319. // --------------------------------------------------
  2320. // Deserialize Contexts & Blocks
  2321. void DeserializeContextData(ContextData contextData, ShaderStage stage)
  2322. {
  2323. // Because Vertex/Fragment Contexts are serialized explicitly
  2324. // we do not need to serialize the Stage value on the ContextData
  2325. contextData.shaderStage = stage;
  2326. var blocks = contextData.blocks.SelectValue().ToList();
  2327. var blockCount = blocks.Count;
  2328. for (int i = 0; i < blockCount; i++)
  2329. {
  2330. // Update NonSerialized data on the BlockNode
  2331. var block = blocks[i];
  2332. // custom interpolators fully regenerate their own descriptor on deserialization
  2333. if (!block.isCustomBlock)
  2334. {
  2335. block.descriptor = m_BlockFieldDescriptors.FirstOrDefault(x => $"{x.tag}.{x.name}" == block.serializedDescriptor);
  2336. }
  2337. if (block.descriptor == null)
  2338. {
  2339. //Hit a descriptor that was not recognized from the assembly (likely from a different SRP)
  2340. //create a new entry for it and continue on
  2341. if (string.IsNullOrEmpty(block.serializedDescriptor))
  2342. {
  2343. throw new Exception($"Block {block} had no serialized descriptor");
  2344. }
  2345. var tmp = block.serializedDescriptor.Split('.');
  2346. if (tmp.Length != 2)
  2347. {
  2348. throw new Exception($"Block {block}'s serialized descriptor {block.serializedDescriptor} did not match expected format {{x.tag}}.{{x.name}}");
  2349. }
  2350. //right thing to do?
  2351. block.descriptor = new BlockFieldDescriptor(tmp[0], tmp[1], null, null, stage, true, true);
  2352. m_BlockFieldDescriptors.Add(block.descriptor);
  2353. }
  2354. block.contextData = contextData;
  2355. }
  2356. }
  2357. // First deserialize the ContextDatas
  2358. DeserializeContextData(m_VertexContext, ShaderStage.Vertex);
  2359. DeserializeContextData(m_FragmentContext, ShaderStage.Fragment);
  2360. // there should be no unknown potential targets at this point
  2361. Assert.IsFalse(m_AllPotentialTargets.Any(pt => pt.IsUnknown()));
  2362. foreach (var target in m_ActiveTargets.SelectValue())
  2363. {
  2364. var targetType = target.GetType();
  2365. if (targetType == typeof(MultiJsonInternal.UnknownTargetType))
  2366. {
  2367. // register any active UnknownTargetType as a potential target
  2368. m_AllPotentialTargets.Add(new PotentialTarget(target));
  2369. }
  2370. else
  2371. {
  2372. // active known targets should replace the stored Target in AllPotentialTargets
  2373. int targetIndex = m_AllPotentialTargets.FindIndex(pt => pt.knownType == targetType);
  2374. m_AllPotentialTargets[targetIndex].ReplaceStoredTarget(target);
  2375. }
  2376. }
  2377. SortActiveTargets();
  2378. }
  2379. private void ReplaceNodeWithNode(LegacyUnknownTypeNode nodeToReplace, AbstractMaterialNode nodeReplacement)
  2380. {
  2381. var oldSlots = new List<MaterialSlot>();
  2382. nodeToReplace.GetSlots(oldSlots);
  2383. var newSlots = new List<MaterialSlot>();
  2384. nodeReplacement.GetSlots(newSlots);
  2385. for (int i = 0; i < oldSlots.Count; i++)
  2386. {
  2387. newSlots[i].CopyValuesFrom(oldSlots[i]);
  2388. var oldSlotRef = nodeToReplace.GetSlotReference(oldSlots[i].id);
  2389. var newSlotRef = nodeReplacement.GetSlotReference(newSlots[i].id);
  2390. for (int x = 0; x < m_Edges.Count; x++)
  2391. {
  2392. var edge = m_Edges[x];
  2393. if (edge.inputSlot.Equals(oldSlotRef))
  2394. {
  2395. var outputSlot = edge.outputSlot;
  2396. m_Edges.Remove(edge);
  2397. m_Edges.Add(new Edge(outputSlot, newSlotRef));
  2398. }
  2399. else if (edge.outputSlot.Equals(oldSlotRef))
  2400. {
  2401. var inputSlot = edge.inputSlot;
  2402. m_Edges.Remove(edge);
  2403. m_Edges.Add(new Edge(newSlotRef, inputSlot));
  2404. }
  2405. }
  2406. }
  2407. }
  2408. public void OnEnable()
  2409. {
  2410. foreach (var node in GetNodes<AbstractMaterialNode>().OfType<IOnAssetEnabled>())
  2411. {
  2412. node.OnEnable();
  2413. }
  2414. ShaderGraphPreferences.onVariantLimitChanged += OnKeywordChanged;
  2415. }
  2416. public void OnDisable()
  2417. {
  2418. ShaderGraphPreferences.onVariantLimitChanged -= OnKeywordChanged;
  2419. foreach (var node in GetNodes<AbstractMaterialNode>())
  2420. node.Dispose();
  2421. }
  2422. internal void ValidateCustomBlockLimit()
  2423. {
  2424. if (m_ActiveTargets.Count() == 0)
  2425. return;
  2426. int nonCustomUsage = 0;
  2427. foreach (var bnode in vertexContext.blocks.Where(jb => !jb.value.isCustomBlock).Select(b => b.value))
  2428. {
  2429. if (bnode == null || bnode.descriptor == null)
  2430. continue;
  2431. if (bnode.descriptor.HasPreprocessor() || bnode.descriptor.HasSemantic() || bnode.descriptor.vectorCount == 0) // not packable.
  2432. nonCustomUsage += 4;
  2433. else nonCustomUsage += bnode.descriptor.vectorCount;
  2434. }
  2435. int maxTargetUsage = m_ActiveTargets.Select(jt => jt.value.padCustomInterpolatorLimit).Max() * 4;
  2436. int padding = nonCustomUsage + maxTargetUsage;
  2437. int errRange = ShaderGraphProjectSettings.instance.customInterpolatorErrorThreshold;
  2438. int warnRange = ShaderGraphProjectSettings.instance.customInterpolatorWarningThreshold;
  2439. int errorLevel = errRange * 4 - padding;
  2440. int warnLevel = warnRange * 4 - padding;
  2441. int total = 0;
  2442. // warn based on the interpolator's location in the block list.
  2443. foreach (var cib in vertexContext.blocks.Where(jb => jb.value.isCustomBlock).Select(b => b.value))
  2444. {
  2445. ClearErrorsForNode(cib);
  2446. total += (int)cib.customWidth;
  2447. if (total > errorLevel)
  2448. {
  2449. AddValidationError(cib.objectId, $"{cib.customName} exceeds the interpolation channel error threshold: {errRange}. See ShaderGraph project settings.");
  2450. }
  2451. else if (total > warnLevel)
  2452. {
  2453. AddValidationError(cib.objectId, $"{cib.customName} exceeds the interpolation channel warning threshold: {warnRange}. See ShaderGraph project settings.", ShaderCompilerMessageSeverity.Warning);
  2454. }
  2455. }
  2456. }
  2457. void ValidateContextBlocks()
  2458. {
  2459. void ValidateContext(ContextData contextData, ShaderStage expectedShaderStage)
  2460. {
  2461. if (contextData == null)
  2462. return;
  2463. foreach (var block in contextData.blocks)
  2464. {
  2465. var slots = block.value.GetInputSlots<MaterialSlot>();
  2466. foreach (var slot in slots)
  2467. FindAndReportSlotErrors(slot, expectedShaderStage);
  2468. }
  2469. };
  2470. ValidateContext(vertexContext, ShaderStage.Vertex);
  2471. ValidateContext(fragmentContext, ShaderStage.Fragment);
  2472. }
  2473. void FindAndReportSlotErrors(MaterialSlot initialSlot, ShaderStage expectedShaderStage)
  2474. {
  2475. var expectedCapability = expectedShaderStage.GetShaderStageCapability();
  2476. var errorSourceSlots = new HashSet<MaterialSlot>();
  2477. var visitedNodes = new HashSet<AbstractMaterialNode>();
  2478. var graph = initialSlot.owner.owner;
  2479. var slotStack = new Stack<MaterialSlot>();
  2480. slotStack.Clear();
  2481. slotStack.Push(initialSlot);
  2482. // Trace back and find any edges that introduce an error
  2483. while (slotStack.Any())
  2484. {
  2485. var slot = slotStack.Pop();
  2486. // If the slot is an input, jump across the connected edge to the output it's connected to
  2487. if (slot.isInputSlot)
  2488. {
  2489. foreach (var edge in graph.GetEdges(slot.slotReference))
  2490. {
  2491. var node = edge.outputSlot.node;
  2492. var outputSlot = node.FindOutputSlot<MaterialSlot>(edge.outputSlot.slotId);
  2493. // If the output slot this is connected to is invalid then this is a source of an error.
  2494. // Mark the slot and stop iterating, otherwise continue the recursion
  2495. if (!outputSlot.stageCapability.HasFlag(expectedCapability))
  2496. errorSourceSlots.Add(outputSlot);
  2497. else
  2498. slotStack.Push(outputSlot);
  2499. }
  2500. }
  2501. else
  2502. {
  2503. // No need to double visit nodes
  2504. if (visitedNodes.Contains(slot.owner))
  2505. continue;
  2506. visitedNodes.Add(slot.owner);
  2507. var ownerSlots = slot.owner.GetInputSlots<MaterialSlot>(slot);
  2508. foreach (var ownerSlot in ownerSlots)
  2509. slotStack.Push(ownerSlot);
  2510. }
  2511. }
  2512. bool IsEntireNodeStageLocked(AbstractMaterialNode node, ShaderStageCapability expectedNodeCapability)
  2513. {
  2514. var slots = node.GetOutputSlots<MaterialSlot>();
  2515. foreach (var slot in slots)
  2516. {
  2517. if (expectedNodeCapability != slot.stageCapability)
  2518. return false;
  2519. }
  2520. return true;
  2521. };
  2522. foreach (var errorSourceSlot in errorSourceSlots)
  2523. {
  2524. var errorNode = errorSourceSlot.owner;
  2525. // Determine if only one slot or the entire node is at fault. Currently only slots are
  2526. // denoted with stage capabilities so deduce this by checking all outputs
  2527. string errorSource;
  2528. if (IsEntireNodeStageLocked(errorNode, errorSourceSlot.stageCapability))
  2529. errorSource = $"Node {errorNode.name}";
  2530. else
  2531. errorSource = $"Slot {errorSourceSlot.RawDisplayName()}";
  2532. // Determine what action they can take. If the stage capability is None then this can't be connected to anything.
  2533. string actionToTake;
  2534. if (errorSourceSlot.stageCapability != ShaderStageCapability.None)
  2535. {
  2536. var validStageName = errorSourceSlot.stageCapability.ToString().ToLower();
  2537. actionToTake = $"reconnect to a {validStageName} block or delete invalid connection";
  2538. }
  2539. else
  2540. actionToTake = "delete invalid connection";
  2541. var invalidStageName = expectedShaderStage.ToString().ToLower();
  2542. string message = $"{errorSource} is not compatible with {invalidStageName} block {initialSlot.RawDisplayName()}, {actionToTake}.";
  2543. AddValidationError(errorNode.objectId, message, ShaderCompilerMessageSeverity.Error);
  2544. }
  2545. }
  2546. }
  2547. [Serializable]
  2548. class InspectorPreviewData
  2549. {
  2550. public SerializableMesh serializedMesh = new SerializableMesh();
  2551. public bool preventRotation;
  2552. [NonSerialized]
  2553. public Quaternion rotation = Quaternion.identity;
  2554. [NonSerialized]
  2555. public float scale = 1f;
  2556. }
  2557. }