No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SubGraphNode.cs 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEngine;
  5. using UnityEditor.Graphing;
  6. using UnityEditor.ShaderGraph.Internal;
  7. namespace UnityEditor.ShaderGraph
  8. {
  9. [HasDependencies(typeof(MinimalSubGraphNode))]
  10. [Title("Utility", "Sub-graph")]
  11. class SubGraphNode : AbstractMaterialNode
  12. , IGeneratesBodyCode
  13. , IOnAssetEnabled
  14. , IGeneratesFunction
  15. , IMayRequireNormal
  16. , IMayRequireTangent
  17. , IMayRequireBitangent
  18. , IMayRequireMeshUV
  19. , IMayRequireScreenPosition
  20. , IMayRequireNDCPosition
  21. , IMayRequirePixelPosition
  22. , IMayRequireViewDirection
  23. , IMayRequirePosition
  24. , IMayRequirePositionPredisplacement
  25. , IMayRequireVertexColor
  26. , IMayRequireTime
  27. , IMayRequireFaceSign
  28. , IMayRequireCameraOpaqueTexture
  29. , IMayRequireDepthTexture
  30. , IMayRequireVertexSkinning
  31. , IMayRequireVertexID
  32. , IMayRequireInstanceID
  33. , IDisposable
  34. {
  35. [Serializable]
  36. public class MinimalSubGraphNode : IHasDependencies
  37. {
  38. [SerializeField]
  39. string m_SerializedSubGraph = string.Empty;
  40. public void GetSourceAssetDependencies(AssetCollection assetCollection)
  41. {
  42. var assetReference = JsonUtility.FromJson<SubGraphAssetReference>(m_SerializedSubGraph);
  43. string guidString = assetReference?.subGraph?.guid;
  44. if (!string.IsNullOrEmpty(guidString) && GUID.TryParse(guidString, out GUID guid))
  45. {
  46. // subgraphs are read as artifacts
  47. // they also should be pulled into .unitypackages
  48. assetCollection.AddAssetDependency(
  49. guid,
  50. AssetCollection.Flags.ArtifactDependency |
  51. AssetCollection.Flags.IsSubGraph |
  52. AssetCollection.Flags.IncludeInExportPackage);
  53. }
  54. }
  55. }
  56. [Serializable]
  57. class SubGraphHelper
  58. {
  59. public SubGraphAsset subGraph;
  60. }
  61. [Serializable]
  62. class SubGraphAssetReference
  63. {
  64. public AssetReference subGraph = default;
  65. public override string ToString()
  66. {
  67. return $"subGraph={subGraph}";
  68. }
  69. }
  70. [Serializable]
  71. class AssetReference
  72. {
  73. public long fileID = default;
  74. public string guid = default;
  75. public int type = default;
  76. public override string ToString()
  77. {
  78. return $"fileID={fileID}, guid={guid}, type={type}";
  79. }
  80. }
  81. [SerializeField]
  82. string m_SerializedSubGraph = string.Empty;
  83. [NonSerialized]
  84. SubGraphAsset m_SubGraph; // This should not be accessed directly by most code -- use the asset property instead, and check for NULL! :)
  85. [SerializeField]
  86. List<string> m_PropertyGuids = new List<string>();
  87. [SerializeField]
  88. List<int> m_PropertyIds = new List<int>();
  89. [SerializeField]
  90. List<string> m_Dropdowns = new List<string>();
  91. [SerializeField]
  92. List<string> m_DropdownSelectedEntries = new List<string>();
  93. public string subGraphGuid
  94. {
  95. get
  96. {
  97. var assetReference = JsonUtility.FromJson<SubGraphAssetReference>(m_SerializedSubGraph);
  98. return assetReference?.subGraph?.guid;
  99. }
  100. }
  101. void LoadSubGraph()
  102. {
  103. if (m_SubGraph == null)
  104. {
  105. if (string.IsNullOrEmpty(m_SerializedSubGraph))
  106. {
  107. return;
  108. }
  109. var graphGuid = subGraphGuid;
  110. var assetPath = AssetDatabase.GUIDToAssetPath(graphGuid);
  111. if (string.IsNullOrEmpty(assetPath))
  112. {
  113. // this happens if the editor has never seen the GUID
  114. // error will be printed by validation code in this case
  115. return;
  116. }
  117. m_SubGraph = AssetDatabase.LoadAssetAtPath<SubGraphAsset>(assetPath);
  118. if (m_SubGraph == null)
  119. {
  120. // this happens if the editor has seen the GUID, but the file has been deleted since then
  121. // error will be printed by validation code in this case
  122. return;
  123. }
  124. m_SubGraph.LoadGraphData();
  125. m_SubGraph.LoadDependencyData();
  126. name = m_SubGraph.name;
  127. }
  128. }
  129. public SubGraphAsset asset
  130. {
  131. get
  132. {
  133. LoadSubGraph();
  134. return m_SubGraph;
  135. }
  136. set
  137. {
  138. if (asset == value)
  139. return;
  140. var helper = new SubGraphHelper();
  141. helper.subGraph = value;
  142. m_SerializedSubGraph = EditorJsonUtility.ToJson(helper, true);
  143. m_SubGraph = null;
  144. UpdateSlots();
  145. Dirty(ModificationScope.Topological);
  146. }
  147. }
  148. public override bool hasPreview
  149. {
  150. get { return true; }
  151. }
  152. public override PreviewMode previewMode
  153. {
  154. get
  155. {
  156. PreviewMode mode = m_PreviewMode;
  157. if ((mode == PreviewMode.Inherit) && (asset != null))
  158. mode = asset.previewMode;
  159. return mode;
  160. }
  161. }
  162. public SubGraphNode()
  163. {
  164. name = "Sub Graph";
  165. }
  166. public override bool allowedInSubGraph
  167. {
  168. get { return true; }
  169. }
  170. public override bool canSetPrecision
  171. {
  172. get { return asset?.subGraphGraphPrecision == GraphPrecision.Graph; }
  173. }
  174. public override void GetInputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots)
  175. {
  176. var allSlots = new List<T>();
  177. GetInputSlots<T>(allSlots);
  178. var info = asset?.GetOutputDependencies(startingSlot.RawDisplayName());
  179. if (info != null)
  180. {
  181. foreach (var slot in allSlots)
  182. {
  183. if (info.ContainsSlot(slot))
  184. foundSlots.Add(slot);
  185. }
  186. }
  187. }
  188. public override void GetOutputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots)
  189. {
  190. var allSlots = new List<T>();
  191. GetOutputSlots<T>(allSlots);
  192. var info = asset?.GetInputDependencies(startingSlot.RawDisplayName());
  193. if (info != null)
  194. {
  195. foreach (var slot in allSlots)
  196. {
  197. if (info.ContainsSlot(slot))
  198. foundSlots.Add(slot);
  199. }
  200. }
  201. }
  202. ShaderStageCapability GetSlotCapability(MaterialSlot slot)
  203. {
  204. SlotDependencyInfo dependencyInfo;
  205. if (slot.isInputSlot)
  206. dependencyInfo = asset?.GetInputDependencies(slot.RawDisplayName());
  207. else
  208. dependencyInfo = asset?.GetOutputDependencies(slot.RawDisplayName());
  209. if (dependencyInfo != null)
  210. return dependencyInfo.capabilities;
  211. return ShaderStageCapability.All;
  212. }
  213. public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
  214. {
  215. var outputGraphPrecision = asset?.outputGraphPrecision ?? GraphPrecision.Single;
  216. var outputPrecision = outputGraphPrecision.ToConcrete(concretePrecision);
  217. if (asset == null || hasError)
  218. {
  219. var outputSlots = new List<MaterialSlot>();
  220. GetOutputSlots(outputSlots);
  221. foreach (var slot in outputSlots)
  222. {
  223. sb.AppendLine($"{slot.concreteValueType.ToShaderString(outputPrecision)} {GetVariableNameForSlot(slot.id)} = {slot.GetDefaultValue(GenerationMode.ForReals)};");
  224. }
  225. return;
  226. }
  227. var inputVariableName = $"_{GetVariableNameForNode()}";
  228. GenerationUtils.GenerateSurfaceInputTransferCode(sb, asset.requirements, asset.inputStructName, inputVariableName);
  229. // declare output variables
  230. foreach (var outSlot in asset.outputs)
  231. sb.AppendLine("{0} {1};", outSlot.concreteValueType.ToShaderString(outputPrecision), GetVariableNameForSlot(outSlot.id));
  232. var arguments = new List<string>();
  233. foreach (AbstractShaderProperty prop in asset.inputs)
  234. {
  235. // setup the property concrete precision (fallback to node concrete precision when it's switchable)
  236. prop.SetupConcretePrecision(this.concretePrecision);
  237. var inSlotId = m_PropertyIds[m_PropertyGuids.IndexOf(prop.guid.ToString())];
  238. arguments.Add(GetSlotValue(inSlotId, generationMode, prop.concretePrecision));
  239. if (prop.isConnectionTestable)
  240. arguments.Add(IsSlotConnected(inSlotId) ? "true" : "false");
  241. }
  242. var dropdowns = asset.dropdowns;
  243. foreach (var dropdown in dropdowns)
  244. {
  245. var name = GetDropdownEntryName(dropdown.referenceName);
  246. if (dropdown.ContainsEntry(name))
  247. arguments.Add(dropdown.IndexOfName(name).ToString());
  248. else
  249. arguments.Add(dropdown.value.ToString());
  250. }
  251. // pass surface inputs through
  252. arguments.Add(inputVariableName);
  253. foreach (var outSlot in asset.outputs)
  254. arguments.Add(GetVariableNameForSlot(outSlot.id));
  255. foreach (var feedbackSlot in asset.vtFeedbackVariables)
  256. {
  257. string feedbackVar = GetVariableNameForNode() + "_" + feedbackSlot;
  258. sb.AppendLine("{0} {1};", ConcreteSlotValueType.Vector4.ToShaderString(ConcretePrecision.Single), feedbackVar);
  259. arguments.Add(feedbackVar);
  260. }
  261. sb.TryAppendIndentation();
  262. sb.Append(asset.functionName);
  263. sb.Append("(");
  264. bool firstArg = true;
  265. foreach (var arg in arguments)
  266. {
  267. if (!firstArg)
  268. sb.Append(", ");
  269. firstArg = false;
  270. sb.Append(arg);
  271. }
  272. sb.Append(");");
  273. sb.AppendNewLine();
  274. }
  275. public void OnEnable()
  276. {
  277. UpdateSlots();
  278. }
  279. public bool Reload(HashSet<string> changedFileDependencyGUIDs)
  280. {
  281. if (!changedFileDependencyGUIDs.Contains(subGraphGuid))
  282. {
  283. return false;
  284. }
  285. if (asset == null)
  286. {
  287. // asset missing or deleted
  288. return true;
  289. }
  290. if (changedFileDependencyGUIDs.Contains(asset.assetGuid) || asset.descendents.Any(changedFileDependencyGUIDs.Contains))
  291. {
  292. m_SubGraph = null;
  293. UpdateSlots();
  294. if (hasError)
  295. {
  296. return true;
  297. }
  298. owner.ClearErrorsForNode(this);
  299. ValidateNode();
  300. Dirty(ModificationScope.Graph);
  301. }
  302. return true;
  303. }
  304. public override void UpdatePrecision(List<MaterialSlot> inputSlots)
  305. {
  306. if (asset != null)
  307. {
  308. if (asset.subGraphGraphPrecision == GraphPrecision.Graph)
  309. {
  310. // subgraph is defined to be switchable, so use the default behavior to determine precision
  311. base.UpdatePrecision(inputSlots);
  312. }
  313. else
  314. {
  315. // subgraph sets a specific precision, force that
  316. graphPrecision = asset.subGraphGraphPrecision;
  317. concretePrecision = graphPrecision.ToConcrete(owner.graphDefaultConcretePrecision);
  318. }
  319. }
  320. else
  321. {
  322. // no subgraph asset; use default behavior
  323. base.UpdatePrecision(inputSlots);
  324. }
  325. }
  326. public virtual void UpdateSlots()
  327. {
  328. var validNames = new List<int>();
  329. if (asset == null)
  330. {
  331. return;
  332. }
  333. var props = asset.inputs;
  334. var toFix = new HashSet<(SlotReference from, SlotReference to)>();
  335. foreach (var prop in props)
  336. {
  337. SlotValueType valueType = prop.concreteShaderValueType.ToSlotValueType();
  338. var propertyString = prop.guid.ToString();
  339. var propertyIndex = m_PropertyGuids.IndexOf(propertyString);
  340. if (propertyIndex < 0)
  341. {
  342. propertyIndex = m_PropertyGuids.Count;
  343. m_PropertyGuids.Add(propertyString);
  344. m_PropertyIds.Add(prop.guid.GetHashCode());
  345. }
  346. var id = m_PropertyIds[propertyIndex];
  347. //for whatever reason, it seems like shader property ids changed between 21.2a17 and 21.2b1
  348. //tried tracking it down, couldnt find any reason for it, so we gotta fix it in post (after we deserialize)
  349. List<MaterialSlot> inputs = new List<MaterialSlot>();
  350. MaterialSlot found = null;
  351. GetInputSlots(inputs);
  352. foreach (var input in inputs)
  353. {
  354. if (input.shaderOutputName == prop.referenceName && input.id != id)
  355. {
  356. found = input;
  357. break;
  358. }
  359. }
  360. MaterialSlot slot = MaterialSlot.CreateMaterialSlot(valueType, id, prop.displayName, prop.referenceName, SlotType.Input, Vector4.zero, ShaderStageCapability.All);
  361. // Copy defaults
  362. switch (prop.concreteShaderValueType)
  363. {
  364. case ConcreteSlotValueType.SamplerState:
  365. {
  366. var tSlot = slot as SamplerStateMaterialSlot;
  367. var tProp = prop as SamplerStateShaderProperty;
  368. if (tSlot != null && tProp != null)
  369. tSlot.defaultSamplerState = tProp.value;
  370. }
  371. break;
  372. case ConcreteSlotValueType.Matrix4:
  373. {
  374. var tSlot = slot as Matrix4MaterialSlot;
  375. var tProp = prop as Matrix4ShaderProperty;
  376. if (tSlot != null && tProp != null)
  377. tSlot.value = tProp.value;
  378. }
  379. break;
  380. case ConcreteSlotValueType.Matrix3:
  381. {
  382. var tSlot = slot as Matrix3MaterialSlot;
  383. var tProp = prop as Matrix3ShaderProperty;
  384. if (tSlot != null && tProp != null)
  385. tSlot.value = tProp.value;
  386. }
  387. break;
  388. case ConcreteSlotValueType.Matrix2:
  389. {
  390. var tSlot = slot as Matrix2MaterialSlot;
  391. var tProp = prop as Matrix2ShaderProperty;
  392. if (tSlot != null && tProp != null)
  393. tSlot.value = tProp.value;
  394. }
  395. break;
  396. case ConcreteSlotValueType.Texture2D:
  397. {
  398. var tSlot = slot as Texture2DInputMaterialSlot;
  399. var tProp = prop as Texture2DShaderProperty;
  400. if (tSlot != null && tProp != null)
  401. tSlot.texture = tProp.value.texture;
  402. }
  403. break;
  404. case ConcreteSlotValueType.Texture2DArray:
  405. {
  406. var tSlot = slot as Texture2DArrayInputMaterialSlot;
  407. var tProp = prop as Texture2DArrayShaderProperty;
  408. if (tSlot != null && tProp != null)
  409. tSlot.textureArray = tProp.value.textureArray;
  410. }
  411. break;
  412. case ConcreteSlotValueType.Texture3D:
  413. {
  414. var tSlot = slot as Texture3DInputMaterialSlot;
  415. var tProp = prop as Texture3DShaderProperty;
  416. if (tSlot != null && tProp != null)
  417. tSlot.texture = tProp.value.texture;
  418. }
  419. break;
  420. case ConcreteSlotValueType.Cubemap:
  421. {
  422. var tSlot = slot as CubemapInputMaterialSlot;
  423. var tProp = prop as CubemapShaderProperty;
  424. if (tSlot != null && tProp != null)
  425. tSlot.cubemap = tProp.value.cubemap;
  426. }
  427. break;
  428. case ConcreteSlotValueType.Gradient:
  429. {
  430. var tSlot = slot as GradientInputMaterialSlot;
  431. var tProp = prop as GradientShaderProperty;
  432. if (tSlot != null && tProp != null)
  433. tSlot.value = tProp.value;
  434. }
  435. break;
  436. case ConcreteSlotValueType.Vector4:
  437. {
  438. var tSlot = slot as Vector4MaterialSlot;
  439. var vector4Prop = prop as Vector4ShaderProperty;
  440. var colorProp = prop as ColorShaderProperty;
  441. if (tSlot != null && vector4Prop != null)
  442. tSlot.value = vector4Prop.value;
  443. else if (tSlot != null && colorProp != null)
  444. tSlot.value = colorProp.value;
  445. }
  446. break;
  447. case ConcreteSlotValueType.Vector3:
  448. {
  449. var tSlot = slot as Vector3MaterialSlot;
  450. var tProp = prop as Vector3ShaderProperty;
  451. if (tSlot != null && tProp != null)
  452. tSlot.value = tProp.value;
  453. }
  454. break;
  455. case ConcreteSlotValueType.Vector2:
  456. {
  457. var tSlot = slot as Vector2MaterialSlot;
  458. var tProp = prop as Vector2ShaderProperty;
  459. if (tSlot != null && tProp != null)
  460. tSlot.value = tProp.value;
  461. }
  462. break;
  463. case ConcreteSlotValueType.Vector1:
  464. {
  465. var tSlot = slot as Vector1MaterialSlot;
  466. var tProp = prop as Vector1ShaderProperty;
  467. if (tSlot != null && tProp != null)
  468. tSlot.value = tProp.value;
  469. }
  470. break;
  471. case ConcreteSlotValueType.Boolean:
  472. {
  473. var tSlot = slot as BooleanMaterialSlot;
  474. var tProp = prop as BooleanShaderProperty;
  475. if (tSlot != null && tProp != null)
  476. tSlot.value = tProp.value;
  477. }
  478. break;
  479. }
  480. AddSlot(slot);
  481. validNames.Add(id);
  482. if (found != null)
  483. {
  484. List<IEdge> edges = new List<IEdge>();
  485. owner.GetEdges(found.slotReference, edges);
  486. foreach (var edge in edges)
  487. {
  488. toFix.Add((edge.outputSlot, slot.slotReference));
  489. }
  490. }
  491. }
  492. foreach (var slot in asset.outputs)
  493. {
  494. var outputStage = GetSlotCapability(slot);
  495. var newSlot = MaterialSlot.CreateMaterialSlot(slot.valueType, slot.id, slot.RawDisplayName(),
  496. slot.shaderOutputName, SlotType.Output, Vector4.zero, outputStage, slot.hidden);
  497. AddSlot(newSlot);
  498. validNames.Add(slot.id);
  499. }
  500. RemoveSlotsNameNotMatching(validNames, true);
  501. // sort slot order to match subgraph property order
  502. SetSlotOrder(validNames);
  503. foreach (var (from, to) in toFix)
  504. {
  505. //for whatever reason, in this particular error fix, GraphView will incorrectly either add two edgeViews or none
  506. //but it does work correctly if we dont notify GraphView of this added edge. Gross.
  507. owner.UnnotifyAddedEdge(owner.Connect(from, to));
  508. }
  509. }
  510. void ValidateShaderStage()
  511. {
  512. if (asset != null)
  513. {
  514. List<MaterialSlot> slots = new List<MaterialSlot>();
  515. GetInputSlots(slots);
  516. GetOutputSlots(slots);
  517. foreach (MaterialSlot slot in slots)
  518. slot.stageCapability = GetSlotCapability(slot);
  519. }
  520. }
  521. public override void ValidateNode()
  522. {
  523. base.ValidateNode();
  524. if (asset == null)
  525. {
  526. hasError = true;
  527. var assetGuid = subGraphGuid;
  528. var assetPath = string.IsNullOrEmpty(subGraphGuid) ? null : AssetDatabase.GUIDToAssetPath(assetGuid);
  529. if (string.IsNullOrEmpty(assetPath))
  530. {
  531. owner.AddValidationError(objectId, $"Could not find Sub Graph asset with GUID {assetGuid}.");
  532. }
  533. else
  534. {
  535. owner.AddValidationError(objectId, $"Could not load Sub Graph asset at \"{assetPath}\" with GUID {assetGuid}.");
  536. }
  537. return;
  538. }
  539. if (owner.isSubGraph && (asset.descendents.Contains(owner.assetGuid) || asset.assetGuid == owner.assetGuid))
  540. {
  541. hasError = true;
  542. owner.AddValidationError(objectId, $"Detected a recursion in Sub Graph asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
  543. }
  544. else if (!asset.isValid)
  545. {
  546. hasError = true;
  547. owner.AddValidationError(objectId, $"Sub Graph has errors, asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
  548. }
  549. else if (!owner.isSubGraph && owner.activeTargets.Any(x => asset.unsupportedTargets.Contains(x)))
  550. {
  551. SetOverrideActiveState(ActiveState.ExplicitInactive);
  552. owner.AddValidationError(objectId, $"Sub Graph contains nodes that are unsupported by the current active targets, asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
  553. }
  554. // detect disconnected VT properties, and VT layer count mismatches
  555. foreach (var paramProp in asset.inputs)
  556. {
  557. if (paramProp is VirtualTextureShaderProperty vtProp)
  558. {
  559. int paramLayerCount = vtProp.value.layers.Count;
  560. var argSlotId = m_PropertyIds[m_PropertyGuids.IndexOf(paramProp.guid.ToString())]; // yikes
  561. if (!IsSlotConnected(argSlotId))
  562. {
  563. owner.AddValidationError(objectId, $"A VirtualTexture property must be connected to the input slot \"{paramProp.displayName}\"");
  564. }
  565. else
  566. {
  567. var argProp = GetSlotProperty(argSlotId) as VirtualTextureShaderProperty;
  568. if (argProp != null)
  569. {
  570. int argLayerCount = argProp.value.layers.Count;
  571. if (argLayerCount != paramLayerCount)
  572. owner.AddValidationError(objectId, $"Input \"{paramProp.displayName}\" has different number of layers from the connected property \"{argProp.displayName}\"");
  573. }
  574. else
  575. {
  576. owner.AddValidationError(objectId, $"Input \"{paramProp.displayName}\" is not connected to a valid VirtualTexture property");
  577. }
  578. }
  579. break;
  580. }
  581. }
  582. ValidateShaderStage();
  583. }
  584. public override void CollectShaderProperties(PropertyCollector visitor, GenerationMode generationMode)
  585. {
  586. base.CollectShaderProperties(visitor, generationMode);
  587. if (asset == null)
  588. return;
  589. foreach (var property in asset.nodeProperties)
  590. {
  591. visitor.AddShaderProperty(property);
  592. }
  593. }
  594. public AbstractShaderProperty GetShaderProperty(int id)
  595. {
  596. var index = m_PropertyIds.IndexOf(id);
  597. if (index >= 0)
  598. {
  599. var guid = m_PropertyGuids[index];
  600. return asset?.inputs.Where(x => x.guid.ToString().Equals(guid)).FirstOrDefault();
  601. }
  602. return null;
  603. }
  604. public void CollectShaderKeywords(KeywordCollector keywords, GenerationMode generationMode)
  605. {
  606. if (asset == null)
  607. return;
  608. foreach (var keyword in asset.keywords)
  609. {
  610. keywords.AddShaderKeyword(keyword as ShaderKeyword);
  611. }
  612. }
  613. public override void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
  614. {
  615. base.CollectPreviewMaterialProperties(properties);
  616. if (asset == null)
  617. return;
  618. foreach (var property in asset.nodeProperties)
  619. {
  620. properties.Add(property.GetPreviewMaterialProperty());
  621. }
  622. }
  623. public virtual void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
  624. {
  625. if (asset == null || hasError)
  626. return;
  627. registry.RequiresIncludes(asset.includes);
  628. var graphData = registry.builder.currentNode.owner;
  629. var graphDefaultConcretePrecision = graphData.graphDefaultConcretePrecision;
  630. foreach (var function in asset.functions)
  631. {
  632. var name = function.key;
  633. var source = function.value;
  634. var graphPrecisionFlags = function.graphPrecisionFlags;
  635. // the subgraph may use multiple precision variants of this function internally
  636. // here we iterate through all the requested precisions and forward those requests out to the graph
  637. for (int requestedGraphPrecision = 0; requestedGraphPrecision <= (int)GraphPrecision.Half; requestedGraphPrecision++)
  638. {
  639. // only provide requested precisions
  640. if ((graphPrecisionFlags & (1 << requestedGraphPrecision)) != 0)
  641. {
  642. // when a function coming from a subgraph asset has a graph precision of "Graph",
  643. // that means it is up to the subgraph NODE to decide (i.e. us!)
  644. GraphPrecision actualGraphPrecision = (GraphPrecision)requestedGraphPrecision;
  645. // subgraph asset setting falls back to this node setting (when switchable)
  646. actualGraphPrecision = actualGraphPrecision.GraphFallback(this.graphPrecision);
  647. // which falls back to the graph default concrete precision
  648. ConcretePrecision actualConcretePrecision = actualGraphPrecision.ToConcrete(graphDefaultConcretePrecision);
  649. // forward the function into the current graph
  650. registry.ProvideFunction(name, actualGraphPrecision, actualConcretePrecision, sb => sb.AppendLines(source));
  651. }
  652. }
  653. }
  654. }
  655. public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
  656. {
  657. if (asset == null)
  658. return NeededCoordinateSpace.None;
  659. return asset.requirements.requiresNormal;
  660. }
  661. public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
  662. {
  663. if (asset == null)
  664. return false;
  665. return asset.requirements.requiresMeshUVs.Contains(channel);
  666. }
  667. public bool RequiresScreenPosition(ShaderStageCapability stageCapability)
  668. {
  669. if (asset == null)
  670. return false;
  671. return asset.requirements.requiresScreenPosition;
  672. }
  673. public bool RequiresNDCPosition(ShaderStageCapability stageCapability)
  674. {
  675. if (asset == null)
  676. return false;
  677. return asset.requirements.requiresNDCPosition;
  678. }
  679. public bool RequiresPixelPosition(ShaderStageCapability stageCapability)
  680. {
  681. if (asset == null)
  682. return false;
  683. return asset.requirements.requiresPixelPosition;
  684. }
  685. public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability)
  686. {
  687. if (asset == null)
  688. return NeededCoordinateSpace.None;
  689. return asset.requirements.requiresViewDir;
  690. }
  691. public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
  692. {
  693. if (asset == null)
  694. return NeededCoordinateSpace.None;
  695. return asset.requirements.requiresPosition;
  696. }
  697. public NeededCoordinateSpace RequiresPositionPredisplacement(ShaderStageCapability stageCapability = ShaderStageCapability.All)
  698. {
  699. if (asset == null)
  700. return NeededCoordinateSpace.None;
  701. return asset.requirements.requiresPositionPredisplacement;
  702. }
  703. public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
  704. {
  705. if (asset == null)
  706. return NeededCoordinateSpace.None;
  707. return asset.requirements.requiresTangent;
  708. }
  709. public bool RequiresTime()
  710. {
  711. if (asset == null)
  712. return false;
  713. return asset.requirements.requiresTime;
  714. }
  715. public bool RequiresFaceSign(ShaderStageCapability stageCapability)
  716. {
  717. if (asset == null)
  718. return false;
  719. return asset.requirements.requiresFaceSign;
  720. }
  721. public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
  722. {
  723. if (asset == null)
  724. return NeededCoordinateSpace.None;
  725. return asset.requirements.requiresBitangent;
  726. }
  727. public bool RequiresVertexColor(ShaderStageCapability stageCapability)
  728. {
  729. if (asset == null)
  730. return false;
  731. return asset.requirements.requiresVertexColor;
  732. }
  733. public bool RequiresCameraOpaqueTexture(ShaderStageCapability stageCapability)
  734. {
  735. if (asset == null)
  736. return false;
  737. return asset.requirements.requiresCameraOpaqueTexture;
  738. }
  739. public bool RequiresDepthTexture(ShaderStageCapability stageCapability)
  740. {
  741. if (asset == null)
  742. return false;
  743. return asset.requirements.requiresDepthTexture;
  744. }
  745. public bool RequiresVertexSkinning(ShaderStageCapability stageCapability)
  746. {
  747. if (asset == null)
  748. return false;
  749. return asset.requirements.requiresVertexSkinning;
  750. }
  751. public bool RequiresVertexID(ShaderStageCapability stageCapability)
  752. {
  753. if (asset == null)
  754. return false;
  755. return asset.requirements.requiresVertexID;
  756. }
  757. public bool RequiresInstanceID(ShaderStageCapability stageCapability)
  758. {
  759. if (asset == null)
  760. return false;
  761. return asset.requirements.requiresInstanceID;
  762. }
  763. public string GetDropdownEntryName(string referenceName)
  764. {
  765. var index = m_Dropdowns.IndexOf(referenceName);
  766. return index >= 0 ? m_DropdownSelectedEntries[index] : string.Empty;
  767. }
  768. public void SetDropdownEntryName(string referenceName, string value)
  769. {
  770. var index = m_Dropdowns.IndexOf(referenceName);
  771. if (index >= 0)
  772. {
  773. m_DropdownSelectedEntries[index] = value;
  774. }
  775. else
  776. {
  777. m_Dropdowns.Add(referenceName);
  778. m_DropdownSelectedEntries.Add(value);
  779. }
  780. }
  781. public override void Dispose()
  782. {
  783. base.Dispose();
  784. m_SubGraph = null;
  785. }
  786. }
  787. }