Example #1
0
 public void LoadGraphData()
 {
     m_SubGraphData = new SubGraphData();
     if (!String.IsNullOrEmpty(m_SerializedSubGraphData.JSONnodeData))
     {
         MultiJson.Deserialize(m_SubGraphData, m_SerializedSubGraphData.JSONnodeData);
     }
 }
Example #2
0
        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);
        }
Example #3
0
        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();
        }
Example #5
0
        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));
        }
Example #6
0
 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);
     }
 }
Example #7
0
        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);
        }
Example #8
0
        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();
        }
Example #9
0
        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);
            }
        }
Example #11
0
        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);
            }
        }
Example #12
0
        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);
                }
            }
        }
Example #13
0
        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);
                }
            }
        }
Example #15
0
        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;
            }
        }
Example #16
0
        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);
        }
Example #18
0
        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);
                }
            }
        }
Example #20
0
        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);
                }
            }
        }