public void LoadGraphData() { m_SubGraphData = new SubGraphData(); if (!String.IsNullOrEmpty(m_SerializedSubGraphData.JSONnodeData)) { MultiJson.Deserialize(m_SubGraphData, m_SerializedSubGraphData.JSONnodeData); } }
private static StringBuilder TryBuildFromShaderGraph(VFXShaderGraphParticleOutput context, VFXContextCompiledData contextData) { var stringBuilder = new StringBuilder(); // Reconstruct the ShaderGraph. var path = AssetDatabase.GetAssetPath(context.GetOrRefreshShaderGraphObject()); List <PropertyCollector.TextureInfo> configuredTextures; AssetCollection assetCollection = new AssetCollection(); MinimalGraphData.GatherMinimalDependenciesFromFile(path, assetCollection); var textGraph = File.ReadAllText(path, Encoding.UTF8); var graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); // Check the validity of the shader graph (unsupported keywords or shader property usage). if (VFXLibrary.currentSRPBinder == null || !VFXLibrary.currentSRPBinder.IsGraphDataValid(graph)) { return(null); } var target = graph.activeTargets.Where(o => { if (o.SupportsVFX()) { //We are assuming the target has been implemented in the same package than srp binder. var srpBinderAssembly = VFXLibrary.currentSRPBinder.GetType().Assembly; var targetAssembly = o.GetType().Assembly; if (srpBinderAssembly == targetAssembly) { return(true); } } return(false); }).FirstOrDefault(); if (target == null || !target.TryConfigureContextData(context, contextData)) { return(null); } // Use ShaderGraph to generate the VFX shader. var text = ShaderGraphImporter.GetShaderText(path, out configuredTextures, assetCollection, graph, GenerationMode.VFX, new[] { target }); // Append the shader + strip the name header (VFX stamps one in later on). stringBuilder.Append(text); stringBuilder.Remove(0, text.IndexOf("{", StringComparison.Ordinal)); return(stringBuilder); }
GraphData DeserializeGraph() { var json = m_SerializedGraph.JSONnodeData; var deserializedGraph = new GraphData { isSubGraph = m_IsSubGraph, assetGuid = m_AssetGuid }; MultiJson.Deserialize(deserializedGraph, json); m_DeserializedVersion = m_SerializedVersion; m_SerializedGraph = default; return(deserializedGraph); }
public void Initialize(string graphPath) { hideFlags = HideFlags.HideAndDontSave; var textGraph = File.ReadAllText(graphPath, Encoding.UTF8); graph = new GraphData(); graph.messageManager = new MessageManager(); graph.assetGuid = AssetDatabase.AssetPathToGUID(graphPath); MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); }
internal static string GetShaderText(string path, out List <PropertyCollector.TextureInfo> configuredTextures) { var textGraph = File.ReadAllText(path, Encoding.UTF8); GraphData graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); return(GetShaderText(path, out configuredTextures, null, graph)); }
internal static CopyPasteGraph FromJson(string copyBuffer, GraphData targetGraph) { try { var graph = new CopyPasteGraph(); MultiJson.Deserialize(graph, copyBuffer, targetGraph, true); return(graph); } catch { // ignored. just means copy buffer was not a graph :( return(null); } }
public override void OnImportAsset(AssetImportContext ctx) { var graphAsset = ScriptableObject.CreateInstance <SubGraphAsset>(); var subGraphPath = ctx.assetPath; var subGraphGuid = AssetDatabase.AssetPathToGUID(subGraphPath); graphAsset.assetGuid = subGraphGuid; var textGraph = File.ReadAllText(subGraphPath, Encoding.UTF8); var messageManager = new MessageManager(); var graphData = new GraphData { isSubGraph = true, assetGuid = subGraphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData, textGraph); try { ProcessSubGraph(graphAsset, graphData); } catch (Exception e) { graphAsset.isValid = false; Debug.LogException(e, graphAsset); } finally { if (messageManager.AnyError()) { graphAsset.isValid = false; foreach (var pair in messageManager.GetNodeMessages()) { var node = graphData.GetNodeFromId(pair.Key); foreach (var message in pair.Value) { MessageManager.Log(node, subGraphPath, message, graphAsset); } } } messageManager.ClearAll(); } Texture2D texture = Resources.Load <Texture2D>("Icons/sg_subgraph_icon@64"); ctx.AddObjectToAsset("MainAsset", graphAsset, texture); ctx.SetMainObject(graphAsset); }
public void CopyOverAndImport(string assetPath) { string fileName = Path.GetFileName(assetPath); string fileContents = File.ReadAllText(assetPath); string localFilePath = "Assets/Testing/ImportTests/" + fileName; File.WriteAllText(Application.dataPath + "/Testing/ImportTests/" + fileName, fileContents); AssetDatabase.ImportAsset(localFilePath); var graphGuid = AssetDatabase.AssetPathToGUID(localFilePath); var messageManager = new MessageManager(); GraphData graphData = new GraphData() { assetGuid = graphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData, fileContents); graphData.OnEnable(); graphData.ValidateGraph(); }
public override void OnImportAsset(AssetImportContext ctx) { var oldShader = AssetDatabase.LoadAssetAtPath <Shader>(ctx.assetPath); if (oldShader != null) { ShaderUtil.ClearShaderMessages(oldShader); } List <PropertyCollector.TextureInfo> configuredTextures; string path = ctx.assetPath; AssetCollection assetCollection = new AssetCollection(); MinimalGraphData.GatherMinimalDependenciesFromFile(assetPath, assetCollection); var textGraph = File.ReadAllText(path, Encoding.UTF8); var graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); Shader shader = null; #if VFX_GRAPH_10_0_0_OR_NEWER if (!graph.isOnlyVFXTarget) #endif { // build the shader text // this will also add Target dependencies into the asset collection var text = GetShaderText(path, out configuredTextures, assetCollection, graph); #if UNITY_2021_1_OR_NEWER // 2021.1 or later is guaranteed to have the new version of this function shader = ShaderUtil.CreateShaderAsset(ctx, text, false); #else // earlier builds of Unity may or may not have it // here we try to invoke the new version via reflection var createShaderAssetMethod = typeof(ShaderUtil).GetMethod( "CreateShaderAsset", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.ExactBinding, null, new Type[] { typeof(AssetImportContext), typeof(string), typeof(bool) }, null); if (createShaderAssetMethod != null) { shader = createShaderAssetMethod.Invoke(null, new Object[] { ctx, text, false }) as Shader; } else { // method doesn't exist in this version of Unity, call old version // this doesn't create dependencies properly, but is the best that we can do shader = ShaderUtil.CreateShaderAsset(text, false); } #endif if (graph.messageManager.nodeMessagesChanged) { foreach (var pair in graph.messageManager.GetNodeMessages()) { var node = graph.GetNodeFromId(pair.Key); MessageManager.Log(node, path, pair.Value.First(), shader); } } EditorMaterialUtility.SetShaderDefaults( shader, configuredTextures.Where(x => x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); EditorMaterialUtility.SetShaderNonModifiableDefaults( shader, configuredTextures.Where(x => !x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => !x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); } UnityEngine.Object mainObject = shader; #if VFX_GRAPH_10_0_0_OR_NEWER ShaderGraphVfxAsset vfxAsset = null; if (graph.hasVFXTarget) { vfxAsset = GenerateVfxShaderGraphAsset(graph); if (mainObject == null) { mainObject = vfxAsset; } else { //Correct main object if we have a shader and ShaderGraphVfxAsset : save as sub asset vfxAsset.name = Path.GetFileNameWithoutExtension(path); ctx.AddObjectToAsset("VFXShaderGraph", vfxAsset); } } #endif Texture2D texture = Resources.Load <Texture2D>("Icons/sg_graph_icon"); ctx.AddObjectToAsset("MainAsset", mainObject, texture); ctx.SetMainObject(mainObject); foreach (var target in graph.activeTargets) { if (target is IHasMetadata iHasMetadata) { var metadata = iHasMetadata.GetMetadataObject(); if (metadata == null) { continue; } metadata.hideFlags = HideFlags.HideInHierarchy; ctx.AddObjectToAsset($"{iHasMetadata.identifier}:Metadata", metadata); } } var sgMetadata = ScriptableObject.CreateInstance <ShaderGraphMetadata>(); sgMetadata.hideFlags = HideFlags.HideInHierarchy; sgMetadata.assetDependencies = new List <UnityEngine.Object>(); foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.IncludeInExportPackage)) { // this sucks that we have to fully load these assets just to set the reference, // which then gets serialized as the GUID that we already have here. :P var dependencyPath = AssetDatabase.GUIDToAssetPath(asset.Key); if (!string.IsNullOrEmpty(dependencyPath)) { sgMetadata.assetDependencies.Add( AssetDatabase.LoadAssetAtPath(dependencyPath, typeof(UnityEngine.Object))); } } } ctx.AddObjectToAsset("SGInternal:Metadata", sgMetadata); // declare dependencies foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.SourceDependency)) { ctx.DependsOnSourceAsset(asset.Key); // I'm not sure if this warning below is actually used or not, keeping it to be safe var assetPath = AssetDatabase.GUIDToAssetPath(asset.Key); // Ensure that dependency path is relative to project if (!string.IsNullOrEmpty(assetPath) && !assetPath.StartsWith("Packages/") && !assetPath.StartsWith("Assets/")) { Debug.LogWarning($"Invalid dependency path: {assetPath}", mainObject); } } // NOTE: dependencies declared by GatherDependenciesFromSourceFile are automatically registered as artifact dependencies // HOWEVER: that path ONLY grabs dependencies via MinimalGraphData, and will fail to register dependencies // on GUIDs that don't exist in the project. For both of those reasons, we re-declare the dependencies here. if (asset.Value.HasFlag(AssetCollection.Flags.ArtifactDependency)) { ctx.DependsOnArtifact(asset.Key); } } }
public override void OnImportAsset(AssetImportContext ctx) { var oldShader = AssetDatabase.LoadAssetAtPath <Shader>(ctx.assetPath); if (oldShader != null) { ShaderUtil.ClearShaderMessages(oldShader); } List <PropertyCollector.TextureInfo> configuredTextures; string path = ctx.assetPath; var sourceAssetDependencyPaths = new List <string>(); var textGraph = File.ReadAllText(path, Encoding.UTF8); var graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); Shader shader = null; #if VFX_GRAPH_10_0_0_OR_NEWER if (!graph.isOnlyVFXTarget) #endif { var text = GetShaderText(path, out configuredTextures, sourceAssetDependencyPaths, graph); #if UNITY_2021_1_OR_NEWER // 2021.1 or later is guaranteed to have the new version of this function shader = ShaderUtil.CreateShaderAsset(ctx, text, false); #else // earlier builds of Unity may or may not have it // here we try to invoke the new version via reflection var createShaderAssetMethod = typeof(ShaderUtil).GetMethod( "CreateShaderAsset", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.ExactBinding, null, new Type[] { typeof(AssetImportContext), typeof(string), typeof(bool) }, null); if (createShaderAssetMethod != null) { shader = createShaderAssetMethod.Invoke(null, new Object[] { ctx, text, false }) as Shader; } else { // method doesn't exist in this version of Unity, call old version // this doesn't create dependencies properly, but is the best that we can do shader = ShaderUtil.CreateShaderAsset(text, false); } #endif if (graph.messageManager.nodeMessagesChanged) { foreach (var pair in graph.messageManager.GetNodeMessages()) { var node = graph.GetNodeFromId(pair.Key); MessageManager.Log(node, path, pair.Value.First(), shader); } } EditorMaterialUtility.SetShaderDefaults( shader, configuredTextures.Where(x => x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); EditorMaterialUtility.SetShaderNonModifiableDefaults( shader, configuredTextures.Where(x => !x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => !x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); } UnityEngine.Object mainObject = shader; #if VFX_GRAPH_10_0_0_OR_NEWER ShaderGraphVfxAsset vfxAsset = null; if (graph.hasVFXTarget) { vfxAsset = GenerateVfxShaderGraphAsset(graph); if (mainObject == null) { mainObject = vfxAsset; } else { //Correct main object if we have a shader and ShaderGraphVfxAsset : save as sub asset vfxAsset.name = Path.GetFileNameWithoutExtension(path); ctx.AddObjectToAsset("VFXShaderGraph", vfxAsset); } } #endif Texture2D texture = Resources.Load <Texture2D>("Icons/sg_graph_icon@64"); ctx.AddObjectToAsset("MainAsset", mainObject, texture); ctx.SetMainObject(mainObject); foreach (var target in graph.activeTargets) { if (target is IHasMetadata iHasMetadata) { var metadata = iHasMetadata.GetMetadataObject(); if (metadata == null) { continue; } metadata.hideFlags = HideFlags.HideInHierarchy; ctx.AddObjectToAsset($"{iHasMetadata.identifier}:Metadata", metadata); } } var sgMetadata = ScriptableObject.CreateInstance <ShaderGraphMetadata>(); sgMetadata.hideFlags = HideFlags.HideInHierarchy; sgMetadata.assetDependencies = new List <UnityEngine.Object>(); var deps = GatherDependenciesFromSourceFile(ctx.assetPath); foreach (string dependency in deps) { sgMetadata.assetDependencies.Add(AssetDatabase.LoadAssetAtPath(dependency, typeof(UnityEngine.Object))); } ctx.AddObjectToAsset("SGInternal:Metadata", sgMetadata); foreach (var sourceAssetDependencyPath in sourceAssetDependencyPaths.Distinct()) { // Ensure that dependency path is relative to project if (!sourceAssetDependencyPath.StartsWith("Packages/") && !sourceAssetDependencyPath.StartsWith("Assets/")) { Debug.LogWarning($"Invalid dependency path: {sourceAssetDependencyPath}", mainObject); continue; } ctx.DependsOnSourceAsset(sourceAssetDependencyPath); } }
public override void OnImportAsset(AssetImportContext ctx) { var oldShader = AssetDatabase.LoadAssetAtPath <Shader>(ctx.assetPath); if (oldShader != null) { ShaderUtil.ClearShaderMessages(oldShader); } List <PropertyCollector.TextureInfo> configuredTextures; string path = ctx.assetPath; var sourceAssetDependencyPaths = new List <string>(); UnityEngine.Object mainObject; var textGraph = File.ReadAllText(path, Encoding.UTF8); var graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); // TODO: How to handle this? if (graph.isVFXTarget) { var vfxAsset = GenerateVfxShaderGraphAsset(graph); mainObject = vfxAsset; } else { var text = GetShaderText(path, out configuredTextures, sourceAssetDependencyPaths, graph); var shader = ShaderUtil.CreateShaderAsset(text, false); if (graph != null && graph.messageManager.nodeMessagesChanged) { foreach (var pair in graph.messageManager.GetNodeMessages()) { var node = graph.GetNodeFromId(pair.Key); MessageManager.Log(node, path, pair.Value.First(), shader); } } EditorMaterialUtility.SetShaderDefaults( shader, configuredTextures.Where(x => x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); EditorMaterialUtility.SetShaderNonModifiableDefaults( shader, configuredTextures.Where(x => !x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => !x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); mainObject = shader; } Texture2D texture = Resources.Load <Texture2D>("Icons/sg_graph_icon@64"); ctx.AddObjectToAsset("MainAsset", mainObject, texture); ctx.SetMainObject(mainObject); if (graph != null) { foreach (var target in graph.activeTargets) { if (target is IHasMetadata iHasMetadata) { var metadata = iHasMetadata.GetMetadataObject(); if (metadata == null) { continue; } metadata.hideFlags = HideFlags.HideInHierarchy; ctx.AddObjectToAsset($"{iHasMetadata.identifier}:Metadata", metadata); } } } var sgMetadata = ScriptableObject.CreateInstance <ShaderGraphMetadata>(); sgMetadata.hideFlags = HideFlags.HideInHierarchy; sgMetadata.assetDependencies = new List <UnityEngine.Object>(); var deps = GatherDependenciesFromSourceFile(ctx.assetPath); foreach (string dependency in deps) { sgMetadata.assetDependencies.Add(AssetDatabase.LoadAssetAtPath(dependency, typeof(UnityEngine.Object))); } ctx.AddObjectToAsset("SGInternal:Metadata", sgMetadata); foreach (var sourceAssetDependencyPath in sourceAssetDependencyPaths.Distinct()) { // Ensure that dependency path is relative to project if (!sourceAssetDependencyPath.StartsWith("Packages/") && !sourceAssetDependencyPath.StartsWith("Assets/")) { Debug.LogWarning($"Invalid dependency path: {sourceAssetDependencyPath}", mainObject); continue; } ctx.DependsOnSourceAsset(sourceAssetDependencyPath); } }
public override void OnImportAsset(AssetImportContext ctx) { var importLog = new AssetImportErrorLog(ctx); string path = ctx.assetPath; AssetCollection assetCollection = new AssetCollection(); MinimalGraphData.GatherMinimalDependenciesFromFile(assetPath, assetCollection); var textGraph = File.ReadAllText(path, Encoding.UTF8); var graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); UnityEngine.Object mainObject = null; #if VFX_GRAPH_10_0_0_OR_NEWER if (!graph.isOnlyVFXTarget) #endif { // build shaders mainObject = BuildAllShaders(ctx, importLog, assetCollection, graph); } #if VFX_GRAPH_10_0_0_OR_NEWER ShaderGraphVfxAsset vfxAsset = null; if (graph.hasVFXTarget) { vfxAsset = GenerateVfxShaderGraphAsset(graph); if (mainObject == null) { mainObject = vfxAsset; } else { //Correct main object if we have a shader and ShaderGraphVfxAsset : save as sub asset vfxAsset.name = Path.GetFileNameWithoutExtension(path); ctx.AddObjectToAsset("VFXShaderGraph", vfxAsset); } } #endif Texture2D texture = Resources.Load <Texture2D>("Icons/sg_graph_icon"); ctx.AddObjectToAsset("MainAsset", mainObject, texture); ctx.SetMainObject(mainObject); foreach (var target in graph.activeTargets) { if (target is IHasMetadata iHasMetadata) { var metadata = iHasMetadata.GetMetadataObject(); if (metadata == null) { continue; } metadata.hideFlags = HideFlags.HideInHierarchy; ctx.AddObjectToAsset($"{iHasMetadata.identifier}:Metadata", metadata); } } var sgMetadata = ScriptableObject.CreateInstance <ShaderGraphMetadata>(); sgMetadata.hideFlags = HideFlags.HideInHierarchy; sgMetadata.assetDependencies = new List <UnityEngine.Object>(); foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.IncludeInExportPackage)) { // this sucks that we have to fully load these assets just to set the reference, // which then gets serialized as the GUID that we already have here. :P var dependencyPath = AssetDatabase.GUIDToAssetPath(asset.Key); if (!string.IsNullOrEmpty(dependencyPath)) { sgMetadata.assetDependencies.Add( AssetDatabase.LoadAssetAtPath(dependencyPath, typeof(UnityEngine.Object))); } } } List <GraphInputData> inputInspectorDataList = new List <GraphInputData>(); foreach (AbstractShaderProperty property in graph.properties) { // Don't write out data for non-exposed blackboard items if (!property.isExposed) { continue; } // VTs are treated differently if (property is VirtualTextureShaderProperty virtualTextureShaderProperty) { inputInspectorDataList.Add(MinimalCategoryData.ProcessVirtualTextureProperty(virtualTextureShaderProperty)); } else { inputInspectorDataList.Add(new GraphInputData() { referenceName = property.referenceName, propertyType = property.propertyType, isKeyword = false }); } } foreach (ShaderKeyword keyword in graph.keywords) { // Don't write out data for non-exposed blackboard items if (!keyword.isExposed) { continue; } var sanitizedReferenceName = keyword.referenceName; if (keyword.keywordType == KeywordType.Boolean && keyword.referenceName.Contains("_ON")) { sanitizedReferenceName = sanitizedReferenceName.Replace("_ON", String.Empty); } inputInspectorDataList.Add(new GraphInputData() { referenceName = sanitizedReferenceName, keywordType = keyword.keywordType, isKeyword = true }); } sgMetadata.categoryDatas = new List <MinimalCategoryData>(); foreach (CategoryData categoryData in graph.categories) { // Don't write out empty categories if (categoryData.childCount == 0) { continue; } MinimalCategoryData mcd = new MinimalCategoryData() { categoryName = categoryData.name, propertyDatas = new List <GraphInputData>() }; foreach (var input in categoryData.Children) { GraphInputData propData; // Only write out data for exposed blackboard items if (input.isExposed == false) { continue; } // VTs are treated differently if (input is VirtualTextureShaderProperty virtualTextureShaderProperty) { propData = MinimalCategoryData.ProcessVirtualTextureProperty(virtualTextureShaderProperty); inputInspectorDataList.RemoveAll(inputData => inputData.referenceName == propData.referenceName); mcd.propertyDatas.Add(propData); continue; } else if (input is ShaderKeyword keyword) { var sanitizedReferenceName = keyword.referenceName; if (keyword.keywordType == KeywordType.Boolean && keyword.referenceName.Contains("_ON")) { sanitizedReferenceName = sanitizedReferenceName.Replace("_ON", String.Empty); } propData = new GraphInputData() { referenceName = sanitizedReferenceName, keywordType = keyword.keywordType, isKeyword = true }; } else { var prop = input as AbstractShaderProperty; propData = new GraphInputData() { referenceName = input.referenceName, propertyType = prop.propertyType, isKeyword = false }; } mcd.propertyDatas.Add(propData); inputInspectorDataList.Remove(propData); } sgMetadata.categoryDatas.Add(mcd); } // Any uncategorized elements get tossed into an un-named category at the top as a fallback if (inputInspectorDataList.Count > 0) { sgMetadata.categoryDatas.Insert(0, new MinimalCategoryData() { categoryName = "", propertyDatas = inputInspectorDataList }); } ctx.AddObjectToAsset("SGInternal:Metadata", sgMetadata); // declare dependencies foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.SourceDependency)) { ctx.DependsOnSourceAsset(asset.Key); // I'm not sure if this warning below is actually used or not, keeping it to be safe var assetPath = AssetDatabase.GUIDToAssetPath(asset.Key); // Ensure that dependency path is relative to project if (!string.IsNullOrEmpty(assetPath) && !assetPath.StartsWith("Packages/") && !assetPath.StartsWith("Assets/")) { importLog.LogWarning($"Invalid dependency path: {assetPath}", mainObject); } } // NOTE: dependencies declared by GatherDependenciesFromSourceFile are automatically registered as artifact dependencies // HOWEVER: that path ONLY grabs dependencies via MinimalGraphData, and will fail to register dependencies // on GUIDs that don't exist in the project. For both of those reasons, we re-declare the dependencies here. if (asset.Value.HasFlag(AssetCollection.Flags.ArtifactDependency)) { ctx.DependsOnArtifact(asset.Key); } } }
public override void OnInspectorGUI() { GraphData GetGraphData(AssetImporter importer) { var textGraph = File.ReadAllText(importer.assetPath, Encoding.UTF8); var graphObject = CreateInstance <GraphObject>(); graphObject.hideFlags = HideFlags.HideAndDontSave; bool isSubGraph; var extension = Path.GetExtension(importer.assetPath).Replace(".", ""); switch (extension) { case ShaderGraphImporter.Extension: isSubGraph = false; break; case ShaderGraphImporter.LegacyExtension: isSubGraph = false; break; case ShaderSubGraphImporter.Extension: isSubGraph = true; break; default: throw new Exception($"Invalid file extension {extension}"); } var assetGuid = AssetDatabase.AssetPathToGUID(importer.assetPath); graphObject.graph = new GraphData { assetGuid = assetGuid, isSubGraph = isSubGraph, messageManager = null }; MultiJson.Deserialize(graphObject.graph, textGraph); graphObject.graph.OnEnable(); graphObject.graph.ValidateGraph(); return(graphObject.graph); } if (GUILayout.Button("Open Shader Editor")) { AssetImporter importer = target as AssetImporter; Debug.Assert(importer != null, "importer != null"); ShowGraphEditWindow(importer.assetPath); } using (var horizontalScope = new GUILayout.HorizontalScope("box")) { AssetImporter importer = target as AssetImporter; string assetName = Path.GetFileNameWithoutExtension(importer.assetPath); string path = String.Format("Temp/GeneratedFromGraph-{0}.shader", assetName.Replace(" ", "")); bool alreadyExists = File.Exists(path); bool update = false; bool open = false; if (GUILayout.Button("View Generated Shader")) { update = true; open = true; } if (alreadyExists && GUILayout.Button("Regenerate")) { update = true; } if (update) { var graphData = GetGraphData(importer); var generator = new Generator(graphData, null, GenerationMode.ForReals, assetName, null); if (!GraphUtil.WriteToFile(path, generator.generatedShader)) { open = false; } } if (open) { GraphUtil.OpenFile(path); } } if (Unsupported.IsDeveloperMode()) { if (GUILayout.Button("View Preview Shader")) { AssetImporter importer = target as AssetImporter; string assetName = Path.GetFileNameWithoutExtension(importer.assetPath); string path = String.Format("Temp/GeneratedFromGraph-{0}-Preview.shader", assetName.Replace(" ", "")); var graphData = GetGraphData(importer); var generator = new Generator(graphData, null, GenerationMode.Preview, $"{assetName}-Preview", null); if (GraphUtil.WriteToFile(path, generator.generatedShader)) { GraphUtil.OpenFile(path); } } } if (GUILayout.Button("Copy Shader")) { AssetImporter importer = target as AssetImporter; string assetName = Path.GetFileNameWithoutExtension(importer.assetPath); var graphData = GetGraphData(importer); var generator = new Generator(graphData, null, GenerationMode.ForReals, assetName, null); GUIUtility.systemCopyBuffer = generator.generatedShader; } ApplyRevertGUI(); }
public override void OnImportAsset(AssetImportContext ctx) { var oldShader = AssetDatabase.LoadAssetAtPath <Shader>(ctx.assetPath); if (oldShader != null) { ShaderUtil.ClearShaderMessages(oldShader); } List <PropertyCollector.TextureInfo> configuredTextures; string path = ctx.assetPath; AssetCollection assetCollection = new AssetCollection(); MinimalGraphData.GatherMinimalDependenciesFromFile(assetPath, assetCollection); var textGraph = File.ReadAllText(path, Encoding.UTF8); var graph = new GraphData { messageManager = new MessageManager(), assetGuid = AssetDatabase.AssetPathToGUID(path) }; MultiJson.Deserialize(graph, textGraph); graph.OnEnable(); graph.ValidateGraph(); Shader shader = null; #if VFX_GRAPH_10_0_0_OR_NEWER if (!graph.isOnlyVFXTarget) #endif { // build the shader text // this will also add Target dependencies into the asset collection var text = GetShaderText(path, out configuredTextures, assetCollection, graph); #if UNITY_2021_1_OR_NEWER // 2021.1 or later is guaranteed to have the new version of this function shader = ShaderUtil.CreateShaderAsset(ctx, text, false); #else // earlier builds of Unity may or may not have it // here we try to invoke the new version via reflection var createShaderAssetMethod = typeof(ShaderUtil).GetMethod( "CreateShaderAsset", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.ExactBinding, null, new Type[] { typeof(AssetImportContext), typeof(string), typeof(bool) }, null); if (createShaderAssetMethod != null) { shader = createShaderAssetMethod.Invoke(null, new Object[] { ctx, text, false }) as Shader; } else { // method doesn't exist in this version of Unity, call old version // this doesn't create dependencies properly, but is the best that we can do shader = ShaderUtil.CreateShaderAsset(text, false); } #endif ReportErrors(graph, shader, path); EditorMaterialUtility.SetShaderDefaults( shader, configuredTextures.Where(x => x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); EditorMaterialUtility.SetShaderNonModifiableDefaults( shader, configuredTextures.Where(x => !x.modifiable).Select(x => x.name).ToArray(), configuredTextures.Where(x => !x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray()); } UnityEngine.Object mainObject = shader; #if VFX_GRAPH_10_0_0_OR_NEWER ShaderGraphVfxAsset vfxAsset = null; if (graph.hasVFXTarget) { vfxAsset = GenerateVfxShaderGraphAsset(graph); if (mainObject == null) { mainObject = vfxAsset; } else { //Correct main object if we have a shader and ShaderGraphVfxAsset : save as sub asset vfxAsset.name = Path.GetFileNameWithoutExtension(path); ctx.AddObjectToAsset("VFXShaderGraph", vfxAsset); } } #endif Texture2D texture = Resources.Load <Texture2D>("Icons/sg_graph_icon"); ctx.AddObjectToAsset("MainAsset", mainObject, texture); ctx.SetMainObject(mainObject); foreach (var target in graph.activeTargets) { if (target is IHasMetadata iHasMetadata) { var metadata = iHasMetadata.GetMetadataObject(); if (metadata == null) { continue; } metadata.hideFlags = HideFlags.HideInHierarchy; ctx.AddObjectToAsset($"{iHasMetadata.identifier}:Metadata", metadata); } } var sgMetadata = ScriptableObject.CreateInstance <ShaderGraphMetadata>(); sgMetadata.hideFlags = HideFlags.HideInHierarchy; sgMetadata.assetDependencies = new List <UnityEngine.Object>(); foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.IncludeInExportPackage)) { // this sucks that we have to fully load these assets just to set the reference, // which then gets serialized as the GUID that we already have here. :P var dependencyPath = AssetDatabase.GUIDToAssetPath(asset.Key); if (!string.IsNullOrEmpty(dependencyPath)) { sgMetadata.assetDependencies.Add( AssetDatabase.LoadAssetAtPath(dependencyPath, typeof(UnityEngine.Object))); } } } List <MinimalCategoryData.GraphInputData> inputInspectorDataList = new List <MinimalCategoryData.GraphInputData>(); foreach (AbstractShaderProperty property in graph.properties) { // Don't write out data for non-exposed blackboard items if (!property.isExposed) { continue; } // VTs are treated differently if (property is VirtualTextureShaderProperty virtualTextureShaderProperty) { inputInspectorDataList.Add(MinimalCategoryData.ProcessVirtualTextureProperty(virtualTextureShaderProperty)); } else { inputInspectorDataList.Add(new MinimalCategoryData.GraphInputData() { referenceName = property.referenceName, propertyType = property.propertyType, isKeyword = false }); } } foreach (ShaderKeyword keyword in graph.keywords) { // Don't write out data for non-exposed blackboard items if (!keyword.isExposed) { continue; } var sanitizedReferenceName = keyword.referenceName; if (keyword.keywordType == KeywordType.Boolean && keyword.referenceName.Contains("_ON")) { sanitizedReferenceName = sanitizedReferenceName.Replace("_ON", String.Empty); } inputInspectorDataList.Add(new MinimalCategoryData.GraphInputData() { referenceName = sanitizedReferenceName, keywordType = keyword.keywordType, isKeyword = true }); } sgMetadata.categoryDatas = new List <MinimalCategoryData>(); foreach (CategoryData categoryData in graph.categories) { // Don't write out empty categories if (categoryData.childCount == 0) { continue; } MinimalCategoryData mcd = new MinimalCategoryData() { categoryName = categoryData.name, propertyDatas = new List <MinimalCategoryData.GraphInputData>() }; foreach (var input in categoryData.Children) { MinimalCategoryData.GraphInputData propData; // Only write out data for exposed blackboard items if (input.isExposed == false) { continue; } // VTs are treated differently if (input is VirtualTextureShaderProperty virtualTextureShaderProperty) { propData = MinimalCategoryData.ProcessVirtualTextureProperty(virtualTextureShaderProperty); inputInspectorDataList.RemoveAll(inputData => inputData.referenceName == propData.referenceName); mcd.propertyDatas.Add(propData); continue; } else if (input is ShaderKeyword keyword) { var sanitizedReferenceName = keyword.referenceName; if (keyword.keywordType == KeywordType.Boolean && keyword.referenceName.Contains("_ON")) { sanitizedReferenceName = sanitizedReferenceName.Replace("_ON", String.Empty); } propData = new MinimalCategoryData.GraphInputData() { referenceName = sanitizedReferenceName, keywordType = keyword.keywordType, isKeyword = true }; } else { var prop = input as AbstractShaderProperty; propData = new MinimalCategoryData.GraphInputData() { referenceName = input.referenceName, propertyType = prop.propertyType, isKeyword = false }; } mcd.propertyDatas.Add(propData); inputInspectorDataList.Remove(propData); } sgMetadata.categoryDatas.Add(mcd); } // Any uncategorized elements get tossed into an un-named category at the top as a fallback if (inputInspectorDataList.Count > 0) { sgMetadata.categoryDatas.Insert(0, new MinimalCategoryData() { categoryName = "", propertyDatas = inputInspectorDataList }); } ctx.AddObjectToAsset("SGInternal:Metadata", sgMetadata); // declare dependencies foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.SourceDependency)) { ctx.DependsOnSourceAsset(asset.Key); // I'm not sure if this warning below is actually used or not, keeping it to be safe var assetPath = AssetDatabase.GUIDToAssetPath(asset.Key); // Ensure that dependency path is relative to project if (!string.IsNullOrEmpty(assetPath) && !assetPath.StartsWith("Packages/") && !assetPath.StartsWith("Assets/")) { Debug.LogWarning($"Invalid dependency path: {assetPath}", mainObject); } } // NOTE: dependencies declared by GatherDependenciesFromSourceFile are automatically registered as artifact dependencies // HOWEVER: that path ONLY grabs dependencies via MinimalGraphData, and will fail to register dependencies // on GUIDs that don't exist in the project. For both of those reasons, we re-declare the dependencies here. if (asset.Value.HasFlag(AssetCollection.Flags.ArtifactDependency)) { ctx.DependsOnArtifact(asset.Key); } } }
public void Initialize(string assetGuid) { try { m_ColorSpace = PlayerSettings.colorSpace; m_RenderPipelineAsset = GraphicsSettings.renderPipelineAsset; var asset = AssetDatabase.LoadAssetAtPath <Object>(AssetDatabase.GUIDToAssetPath(assetGuid)); if (asset == null) { return; } if (!EditorUtility.IsPersistent(asset)) { return; } if (selectedGuid == assetGuid) { return; } var path = AssetDatabase.GetAssetPath(asset); var extension = Path.GetExtension(path); if (extension == null) { return; } // Path.GetExtension returns the extension prefixed with ".", so we remove it. We force lower case such that // the comparison will be case-insensitive. extension = extension.Substring(1).ToLowerInvariant(); bool isSubGraph; switch (extension) { case ShaderGraphImporter.Extension: isSubGraph = false; break; case ShaderSubGraphImporter.Extension: isSubGraph = true; break; default: return; } selectedGuid = assetGuid; using (GraphLoadMarker.Auto()) { var textGraph = File.ReadAllText(path, Encoding.UTF8); graphObject = CreateInstance <GraphObject>(); graphObject.hideFlags = HideFlags.HideAndDontSave; graphObject.graph = new GraphData { assetGuid = assetGuid, isSubGraph = isSubGraph, messageManager = messageManager }; MultiJson.Deserialize(graphObject.graph, textGraph); graphObject.graph.OnEnable(); graphObject.graph.ValidateGraph(); } using (CreateGraphEditorViewMarker.Auto()) { graphEditorView = new GraphEditorView(this, m_GraphObject.graph, messageManager) { viewDataKey = selectedGuid, assetName = asset.name.Split('/').Last() }; } Texture2D icon = GetThemeIcon(graphObject.graph); // This is adding the icon at the front of the tab titleContent = EditorGUIUtility.TrTextContentWithIcon(selectedGuid, icon); UpdateTitle(); Repaint(); } catch (Exception) { m_HasError = true; m_GraphEditorView = null; graphObject = null; throw; } }
public void TestImportAsset(string unityLocalPath, string fullPath) { unityLocalPath = unityLocalPath.Replace("\\", "/"); Debug.Log("Testing file: " + unityLocalPath); // invoke an import AssetDatabase.ImportAsset(unityLocalPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer); // double check we can load it up and validate it string fileContents = File.ReadAllText(fullPath); Assert.Greater(fileContents.Length, 0); var graphGuid = AssetDatabase.AssetPathToGUID(unityLocalPath); var messageManager = new MessageManager(); GraphData graphData = new GraphData() { assetGuid = graphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData, fileContents); graphData.OnEnable(); graphData.ValidateGraph(); string fileExtension = Path.GetExtension(fullPath).ToLower(); bool isSubgraph = (fileExtension == "shadersubgraph"); if (isSubgraph) { // check that the SubGraphAsset is the same after versioning twice // this is important to ensure we're not importing subgraphs non-deterministically when they are out-of-date on disk AssetDatabase.ImportAsset(unityLocalPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer); var subGraph = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(unityLocalPath); var serialized = EditorJsonUtility.ToJson(subGraph); AssetDatabase.ImportAsset(unityLocalPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer); var subGraph2 = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(unityLocalPath); var serialized2 = EditorJsonUtility.ToJson(subGraph2); Assert.AreEqual(serialized, serialized2, $"Importing the subgraph {unityLocalPath} twice resulted in different subgraph assets."); } else { // check that the generated shader is the same after versioning twice // this is important to ensure we're not importing shaders non-deterministically when they are out-of-date on disk string fileNameNoExtension = Path.GetFileNameWithoutExtension(fullPath); var generator = new Generator(graphData, graphData.outputNode, GenerationMode.ForReals, fileNameNoExtension, null); string shader = generator.generatedShader; // version again GraphData graphData2 = new GraphData() { assetGuid = graphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData2, fileContents); graphData2.OnEnable(); graphData2.ValidateGraph(); var generator2 = new Generator(graphData2, graphData2.outputNode, GenerationMode.ForReals, fileNameNoExtension, null); string shader2 = generator2.generatedShader; Assert.AreEqual(shader, shader2, $"Importing the graph {unityLocalPath} twice resulted in different generated shaders."); } }
public static T Deserialize <T>(JSONSerializedElement item, Dictionary <TypeSerializationInfo, TypeSerializationInfo> remapper, params object[] constructorArgs) where T : class { T instance; if (typeof(T) == typeof(JsonObject) || typeof(T).IsSubclassOf(typeof(JsonObject))) { try { var culture = CultureInfo.CurrentCulture; var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; instance = Activator.CreateInstance(typeof(T), flags, null, constructorArgs, culture) as T; } catch (Exception e) { throw new Exception(string.Format("Could not construct instance of: {0}", typeof(T)), e); } MultiJson.Deserialize(instance as JsonObject, item.JSONnodeData); return(instance); } if (!item.typeInfo.IsValid() || string.IsNullOrEmpty(item.JSONnodeData)) { throw new ArgumentException(string.Format("Can not deserialize {0}, it is invalid", item)); } TypeSerializationInfo info = item.typeInfo; info.fullName = info.fullName.Replace("UnityEngine.MaterialGraph", "UnityEditor.ShaderGraph"); info.fullName = info.fullName.Replace("UnityEngine.Graphing", "UnityEditor.Graphing"); if (remapper != null) { info = DoTypeRemap(info, remapper); } var type = GetTypeFromSerializedString(info); //if type is null but T is an abstract material node, instead we create an unknowntype node if (type == null) { throw new ArgumentException(string.Format("Can not deserialize ({0}), type is invalid", info.fullName)); } try { var culture = CultureInfo.CurrentCulture; var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; instance = Activator.CreateInstance(type, flags, null, constructorArgs, culture) as T; } catch (Exception e) { throw new Exception(string.Format("Could not construct instance of: {0}", type), e); } if (instance != null) { JsonUtility.FromJsonOverwrite(item.JSONnodeData, instance); return(instance); } Debug.Log("UhOh"); return(null); }
public void TestImportAsset(string unityLocalPath, string fullPath) { unityLocalPath = unityLocalPath.Replace("\\", "/"); Debug.Log("Testing file: " + unityLocalPath); // invoke an import AssetDatabase.ImportAsset(unityLocalPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer); // double check we can load it up and validate it string fileContents = File.ReadAllText(fullPath); Assert.Greater(fileContents.Length, 0); var graphGuid = AssetDatabase.AssetPathToGUID(unityLocalPath); var messageManager = new MessageManager(); GraphData graphData = new GraphData() { assetGuid = graphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData, fileContents); graphData.OnEnable(); graphData.ValidateGraph(); string fileExtension = Path.GetExtension(fullPath).ToLower(); bool isSubgraph = (fileExtension == "shadersubgraph"); if (isSubgraph) { // check that the SubGraphAsset is the same after versioning twice // this is important to ensure we're not importing subgraphs non-deterministically when they are out-of-date on disk AssetDatabase.ImportAsset(unityLocalPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer); var subGraph = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(unityLocalPath); var serialized = EditorJsonUtility.ToJson(subGraph); AssetDatabase.ImportAsset(unityLocalPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | ImportAssetOptions.DontDownloadFromCacheServer); var subGraph2 = AssetDatabase.LoadAssetAtPath <SubGraphAsset>(unityLocalPath); var serialized2 = EditorJsonUtility.ToJson(subGraph2); Assert.AreEqual(serialized, serialized2, $"Importing the subgraph {unityLocalPath} twice resulted in different subgraph assets."); } else { // check that the generated shader is the same after versioning twice // this is important to ensure we're not importing shaders non-deterministically when they are out-of-date on disk string fileNameNoExtension = Path.GetFileNameWithoutExtension(fullPath); var generator = new Generator(graphData, graphData.outputNode, GenerationMode.ForReals, fileNameNoExtension, null); string shader = generator.generatedShader; // version again GraphData graphData2 = new GraphData() { assetGuid = graphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData2, fileContents); graphData2.OnEnable(); graphData2.ValidateGraph(); var generator2 = new Generator(graphData2, graphData2.outputNode, GenerationMode.ForReals, fileNameNoExtension, null); string shader2 = generator2.generatedShader; Assert.AreEqual(shader, shader2, $"Importing the graph {unityLocalPath} twice resulted in different generated shaders."); // Texture test won't work on platforms that don't support more than 16 samplers bool isGL = (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore) || (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2) || (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3); bool isOSX = (Application.platform == RuntimePlatform.OSXEditor) || (Application.platform == RuntimePlatform.OSXPlayer); bool samplersSupported = !(isOSX && isGL); if (!samplersSupported && fullPath.Contains("TextureTest")) { // skip the compile test -- we know this shader won't compile on these platforms } else { // now create a Unity Shader from the string var compiledShader = ShaderUtil.CreateShaderAsset(shader, true); compiledShader.hideFlags = HideFlags.HideAndDontSave; Assert.NotNull(compiledShader); // compile all the shader passes to see if there are any errors var mat = new Material(compiledShader) { hideFlags = HideFlags.HideAndDontSave }; for (int pass = 0; pass < mat.passCount; pass++) { ShaderUtil.CompilePass(mat, pass, true); } } } }
public override void OnImportAsset(AssetImportContext ctx) { var graphAsset = ScriptableObject.CreateInstance <SubGraphAsset>(); var subGraphPath = ctx.assetPath; var subGraphGuid = AssetDatabase.AssetPathToGUID(subGraphPath); graphAsset.assetGuid = subGraphGuid; var textGraph = File.ReadAllText(subGraphPath, Encoding.UTF8); var messageManager = new MessageManager(); var graphData = new GraphData { isSubGraph = true, assetGuid = subGraphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData, textGraph); try { ProcessSubGraph(graphAsset, graphData); } catch (Exception e) { graphAsset.isValid = false; Debug.LogException(e, graphAsset); } finally { if (messageManager.AnyError()) { graphAsset.isValid = false; foreach (var pair in messageManager.GetNodeMessages()) { var node = graphData.GetNodeFromId(pair.Key); foreach (var message in pair.Value) { MessageManager.Log(node, subGraphPath, message, graphAsset); } } } messageManager.ClearAll(); } Texture2D texture = Resources.Load <Texture2D>("Icons/sg_subgraph_icon@64"); ctx.AddObjectToAsset("MainAsset", graphAsset, texture); ctx.SetMainObject(graphAsset); var metadata = ScriptableObject.CreateInstance <ShaderSubGraphMetadata>(); metadata.hideFlags = HideFlags.HideInHierarchy; metadata.assetDependencies = new List <UnityEngine.Object>(); AssetCollection assetCollection = new AssetCollection(); MinimalGraphData.GatherMinimalDependenciesFromFile(assetPath, assetCollection); foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.IncludeInExportPackage)) { // this sucks that we have to fully load these assets just to set the reference, // which then gets serialized as the GUID that we already have here. :P var dependencyPath = AssetDatabase.GUIDToAssetPath(asset.Key); if (!string.IsNullOrEmpty(dependencyPath)) { metadata.assetDependencies.Add( AssetDatabase.LoadAssetAtPath(dependencyPath, typeof(UnityEngine.Object))); } } } ctx.AddObjectToAsset("Metadata", metadata); // declare dependencies foreach (var asset in assetCollection.assets) { if (asset.Value.HasFlag(AssetCollection.Flags.SourceDependency)) { ctx.DependsOnSourceAsset(asset.Key); // I'm not sure if this warning below is actually used or not, keeping it to be safe var assetPath = AssetDatabase.GUIDToAssetPath(asset.Key); // Ensure that dependency path is relative to project if (!string.IsNullOrEmpty(assetPath) && !assetPath.StartsWith("Packages/") && !assetPath.StartsWith("Assets/")) { Debug.LogWarning($"Invalid dependency path: {assetPath}", graphAsset); } } // NOTE: dependencies declared by GatherDependenciesFromSourceFile are automatically registered as artifact dependencies // HOWEVER: that path ONLY grabs dependencies via MinimalGraphData, and will fail to register dependencies // on GUIDs that don't exist in the project. For both of those reasons, we re-declare the dependencies here. if (asset.Value.HasFlag(AssetCollection.Flags.ArtifactDependency)) { ctx.DependsOnArtifact(asset.Key); } } }
public void SubGraphDescendentsTests() { var graphPath = targetUnityDirectoryPath + "/ShaderStageCapability_Graph.shadergraph"; string fileContents = File.ReadAllText(graphPath); var graphGuid = AssetDatabase.AssetPathToGUID(graphPath); var messageManager = new MessageManager(); GraphData graphData = new GraphData() { assetGuid = graphGuid, messageManager = messageManager }; MultiJson.Deserialize(graphData, fileContents); graphData.OnEnable(); graphData.ValidateGraph(); var subGraphnodeName = "ShaderStageCapability_SubGraph"; var subGraphNode = FindFirstNodeOfType <SubGraphNode>(graphData, subGraphnodeName); if (subGraphNode == null) { Assert.Fail("Failed to find sub graph node for {0}", subGraphnodeName); return; } var expectedSlotCapabilities = new Dictionary <string, ShaderStageCapability> { { "NotConnectedOut", ShaderStageCapability.All }, { "NotConnectedInput", ShaderStageCapability.All }, { "InternalVertexLockedOut", ShaderStageCapability.Vertex }, { "InternalFragmentLockedOut", ShaderStageCapability.Fragment }, { "InternalBothLockedOut", ShaderStageCapability.None }, { "InternalVertexLockedInput", ShaderStageCapability.Vertex }, { "InternalFragmentLockedInput", ShaderStageCapability.Fragment }, { "InternalBothLockedInput", ShaderStageCapability.None }, // Output A is connected to InputA which is attached to a vertex locked node in the parent graph { "OutputA", ShaderStageCapability.Vertex }, // Output B is connected to InputB which is attached to a fragment locked node in the parent graph { "OutputB", ShaderStageCapability.Fragment }, // OutputAB is connected to InputA and InputB { "OutputAB", ShaderStageCapability.None }, // InputC is connected to OutputC which is hooked up to the vertex output in the parent graph { "InputC", ShaderStageCapability.Vertex }, // InputD is connected to OutputD which is hooked up to the fragment output in the parent graph { "InputD", ShaderStageCapability.Fragment }, // InputEF is split into OutputE and OutputF which are hooked up to vertex and fragment outputs in the parent graph { "InputEF", ShaderStageCapability.None }, }; var slotNameToId = new Dictionary <string, MaterialSlot>(); var slots = subGraphNode.GetSlots <MaterialSlot>(); foreach (var slot in slots) { slotNameToId[slot.RawDisplayName()] = slot; } foreach (var expectedSlotResult in expectedSlotCapabilities) { var slotName = expectedSlotResult.Key; var expectedSlotValue = expectedSlotResult.Value; if (slotNameToId.TryGetValue(slotName, out var slot)) { var capabilities = NodeUtils.GetEffectiveShaderStageCapability(slot, true) & NodeUtils.GetEffectiveShaderStageCapability(slot, false); Assert.AreEqual(capabilities, expectedSlotValue, "Slot {0} expected shader capability {1} but was {2}", slotName, expectedSlotValue, capabilities); } else { Assert.Fail("Expected slot {0} wasn't found", slotName); } } }