static string[] OnWillSaveAssets(string[] assetPaths)
        {
            //find all TextureRecipe assets, then check if any are being saved
            List <string> recipeAssetPaths = new List <string>();

            var recipeGUIDs = AssetDatabase.FindAssets("t:TextureRecipe");

            foreach (var recipeID in recipeGUIDs)
            {
                recipeAssetPaths.Add(AssetDatabase.GUIDToAssetPath(recipeID));
            }

            foreach (string assetPath in assetPaths)
            {
                if (recipeAssetPaths.Contains(assetPath))
                {
                    //We are saving this recipe so generate any other assets that it requires
                    TextureRecipe recipe = AssetDatabase.LoadAssetAtPath <TextureRecipe>(assetPath);
                    GeneratorFactory.createMappings();

                    var builtSubAssetPaths = bakeSubAssets(recipe);

                    var oldAssetPaths = new List <string>(assetPaths);
                    oldAssetPaths.AddRange(builtSubAssetPaths);
                    assetPaths = oldAssetPaths.ToArray();
                }
            }

            return(assetPaths);
        }
        static void bakeShaderLayerSubAssets(ShaderLayer shaderLayer, TextureRecipe recipe, List <string> subAssetPaths)
        {
            var shader = ShaderGenerator.bakeShader(shaderLayer, recipe);

            if (null == shader)
            {
                UnityEngine.Debug.LogError("Failed to load shader " + shaderLayer.name);
                return;
            }
            subAssetPaths.Add(shader.name);

            foreach (var node in shaderLayer.nodes)
            {
                if (!node.assetsDirty)
                {
                    continue;
                }

                var nodeAssetGenerator = GeneratorFactory.getAssetGenerator(node);
                if (null != nodeAssetGenerator)
                {
                    var newAssetPaths = nodeAssetGenerator.generateAssets(node);
                    if (newAssetPaths.Count > 0)
                    {
                        subAssetPaths.AddRange(newAssetPaths);
                    }
                    node.assetsDirty = false;
                }
            }
        }
