SubGraphNode.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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. , IMayRequireViewDirection
  21. , IMayRequirePosition
  22. , IMayRequireVertexColor
  23. , IMayRequireTime
  24. , IMayRequireFaceSign
  25. , IMayRequireCameraOpaqueTexture
  26. , IMayRequireDepthTexture
  27. {
  28. [Serializable]
  29. public class MinimalSubGraphNode : IHasDependencies
  30. {
  31. [SerializeField]
  32. string m_SerializedSubGraph = string.Empty;
  33. public void GetSourceAssetDependencies(List<string> paths)
  34. {
  35. var assetReference = JsonUtility.FromJson<SubGraphAssetReference>(m_SerializedSubGraph);
  36. var guid = assetReference?.subGraph?.guid;
  37. if (guid != null)
  38. {
  39. paths.Add(AssetDatabase.GUIDToAssetPath(guid));
  40. }
  41. }
  42. }
  43. [Serializable]
  44. class SubGraphHelper
  45. {
  46. public SubGraphAsset subGraph;
  47. }
  48. [Serializable]
  49. class SubGraphAssetReference
  50. {
  51. public AssetReference subGraph = default;
  52. public override string ToString()
  53. {
  54. return $"subGraph={subGraph}";
  55. }
  56. }
  57. [Serializable]
  58. class AssetReference
  59. {
  60. public long fileID = default;
  61. public string guid = default;
  62. public int type = default;
  63. public override string ToString()
  64. {
  65. return $"fileID={fileID}, guid={guid}, type={type}";
  66. }
  67. }
  68. [SerializeField]
  69. string m_SerializedSubGraph = string.Empty;
  70. [NonSerialized]
  71. SubGraphAsset m_SubGraph;
  72. [SerializeField]
  73. List<string> m_PropertyGuids = new List<string>();
  74. [SerializeField]
  75. List<int> m_PropertyIds = new List<int>();
  76. public string subGraphGuid
  77. {
  78. get
  79. {
  80. var assetReference = JsonUtility.FromJson<SubGraphAssetReference>(m_SerializedSubGraph);
  81. return assetReference?.subGraph?.guid;
  82. }
  83. }
  84. void LoadSubGraph()
  85. {
  86. if (m_SubGraph == null)
  87. {
  88. if (string.IsNullOrEmpty(m_SerializedSubGraph))
  89. {
  90. return;
  91. }
  92. var graphGuid = subGraphGuid;
  93. var assetPath = AssetDatabase.GUIDToAssetPath(graphGuid);
  94. m_SubGraph = AssetDatabase.LoadAssetAtPath<SubGraphAsset>(assetPath);
  95. if (m_SubGraph == null)
  96. {
  97. return;
  98. }
  99. name = m_SubGraph.name;
  100. concretePrecision = m_SubGraph.outputPrecision;
  101. }
  102. }
  103. public SubGraphAsset asset
  104. {
  105. get
  106. {
  107. LoadSubGraph();
  108. return m_SubGraph;
  109. }
  110. set
  111. {
  112. if (asset == value)
  113. return;
  114. var helper = new SubGraphHelper();
  115. helper.subGraph = value;
  116. m_SerializedSubGraph = EditorJsonUtility.ToJson(helper, true);
  117. m_SubGraph = null;
  118. UpdateSlots();
  119. Dirty(ModificationScope.Topological);
  120. }
  121. }
  122. public override bool hasPreview
  123. {
  124. get { return asset != null; }
  125. }
  126. public override PreviewMode previewMode
  127. {
  128. get
  129. {
  130. if (asset == null)
  131. return PreviewMode.Preview2D;
  132. return PreviewMode.Preview3D;
  133. }
  134. }
  135. public SubGraphNode()
  136. {
  137. name = "Sub Graph";
  138. }
  139. public override bool allowedInSubGraph
  140. {
  141. get { return true; }
  142. }
  143. public override bool canSetPrecision
  144. {
  145. get { return false; }
  146. }
  147. public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
  148. {
  149. if (asset == null || hasError)
  150. {
  151. var outputSlots = new List<MaterialSlot>();
  152. GetOutputSlots(outputSlots);
  153. var outputPrecision = asset != null ? asset.outputPrecision : ConcretePrecision.Float;
  154. foreach (var slot in outputSlots)
  155. {
  156. sb.AppendLine($"{slot.concreteValueType.ToShaderString(outputPrecision)} {GetVariableNameForSlot(slot.id)} = {slot.GetDefaultValue(GenerationMode.ForReals)};");
  157. }
  158. return;
  159. }
  160. var inputVariableName = $"_{GetVariableNameForNode()}";
  161. SubShaderGenerator.GenerateSurfaceInputTransferCode(sb, asset.requirements, asset.inputStructName, inputVariableName);
  162. foreach (var outSlot in asset.outputs)
  163. sb.AppendLine("{0} {1};", outSlot.concreteValueType.ToShaderString(asset.outputPrecision), GetVariableNameForSlot(outSlot.id));
  164. var arguments = new List<string>();
  165. foreach (var prop in asset.inputs)
  166. {
  167. prop.ValidateConcretePrecision(asset.graphPrecision);
  168. var inSlotId = m_PropertyIds[m_PropertyGuids.IndexOf(prop.guid.ToString())];
  169. switch(prop)
  170. {
  171. case Texture2DShaderProperty texture2DProp:
  172. arguments.Add(string.Format("TEXTURE2D_ARGS({0}, sampler{0}), {0}_TexelSize", GetSlotValue(inSlotId, generationMode, prop.concretePrecision)));
  173. break;
  174. case Texture2DArrayShaderProperty texture2DArrayProp:
  175. arguments.Add(string.Format("TEXTURE2D_ARRAY_ARGS({0}, sampler{0})", GetSlotValue(inSlotId, generationMode, prop.concretePrecision)));
  176. break;
  177. case Texture3DShaderProperty texture3DProp:
  178. arguments.Add(string.Format("TEXTURE3D_ARGS({0}, sampler{0})", GetSlotValue(inSlotId, generationMode, prop.concretePrecision)));
  179. break;
  180. case CubemapShaderProperty cubemapProp:
  181. arguments.Add(string.Format("TEXTURECUBE_ARGS({0}, sampler{0})", GetSlotValue(inSlotId, generationMode, prop.concretePrecision)));
  182. break;
  183. default:
  184. arguments.Add(string.Format("{0}", GetSlotValue(inSlotId, generationMode, prop.concretePrecision)));
  185. break;
  186. }
  187. }
  188. // pass surface inputs through
  189. arguments.Add(inputVariableName);
  190. foreach (var outSlot in asset.outputs)
  191. arguments.Add(GetVariableNameForSlot(outSlot.id));
  192. sb.AppendLine("{0}({1});", asset.functionName, arguments.Aggregate((current, next) => string.Format("{0}, {1}", current, next)));
  193. }
  194. public void OnEnable()
  195. {
  196. UpdateSlots();
  197. }
  198. public void Reload(HashSet<string> changedFileDependencies)
  199. {
  200. if (asset == null)
  201. {
  202. return;
  203. }
  204. if (changedFileDependencies.Contains(asset.assetGuid) || asset.descendents.Any(changedFileDependencies.Contains))
  205. {
  206. m_SubGraph = null;
  207. UpdateSlots();
  208. if (hasError)
  209. {
  210. return;
  211. }
  212. owner.ClearErrorsForNode(this);
  213. ValidateNode();
  214. Dirty(ModificationScope.Graph);
  215. }
  216. }
  217. public virtual void UpdateSlots()
  218. {
  219. var validNames = new List<int>();
  220. if (asset == null)
  221. {
  222. return;
  223. }
  224. var props = asset.inputs;
  225. foreach (var prop in props)
  226. {
  227. SlotValueType valueType = prop.concreteShaderValueType.ToSlotValueType();
  228. var propertyString = prop.guid.ToString();
  229. var propertyIndex = m_PropertyGuids.IndexOf(propertyString);
  230. if (propertyIndex < 0)
  231. {
  232. propertyIndex = m_PropertyGuids.Count;
  233. m_PropertyGuids.Add(propertyString);
  234. m_PropertyIds.Add(prop.guid.GetHashCode());
  235. }
  236. var id = m_PropertyIds[propertyIndex];
  237. MaterialSlot slot = MaterialSlot.CreateMaterialSlot(valueType, id, prop.displayName, prop.referenceName, SlotType.Input, Vector4.zero, ShaderStageCapability.All);
  238. // Copy defaults
  239. switch(prop.concreteShaderValueType)
  240. {
  241. case ConcreteSlotValueType.Matrix4:
  242. {
  243. var tSlot = slot as Matrix4MaterialSlot;
  244. var tProp = prop as Matrix4ShaderProperty;
  245. if (tSlot != null && tProp != null)
  246. tSlot.value = tProp.value;
  247. }
  248. break;
  249. case ConcreteSlotValueType.Matrix3:
  250. {
  251. var tSlot = slot as Matrix3MaterialSlot;
  252. var tProp = prop as Matrix3ShaderProperty;
  253. if (tSlot != null && tProp != null)
  254. tSlot.value = tProp.value;
  255. }
  256. break;
  257. case ConcreteSlotValueType.Matrix2:
  258. {
  259. var tSlot = slot as Matrix2MaterialSlot;
  260. var tProp = prop as Matrix2ShaderProperty;
  261. if (tSlot != null && tProp != null)
  262. tSlot.value = tProp.value;
  263. }
  264. break;
  265. case ConcreteSlotValueType.Texture2D:
  266. {
  267. var tSlot = slot as Texture2DInputMaterialSlot;
  268. var tProp = prop as Texture2DShaderProperty;
  269. if (tSlot != null && tProp != null)
  270. tSlot.texture = tProp.value.texture;
  271. }
  272. break;
  273. case ConcreteSlotValueType.Texture2DArray:
  274. {
  275. var tSlot = slot as Texture2DArrayInputMaterialSlot;
  276. var tProp = prop as Texture2DArrayShaderProperty;
  277. if (tSlot != null && tProp != null)
  278. tSlot.textureArray = tProp.value.textureArray;
  279. }
  280. break;
  281. case ConcreteSlotValueType.Texture3D:
  282. {
  283. var tSlot = slot as Texture3DInputMaterialSlot;
  284. var tProp = prop as Texture3DShaderProperty;
  285. if (tSlot != null && tProp != null)
  286. tSlot.texture = tProp.value.texture;
  287. }
  288. break;
  289. case ConcreteSlotValueType.Cubemap:
  290. {
  291. var tSlot = slot as CubemapInputMaterialSlot;
  292. var tProp = prop as CubemapShaderProperty;
  293. if (tSlot != null && tProp != null)
  294. tSlot.cubemap = tProp.value.cubemap;
  295. }
  296. break;
  297. case ConcreteSlotValueType.Gradient:
  298. {
  299. var tSlot = slot as GradientInputMaterialSlot;
  300. var tProp = prop as GradientShaderProperty;
  301. if (tSlot != null && tProp != null)
  302. tSlot.value = tProp.value;
  303. }
  304. break;
  305. case ConcreteSlotValueType.Vector4:
  306. {
  307. var tSlot = slot as Vector4MaterialSlot;
  308. var vector4Prop = prop as Vector4ShaderProperty;
  309. var colorProp = prop as ColorShaderProperty;
  310. if (tSlot != null && vector4Prop != null)
  311. tSlot.value = vector4Prop.value;
  312. else if (tSlot != null && colorProp != null)
  313. tSlot.value = colorProp.value;
  314. }
  315. break;
  316. case ConcreteSlotValueType.Vector3:
  317. {
  318. var tSlot = slot as Vector3MaterialSlot;
  319. var tProp = prop as Vector3ShaderProperty;
  320. if (tSlot != null && tProp != null)
  321. tSlot.value = tProp.value;
  322. }
  323. break;
  324. case ConcreteSlotValueType.Vector2:
  325. {
  326. var tSlot = slot as Vector2MaterialSlot;
  327. var tProp = prop as Vector2ShaderProperty;
  328. if (tSlot != null && tProp != null)
  329. tSlot.value = tProp.value;
  330. }
  331. break;
  332. case ConcreteSlotValueType.Vector1:
  333. {
  334. var tSlot = slot as Vector1MaterialSlot;
  335. var tProp = prop as Vector1ShaderProperty;
  336. if (tSlot != null && tProp != null)
  337. tSlot.value = tProp.value;
  338. }
  339. break;
  340. case ConcreteSlotValueType.Boolean:
  341. {
  342. var tSlot = slot as BooleanMaterialSlot;
  343. var tProp = prop as BooleanShaderProperty;
  344. if (tSlot != null && tProp != null)
  345. tSlot.value = tProp.value;
  346. }
  347. break;
  348. }
  349. AddSlot(slot);
  350. validNames.Add(id);
  351. }
  352. var outputStage = asset.effectiveShaderStage;
  353. foreach (var slot in asset.outputs)
  354. {
  355. AddSlot(MaterialSlot.CreateMaterialSlot(slot.valueType, slot.id, slot.RawDisplayName(),
  356. slot.shaderOutputName, SlotType.Output, Vector4.zero, outputStage));
  357. validNames.Add(slot.id);
  358. }
  359. RemoveSlotsNameNotMatching(validNames, true);
  360. }
  361. void ValidateShaderStage()
  362. {
  363. if (asset != null)
  364. {
  365. List<MaterialSlot> slots = new List<MaterialSlot>();
  366. GetInputSlots(slots);
  367. GetOutputSlots(slots);
  368. var outputStage = asset.effectiveShaderStage;
  369. foreach (MaterialSlot slot in slots)
  370. slot.stageCapability = outputStage;
  371. }
  372. }
  373. public override void ValidateNode()
  374. {
  375. base.ValidateNode();
  376. if (asset == null)
  377. {
  378. hasError = true;
  379. var assetGuid = subGraphGuid;
  380. var assetPath = string.IsNullOrEmpty(subGraphGuid) ? null : AssetDatabase.GUIDToAssetPath(assetGuid);
  381. if (string.IsNullOrEmpty(assetPath))
  382. {
  383. owner.AddValidationError(tempId, $"Could not find Sub Graph asset with GUID {assetGuid}.");
  384. }
  385. else
  386. {
  387. owner.AddValidationError(tempId, $"Could not load Sub Graph asset at \"{assetPath}\" with GUID {assetGuid}.");
  388. }
  389. return;
  390. }
  391. if (asset.isRecursive || owner.isSubGraph && (asset.descendents.Contains(owner.assetGuid) || asset.assetGuid == owner.assetGuid))
  392. {
  393. hasError = true;
  394. owner.AddValidationError(tempId, $"Detected a recursion in Sub Graph asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
  395. }
  396. else if (!asset.isValid)
  397. {
  398. hasError = true;
  399. owner.AddValidationError(tempId, $"Invalid Sub Graph asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
  400. }
  401. ValidateShaderStage();
  402. }
  403. public override void CollectShaderProperties(PropertyCollector visitor, GenerationMode generationMode)
  404. {
  405. base.CollectShaderProperties(visitor, generationMode);
  406. if (asset == null)
  407. return;
  408. foreach (var property in asset.nodeProperties)
  409. {
  410. visitor.AddShaderProperty(property);
  411. }
  412. }
  413. public void CollectShaderKeywords(KeywordCollector keywords, GenerationMode generationMode)
  414. {
  415. if (asset == null)
  416. return;
  417. foreach (var keyword in asset.keywords)
  418. {
  419. keywords.AddShaderKeyword(keyword as ShaderKeyword);
  420. }
  421. }
  422. public override void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
  423. {
  424. base.CollectPreviewMaterialProperties(properties);
  425. if (asset == null)
  426. return;
  427. foreach (var property in asset.nodeProperties)
  428. {
  429. properties.Add(property.GetPreviewMaterialProperty());
  430. }
  431. }
  432. public virtual void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
  433. {
  434. if (asset == null || hasError)
  435. return;
  436. foreach (var function in asset.functions)
  437. {
  438. registry.ProvideFunction(function.key, s =>
  439. {
  440. s.AppendLines(function.value);
  441. });
  442. }
  443. }
  444. public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
  445. {
  446. if (asset == null)
  447. return NeededCoordinateSpace.None;
  448. return asset.requirements.requiresNormal;
  449. }
  450. public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
  451. {
  452. if (asset == null)
  453. return false;
  454. return asset.requirements.requiresMeshUVs.Contains(channel);
  455. }
  456. public bool RequiresScreenPosition(ShaderStageCapability stageCapability)
  457. {
  458. if (asset == null)
  459. return false;
  460. return asset.requirements.requiresScreenPosition;
  461. }
  462. public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability)
  463. {
  464. if (asset == null)
  465. return NeededCoordinateSpace.None;
  466. return asset.requirements.requiresViewDir;
  467. }
  468. public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
  469. {
  470. if (asset == null)
  471. return NeededCoordinateSpace.None;
  472. return asset.requirements.requiresPosition;
  473. }
  474. public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
  475. {
  476. if (asset == null)
  477. return NeededCoordinateSpace.None;
  478. return asset.requirements.requiresTangent;
  479. }
  480. public bool RequiresTime()
  481. {
  482. if (asset == null)
  483. return false;
  484. return asset.requirements.requiresTime;
  485. }
  486. public bool RequiresFaceSign(ShaderStageCapability stageCapability)
  487. {
  488. if (asset == null)
  489. return false;
  490. return asset.requirements.requiresFaceSign;
  491. }
  492. public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
  493. {
  494. if (asset == null)
  495. return NeededCoordinateSpace.None;
  496. return asset.requirements.requiresBitangent;
  497. }
  498. public bool RequiresVertexColor(ShaderStageCapability stageCapability)
  499. {
  500. if (asset == null)
  501. return false;
  502. return asset.requirements.requiresVertexColor;
  503. }
  504. public bool RequiresCameraOpaqueTexture(ShaderStageCapability stageCapability)
  505. {
  506. if (asset == null)
  507. return false;
  508. return asset.requirements.requiresCameraOpaqueTexture;
  509. }
  510. public bool RequiresDepthTexture(ShaderStageCapability stageCapability)
  511. {
  512. if (asset == null)
  513. return false;
  514. return asset.requirements.requiresDepthTexture;
  515. }
  516. }
  517. }