Пример #1
0
        // error messages should be reported through the asset import context, so that object references are translated properly (in the future), and the error is associated with the import
        static void ReportErrors(GraphData graph, Shader shader, string path, AssetImportErrorLog importLog)
        {
            // Grab any messages from the shader compiler
            var messages = ShaderUtil.GetShaderMessages(shader);

            var errors   = graph.messageManager.ErrorStrings((nodeId) => NodeWasUsedByGraph(nodeId, graph));
            int errCount = errors.Count();

            // Find the first compiler message that's an error
            int firstShaderUtilErrorIndex = -1;

            if (messages != null)
            {
                firstShaderUtilErrorIndex = Array.FindIndex(messages, m => (m.severity == Rendering.ShaderCompilerMessageSeverity.Error));
            }

            // Display only one message. Bias towards shader compiler messages over node messages and within that bias errors over warnings.
            if (firstShaderUtilErrorIndex != -1)
            {
                // if shader compiler reported an error, show that
                MessageManager.Log(path, messages[firstShaderUtilErrorIndex], shader, importLog);
            }
            else if (errCount > 0)
            {
                // otherwise show node errors
                var firstError = errors.FirstOrDefault();
                importLog.LogError($"Shader Graph at {path} has {errCount} error(s), the first is: {firstError}", shader);
            }
            else if (messages.Length != 0)
            {
                // otherwise show shader compiler warnings
                MessageManager.Log(path, messages[0], shader, importLog);
            }
            else if (graph.messageManager.nodeMessagesChanged)
            {
                // otherwise show node warnings
                var warnings     = graph.messageManager.ErrorStrings((nodeId) => NodeWasUsedByGraph(nodeId, graph), Rendering.ShaderCompilerMessageSeverity.Warning);
                var warnCount    = warnings.Count();
                var firstWarning = warnings.FirstOrDefault();
                if (warnCount > 0)
                {
                    importLog.LogWarning($"Shader Graph at {path} has {warnCount} warning(s), the first is: {firstWarning}", shader);
                }
            }
        }
Пример #2
0
        Shader BuildAllShaders(
            AssetImportContext importContext,
            AssetImportErrorLog importErrorLog,
            AssetCollection allImportAssetDependencies,
            GraphData graph)
        {
            Shader primaryShader = null;

            string path = importContext.assetPath;
            var    primaryShaderName = Path.GetFileNameWithoutExtension(path);

            try
            {
                // this will also add Target dependencies into the asset collection
                Generator generator;
                generator = new Generator(graph, graph.outputNode, GenerationMode.ForReals, primaryShaderName, assetCollection: allImportAssetDependencies);

                bool first = true;
                foreach (var generatedShader in generator.allGeneratedShaders)
                {
                    var shaderString = generatedShader.codeString;

                    // we only care if an error was reported for a node that we actually used
                    if (graph.messageManager.AnyError((nodeId) => NodeWasUsedByGraph(nodeId, graph)) ||
                        shaderString == null)
                    {
                        shaderString = k_ErrorShader.Replace("Hidden/GraphErrorShader2", generatedShader.shaderName);
                    }

                    var shader = ShaderUtil.CreateShaderAsset(importContext, shaderString, false);

                    ReportErrors(graph, shader, path, importErrorLog);

                    EditorMaterialUtility.SetShaderDefaults(
                        shader,
                        generatedShader.assignedTextures.Where(x => x.modifiable).Select(x => x.name).ToArray(),
                        generatedShader.assignedTextures.Where(x => x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray());

                    EditorMaterialUtility.SetShaderNonModifiableDefaults(
                        shader,
                        generatedShader.assignedTextures.Where(x => !x.modifiable).Select(x => x.name).ToArray(),
                        generatedShader.assignedTextures.Where(x => !x.modifiable).Select(x => EditorUtility.InstanceIDToObject(x.textureId) as Texture).ToArray());

                    if (first)
                    {
                        // first shader is always the primary shader
                        // we return the primary shader so it can be attached to the import context at the outer level
                        // allowing it to bind a custom icon as well
                        primaryShader = shader;

                        // only the main shader gets a material created
                        Material material = new Material(shader)
                        {
                            name = primaryShaderName + " Material"
                        };
                        importContext.AddObjectToAsset("Material", material);

                        first = false;
                    }
                    else
                    {
                        importContext.AddObjectToAsset($"Shader-{generatedShader.shaderName}", shader);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                // ignored
            }

            return(primaryShader);
        }
Пример #3
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);
                }
            }
        }