Example #3
0
        public TextureRecipeRenderer(TextureRecipe _recipe)
        {
            //We are performing a deep copy of the TextureRecipe asset hierarchy. This is so that changes made to the TextureRecipe asset at runtime in the editor
            //don't persist after exiting Play Mode. It also allows us to have multiple renderers using the same TextureRecipe as a source without writing over
            //each others settings. We need to traverse the layer and node hierarchies to clone each object individually and also to remap any references onto the
            //newly-instantiated objects.
            //TODO: move the layer copying logic into the layer classes to give better layer abstraction
            recipe           = GameObject.Instantiate(_recipe); //TODO: better naming here
            recipe.name      = _recipe.name;                    //TODO: necessary?
            recipe.unique_id = _recipe.unique_id;

            int numLayers = recipe.layerList.Count;

            for (int i = 0; i < numLayers; i++)
            {
                var originalLayer = recipe.layerList[i];
                var newLayer      = GameObject.Instantiate(originalLayer);
                recipe.layerList[i] = newLayer;

                //TODO: layer abstraction
                if (originalLayer is ShaderLayer)
                {
                    Dictionary <BaseNode, BaseNode> nodeRemap = new Dictionary <BaseNode, BaseNode>();
                    var shaderLayer = (ShaderLayer)recipe.layerList[i];
                    int numNodes    = shaderLayer.nodes.Count;
                    for (int j = 0; j < numNodes; j++)
                    {
                        var originalNode = shaderLayer.nodes[j];
                        var newNode      = GameObject.Instantiate(originalNode);
                        shaderLayer.nodes[j]    = newNode;
                        nodeRemap[originalNode] = newNode;
                    }

                    for (int j = 0; j < numNodes; j++)
                    {
                        var newNode = shaderLayer.nodes[j];
                        for (int k = 0; k < newNode.inputs.Count; k++)
                        {
                            var oldInputNode = newNode.inputs[k];
                            if (null != oldInputNode.inputNode)
                            {
                                // Debug.Assert(nodeRemap.ContainsKey(oldInputNode.inputNode));
                                if (nodeRemap.ContainsKey(oldInputNode.inputNode))
                                {
                                    BaseNode.NodeInput input = newNode.inputs[k];
                                    input.inputNode   = nodeRemap[oldInputNode.inputNode];
                                    newNode.inputs[k] = input;
                                    Debug.Assert(newNode.inputs[k].inputNode.name.Contains("Clone"));
                                }
                            }
                        }
                    }

                    shaderLayer.root = (RootNode)nodeRemap[shaderLayer.root];
                }
            }
        }
        public override void OnPreviewGUI(Rect r, GUIStyle s)
        {
            //We don't want any of our preview in the header, just normal header elements
            if (drawingHeader)
            {
                return;
            }

            TextureRecipe targetRecipe = (TextureRecipe)target;

            EditorGUILayout.BeginHorizontal();

            bool autoRefreshWasEnabled = targetRecipe.autoRefresh;

            targetRecipe.autoRefresh = GUILayout.Toggle(targetRecipe.autoRefresh, "auto");
            if (!autoRefreshWasEnabled && targetRecipe.autoRefresh)
            {
                refreshPreviewTexture = true;
            }

            if (GUILayout.Button(refreshTex, GUILayout.Width(30.0f)))
            {
                refreshPreviewTexture = true;
            }

            EditorGUILayout.EndHorizontal();
            Texture2D previewTex = null;
            string    key        = AssetDatabase.GetAssetPath(target);

            previewTextures.TryGetValue(key, out previewTex);

            if (!previewTex)
            {
                previewTex           = new Texture2D(128, 128);
                previewTextures[key] = previewTex;
            }

            UpdatePreviewTexture(targetRecipe, previewTex);

            if (r.width > r.height)
            {
                r.x     = (r.width - r.height) / 2.0f;
                r.width = r.height;
            }
            else
            {
                r.y     += (r.height - r.width) / 2.0f;
                r.height = r.width;
            }

            EditorGUI.DrawTextureTransparent(r, previewTex);
        }
 static void buildModifiedShaders()
 {
     if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
     {
         //TODO: is there a way to figure out which shaders need building?
         var recipeGUIDs = AssetDatabase.FindAssets("t:TextureRecipe");
         foreach (var recipeID in recipeGUIDs)
         {
             string        assetPath = AssetDatabase.GUIDToAssetPath(recipeID);
             TextureRecipe recipe    = AssetDatabase.LoadAssetAtPath <TextureRecipe>(assetPath);
             GeneratorFactory.createMappings();
             bakeSubAssets(recipe);
         }
     }
 }
        public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height)
        {
            Texture2D previewTex = null;

            previewTextures.TryGetValue(assetPath, out previewTex);
            if (null == previewTex)
            {
                TextureRecipe targetRecipe = (TextureRecipe)target;

                previewTex = new Texture2D(128, 128);
                previewTextures[assetPath] = previewTex;

                UpdatePreviewTexture(targetRecipe, previewTex);
            }
            return(Instantiate(previewTex));
        }
        public static List <string> bakeSubAssets(TextureRecipe recipe)
        {
            List <string> subAssetPaths = new List <string>();

            foreach (var layer in recipe.layerList)
            {
                if (layer is ShaderLayer)
                {
                    bakeShaderLayerSubAssets((ShaderLayer)layer, recipe, subAssetPaths);
                }
            }

            if (subAssetPaths.Count > 0)
            {
                AssetDatabase.Refresh();
            }
            return(subAssetPaths);
        }
        void CreateLayerCallback(object obj)
        {
            Type          objectType = (Type)obj;
            TextureRecipe t          = (TextureRecipe)target;

            Undo.RecordObject(t, "Create " + objectType.Name);
            {
                var layerObject = (RecipeLayerBase)CreateInstance(objectType);
                t.layerList.Add(layerObject);

                layerObject.layerName = Utilities.MakeNiceName(objectType.Name);

                AssetDatabase.AddObjectToAsset(layerObject, t);
                AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(layerObject));
            }
            Undo.FlushUndoRecordObjects();

            serializedObject.Update();

            EditorUtility.SetDirty(target);
        }
        void UpdatePreviewTexture(TextureRecipe targetRecipe, Texture2D previewTex)
        {
            if (!refreshPreviewTexture)
            {
                return;
            }

            refreshPreviewTexture = false;

            GeneratorFactory.createMappings();
            RecipeBuildPreProcess.bakeSubAssets(targetRecipe);

            TextureRecipeRenderer renderer = new TextureRecipeRenderer(targetRecipe);

            renderer.renderSetup(previewTex);
            renderer.renderRecipeInternal();

            previewTex.ReadPixels(new Rect(0, 0, previewTex.width, previewTex.height), 0, 0);
            previewTex.Apply();

            RenderTexture.active = null;

            renderer.renderTeardown();
        }
        private void DrawListElement(Rect rect, int index, bool active, bool focused)
        {
            serializedObject.Update();

            var layerList = serializedObject.FindProperty("layerList");

            if (index < layerList.arraySize)
            {
                var listItem = layerList.GetArrayElementAtIndex(index);
                if (null == listItem.objectReferenceValue)
                {
                    return;
                }

                RecipeLayerBase layerObj = (RecipeLayerBase)listItem.objectReferenceValue;

                var layerType = listItem.objectReferenceValue.GetType();
                var ft        = layerType.GetField("layerColor");
                EditorGUI.DrawRect(rect, (Color)ft.GetValue(null));

                SerializedObject subObject = new SerializedObject(listItem.objectReferenceValue);
                var nameProp = subObject.FindProperty("layerName");

                string layerName = nameProp.stringValue;

                if (layerName == null || layerName == "")
                {
                    layerName = Utilities.MakeNiceName(layerName);
                }

                var textRect       = rect;
                var textDimensions = GUI.skin.label.CalcSize(new GUIContent(layerName));
                textRect.xMax = textRect.xMin + textDimensions.x + 3;

                GUI.backgroundColor = new Color(1.0f, 1.0f, 1.0f, 0.5f);

                EditorGUI.BeginChangeCheck();
                string newLayerName = EditorGUI.TextField(textRect, layerName);
                //ensure name value is unique if it changed
                if (EditorGUI.EndChangeCheck())
                {
                    TextureRecipe recipe          = (TextureRecipe)target;
                    bool          nameAlreadyUsed = false;
                    foreach (var layer in recipe.layerList)
                    {
                        if (layer.layerName == newLayerName)
                        {
                            nameAlreadyUsed = true;
                            break;
                        }
                    }

                    if (!nameAlreadyUsed)
                    {
                        nameProp.stringValue = newLayerName;
                    }
                }

                subObject.ApplyModifiedProperties();
            }
        }
        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            TextureRecipe targetRecipe  = (TextureRecipe)target;
            var           texWidthProp  = serializedObject.FindProperty("TextureWidth");
            var           texHeightProp = serializedObject.FindProperty("TextureHeight");

            texWidthProp.intValue  = EditorGUILayout.IntField("Width", texWidthProp.intValue);
            texHeightProp.intValue = EditorGUILayout.IntField("Height", texHeightProp.intValue);
            serializedObject.ApplyModifiedProperties();

            if (GUILayout.Button("Bake"))
            {
                GeneratorFactory.createMappings();
                RecipeBuildPreProcess.bakeSubAssets(targetRecipe);

                TextureRecipeRenderer renderer = new TextureRecipeRenderer(targetRecipe);
                renderer.renderSetup();
                renderer.renderRecipeInternal();

                TextureWriter.WriteRenderBufferToTextureFile(targetRecipe.TextureWidth, targetRecipe.TextureHeight, targetRecipe);
                RenderTexture.active = null;

                renderer.renderTeardown();
            }

            reorderableList.DoLayoutList();

            int listIndex = reorderableList.index;

            var layerList = serializedObject.FindProperty("layerList");

            if (listIndex >= 0 && listIndex < layerList.arraySize)
            {
                var listItem = layerList.GetArrayElementAtIndex(listIndex);
                if (null != listItem.objectReferenceValue)
                {
                    if (subEditorType != listItem.objectReferenceValue.GetType())
                    {
                        subEditor = null;
                    }
                    EditorGUI.BeginChangeCheck();

                    ShaderLayerEditor.recipe = (TextureRecipe)target;
                    CreateCachedEditor(listItem.objectReferenceValue, null, ref subEditor);
                    subEditorType = listItem.objectReferenceValue.GetType();

                    subEditor.serializedObject.Update();
                    subEditor.OnInspectorGUI();
                    subEditor.serializedObject.ApplyModifiedProperties();

                    serializedObject.ApplyModifiedProperties();
                    if (EditorGUI.EndChangeCheck() && targetRecipe.autoRefresh)
                    {
                        //do we need this if we set dirty on the target every frame?
                        EditorUtility.SetDirty(listItem.objectReferenceValue);
                        refreshPreviewTexture = true;
                    }

                    if (ShaderLayerEditor.nodeEditor && ShaderLayerEditor.nodeEditor.refreshShader && targetRecipe.autoRefresh)
                    {
                        refreshPreviewTexture = true;
                        ShaderLayerEditor.nodeEditor.refreshShader = false;
                    }
                }
            }

            //We have to do this every frame otherwise changes to the child objects aren't reflected in the inspector when we Undo them
            //there is perhaps an issue with the order of operations
            EditorUtility.SetDirty(target);
        }
Example #12
0
 public static string getShaderName(TextureRecipe recipe, ShaderLayer shaderLayer)
 {
     return(recipe.unique_id + "-" + recipe.name + "-" + shaderLayer.layerName);
 }
Example #13
0
 public static string getShaderPath(TextureRecipe recipe, ShaderLayer shaderLayer)
 {
     return("TextureKit/" + getShaderName(recipe, shaderLayer));
 }
Example #14
0
        public static Shader bakeShader(ShaderLayer shaderLayer, TextureRecipe recipe)
        {
            TextAsset shaderBaseText = (TextAsset)Resources.Load("template.shader");

            if (shaderBaseText == null)
            {
                Debug.LogError("Failed to template shader");
                return(null);
            }

            string        shaderName = ShaderLayer.getShaderName(recipe, shaderLayer);
            string        shaderPath = ShaderLayer.getShaderPath(recipe, shaderLayer);
            string        shaderText = shaderBaseText.text;
            List <string> lines      = shaderText.Split('\n').ToList <string>();

            int numLines = lines.Count;

            for (int i = 0; i < numLines; i++)
            {
                string line = lines[i];
                if (line.Contains("$$"))
                {
                    if (line.Contains("$$ASSETNAME"))
                    {
                        line     = line.Replace("$$ASSETNAME", shaderPath);
                        lines[i] = line;
                    }
                    else if (line.Contains("$$STARTFUNCS"))
                    {
                        var funcs = getFunctions(shaderLayer);
                        lines.InsertRange(i + 1, funcs);
                        i       += funcs.Count;
                        numLines = lines.Count;
                    }
                    else if (line.Contains("$$STARTPROPS"))
                    {
                        var props = getProperties(shaderLayer);
                        lines.InsertRange(i + 1, props);
                        i       += props.Count;
                        numLines = lines.Count;
                    }
                    else if (line.Contains("$$STARTINPUTS"))
                    {
                        var inputs = getInputs(shaderLayer);
                        lines.InsertRange(i + 1, inputs);
                        i       += inputs.Count;
                        numLines = lines.Count;
                    }
                    else if (line.Contains("$$STARTSHADER"))
                    {
                        lines.Insert(i + 1, getCalls(shaderLayer));
                        numLines = lines.Count;
                        i++;
                    }
                }
            }

            string baseAssetPath   = "Assets/TextureRecipes/Resources/";
            string shaderAssetPath = baseAssetPath + shaderName + ".shader";

            //Can't do this because we want to overwrite an existing asset! shaderPath = AssetDatabase.GenerateUniqueAssetPath(shaderPath);

            //Generating the text for the shader is a very fast operation. Rather than trying to figure out which changes
            //would have altered the shader, we simply generate the shader as a byproduct of every change, and then see
            //if it has changed since the last time we output it. The really expensive part is outputting the shader and
            //triggering the Unity shader post-process and compilation steps (we're talking >1.5s vs 3ms).
            string[] existingLines = null;
            existingShaderLines.TryGetValue(shaderPath, out existingLines);

            string[] newLines = lines.ToArray();
            if (existingLines != null)
            {
                if (existingLines.Length == newLines.Length)
                {
                    bool allMatch = true;
                    for (int i = 0; allMatch && (i < newLines.Length); i++)
                    {
                        if (existingLines[i] != newLines[i])
                        {
                            allMatch = false;
                        }
                    }

                    if (allMatch)
                    {
                        return(Shader.Find(shaderPath));
                    }
                }
            }

            //File.WriteAllLines(shaderAssetPath, newLines);
            using (StreamWriter writer = new StreamWriter(shaderAssetPath, false))
            {
                writer.NewLine = "\n";
                foreach (string str in newLines)
                {
                    writer.WriteLine(str);
                }
            }

            existingShaderLines[shaderPath] = newLines;

            AssetDatabase.Refresh();
            return(Shader.Find(shaderPath));
        }