Exemple #1
0
        static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
        {
            var cfgs = AssetDatabase.FindAssets("t:TextureArrayConfig");

            for (int i = 0; i < cfgs.Length; ++i)
            {
                var asset = AssetDatabase.GUIDToAssetPath(cfgs[i]);
                var cfg   = AssetDatabase.LoadAssetAtPath <TextureArrayConfig>(asset);
                if (cfg != null)
                {
                    int hash = GetNewHash(cfg);
                    if (hash != cfg.hash)
                    {
                        cfg.hash = hash;
                        EditorUtility.SetDirty(cfg);
                        try
                        {
                            sIsPostProcessing = true;
                            TextureArrayConfigEditor.CompileConfig(cfg);
                        }
                        finally
                        {
                            sIsPostProcessing = false;
                            AssetDatabase.Refresh();
                            AssetDatabase.SaveAssets();
                            MicroSplatTerrain.SyncAll();
                        }
                    }
                }
            }
        }
Exemple #2
0
    public void ComputeCavityFlowMap(MicroSplatTerrain bt)
    {
        Terrain   t    = bt.terrain;
        Texture2D data = new Texture2D(t.terrainData.heightmapResolution, t.terrainData.heightmapResolution, TextureFormat.RGBA32, true, true);

        CurvatureMapGenerator.CreateMap(t.terrainData.GetHeights(0, 0, data.width, data.height), data);

        var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData);

        path += "/" + t.name + "_cavity.png";
        var bytes = data.EncodeToPNG();

        System.IO.File.WriteAllBytes(path, bytes);
        GameObject.DestroyImmediate(data);
        AssetDatabase.Refresh();
        bt.cavityMap = AssetDatabase.LoadAssetAtPath <Texture2D>(path);
        var ai = AssetImporter.GetAtPath(path);
        var ti = ai as TextureImporter;

        if (ti.sRGBTexture != false)
        {
            ti.sRGBTexture = false;
            ti.SaveAndReimport();
        }
        bt.sTerrainDirty = false;
        EditorUtility.SetDirty(bt);
        EditorUtility.SetDirty(t);
        MicroSplatTerrain.SyncAll();
        AssetDatabase.SaveAssets();
    }
Exemple #3
0
    public static void GenerateTerrainBlendData(MicroSplatTerrain bt)
    {
        Terrain t = bt.GetComponent <Terrain>();
        int     w = t.terrainData.heightmapResolution;
        int     h = t.terrainData.heightmapResolution;

        Texture2D data = new Texture2D(w, h, TextureFormat.RGBAHalf, true, true);

        for (int x = 0; x < w; ++x)
        {
            for (int y = 0; y < h; ++y)
            {
                float   height = t.terrainData.GetHeight(x, y);
                Vector3 normal = t.terrainData.GetInterpolatedNormal((float)x / w, (float)y / h);
                // When you save a texture to EXR format, either in the saving or import stage,
                // some type of gamma curve is applied regardless of the fact that the textures is
                // set to linear. So we pow it here to counteract it, whis is total BS, but works..
                normal.x = (normal.x >= 0) ? Mathf.Pow(normal.x, 2.0f) : Mathf.Pow(normal.x, 2) * -1;
                normal.z = (normal.z >= 0) ? Mathf.Pow(normal.z, 2.0f) : Mathf.Pow(normal.z, 2) * -1;
                data.SetPixel(x, y, new Color(normal.x, normal.y, normal.z, height));
            }
        }
        data.Apply();

        var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData);

        path += "/" + t.name + ".exr";
        var bytes = data.EncodeToEXR(Texture2D.EXRFlags.OutputAsFloat);

        System.IO.File.WriteAllBytes(path, bytes);
        GameObject.DestroyImmediate(data);
        AssetDatabase.Refresh();
        bt.terrainDesc = AssetDatabase.LoadAssetAtPath <Texture2D>(path);
        var ai = AssetImporter.GetAtPath(path);
        var ti = ai as TextureImporter;
        var ps = ti.GetDefaultPlatformTextureSettings();

        if (ti.isReadable == true ||
            ti.wrapMode != TextureWrapMode.Clamp ||
            ps.format != TextureImporterFormat.RGBAHalf ||
            ps.textureCompression != TextureImporterCompression.Uncompressed ||
            ps.overridden != true ||
            ti.filterMode != FilterMode.Bilinear ||
            ti.sRGBTexture != false)
        {
            ti.sRGBTexture        = false;
            ti.filterMode         = FilterMode.Bilinear;
            ti.mipmapEnabled      = true;
            ti.wrapMode           = TextureWrapMode.Clamp;
            ti.isReadable         = false;
            ps.format             = TextureImporterFormat.RGBAHalf;
            ps.textureCompression = TextureImporterCompression.Uncompressed;
            ps.overridden         = true;
            ti.SetPlatformTextureSettings(ps);
            ti.SaveAndReimport();
        }
        bt.sTerrainDirty = false;
        EditorUtility.SetDirty(bt);
        MicroSplatTerrain.SyncAll();
    }
    public void DoTerrainDescGUI()
    {
        MicroSplatTerrain bt = target as MicroSplatTerrain;
        Terrain           t  = bt.GetComponent <Terrain>();

        if (t == null || t.terrainData == null)
        {
            return;
        }
        if (t.materialTemplate == null)
        {
            return;
        }
        if (bt.keywordSO == null)
        {
            return;
        }

        if (!bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING") && !bt.keywordSO.IsKeywordEnabled("_DYNAMICFLOWS"))
        {
            return;
        }
        EditorGUILayout.Space();

        if (bt.blendMat == null && bt.templateMaterial != null && bt.keywordSO != null && bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING"))
        {
            var path = AssetDatabase.GetAssetPath(bt.templateMaterial);
            path        = path.Replace(".mat", "_TerrainObjectBlend.mat");
            bt.blendMat = AssetDatabase.LoadAssetAtPath <Material>(path);
            if (bt.blendMat == null)
            {
                string shaderPath = path.Replace(".mat", ".shader");
                Shader shader     = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath);
                if (shader == null)
                {
                    shaderPath = path.Replace(".shader", ".surfshader");
                    shader     = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath);
                }

                if (shader == null)
                {
                    shaderPath = AssetDatabase.GetAssetPath(bt.templateMaterial.shader);
                    shaderPath = shaderPath.Replace(".shader", "_TerrainObjectBlend.shader");
                    shaderPath = shaderPath.Replace(".surfshader", "_TerrainObjectBlend.surfshader");
                    shader     = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath);
                }
                if (shader != null)
                {
                    Material mat = new Material(shader);
                    AssetDatabase.CreateAsset(mat, path);
                    AssetDatabase.SaveAssets();
                    MicroSplatTerrain.SyncAll();
                }
            }
        }
        EditorUtility.SetDirty(bt);
        EditorUtility.SetDirty(bt.terrain);
    }
Exemple #5
0
    public static void SyncAll()
    {
        MicroSplatTerrain.SyncAll();
#if __MICROSPLAT_MESHTERRAIN__
        MicroSplatMeshTerrain.SyncAll();
#endif
#if __MICROSPLAT_MESH__
        MicroSplatMesh.SyncAll();
#endif
    }
        void RevertTerrainSplats(MicroSplatTerrain t)
        {
            MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);

            if (keywords.IsKeywordEnabled("_CUSTOMSPLATTEXTURES"))
            {
                if (t.customControl0 == null)
                {
                    Debug.LogError("Could not revert terrain because textures are missing!");
                    return;
                }

                UncompressTexture(t.customControl0);
                UncompressTexture(t.customControl1);
                UncompressTexture(t.customControl2);
                UncompressTexture(t.customControl3);
                UncompressTexture(t.customControl4);
                UncompressTexture(t.customControl5);
                UncompressTexture(t.customControl6);
                UncompressTexture(t.customControl7);

                int size   = t.customControl0.width;
                int layers = t.terrain.terrainData.alphamapLayers;
                t.terrain.terrainData.alphamapResolution = size;
                var maps = t.terrain.terrainData.GetAlphamaps(0, 0, size, size);

                ExtractSplats(ref maps, t.customControl0, 0, layers);
                ExtractSplats(ref maps, t.customControl1, 4, layers);
                ExtractSplats(ref maps, t.customControl2, 8, layers);
                ExtractSplats(ref maps, t.customControl3, 12, layers);
                ExtractSplats(ref maps, t.customControl4, 16, layers);
                ExtractSplats(ref maps, t.customControl5, 20, layers);
                ExtractSplats(ref maps, t.customControl6, 24, layers);
                ExtractSplats(ref maps, t.customControl7, 28, layers);

                t.terrain.terrainData.SetAlphamaps(0, 0, maps);
                EditorUtility.SetDirty(t.terrain.terrainData);


                keywords.DisableKeyword("_CUSTOMSPLATTEXTURES");
                MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler();
                compiler.Compile(t.templateMaterial);
                t.customControl0 = null;
                t.customControl1 = null;
                t.customControl2 = null;
                t.customControl3 = null;
                t.customControl4 = null;
                t.customControl5 = null;
                t.customControl6 = null;
                t.customControl7 = null;
                EditorUtility.SetDirty(t);
                MicroSplatTerrain.SyncAll();
            }
        }
Exemple #7
0
    public static void GenerateTerrainNormalMap(MicroSplatTerrain bt)
    {
        Terrain t = bt.GetComponent <Terrain>();
        int     w = t.terrainData.heightmapResolution;
        int     h = t.terrainData.heightmapResolution;

        Texture2D data = new Texture2D(w, h, TextureFormat.RGBA32, true, true);

        for (int x = 0; x < w; ++x)
        {
            for (int y = 0; y < h; ++y)
            {
                Vector3 normal = t.terrainData.GetInterpolatedNormal((float)x / w, (float)y / h);
                data.SetPixel(x, y, new Color(normal.x * 0.5f + 0.5f, normal.z * 0.5f + 0.5f, normal.y));
            }
        }
        data.Apply();

        var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData);

        path += "/" + t.name + "_normal.png";
        var bytes = data.EncodeToPNG();

        System.IO.File.WriteAllBytes(path, bytes);
        GameObject.DestroyImmediate(data);
        AssetDatabase.Refresh();
        bt.perPixelNormal = AssetDatabase.LoadAssetAtPath <Texture2D>(path);
        var ai = AssetImporter.GetAtPath(path);
        var ti = ai as TextureImporter;
        var ps = ti.GetDefaultPlatformTextureSettings();

        if (ti.isReadable == true ||
            ti.wrapMode != TextureWrapMode.Clamp ||
            ps.overridden != true ||
            ti.textureType != TextureImporterType.NormalMap)

        {
            ti.textureType   = TextureImporterType.NormalMap;
            ti.mipmapEnabled = true;
            ti.wrapMode      = TextureWrapMode.Clamp;
            ti.isReadable    = false;
            ps.overridden    = true;
            ti.SetPlatformTextureSettings(ps);
            ti.SaveAndReimport();
        }
        bt.sTerrainDirty = false;
        EditorUtility.SetDirty(bt);
        EditorUtility.SetDirty(bt.terrain);
        MicroSplatTerrain.SyncAll();
        AssetDatabase.SaveAssets();
    }
    public static void SyncAll()
    {
        MicroSplatTerrain.SyncAll();
#if __MICROSPLAT_MESHTERRAIN__
        MicroSplatMeshTerrain.SyncAll();
#endif
#if __MICROSPLAT_MESH__
        MicroSplatMesh.SyncAll();
        MicroSplatVertexMesh.SyncAll();
#endif
#if __MICROSPLAT_POLARIS__
        MicroSplatPolarisMesh.SyncAll();
#endif
    }
        private static void CheckConfigForUpdates(TextureArrayConfig cfg)
        {
            int hash = GetNewHash(cfg);

            if (hash != cfg.hash)
            {
                cfg.hash = hash;
                EditorUtility.SetDirty(cfg);
                try
                {
                    sIsPostProcessing = true;
                    TextureArrayConfigEditor.CompileConfig(cfg);
                }
                finally
                {
                    sIsPostProcessing = false;
                    AssetDatabase.Refresh();
                    AssetDatabase.SaveAssets();
                    MicroSplatTerrain.SyncAll();
                }
            }
        }
Exemple #10
0
    public void DoTerrainDescGUI()
    {
        MicroSplatTerrain bt = target as MicroSplatTerrain;
        Terrain           t  = bt.GetComponent <Terrain>();

        if (t == null || t.terrainData == null)
        {
            EditorGUILayout.HelpBox("No Terrain found, please add this component to your terrain", MessageType.Error);
            return;
        }
        if (t.materialTemplate == null)
        {
            return;
        }
        if (bt.keywordSO == null)
        {
            return;
        }

        if (!bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING") && !bt.keywordSO.IsKeywordEnabled("_DYNAMICFLOWS"))
        {
            return;
        }
        EditorGUILayout.Space();


        if (bt.terrainDesc == null)
        {
            EditorGUILayout.HelpBox("Terrain Descriptor Data is not present, please generate", MessageType.Info);
        }

        if (bt.terrainDesc != null && bt.sTerrainDirty)
        {
            EditorGUILayout.HelpBox("Terrain Descriptor data is out of date, please update", MessageType.Info);
        }
        if (GUILayout.Button(bt.terrainDesc == null ? "Generate Terrain Descriptor Data" : "Update Terrain Descriptor Data"))
        {
            GenerateTerrainBlendData(bt);
        }

        if (bt.terrainDesc != null && GUILayout.Button("Clear Terrain Descriptor Data"))
        {
            bt.terrainDesc = null;
        }

        if (bt.terrainDesc != null)
        {
            EditorGUILayout.BeginHorizontal();
            int mem = bt.terrainDesc.width * bt.terrainDesc.height;
            mem /= 128;
            EditorGUILayout.LabelField("Terrain Descriptor Data Memory: " + mem.ToString() + "kb");
            EditorGUILayout.EndHorizontal();
        }

        if (bt.blendMat == null && bt.templateMaterial != null && bt.keywordSO != null && bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING"))
        {
            var path = AssetDatabase.GetAssetPath(bt.templateMaterial);
            path        = path.Replace(".mat", "_TerrainObjectBlend.mat");
            bt.blendMat = AssetDatabase.LoadAssetAtPath <Material>(path);
            if (bt.blendMat == null)
            {
                string shaderPath = path.Replace(".mat", ".shader");
                Shader shader     = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath);
                if (shader == null)
                {
                    shaderPath = AssetDatabase.GetAssetPath(bt.templateMaterial.shader);
                    shaderPath = shaderPath.Replace(".shader", "_TerrainObjectBlend.shader");
                    shader     = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath);
                }
                if (shader != null)
                {
                    Material mat = new Material(shader);
                    AssetDatabase.CreateAsset(mat, path);
                    AssetDatabase.SaveAssets();
                    MicroSplatTerrain.SyncAll();
                }
            }
        }
        EditorUtility.SetDirty(bt);
        EditorUtility.SetDirty(bt.terrain);
    }
Exemple #11
0
    public override void OnInspectorGUI()
    {
        MicroSplatTerrain t = target as MicroSplatTerrain;

        if (t == null)
        {
            EditorGUILayout.HelpBox("No Terrain Present, please put this component on a terrain", MessageType.Error);
            return;
        }
        EditorGUI.BeginChangeCheck();
        t.templateMaterial = EditorGUILayout.ObjectField(CTemplateMaterial, t.templateMaterial, typeof(Material), false) as Material;
        t.propData         = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
        if (EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(t);
        }
        if (t.templateMaterial == null)
        {
            if (GUILayout.Button("Convert to MicroSplat"))
            {
                // get all terrains in selection, not just this one, and treat as one giant terrain
                var            objs     = Selection.gameObjects;
                List <Terrain> terrains = new List <Terrain>();
                for (int i = 0; i < objs.Length; ++i)
                {
                    Terrain ter = objs[i].GetComponent <Terrain>();
                    if (ter != null)
                    {
                        terrains.Add(ter);
                    }
                    Terrain[] trs = objs[i].GetComponentsInChildren <Terrain>();
                    for (int x = 0; x < trs.Length; ++x)
                    {
                        if (!terrains.Contains(trs[x]))
                        {
                            terrains.Add(trs[x]);
                        }
                    }
                }
                // setup this terrain
                Terrain terrain = t.GetComponent <Terrain>();
                t.templateMaterial = MicroSplatShaderGUI.NewShaderAndMaterial(terrain);
                var config = TextureArrayConfigEditor.CreateConfig(terrain);
                t.templateMaterial.SetTexture("_Diffuse", config.diffuseArray);
                t.templateMaterial.SetTexture("_NormalSAO", config.normalSAOArray);

                t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
                bool perTexScaleOffset = false;
                if (terrain.terrainData.splatPrototypes.Length > 0)
                {
                    var uvScale  = terrain.terrainData.splatPrototypes[0].tileSize;
                    var uvOffset = terrain.terrainData.splatPrototypes[0].tileOffset;
                    for (int i = 1; i < terrain.terrainData.splatPrototypes.Length; ++i)
                    {
                        if (uvScale != terrain.terrainData.splatPrototypes[0].tileSize ||
                            uvOffset != terrain.terrainData.splatPrototypes[0].tileOffset)
                        {
                            perTexScaleOffset = true;
                        }
                    }
                    if (!perTexScaleOffset)
                    {
                        uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                        uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                        uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                        Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                        t.templateMaterial.SetVector("_UVScale", scaleOffset);
                    }
                }

                if (perTexScaleOffset)
                {
                    t.templateMaterial.SetVector("_UVScale", new Vector4(1, 1, 0, 0));
                    t.templateMaterial.EnableKeyword("_PERTEXUVSCALE");
                    t.templateMaterial.EnableKeyword("_PERTEXUVOFFSET");
                    var propTex = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
                    // set per tex props for each texture
                    for (int i = 0; i < terrain.terrainData.splatPrototypes.Length; ++i)
                    {
                        var uvScale  = terrain.terrainData.splatPrototypes[i].tileSize;
                        var uvOffset = terrain.terrainData.splatPrototypes[i].tileOffset;
                        uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                        uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                        uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                        Color c = new Color(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                        propTex.SetValue(i, 0, c);
                    }
                }

                // now make sure others all have the same settings as well.
                for (int i = 0; i < terrains.Count; ++i)
                {
                    var nt  = terrains[i];
                    var mgr = nt.GetComponent <MicroSplatTerrain>();
                    if (mgr == null)
                    {
                        mgr = nt.gameObject.AddComponent <MicroSplatTerrain>();
                    }
                    mgr.templateMaterial = t.templateMaterial;

                    if (mgr.propData == null)
                    {
                        mgr.propData = MicroSplatShaderGUI.FindOrCreatePropTex(mgr.templateMaterial);
                    }
                }
                Selection.SetActiveObjectWithContext(config, config);
            }
            MicroSplatTerrain.SyncAll();
            return;
        }

        if (t.propData == null)
        {
            t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
        }

#if __MICROSPLAT_TERRAINBLEND__ || __MICROSPLAT_STREAMS__
        DoTerrainDescGUI();
#endif

        // could move this to some type of interfaced component created by the module if this becomes a thing,
        // but I think this will be most of the cases?

        MicroSplatUtilities.DrawTextureField(t, geoTexOverride, ref t.geoTextureOverride, "_GEOMAP");

        MicroSplatUtilities.DrawTextureField(t, geoTintOverride, ref t.tintMapOverride, "_GLOBALTINT");

        MicroSplatUtilities.DrawTextureField(t, geoNormalOverride, ref t.globalNormalOverride, "_GLOBALNORMALS");

        MicroSplatUtilities.DrawTextureField(t, AdvDetailControl, ref t.advDetailControl, "_ADVANCED_DETAIL");


        if (t.templateMaterial.IsKeywordEnabled("_VSGRASSMAP"))
        {
            EditorGUI.BeginChangeCheck();

            t.vsGrassMap = EditorGUILayout.ObjectField(CVSGrassMap, t.vsGrassMap, typeof(Texture2D), false) as Texture2D;

            if (EditorGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(t);
            }
        }

        if (t.templateMaterial.IsKeywordEnabled("_VSSHADOWMAP"))
        {
            EditorGUI.BeginChangeCheck();


            t.vsShadowMap = EditorGUILayout.ObjectField(CVSShadowMap, t.vsShadowMap, typeof(Texture2D), false) as Texture2D;

            if (EditorGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(t);
            }
        }


        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Sync"))
        {
            var mgr = target as MicroSplatTerrain;
            mgr.Sync();
        }
        if (GUILayout.Button("Sync All"))
        {
            MicroSplatTerrain.SyncAll();
        }
        EditorGUILayout.EndHorizontal();

        BakingGUI(t);
        WeightLimitingGUI(t);
    }
        public void Compile(Material m, string shaderName = null)
        {
            int hash = 0;

            for (int i = 0; i < m.shaderKeywords.Length; ++i)
            {
                hash += 31 + m.shaderKeywords[i].GetHashCode();
            }
            var    path = AssetDatabase.GetAssetPath(m.shader);
            string nm   = m.shader.name;

            if (!string.IsNullOrEmpty(shaderName))
            {
                nm = shaderName;
            }
            string baseName = "Hidden/" + nm + "_Base" + hash.ToString();

            string terrainShader = Compile(m.shaderKeywords, nm, baseName);

            if (renderLoop != null)
            {
                m.EnableKeyword(renderLoop.GetRenderLoopKeyword());
            }
            string blendShader = null;

            // strip extra feature from terrain blending to make it cheaper
            if (m.IsKeywordEnabled("_TERRAINBLENDING"))
            {
                List <string> blendKeywords = new List <string>(m.shaderKeywords);
                if (m.IsKeywordEnabled("_TBDISABLE_DETAILNOISE") && blendKeywords.Contains("_DETAILNOISE"))
                {
                    blendKeywords.Remove("_DETAILNOISE");
                }
                if (m.IsKeywordEnabled("_TBDISABLE_DETAILNOISE") && blendKeywords.Contains("_ANTITILEARRAYDETAIL"))
                {
                    blendKeywords.Remove("_ANTITILEARRAYDETAIL");
                }
                if (m.IsKeywordEnabled("_TBDISABLE_DISTANCENOISE") && blendKeywords.Contains("_DISTANCENOISE"))
                {
                    blendKeywords.Remove("_DISTANCENOISE");
                }
                if (m.IsKeywordEnabled("_TBDISABLE_DISTANCENOISE") && blendKeywords.Contains("_ANTITILEARRAYDISTANCE"))
                {
                    blendKeywords.Remove("_ANTITILEARRAYDISTANCE");
                }
                if (m.IsKeywordEnabled("_TBDISABLE_DISTANCERESAMPLE") && blendKeywords.Contains("_DISTANCERESAMPLE"))
                {
                    blendKeywords.Remove("_DISTANCERESAMPLE");
                }

                blendShader = Compile(blendKeywords.ToArray(), nm, null, true);
            }



            string meshBlendShader = null;

            if (m.IsKeywordEnabled("_MESHOVERLAYSPLATS"))
            {
                List <string> blendKeywords = new List <string>(m.shaderKeywords);
                if (blendKeywords.Contains("_TESSDISTANCE"))
                {
                    blendKeywords.Remove("_TESSDISTANCE");
                }
                meshBlendShader = Compile(blendKeywords.ToArray(), nm, null, true);
            }

            MicroSplatUtilities.Checkout(path);
            System.IO.File.WriteAllText(path, terrainShader);

            if (!m.IsKeywordEnabled("_MICROMESH"))
            {
                // generate fallback
                string[] oldKeywords = new string[m.shaderKeywords.Length];
                System.Array.Copy(m.shaderKeywords, oldKeywords, m.shaderKeywords.Length);
                m.DisableKeyword("_TESSDISTANCE");
                m.DisableKeyword("_PARALLAX");
                m.DisableKeyword("_DETAILNOISE");


                string fallback = Compile(m.shaderKeywords, baseName);
                m.shaderKeywords = oldKeywords;
                string fallbackPath = path.Replace(".shader", "_Base.shader");
                MicroSplatUtilities.Checkout(fallbackPath);
                System.IO.File.WriteAllText(fallbackPath, fallback);
            }


            string terrainBlendPath = path.Replace(".shader", "_TerrainObjectBlend.shader");
            string meshBlendPath    = path.Replace(".shader", "_MeshOverlay.shader");

            if (blendShader != null)
            {
                MicroSplatUtilities.Checkout(terrainBlendPath);
                System.IO.File.WriteAllText(terrainBlendPath, blendShader);
            }
            if (meshBlendShader != null)
            {
                MicroSplatUtilities.Checkout(meshBlendPath);
                System.IO.File.WriteAllText(meshBlendPath, meshBlendShader);
            }

            EditorUtility.SetDirty(m);
            AssetDatabase.Refresh();
            MicroSplatTerrain.SyncAll();
        }
Exemple #13
0
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
    {
        if (cachedTitle == null)
        {
            cachedTitle = "Shader Generator        v:" + MicroSplatVersion;
        }
        if (GUI.enabled == false)
        {
            EditorGUILayout.HelpBox("You must edit the template material, not the instance being used", MessageType.Info);
            return;
        }
        EditorGUI.BeginChangeCheck(); // sync materials
        Material       targetMat = materialEditor.target as Material;
        Texture2DArray diff      = targetMat.GetTexture("_Diffuse") as Texture2DArray;

        var keywordSO = MicroSplatUtilities.FindOrCreateKeywords(targetMat);

        compiler.Init();
        // must unpack everything before the generator draws- otherwise we get IMGUI errors
        for (int i = 0; i < compiler.extensions.Count; ++i)
        {
            var ext = compiler.extensions[i];
            ext.Unpack(keywordSO.keywords.ToArray());
        }

        string shaderName = targetMat.shader.name;

        DrawModules();

        EditorGUI.BeginChangeCheck(); // needs compile

        if (MicroSplatUtilities.DrawRollup(cachedTitle))
        {
            shaderName = EditorGUILayout.DelayedTextField(CShaderName, shaderName);

            if (DrawRenderLoopGUI(keywordSO, targetMat))
            {
                needsCompile = true;
            }

            for (int i = 0; i < compiler.extensions.Count; ++i)
            {
                var e = compiler.extensions[i];
                if (e.GetVersion() == MicroSplatVersion)
                {
                    //using (new GUILayout.VerticalScope(GUI.skin.box))
                    {
                        e.DrawFeatureGUI(keywordSO);
                    }
                }
                else
                {
                    EditorGUILayout.HelpBox("Extension : " + e + " is version " + e.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update", MessageType.Error);
                }
            }

            for (int i = 0; i < availableRenderLoops.Count; ++i)
            {
                var rl = availableRenderLoops[i];
                if (rl.GetVersion() != MicroSplatVersion)
                {
                    EditorGUILayout.HelpBox("Render Loop : " + rl.GetDisplayName() + " is version " + rl.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update", MessageType.Error);
                }
            }
        }
        needsCompile = needsCompile || EditorGUI.EndChangeCheck();

        int featureCount = keywordSO.keywords.Count;

        // note, ideally we wouldn't draw the GUI for the rest of stuff if we need to compile.
        // But we can't really do that without causing IMGUI to split warnings about
        // mismatched GUILayout blocks
        if (!needsCompile)
        {
            for (int i = 0; i < compiler.extensions.Count; ++i)
            {
                var ext = compiler.extensions[i];
                if (ext.GetVersion() == MicroSplatVersion)
                {
                    ext.DrawShaderGUI(this, keywordSO, targetMat, materialEditor, props);
                }
                else
                {
                    EditorGUILayout.HelpBox("Extension : " + ext + " is version " + ext.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update so that all modules are using the same version.", MessageType.Error);
                }
            }


            if (diff != null && MicroSplatUtilities.DrawRollup("Per Texture Properties"))
            {
                var propTex = FindOrCreatePropTex(targetMat);
                perTexIndex = MicroSplatUtilities.DrawTextureSelector(perTexIndex, diff);
                for (int i = 0; i < compiler.extensions.Count; ++i)
                {
                    var ext = compiler.extensions[i];
                    if (ext.GetVersion() == MicroSplatVersion)
                    {
                        ext.DrawPerTextureGUI(perTexIndex, keywordSO, targetMat, propTex);
                    }
                }
            }
        }

        if (!needsCompile)
        {
            if (featureCount != keywordSO.keywords.Count)
            {
                needsCompile = true;
            }
        }


        int arraySampleCount   = 0;
        int textureSampleCount = 0;
        int maxSamples         = 0;
        int tessSamples        = 0;
        int depTexReadLevel    = 0;

        builder.Length = 0;
        for (int i = 0; i < compiler.extensions.Count; ++i)
        {
            var ext = compiler.extensions[i];
            if (ext.GetVersion() == MicroSplatVersion)
            {
                ext.ComputeSampleCounts(keywordSO.keywords.ToArray(), ref arraySampleCount, ref textureSampleCount, ref maxSamples, ref tessSamples, ref depTexReadLevel);
            }
        }
        if (MicroSplatUtilities.DrawRollup("Debug"))
        {
            string shaderModel = compiler.GetShaderModel(keywordSO.keywords.ToArray());
            builder.Append("Shader Model : ");
            builder.AppendLine(shaderModel);
            if (maxSamples != arraySampleCount)
            {
                builder.Append("Texture Array Samples : ");
                builder.AppendLine(arraySampleCount.ToString());

                builder.Append("Regular Samples : ");
                builder.AppendLine(textureSampleCount.ToString());
            }
            else
            {
                builder.Append("Texture Array Samples : ");
                builder.AppendLine(arraySampleCount.ToString());
                builder.Append("Regular Samples : ");
                builder.AppendLine(textureSampleCount.ToString());
            }
            if (tessSamples > 0)
            {
                builder.Append("Tessellation Samples : ");
                builder.AppendLine(tessSamples.ToString());
            }
            if (depTexReadLevel > 0)
            {
                builder.Append(depTexReadLevel.ToString());
                builder.AppendLine(" areas with dependent texture reads");
            }

            EditorGUILayout.HelpBox(builder.ToString(), MessageType.Info);
        }

        if (EditorGUI.EndChangeCheck() && !needsCompile)
        {
            MicroSplatTerrain.SyncAll();
#if __MICROSPLAT_MESH__
            MicroSplatMesh.SyncAll();
#endif
        }

        if (needsCompile)
        {
            needsCompile = false;
            keywordSO.keywords.Clear();
            for (int i = 0; i < compiler.extensions.Count; ++i)
            {
                compiler.extensions[i].Pack(keywordSO);
            }
            if (compiler.renderLoop != null)
            {
                keywordSO.EnableKeyword(compiler.renderLoop.GetRenderLoopKeyword());
            }

            // horrible workaround to GUI warning issues
            compileMat     = targetMat;
            compileName    = shaderName;
            targetCompiler = compiler;
            EditorApplication.delayCall += TriggerCompile;
        }
    }
    //static GUIContent AdvDetailControl = new GUIContent("Advanced Detail Control", "Control map for Advanced Details");

    public override void OnInspectorGUI()
    {
        MicroSplatTerrain t = target as MicroSplatTerrain;

        if (t == null)
        {
            EditorGUILayout.HelpBox("No Terrain Present, please put this component on a terrain", MessageType.Error);
            return;
        }
        EditorGUI.BeginChangeCheck();
        t.templateMaterial = EditorGUILayout.ObjectField(CTemplateMaterial, t.templateMaterial, typeof(Material), false) as Material;


        if (t.templateMaterial == null)
        {
            if (GUILayout.Button("Convert to MicroSplat"))
            {
                // get all terrains in selection, not just this one, and treat as one giant terrain
                var            objs     = Selection.gameObjects;
                List <Terrain> terrains = new List <Terrain>();
                for (int i = 0; i < objs.Length; ++i)
                {
                    Terrain ter = objs[i].GetComponent <Terrain>();
                    if (ter != null)
                    {
                        terrains.Add(ter);
                    }
                    Terrain[] trs = objs[i].GetComponentsInChildren <Terrain>();
                    for (int x = 0; x < trs.Length; ++x)
                    {
                        if (!terrains.Contains(trs[x]))
                        {
                            terrains.Add(trs[x]);
                        }
                    }
                }
                // setup this terrain
                Terrain terrain = t.GetComponent <Terrain>();
                t.templateMaterial = MicroSplatShaderGUI.NewShaderAndMaterial(terrain);
                var config = TextureArrayConfigEditor.CreateConfig(terrain);
                t.templateMaterial.SetTexture("_Diffuse", config.diffuseArray);
                t.templateMaterial.SetTexture("_NormalSAO", config.normalSAOArray);

                t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
                if (terrain.terrainData.splatPrototypes.Length > 0)
                {
                    var uvScale  = terrain.terrainData.splatPrototypes[0].tileSize;
                    var uvOffset = terrain.terrainData.splatPrototypes[0].tileOffset;

                    uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                    uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                    uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                    Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                    t.templateMaterial.SetVector("_UVScale", scaleOffset);
                }

                // now make sure others all have the same settings as well.
                for (int i = 0; i < terrains.Count; ++i)
                {
                    var nt  = terrains[i];
                    var mgr = nt.GetComponent <MicroSplatTerrain>();
                    if (mgr == null)
                    {
                        mgr = nt.gameObject.AddComponent <MicroSplatTerrain>();
                    }
                    mgr.templateMaterial = t.templateMaterial;

                    if (mgr.propData == null)
                    {
                        mgr.propData = MicroSplatShaderGUI.FindOrCreatePropTex(mgr.templateMaterial);
                    }
                }
                Selection.SetActiveObjectWithContext(config, config);
            }
            MicroSplatTerrain.SyncAll();
            return;
        }

        if (t.propData == null)
        {
            t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
        }

#if __MICROSPLAT_TERRAINBLEND__ || __MICROSPLAT_STREAMS__
        DoTerrainDescGUI();
#endif

        // could move this to some type of interfaced component created by the module if this becomes a thing,
        // but I think this will be most of the cases?

        MicroSplatUtilities.DrawTextureField(t, geoTexOverride, ref t.geoTextureOverride, "_GEOMAP");

        MicroSplatUtilities.DrawTextureField(t, geoTintOverride, ref t.tintMapOverride, "_GLOBALTINT");

        MicroSplatUtilities.DrawTextureField(t, geoNormalOverride, ref t.globalNormalOverride, "_GLOBALNORMALS");

#if __MICROSPLAT_ADVANCED_DETAIL__
        DrawAdvancedModuleDetailGUI(t);
#endif

        if (t.templateMaterial.IsKeywordEnabled("_VSGRASSMAP"))
        {
            EditorGUI.BeginChangeCheck();

            t.vsGrassMap = EditorGUILayout.ObjectField(CVSGrassMap, t.vsGrassMap, typeof(Texture2D), false) as Texture2D;

            if (EditorGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(t);
            }
        }

        if (t.templateMaterial.IsKeywordEnabled("_VSSHADOWMAP"))
        {
            EditorGUI.BeginChangeCheck();


            t.vsShadowMap = EditorGUILayout.ObjectField(CVSShadowMap, t.vsShadowMap, typeof(Texture2D), false) as Texture2D;

            if (EditorGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(t);
            }
        }


        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Sync"))
        {
            var mgr = target as MicroSplatTerrain;
            mgr.Sync();
        }
        if (GUILayout.Button("Sync All"))
        {
            MicroSplatTerrain.SyncAll();
        }
        EditorGUILayout.EndHorizontal();

        BakingGUI(t);
        WeightLimitingGUI(t);

#if __MICROSPLAT_ADVANCED_DETAIL__
        DrawAdvancedModuleDetailTooset(t);
#endif

        if (MicroSplatUtilities.DrawRollup("Debug", false, true))
        {
            EditorGUI.indentLevel += 2;
            EditorGUILayout.HelpBox("These should not need to be edited unless something funky has happened. They are automatically managed by MicroSplat.", MessageType.Info);
            t.propData             = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
            t.blendMat             = EditorGUILayout.ObjectField(CBlendMat, t.blendMat, typeof(Material), false) as Material;
            t.terrainDesc          = EditorGUILayout.ObjectField("Terrain Descriptor", t.terrainDesc, typeof(Texture2D), false) as Texture2D;
            t.addPass              = EditorGUILayout.ObjectField("Add Pass", t.addPass, typeof(Shader), false) as Shader;
            EditorGUI.indentLevel -= 2;
        }
        if (EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(t);
        }
    }
Exemple #15
0
    public override void OnInspectorGUI()
    {
        MicroSplatTerrain t = target as MicroSplatTerrain;

        if (t == null)
        {
            EditorGUILayout.HelpBox("No Terrain Present, please put this component on a terrain", MessageType.Error);
            return;
        }
        EditorGUI.BeginChangeCheck();
        t.templateMaterial = EditorGUILayout.ObjectField(CTemplateMaterial, t.templateMaterial, typeof(Material), false) as Material;


        if (t.templateMaterial == null)
        {
            if (GUILayout.Button("Convert to MicroSplat"))
            {
                // get all terrains in selection, not just this one, and treat as one giant terrain
                var            objs     = Selection.gameObjects;
                List <Terrain> terrains = new List <Terrain>();
                for (int i = 0; i < objs.Length; ++i)
                {
                    Terrain ter = objs[i].GetComponent <Terrain>();
                    if (ter != null)
                    {
                        terrains.Add(ter);
                    }
                    Terrain[] trs = objs[i].GetComponentsInChildren <Terrain>();
                    for (int x = 0; x < trs.Length; ++x)
                    {
                        if (!terrains.Contains(trs[x]))
                        {
                            terrains.Add(trs[x]);
                        }
                    }
                }
                // setup this terrain
                Terrain terrain = t.GetComponent <Terrain>();
                t.templateMaterial = MicroSplatShaderGUI.NewShaderAndMaterial(terrain);
                var config = TextureArrayConfigEditor.CreateConfig(terrain);
                t.templateMaterial.SetTexture("_Diffuse", config.diffuseArray);
                t.templateMaterial.SetTexture("_NormalSAO", config.normalSAOArray);

                t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
#if UNITY_2018_3_OR_NEWER
                if (terrain.terrainData.terrainLayers.Length > 0)
                {
                    var uvScale  = terrain.terrainData.terrainLayers[0].tileSize;
                    var uvOffset = terrain.terrainData.terrainLayers[0].tileOffset;

                    uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                    uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                    uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                    Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                    t.templateMaterial.SetVector("_UVScale", scaleOffset);
                }
#else
                if (terrain.terrainData.splatPrototypes.Length > 0)
                {
                    var uvScale  = terrain.terrainData.splatPrototypes[0].tileSize;
                    var uvOffset = terrain.terrainData.splatPrototypes[0].tileOffset;

                    uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                    uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                    uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                    Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                    t.templateMaterial.SetVector("_UVScale", scaleOffset);
                }
#endif

                // now make sure others all have the same settings as well.
                for (int i = 0; i < terrains.Count; ++i)
                {
                    var nt  = terrains[i];
                    var mgr = nt.GetComponent <MicroSplatTerrain>();
                    if (mgr == null)
                    {
                        mgr = nt.gameObject.AddComponent <MicroSplatTerrain>();
                    }
                    mgr.templateMaterial = t.templateMaterial;

                    if (mgr.propData == null)
                    {
                        mgr.propData = MicroSplatShaderGUI.FindOrCreatePropTex(mgr.templateMaterial);
                    }
                }
                Selection.SetActiveObjectWithContext(config, config);
            }
            MicroSplatTerrain.SyncAll();
            return;
        }

        if (t.propData == null)
        {
            t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
            EditorUtility.SetDirty(t);
        }

        if (t.keywordSO == null)
        {
            t.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);
            EditorUtility.SetDirty(t);
        }

        EditorGUI.BeginChangeCheck();

#if __MICROSPLAT_PROCTEX__
        if (t.keywordSO.IsKeywordEnabled("_PROCEDURALTEXTURE") || t.keywordSO.IsKeywordEnabled("_PCHEIGHTGRADIENT") || t.keywordSO.IsKeywordEnabled("_PCHEIGHTHSV"))
        {
            var old = t.procTexCfg;
            t.procTexCfg = MicroSplatProceduralTexture.FindOrCreateProceduralConfig(t.templateMaterial);
            if (old != t.procTexCfg)
            {
                EditorUtility.SetDirty(t);
            }
        }
#endif

#if __MICROSPLAT_TERRAINBLEND__ || __MICROSPLAT_STREAMS__
        DoTerrainDescGUI();
#endif

        DoPerPixelNormalGUI();

#if __MICROSPLAT_PROCTEX__
        if (t.keywordSO.IsKeywordEnabled(MicroSplatProceduralTexture.GetFeatureName(MicroSplatProceduralTexture.DefineFeature._PCCAVITY)) ||
            t.keywordSO.IsKeywordEnabled(MicroSplatProceduralTexture.GetFeatureName(MicroSplatProceduralTexture.DefineFeature._PCFLOW)))
        {
            DoCavityMapGUI();
        }
#endif
        // could move this to some type of interfaced component created by the module if this becomes a thing,
        // but I think this will be most of the cases?

        MicroSplatUtilities.DrawTextureField(t, CCustomSplat0, ref t.customControl0, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat1, ref t.customControl1, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat2, ref t.customControl2, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat3, ref t.customControl3, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat4, ref t.customControl4, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat5, ref t.customControl5, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat6, ref t.customControl6, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat7, ref t.customControl7, "_CUSTOMSPLATTEXTURES");

        // perpixel normal
        MicroSplatUtilities.DrawTextureField(t, perPixelNormal, ref t.perPixelNormal, "_PERPIXELNORMAL");

        // global texture overrides
#if __MICROSPLAT_GLOBALTEXTURE__
        MicroSplatUtilities.DrawTextureField(t, geoTexOverride, ref t.geoTextureOverride, "_GEOMAP");
        MicroSplatUtilities.DrawTextureField(t, geoTintOverride, ref t.tintMapOverride, "_GLOBALTINT");
        MicroSplatUtilities.DrawTextureField(t, geoNormalOverride, ref t.globalNormalOverride, "_GLOBALNORMALS");
        MicroSplatUtilities.DrawTextureField(t, geoSAOMOverride, ref t.globalSAOMOverride, "_GLOBALSMOOTHAOMETAL");
        MicroSplatUtilities.DrawTextureField(t, geoEmisOverride, ref t.globalEmisOverride, "_GLOBALEMIS");
#endif

#if __MICROSPLAT_ALPHAHOLE__
        // alpha hole override
        MicroSplatUtilities.DrawTextureField(t, clipMapOverride, ref t.clipMap, "_ALPHAHOLETEXTURE");
#endif

#if (VEGETATION_STUDIO || VEGETATION_STUDIO_PRO)
        // vsstudio overrides
        MicroSplatUtilities.DrawTextureField(t, CVSGrassMap, ref t.vsGrassMap, "_VSGRASSMAP");
        MicroSplatUtilities.DrawTextureField(t, CVSShadowMap, ref t.vsShadowMap, "_VSSHADOWMAP");
#endif


#if __MICROSPLAT_PROCTEX__
        MicroSplatUtilities.DrawTextureField(t, biomeOverride, ref t.procBiomeMask, "_PROCEDURALTEXTURE");
#endif

#if __MICROSPLAT_ADVANCED_DETAIL__
        DrawAdvancedModuleDetailGUI(t);
#endif


        if (t.propData == null && t.templateMaterial != null)
        {
            t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
            if (t.propData == null)
            {
                // this should really never happen, but users seem to have issues with unassigned propData's a lot. I think
                // this is from external tools like MapMagic not creating it, but the above call should create it.
                EditorGUILayout.HelpBox("PropData is null, please assign", MessageType.Error);
                t.propData = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
            }
        }

        if (EditorGUI.EndChangeCheck())
        {
            MicroSplatTerrain.SyncAll();
        }


        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Sync"))
        {
            var mgr = target as MicroSplatTerrain;
            mgr.Sync();
        }
        if (GUILayout.Button("Sync All"))
        {
            MicroSplatTerrain.SyncAll();
        }
        EditorGUILayout.EndHorizontal();

        BakingGUI(t);
        WeightLimitingGUI(t);
        ImportExportGUI();

#if __MICROSPLAT_ADVANCED_DETAIL__
        DrawAdvancedModuleDetailTooset(t);
#endif

        if (MicroSplatUtilities.DrawRollup("Debug", false, true))
        {
            EditorGUI.indentLevel += 2;
            EditorGUILayout.HelpBox("These should not need to be edited unless something funky has happened. They are automatically managed by MicroSplat.", MessageType.Info);
            t.propData = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
#if __MICROSPLAT_PROCTEX__
            t.procTexCfg = EditorGUILayout.ObjectField("Procedural Config", t.procTexCfg, typeof(MicroSplatProceduralTextureConfig), false) as MicroSplatProceduralTextureConfig;
#endif
            t.keywordSO            = EditorGUILayout.ObjectField("Keywords", t.keywordSO, typeof(MicroSplatKeywords), false) as MicroSplatKeywords;
            t.blendMat             = EditorGUILayout.ObjectField(CBlendMat, t.blendMat, typeof(Material), false) as Material;
            t.terrainDesc          = EditorGUILayout.ObjectField("Terrain Descriptor", t.terrainDesc, typeof(Texture2D), false) as Texture2D;
            t.perPixelNormal       = EditorGUILayout.ObjectField("Normal Data", t.perPixelNormal, typeof(Texture2D), false) as Texture2D;
            t.addPass              = EditorGUILayout.ObjectField("Add Pass", t.addPass, typeof(Shader), false) as Shader;
            EditorGUI.indentLevel -= 2;
        }
        if (EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(t);
        }
    }
Exemple #16
0
    bool DoConvertGUI(MicroSplatTerrain t)
    {
        if (t.templateMaterial == null)
        {
            InitConvertConfigs();
            using (new GUILayout.VerticalScope(GUI.skin.box))
            {
                EditorGUILayout.LabelField("Select a Template:");

                EditorGUILayout.BeginHorizontal();
                for (int i = 0; i < convertConfigs.Count; ++i)
                {
                    var c = convertConfigs [i];
                    if (DrawConvertButton(c, i == selectedConvertConfig))
                    {
                        selectedConvertConfig = i;
                    }
                }
                EditorGUILayout.EndHorizontal();

                var selectedConfig = convertConfigs [selectedConvertConfig];
                if (selectedConfig.missingModules.Count > 0)
                {
                    EditorGUILayout.HelpBox("You are missing some MicroSplat modules needed by this template. The preset may still be used but some features will not be enabled. Missing modules are listed below:", MessageType.Warning);
                    for (int i = 0; i < selectedConfig.missingModules.Count; ++i)
                    {
                        var m = selectedConfig.missingModules [i];
                        DrawMissingModule(m);
                    }
                }

                EditorGUILayout.LabelField("Select any integrations you want to add:");
                // integrations
                for (int i = 0; i < integrationConfigs.Count; ++i)
                {
                    var ic = integrationConfigs [i];
                    if (!ic.assetInstalled)
                    {
                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.LabelField(ic.name, GUILayout.Width(120));
                        EditorGUILayout.LabelField("Not Installed", GUILayout.Width(120));
                        if (GUILayout.Button("Link", GUILayout.Width(120)))
                        {
                            Application.OpenURL(ic.assetLink);
                        }
                        EditorGUILayout.EndHorizontal();
                    }
                    else
                    {
                        EditorGUILayout.BeginHorizontal();
                        ic.include = EditorGUILayout.Toggle(ic.include, GUILayout.Width(20));
                        EditorGUILayout.LabelField(ic.name);
                        EditorGUILayout.EndHorizontal();
                        if (ic.include && ic.missingModules.Count > 0)
                        {
                            using (new GUILayout.VerticalScope(GUI.skin.box))
                            {
                                EditorGUILayout.HelpBox("Some MicroSplat modules requested by this module are not installed. Some or all features of the integration will not be active.", MessageType.Warning);
                                for (int j = 0; j < ic.missingModules.Count; ++j)
                                {
                                    var m = ic.missingModules [j];
                                    DrawMissingModule(m);
                                }
                            }
                        }
                    }
                }

                if (GUILayout.Button("Convert to MicroSplat"))
                {
                    // get all terrains in selection, not just this one, and treat as one giant terrain
                    var            objs     = Selection.gameObjects;
                    List <Terrain> terrains = new List <Terrain> ();
                    for (int i = 0; i < objs.Length; ++i)
                    {
                        Terrain ter = objs [i].GetComponent <Terrain> ();
                        if (ter != null)
                        {
                            terrains.Add(ter);
                        }
                        Terrain [] trs = objs [i].GetComponentsInChildren <Terrain> ();
                        for (int x = 0; x < trs.Length; ++x)
                        {
                            if (!terrains.Contains(trs [x]))
                            {
                                terrains.Add(trs [x]);
                            }
                        }
                    }

                    Terrain terrain  = t.GetComponent <Terrain> ();
                    int     texcount = 16;
#if UNITY_2018_3_OR_NEWER
                    texcount = terrain.terrainData.terrainLayers.Length;
#else
                    texcount = terrain.terrainData.splatPrototypes.Length;
#endif
                    List <string> keywords = new List <string> (selectedConfig.keywords);
                    if (texcount <= 4)
                    {
                        keywords.Add("_MAX4TEXTURES");
                    }
                    else if (texcount <= 8)
                    {
                        keywords.Add("_MAX8TEXTURES");
                    }
                    else if (texcount <= 12)
                    {
                        keywords.Add("_MAX12TEXTURES");
                    }
                    else if (texcount <= 20)
                    {
                        keywords.Add("_MAX20TEXTURES");
                    }
                    else if (texcount <= 24)
                    {
                        keywords.Add("_MAX24TEXTURES");
                    }
                    else if (texcount <= 28)
                    {
                        keywords.Add("_MAX28TEXTURES");
                    }
                    else if (texcount > 28)
                    {
                        keywords.Add("_MAX32TEXTURES");
                    }

                    for (int i = 0; i < integrationConfigs.Count; ++i)
                    {
                        var ic = integrationConfigs [i];
                        if (ic.include)
                        {
                            keywords.AddRange(ic.keywords);
                        }
                    }

                    // setup this terrain
                    t.templateMaterial = MicroSplatShaderGUI.NewShaderAndMaterial(terrain, keywords.ToArray());

                    var config = TextureArrayConfigEditor.CreateConfig(terrain);
                    t.templateMaterial.SetTexture("_Diffuse", config.diffuseArray);
                    t.templateMaterial.SetTexture("_NormalSAO", config.normalSAOArray);

                    t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
#if UNITY_2018_3_OR_NEWER
                    if (terrain.terrainData.terrainLayers.Length > 0)
                    {
                        var uvScale  = terrain.terrainData.terrainLayers[0].tileSize;
                        var uvOffset = terrain.terrainData.terrainLayers[0].tileOffset;

                        uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                        uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                        uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                        Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                        t.templateMaterial.SetVector("_UVScale", scaleOffset);
                    }
#else
                    if (terrain.terrainData.splatPrototypes.Length > 0)
                    {
                        var uvScale  = terrain.terrainData.splatPrototypes [0].tileSize;
                        var uvOffset = terrain.terrainData.splatPrototypes [0].tileOffset;

                        uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                        uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                        uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                        Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                        t.templateMaterial.SetVector("_UVScale", scaleOffset);
                    }
#endif
                    // we need to set a few things on the material if certain features are enabled.
                    // Test for property existence as module might not be installed and feature was culled.
                    if (System.Array.Exists(selectedConfig.keywords, x => x == "_TRIPLANAR"))
                    {
                        if (t.templateMaterial.HasProperty("_TriplanarUVScale"))
                        {
                            t.templateMaterial.SetVector("_TriplanarUVScale", new Vector4(0.25f, 0.25f, 0, 0));
                        }
                    }

                    if (System.Array.Exists(selectedConfig.keywords, x => x == "_NORMALNOISE"))
                    {
                        if (t.templateMaterial.HasProperty("_NormalNoise"))
                        {
                            t.templateMaterial.SetTexture("_NormalNoise", MicroSplatUtilities.GetAutoTexture("microsplat_def_detail_normal_01"));
                        }
                    }

                    // now make sure others all have the same settings as well.
                    for (int i = 0; i < terrains.Count; ++i)
                    {
                        var nt  = terrains [i];
                        var mgr = nt.GetComponent <MicroSplatTerrain> ();
                        if (mgr == null)
                        {
                            mgr = nt.gameObject.AddComponent <MicroSplatTerrain> ();
                        }
                        mgr.templateMaterial = t.templateMaterial;

                        if (mgr.propData == null)
                        {
                            mgr.propData = MicroSplatShaderGUI.FindOrCreatePropTex(mgr.templateMaterial);
                        }
                    }


                    Selection.SetActiveObjectWithContext(config, config);
                    t.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);

                    t.keywordSO.keywords.Clear();
                    t.keywordSO.keywords = new List <string> (keywords);

                    // force recompile, so that basemap shader name gets reset correctly..
                    MicroSplatShaderGUI.MicroSplatCompiler comp = new MicroSplatShaderGUI.MicroSplatCompiler();
                    comp.Compile(t.templateMaterial);

                    MicroSplatTerrain.SyncAll();

                    // trun on draw instanced if enabled and tessellation is disabled, unless render loop is LWRP/URP in which case it does work..
#if UNITY_2018_3_OR_NEWER
                    if (t.keywordSO != null && (!t.keywordSO.IsKeywordEnabled("_TESSDISTANCE") || t.keywordSO.IsKeywordEnabled("_MSRENDERLOOP_UNITYLD")))
                    {
                        for (int i = 0; i < terrains.Count; ++i)
                        {
                            var nt  = terrains [i];
                            var mgr = nt.GetComponent <MicroSplatTerrain> ();
                            if (mgr != null && mgr.keywordSO != null && !mgr.keywordSO.IsKeywordEnabled("_MSRENDERLOOP_UNITYLD"))
                            {
                                nt.drawInstanced = true;
                            }
                        }
                    }
#endif
                    return(true);
                }
            }
        }
        return(false);
    }
Exemple #17
0
        public void Compile(Material m, string shaderName = null)
        {
            int hash = 0;

            for (int i = 0; i < m.shaderKeywords.Length; ++i)
            {
                hash += 31 + m.shaderKeywords[i].GetHashCode();
            }
            var    path = AssetDatabase.GetAssetPath(m.shader);
            string nm   = m.shader.name;

            if (!string.IsNullOrEmpty(shaderName))
            {
                nm = shaderName;
            }
            string baseName = "Hidden/" + nm + "_Base" + hash.ToString();

            string terrainShader = Compile(m.shaderKeywords, nm, baseName);

            string blendShader = null;

            // strip extra feature from terrain blending to make it cheaper
            if (m.IsKeywordEnabled("_TERRAINBLENDING"))
            {
                List <string> blendKeywords = new List <string>(m.shaderKeywords);
                if (m.IsKeywordEnabled("_TBDISABLE_DETAILNOISE") && blendKeywords.Contains("_DETAILNOISE"))
                {
                    blendKeywords.Remove("_DETAILNOISE");
                }
                if (m.IsKeywordEnabled("_TBDISABLE_DISTANCENOISE") && blendKeywords.Contains("_DISTANCENOISE"))
                {
                    blendKeywords.Remove("_DISTANCENOISE");
                }
                if (m.IsKeywordEnabled("_TBDISABLE_DISTANCERESAMPLE") && blendKeywords.Contains("_DISTANCERESAMPLE"))
                {
                    blendKeywords.Remove("_DISTANCERESAMPLE");
                }

                blendShader = Compile(blendKeywords.ToArray(), nm, null, true);
            }


            // generate fallback
            string[] oldKeywords = new string[m.shaderKeywords.Length];
            System.Array.Copy(m.shaderKeywords, oldKeywords, m.shaderKeywords.Length);
            m.DisableKeyword("_TESSDISTANCE");
            m.DisableKeyword("_TESSEDGE");
            m.DisableKeyword("_PARALLAX");
            m.DisableKeyword("_DETAILNOISE");

            // maybe reduce layers in distance? can cause a pop though..
            //m.DisableKeyword("_MAX3LAYER");
            //m.EnableKeyword("_MAX2LAYER");


            string fallback = Compile(m.shaderKeywords, baseName);

            m.shaderKeywords = oldKeywords;

            System.IO.File.WriteAllText(path, terrainShader);
            string fallbackPath     = path.Replace(".shader", "_Base.shader");
            string terrainBlendPath = path.Replace(".shader", "_TerrainObjectBlend.shader");

            System.IO.File.WriteAllText(fallbackPath, fallback);
            if (blendShader != null)
            {
                System.IO.File.WriteAllText(terrainBlendPath, blendShader);
            }

            EditorUtility.SetDirty(m);
            AssetDatabase.Refresh();
            MicroSplatTerrain.SyncAll();
        }
    public override void OnInspectorGUI()
    {
        MicroSplatTerrain t = target as MicroSplatTerrain;

        if (t == null)
        {
            EditorGUILayout.HelpBox("No Terrain Present, please put this component on a terrain", MessageType.Error);
            return;
        }
        EditorGUI.BeginChangeCheck();
        t.templateMaterial = EditorGUILayout.ObjectField(CTemplateMaterial, t.templateMaterial, typeof(Material), false) as Material;
        if (EditorGUI.EndChangeCheck())
        {
            MicroSplatTerrain.SyncAll();
        }
        EditorGUI.BeginChangeCheck();


        if (DoConvertGUI(t))
        {
            return;
        }

        if (t.templateMaterial == null)
        {
            return;
        }

        if (t.propData == null)
        {
            t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
            EditorUtility.SetDirty(t);
            MicroSplatObject.SyncAll();
        }

        if (t.keywordSO == null)
        {
            t.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);
            EditorUtility.SetDirty(t);
        }

        EditorGUI.BeginChangeCheck();

#if __MICROSPLAT_PROCTEX__
        if (t.keywordSO.IsKeywordEnabled("_PROCEDURALTEXTURE") || t.keywordSO.IsKeywordEnabled("_PCHEIGHTGRADIENT") || t.keywordSO.IsKeywordEnabled("_PCHEIGHTHSV"))
        {
            var old = t.procTexCfg;
            t.procTexCfg = MicroSplatProceduralTexture.FindOrCreateProceduralConfig(t.templateMaterial);
            if (old != t.procTexCfg)
            {
                EditorUtility.SetDirty(t);
                MicroSplatObject.SyncAll();
            }
        }
#endif

#if __MICROSPLAT_TERRAINBLEND__ || __MICROSPLAT_STREAMS__
        DoTerrainDescGUI();
#endif

        DoPerPixelNormalGUI();

#if __MICROSPLAT_PROCTEX__
        if (t.keywordSO.IsKeywordEnabled(MicroSplatProceduralTexture.GetFeatureName(MicroSplatProceduralTexture.DefineFeature._PCCAVITY)) ||
            t.keywordSO.IsKeywordEnabled(MicroSplatProceduralTexture.GetFeatureName(MicroSplatProceduralTexture.DefineFeature._PCFLOW)))
        {
            DoCavityMapGUI();
        }
#endif
        // could move this to some type of interfaced component created by the module if this becomes a thing,
        // but I think this will be most of the cases?

        MicroSplatUtilities.DrawTextureField(t, CCustomSplat0, ref t.customControl0, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat1, ref t.customControl1, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat2, ref t.customControl2, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat3, ref t.customControl3, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat4, ref t.customControl4, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat5, ref t.customControl5, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat6, ref t.customControl6, "_CUSTOMSPLATTEXTURES");
        MicroSplatUtilities.DrawTextureField(t, CCustomSplat7, ref t.customControl7, "_CUSTOMSPLATTEXTURES");

        // perpixel normal
        MicroSplatUtilities.DrawTextureField(t, perPixelNormal, ref t.perPixelNormal, "_PERPIXELNORMAL");

        // global texture overrides
#if __MICROSPLAT_GLOBALTEXTURE__
        MicroSplatUtilities.DrawTextureField(t, geoTexOverride, ref t.geoTextureOverride, "_GEOMAP");
        MicroSplatUtilities.DrawTextureField(t, geoTintOverride, ref t.tintMapOverride, "_GLOBALTINT");
        MicroSplatUtilities.DrawTextureField(t, geoNormalOverride, ref t.globalNormalOverride, "_GLOBALNORMALS");
        MicroSplatUtilities.DrawTextureField(t, geoSAOMOverride, ref t.globalSAOMOverride, "_GLOBALSMOOTHAOMETAL");
        MicroSplatUtilities.DrawTextureField(t, geoEmisOverride, ref t.globalEmisOverride, "_GLOBALEMIS");
#endif

#if __MICROSPLAT_SNOW__
        MicroSplatUtilities.DrawTextureField(t, snowMaskOverride, ref t.snowMaskOverride, "_SNOWMASK");
#endif

#if __MICROSPLAT_ALPHAHOLE__
        // alpha hole override
        MicroSplatUtilities.DrawTextureField(t, clipMapOverride, ref t.clipMap, "_ALPHAHOLETEXTURE");
#endif

#if (VEGETATION_STUDIO || VEGETATION_STUDIO_PRO)
        // vsstudio overrides
        MicroSplatUtilities.DrawTextureField(t, CVSGrassMap, ref t.vsGrassMap, "_VSGRASSMAP");
        MicroSplatUtilities.DrawTextureField(t, CVSShadowMap, ref t.vsShadowMap, "_VSSHADOWMAP");
#endif


#if __MICROSPLAT_PROCTEX__
        MicroSplatUtilities.DrawTextureField(t, biomeOverride, ref t.procBiomeMask, "_PROCEDURALTEXTURE");
#endif

#if __MICROSPLAT_STREAMS__
        MicroSplatUtilities.DrawTextureField(t, streamOverride, ref t.streamTexture, "_WETNESS", "_PUDDLES", "_STREAMS", "_LAVA", false);
#endif

#if __MICROSPLAT_ADVANCED_DETAIL__
        DrawAdvancedModuleDetailGUI(t);
#endif


        if (t.propData == null && t.templateMaterial != null)
        {
            t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
            if (t.propData == null)
            {
                // this should really never happen, but users seem to have issues with unassigned propData's a lot. I think
                // this is from external tools like MapMagic not creating it, but the above call should create it.
                EditorGUILayout.HelpBox("PropData is null, please assign", MessageType.Error);
                t.propData = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
            }
        }

        if (EditorGUI.EndChangeCheck())
        {
            MicroSplatTerrain.SyncAll();
        }


        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Sync"))
        {
            var mgr = target as MicroSplatTerrain;
            mgr.Sync();
        }
        if (GUILayout.Button("Sync All"))
        {
            MicroSplatTerrain.SyncAll();
        }
        EditorGUILayout.EndHorizontal();

        BakingGUI(t);
        WeightLimitingGUI(t);
        ImportExportGUI();

#if __MICROSPLAT_ADVANCED_DETAIL__
        DrawAdvancedModuleDetailTooset(t);
#endif

        if (MicroSplatUtilities.DrawRollup("Debug", false, true))
        {
            EditorGUI.indentLevel += 2;
            EditorGUILayout.HelpBox("These should not need to be edited unless something funky has happened. They are automatically managed by MicroSplat.", MessageType.Info);
            t.propData = EditorGUILayout.ObjectField("Per Texture Data", t.propData, typeof(MicroSplatPropData), false) as MicroSplatPropData;
#if __MICROSPLAT_PROCTEX__
            t.procTexCfg = EditorGUILayout.ObjectField("Procedural Config", t.procTexCfg, typeof(MicroSplatProceduralTextureConfig), false) as MicroSplatProceduralTextureConfig;
#endif
            t.keywordSO            = EditorGUILayout.ObjectField("Keywords", t.keywordSO, typeof(MicroSplatKeywords), false) as MicroSplatKeywords;
            t.blendMat             = EditorGUILayout.ObjectField(CBlendMat, t.blendMat, typeof(Material), false) as Material;
            t.addPass              = EditorGUILayout.ObjectField("Add Pass", t.addPass, typeof(Shader), false) as Shader;
            EditorGUI.indentLevel -= 2;
        }
        if (EditorGUI.EndChangeCheck())
        {
            EditorUtility.SetDirty(t);
        }
    }
        void CompressTerrainSplats(MicroSplatTerrain t)
        {
            int splatCount = t.terrain.terrainData.alphamapTextureCount;

            // write out
            for (int i = 0; i < splatCount; ++i)
            {
                var tex  = t.terrain.terrainData.GetAlphamapTexture(i);
                var path = MicroSplatUtilities.RelativePathFromAsset(t);
                path += "/" + t.name + "_splat" + i + ".tga";
                System.IO.File.WriteAllBytes(path, tex.EncodeToTGA());
            }

            AssetDatabase.Refresh();
            // load and adjust importer
            for (int i = 0; i < splatCount; ++i)
            {
                var path = MicroSplatUtilities.RelativePathFromAsset(t);
                path += "/" + t.name + "_splat" + i + ".tga";

                var tex = CompressTexture(path, false);

                if (i == 0)
                {
                    t.customControl0 = tex;
                }
                else if (i == 1)
                {
                    t.customControl1 = tex;
                }
                else if (i == 2)
                {
                    t.customControl2 = tex;
                }
                else if (i == 3)
                {
                    t.customControl3 = tex;
                }
                else if (i == 4)
                {
                    t.customControl4 = tex;
                }
                else if (i == 5)
                {
                    t.customControl5 = tex;
                }
                else if (i == 6)
                {
                    t.customControl6 = tex;
                }
                else if (i == 7)
                {
                    t.customControl7 = tex;
                }
            }
            EditorUtility.SetDirty(t);

            MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);

            if (!keywords.IsKeywordEnabled("_CUSTOMSPLATTEXTURES"))
            {
                keywords.EnableKeyword("_CUSTOMSPLATTEXTURES");
                MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler();
                compiler.Compile(t.templateMaterial);
                MicroSplatTerrain.SyncAll();
            }

            // destructive operation
            t.terrain.terrainData.alphamapResolution = 16;
        }
Exemple #20
0
    public static void GenerateTerrainBlendData(MicroSplatTerrain bt)
    {
        Terrain t = bt.GetComponent <Terrain>();
        int     w = t.terrainData.heightmapResolution;
        int     h = t.terrainData.heightmapResolution;

        Texture2D data = null;

        if (SystemInfo.SupportsTextureFormat(TextureFormat.RGBAHalf))
        {
            data = new Texture2D(w, h, TextureFormat.RGBAHalf, true, true);
        }
#if UNITY_2018_3_OR_NEWER
        else if (SystemInfo.SupportsTextureFormat(TextureFormat.RGBAFloat))
        {
            data = new Texture2D(w, h, TextureFormat.RGBAFloat, true, true);
        }
#endif
        else
        {
            Debug.LogError("System does not support RGBAHalf or RGBAFloat texture formats, Terrain Descriptor cannot be generated.");
            return;
        }

        for (int x = 0; x < w; ++x)
        {
            for (int y = 0; y < h; ++y)
            {
                float   height = t.terrainData.GetHeight(x, y);
                Vector3 normal = t.terrainData.GetInterpolatedNormal((float)x / w, (float)y / h);
                // When you save a texture to EXR format, either in the saving or import stage,
                // some type of gamma curve is applied regardless of the fact that the textures is
                // set to linear. So we pow it here to counteract it, whis is total BS, but works..
                normal.x = (normal.x >= 0) ? Mathf.Pow(normal.x, 2.0f) : Mathf.Pow(normal.x, 2) * -1;
                normal.z = (normal.z >= 0) ? Mathf.Pow(normal.z, 2.0f) : Mathf.Pow(normal.z, 2) * -1;
                data.SetPixel(x, y, new Color(normal.x, normal.y, normal.z, height));
            }
        }
        data.Apply();

        var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData);
        path += "/" + t.name + ".exr";
        var bytes = data.EncodeToEXR(Texture2D.EXRFlags.OutputAsFloat);
        System.IO.File.WriteAllBytes(path, bytes);
        GameObject.DestroyImmediate(data);
        AssetDatabase.Refresh();
        bt.terrainDesc = AssetDatabase.LoadAssetAtPath <Texture2D>(path);
        var ai = AssetImporter.GetAtPath(path);
        var ti = ai as TextureImporter;


        // default platform no longer supports RGBA half/float in newer unity, so we override all platforms
        bool changed   = false;
        var  platforms = System.Enum.GetNames(typeof(BuildTarget));
        for (int i = 0; i < platforms.Length; ++i)
        {
            string platform = platforms [i];
            var    ps       = ti.GetPlatformTextureSettings(platform);

            if (ti.isReadable == true ||
                ti.wrapMode != TextureWrapMode.Clamp ||
#if UNITY_2018_3_OR_NEWER
                (ps.format != TextureImporterFormat.RGBAHalf && ps.format != TextureImporterFormat.RGBAFloat) ||
#else
                ps.format != TextureImporterFormat.RGBAHalf ||
#endif
                ps.textureCompression != TextureImporterCompression.Uncompressed ||
                ps.overridden != true ||
                ti.filterMode != FilterMode.Bilinear ||
                ti.sRGBTexture != false)
            {
                ti.sRGBTexture   = false;
                ti.filterMode    = FilterMode.Bilinear;
                ti.mipmapEnabled = true;
                ti.wrapMode      = TextureWrapMode.Clamp;
                ti.isReadable    = false;

#if UNITY_2018_3_OR_NEWER
                if (SystemInfo.SupportsTextureFormat(TextureFormat.RGBAHalf))
                {
                    ps.format = TextureImporterFormat.RGBAHalf;
                }
                else
                {
                    ps.format = TextureImporterFormat.RGBAFloat;
                }
#else
                ps.format = TextureImporterFormat.RGBAHalf;
#endif
                ps.textureCompression = TextureImporterCompression.Uncompressed;
                ps.overridden         = true;
                try
                {
                    ti.SetPlatformTextureSettings(ps);
                    changed = true;
                }
                catch
                {
                }
            }
        }
        if (changed)
        {
            ti.SaveAndReimport();
        }
        bt.sTerrainDirty = false;
        EditorUtility.SetDirty(bt);
        EditorUtility.SetDirty(bt.terrain);
        MicroSplatTerrain.SyncAll();
        AssetDatabase.SaveAssets();
    }
Exemple #21
0
    bool DoConvertGUI(MicroSplatTerrain t)
    {
        if (t.templateMaterial == null)
        {
            InitConvertConfigs();
            using (new GUILayout.VerticalScope(GUI.skin.box))
            {
                EditorGUILayout.LabelField("Select any integrations you want to add:");
                // integrations
                for (int i = 0; i < integrationConfigs.Count; ++i)
                {
                    var ic = integrationConfigs [i];
                    if (!ic.assetInstalled)
                    {
                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.LabelField(ic.name, GUILayout.Width(120));
                        EditorGUILayout.LabelField("Not Installed", GUILayout.Width(120));
                        if (GUILayout.Button("Link", GUILayout.Width(120)))
                        {
                            Application.OpenURL(ic.assetLink);
                        }
                        EditorGUILayout.EndHorizontal();
                    }
                    else
                    {
                        EditorGUILayout.BeginHorizontal();
                        ic.include = EditorGUILayout.Toggle(ic.include, GUILayout.Width(20));
                        EditorGUILayout.LabelField(ic.name);
                        EditorGUILayout.EndHorizontal();
                        if (ic.include && ic.missingModules.Count > 0)
                        {
                            using (new GUILayout.VerticalScope(GUI.skin.box))
                            {
                                EditorGUILayout.HelpBox("Some MicroSplat modules requested by this module are not installed. Some or all features of the integration will not be active.", MessageType.Warning);
                                for (int j = 0; j < ic.missingModules.Count; ++j)
                                {
                                    var m = ic.missingModules [j];
                                    DrawMissingModule(m);
                                }
                            }
                        }
                    }
                }

                if (GUILayout.Button("Convert to MicroSplat"))
                {
                    // get all terrains in selection, not just this one, and treat as one giant terrain
                    var            objs     = Selection.gameObjects;
                    List <Terrain> terrains = new List <Terrain> ();
                    for (int i = 0; i < objs.Length; ++i)
                    {
                        Terrain ter = objs [i].GetComponent <Terrain> ();
                        if (ter != null)
                        {
                            terrains.Add(ter);
                        }
                        Terrain [] trs = objs [i].GetComponentsInChildren <Terrain> ();
                        for (int x = 0; x < trs.Length; ++x)
                        {
                            if (!terrains.Contains(trs [x]))
                            {
                                terrains.Add(trs [x]);
                            }
                        }
                    }


                    Terrain       terrain  = t.GetComponent <Terrain> ();
                    int           texcount = terrain.terrainData.terrainLayers.Length;
                    List <string> keywords = new List <string> (defaultKeywords);
                    // set initial render pipleine
                    var pipeline = MicroSplatUtilities.DetectPipeline();
                    if (pipeline == MicroSplatUtilities.PipelineType.HDPipeline)
                    {
                        keywords.Add("_MSRENDERLOOP_UNITYHD");
                    }
                    else if (pipeline == MicroSplatUtilities.PipelineType.UniversalPipeline)
                    {
                        keywords.Add("_MSRENDERLOOP_UNITYLD");
                    }


                    // Because new users won't read the manual or read settings before they act, we don't clamp the texture count
                    // down for maximum performance. Way to many support requests complaining of black terrain after adding textures because
                    // they didn't realize they needed to up the max texture count. So now, 16 minimum. This is why we can't have nice things.

                    /*
                     * if (texcount <= 4)
                     * {
                     * keywords.Add ("_MAX4TEXTURES");
                     * }
                     * else if (texcount <= 8)
                     * {
                     * keywords.Add ("_MAX8TEXTURES");
                     * }
                     * else if (texcount <= 12)
                     * {
                     * keywords.Add ("_MAX12TEXTURES");
                     * }
                     */
                    if (texcount > 16 && texcount <= 20)
                    {
                        keywords.Add("_MAX20TEXTURES");
                    }
                    else if (texcount <= 24)
                    {
                        keywords.Add("_MAX24TEXTURES");
                    }
                    else if (texcount <= 28)
                    {
                        keywords.Add("_MAX28TEXTURES");
                    }
                    else if (texcount > 28)
                    {
                        keywords.Add("_MAX32TEXTURES");
                    }


                    for (int i = 0; i < integrationConfigs.Count; ++i)
                    {
                        var ic = integrationConfigs [i];
                        if (ic.include)
                        {
                            keywords.AddRange(ic.keywords);
                        }
                    }

                    // setup this terrain
                    t.templateMaterial = MicroSplatShaderGUI.NewShaderAndMaterial(terrain, keywords.ToArray());

                    var config = TextureArrayConfigEditor.CreateConfig(terrain);
                    t.templateMaterial.SetTexture("_Diffuse", config.diffuseArray);
                    t.templateMaterial.SetTexture("_NormalSAO", config.normalSAOArray);

                    t.propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);
                    EditorUtility.SetDirty(t);
                    if (terrain.terrainData.terrainLayers != null)
                    {
                        if (terrain.terrainData.terrainLayers.Length > 0)
                        {
                            Vector2 min = new Vector2(99999, 99999);
                            Vector2 max = Vector2.zero;


                            for (int x = 0; x < terrain.terrainData.terrainLayers.Length; ++x)
                            {
                                var uv = terrain.terrainData.terrainLayers [x].tileSize;
                                if (min.x > uv.x)
                                {
                                    min.x = uv.x;
                                }
                                if (min.y > uv.y)
                                {
                                    min.y = uv.y;
                                }
                                if (max.x < uv.x)
                                {
                                    max.x = uv.x;
                                }
                                if (max.y < uv.y)
                                {
                                    max.y = uv.y;
                                }
                            }
                            Vector2 average = Vector2.Lerp(min, max, 0.5f);
                            // use per texture UVs instead..
                            float diff = Vector2.Distance(min, max);
                            if (diff > 0.1)
                            {
                                keywords.Add("_PERTEXUVSCALEOFFSET");

                                // if the user has widely different UVs, use the LOD sampler. This is because the gradient mode blends between mip levels,
                                // which looks bad with hugely different UVs. I still don't understand why people do this kind of crap though, ideally
                                // your UVs should not differ per texture, and if so, not by much..
                                if (diff > 10)
                                {
                                    Debug.LogWarning("Terrain has wildly varing UV scales, it's best to keep consistent texture resolution. ");
                                }
                                if (!keywords.Contains("_USEGRADMIP"))
                                {
                                    keywords.Add("_USEGRADMIP");
                                }
                                Vector4 scaleOffset = new Vector4(1, 1, 0, 0);
                                t.templateMaterial.SetVector("_UVScale", scaleOffset);
                                var propData = MicroSplatShaderGUI.FindOrCreatePropTex(t.templateMaterial);

                                for (int x = 0; x < terrain.terrainData.terrainLayers.Length; ++x)
                                {
                                    var uvScale  = terrain.terrainData.terrainLayers [x].tileSize;
                                    var uvOffset = terrain.terrainData.terrainLayers [x].tileOffset;
                                    uvScale   = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                                    uvScale.x = Mathf.RoundToInt(uvScale.x);
                                    uvScale.y = Mathf.RoundToInt(uvScale.y);
                                    propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVScale, uvScale);
                                    propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVOffset, Vector2.zero);
                                }
                                for (int x = terrain.terrainData.terrainLayers.Length; x < 32; ++x)
                                {
                                    propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVScale, average);
                                    propData.SetValue(x, MicroSplatPropData.PerTexVector2.SplatUVOffset, Vector2.zero);
                                }
                                // must init the data, or the editor will write over it.

                                propData.SetValue(0, 15, Color.white);
                                EditorUtility.SetDirty(propData);

                                t.templateMaterial.SetVector("_TriplanarUVScale",
                                                             new Vector4(
                                                                 10.0f / t.terrain.terrainData.size.x,
                                                                 10.0f / t.terrain.terrainData.size.x, 0, 0));
                            }
                            else
                            {
                                var uvScale  = terrain.terrainData.terrainLayers [0].tileSize;
                                var uvOffset = terrain.terrainData.terrainLayers [0].tileOffset;

                                uvScale    = MicroSplatRuntimeUtil.UnityUVScaleToUVScale(uvScale, terrain);
                                uvOffset.x = uvScale.x / terrain.terrainData.size.x * 0.5f * uvOffset.x;
                                uvOffset.y = uvScale.y / terrain.terrainData.size.x * 0.5f * uvOffset.y;
                                Vector4 scaleOffset = new Vector4(uvScale.x, uvScale.y, uvOffset.x, uvOffset.y);
                                t.templateMaterial.SetVector("_UVScale", scaleOffset);
                                t.templateMaterial.SetVector("_TriplanarUVScale",
                                                             new Vector4(
                                                                 10.0f / t.terrain.terrainData.size.x,
                                                                 10.0f / t.terrain.terrainData.size.x, 0, 0));
                            }
                        }
                    }


                    // now make sure others all have the same settings as well.
                    for (int i = 0; i < terrains.Count; ++i)
                    {
                        var nt  = terrains [i];
                        var mgr = nt.GetComponent <MicroSplatTerrain> ();
                        if (mgr == null)
                        {
                            mgr = nt.gameObject.AddComponent <MicroSplatTerrain> ();
                        }
                        mgr.templateMaterial = t.templateMaterial;

                        if (mgr.propData == null)
                        {
                            mgr.propData = MicroSplatShaderGUI.FindOrCreatePropTex(mgr.templateMaterial);
                        }
                    }


                    Selection.SetActiveObjectWithContext(config, config);
                    t.keywordSO = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial);

                    t.keywordSO.keywords.Clear();
                    t.keywordSO.keywords = new List <string> (keywords);



                    // force recompile, so that basemap shader name gets reset correctly..
                    MicroSplatShaderGUI.MicroSplatCompiler comp = new MicroSplatShaderGUI.MicroSplatCompiler();
                    comp.Compile(t.templateMaterial);
                    // for some reason, often when  people create terrains with Gaia but this is unconfirmed,
                    // if instancing is set, the terrain will not draw until a shader feature is toggled.
                    // So for now, turn off instancing when doing conversion.
                    for (int i = 0; i < terrains.Count; ++i)
                    {
                        var nt = terrains [i];
                        nt.drawInstanced = false;
                    }


                    MicroSplatTerrain.SyncAll();

                    /*
                     * // turn on draw instanced if enabled and tessellation is disabled, unless render loop is LWRP/URP in which case it does work..
                     * if (t.keywordSO != null && (!t.keywordSO.IsKeywordEnabled("_TESSDISTANCE") || t.keywordSO.IsKeywordEnabled("_MSRENDERLOOP_UNITYLD")))
                     * {
                     * for (int i = 0; i < terrains.Count; ++i)
                     * {
                     *    var nt = terrains [i];
                     *    var mgr = nt.GetComponent<MicroSplatTerrain> ();
                     *    if (mgr != null && mgr.keywordSO != null && !mgr.keywordSO.IsKeywordEnabled("_MSRENDERLOOP_UNITYLD"))
                     *    {
                     *       nt.drawInstanced = true;
                     *    }
                     * }
                     * }
                     */
                    return(true);
                }
            }
        }
        return(false);
    }
        public override void DrawShaderGUI(MicroSplatShaderGUI shaderGUI, Material mat, MaterialEditor materialEditor, MaterialProperty[] props)
        {
            if (geoTexture && MicroSplatUtilities.DrawRollup("Geo Texture"))
            {
                if (mat.HasProperty("_GeoTex"))
                {
                    var texProp = shaderGUI.FindProp("_GeoTex", props);
                    materialEditor.TexturePropertySingleLine(CGeoTex, texProp);
                    MicroSplatUtilities.EnforceDefaultTexture(texProp, "microsplat_def_geomap_01");

                    Vector4 parms = mat.GetVector("_GeoParams");
                    EditorGUI.BeginChangeCheck();
                    parms.x = EditorGUILayout.Slider("Blend", parms.x, 0, 1);
                    parms.y = 1.0f / Mathf.Max(parms.y, 0.00001f);
                    parms.y = EditorGUILayout.FloatField("World Scale", parms.y);
                    parms.y = 1.0f / Mathf.Max(parms.y, 0.00001f);
                    parms.z = EditorGUILayout.FloatField("World Offset", parms.z);
                    if (EditorGUI.EndChangeCheck())
                    {
                        mat.SetVector("_GeoParams", parms);
                        EditorUtility.SetDirty(mat);
                    }
                    if (geoRange && mat.HasProperty("_GeoRange"))
                    {
                        Vector4 rangeParams = mat.GetVector("_GeoRange");
                        EditorGUI.BeginChangeCheck();
                        rangeParams = EditorGUILayout.Vector4Field(CGeoRange, rangeParams);
                        if (EditorGUI.EndChangeCheck())
                        {
                            if (rangeParams.z < rangeParams.x || rangeParams.z < rangeParams.y)
                            {
                                rangeParams.z = rangeParams.y;
                            }
                            if (rangeParams.y < rangeParams.x)
                            {
                                rangeParams.y = rangeParams.x;
                            }
                            if (rangeParams.w < rangeParams.z)
                            {
                                rangeParams.z = rangeParams.w;
                            }

                            mat.SetVector("_GeoRange", rangeParams);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                    if (geoCurve && mat.HasProperty("_GeoCurveParams"))
                    {
                        var propData = MicroSplatShaderGUI.FindOrCreatePropTex(mat);
                        EditorGUI.BeginChangeCheck();
                        if (propData != null)
                        {
                            propData.geoCurve = EditorGUILayout.CurveField(CGeoCurve, propData.geoCurve);
                        }
                        Vector4 curveParams = mat.GetVector("_GeoCurveParams");
                        curveParams.x = EditorGUILayout.FloatField("Scale", curveParams.x);
                        curveParams.y = EditorGUILayout.FloatField("Offset", curveParams.y);
                        curveParams.z = EditorGUILayout.FloatField("Rotation", curveParams.z);

                        if (EditorGUI.EndChangeCheck())
                        {
                            AnimationCurve c = propData.geoCurve;
                            for (int i = 0; i < c.length; ++i)
                            {
                                c.keys[i].time = Mathf.Clamp01(c.keys[i].time);
                            }
                            mat.SetVector("_GeoCurveParams", curveParams);
                            EditorUtility.SetDirty(mat);
                            EditorUtility.SetDirty(propData);
                            MicroSplatTerrain.SyncAll();
                        }
                    }
                }
            }
            if ((tintBlendMode != BlendMode.Off || normalBlendMode != NormalBlendMode.Off || SAOMBlend != SAOMBlendMode.Off || emisBlend != SAOMBlendMode.Off) && MicroSplatUtilities.DrawRollup("Global Texture"))
            {
                if (tintBlendMode != BlendMode.Off && mat.HasProperty("_GlobalTintTex"))
                {
                    materialEditor.TexturePropertySingleLine(new GUIContent("Tint Texture", "Albedo Tint Texture"), shaderGUI.FindProp("_GlobalTintTex", props));
                    Vector4 parms = mat.GetVector("_GlobalTextureParams");
                    EditorGUI.BeginChangeCheck();
                    parms.x = EditorGUILayout.Slider("Blend", parms.x, 0, 1);
                    if (EditorGUI.EndChangeCheck())
                    {
                        mat.SetVector("_GlobalTextureParams", parms);
                        EditorUtility.SetDirty(mat);
                    }
                    if (mat.HasProperty("_GlobalTintFade"))
                    {
                        Vector4 fade = mat.GetVector("_GlobalTintFade");
                        EditorGUI.BeginChangeCheck();

                        fade.x = EditorGUILayout.FloatField("Begin Fade", fade.x);
                        fade.z = EditorGUILayout.Slider("Opacity At Begin", fade.z, 0, 1);
                        fade.y = EditorGUILayout.FloatField("Fade Range", fade.y);
                        fade.w = EditorGUILayout.Slider("Opacity At End", fade.w, 0, 1);

                        if (EditorGUI.EndChangeCheck())
                        {
                            mat.SetVector("_GlobalTintFade", fade);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                    if (mat.HasProperty("_GlobalTintUVScale"))
                    {
                        Vector4 uv     = mat.GetVector("_GlobalTintUVScale");
                        Vector2 scale  = new Vector2(uv.x, uv.y);
                        Vector2 offset = new Vector2(uv.z, uv.w);

                        EditorGUI.BeginChangeCheck();
                        scale  = EditorGUILayout.Vector2Field(CUVScale, scale);
                        offset = EditorGUILayout.Vector2Field(CUVOffset, offset);

                        if (EditorGUI.EndChangeCheck())
                        {
                            uv = new Vector4(scale.x, scale.y, offset.x, offset.y);
                            mat.SetVector("_GlobalTintUVScale", uv);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                }
                if (normalBlendMode != NormalBlendMode.Off && mat.HasProperty("_GlobalNormalTex"))
                {
                    materialEditor.TexturePropertySingleLine(new GUIContent("Normal Texture", "Global Normal Texture"), shaderGUI.FindProp("_GlobalNormalTex", props));
                    Vector4 parms = mat.GetVector("_GlobalTextureParams");
                    EditorGUI.BeginChangeCheck();
                    parms.y = EditorGUILayout.Slider("Blend", parms.y, 0, 3);
                    if (EditorGUI.EndChangeCheck())
                    {
                        mat.SetVector("_GlobalTextureParams", parms);
                        EditorUtility.SetDirty(mat);
                    }

                    if (mat.HasProperty("_GlobalNormalFade"))
                    {
                        Vector4 fade = mat.GetVector("_GlobalNormalFade");
                        EditorGUI.BeginChangeCheck();

                        fade.x = EditorGUILayout.FloatField("Begin Fade", fade.x);
                        fade.z = EditorGUILayout.Slider("Opacity At Begin", fade.z, 0, 1);
                        fade.y = EditorGUILayout.FloatField("Fade Range", fade.y);
                        fade.w = EditorGUILayout.Slider("Opacity At End", fade.w, 0, 1);

                        if (EditorGUI.EndChangeCheck())
                        {
                            mat.SetVector("_GlobalNormalFade", fade);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                    if (mat.HasProperty("_GlobalNormalUVScale"))
                    {
                        Vector4 uv     = mat.GetVector("_GlobalNormalUVScale");
                        Vector2 scale  = new Vector2(uv.x, uv.y);
                        Vector2 offset = new Vector2(uv.z, uv.w);

                        EditorGUI.BeginChangeCheck();
                        scale  = EditorGUILayout.Vector2Field(CUVScale, scale);
                        offset = EditorGUILayout.Vector2Field(CUVOffset, offset);

                        if (EditorGUI.EndChangeCheck())
                        {
                            uv = new Vector4(scale.x, scale.y, offset.x, offset.y);
                            mat.SetVector("_GlobalNormalUVScale", uv);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                }
                // saom
                if (SAOMBlend != SAOMBlendMode.Off && mat.HasProperty("_GlobalSAOMTex"))
                {
                    materialEditor.TexturePropertySingleLine(new GUIContent("Smoothness(R)/AO(G)/Metal(B) Texture", "Global smoothness, ao, metallic Texture"), shaderGUI.FindProp("_GlobalSAOMTex", props));
                    Vector4 parms = mat.GetVector("_GlobalTextureParams");
                    EditorGUI.BeginChangeCheck();
                    parms.z = EditorGUILayout.Slider("Blend", parms.z, 0, 3);
                    if (EditorGUI.EndChangeCheck())
                    {
                        mat.SetVector("_GlobalTextureParams", parms);
                        EditorUtility.SetDirty(mat);
                    }

                    if (mat.HasProperty("_GlobalSAOMFade"))
                    {
                        Vector4 fade = mat.GetVector("_GlobalSAOMFade");
                        EditorGUI.BeginChangeCheck();

                        fade.x = EditorGUILayout.FloatField("Begin Fade", fade.x);
                        fade.z = EditorGUILayout.Slider("Opacity At Begin", fade.z, 0, 1);
                        fade.y = EditorGUILayout.FloatField("Fade Range", fade.y);
                        fade.w = EditorGUILayout.Slider("Opacity At End", fade.w, 0, 1);

                        if (EditorGUI.EndChangeCheck())
                        {
                            mat.SetVector("_GlobalSAOMFade", fade);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                    if (mat.HasProperty("_GlobalSAOMUVScale"))
                    {
                        Vector4 uv     = mat.GetVector("_GlobalSAOMUVScale");
                        Vector2 scale  = new Vector2(uv.x, uv.y);
                        Vector2 offset = new Vector2(uv.z, uv.w);

                        EditorGUI.BeginChangeCheck();
                        scale  = EditorGUILayout.Vector2Field(CUVScale, scale);
                        offset = EditorGUILayout.Vector2Field(CUVOffset, offset);

                        if (EditorGUI.EndChangeCheck())
                        {
                            uv = new Vector4(scale.x, scale.y, offset.x, offset.y);
                            mat.SetVector("_GlobalSAOMUVScale", uv);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                }

                // emis
                if (emisBlend != SAOMBlendMode.Off && mat.HasProperty("_GlobalEmisTex"))
                {
                    materialEditor.TexturePropertySingleLine(new GUIContent("Emission Texture", "Global Emission"), shaderGUI.FindProp("_GlobalEmisTex", props));
                    Vector4 parms = mat.GetVector("_GlobalTextureParams");
                    EditorGUI.BeginChangeCheck();
                    parms.w = EditorGUILayout.Slider("Blend", parms.w, 0, 3);
                    if (EditorGUI.EndChangeCheck())
                    {
                        mat.SetVector("_GlobalTextureParams", parms);
                        EditorUtility.SetDirty(mat);
                    }

                    if (mat.HasProperty("_GlobalEmisFade"))
                    {
                        Vector4 fade = mat.GetVector("_GlobalEmisFade");
                        EditorGUI.BeginChangeCheck();

                        fade.x = EditorGUILayout.FloatField("Begin Fade", fade.x);
                        fade.z = EditorGUILayout.Slider("Opacity At Begin", fade.z, 0, 1);
                        fade.y = EditorGUILayout.FloatField("Fade Range", fade.y);
                        fade.w = EditorGUILayout.Slider("Opacity At End", fade.w, 0, 1);

                        if (EditorGUI.EndChangeCheck())
                        {
                            mat.SetVector("_GlobalEmisFade", fade);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                    if (mat.HasProperty("_GlobalEmisUVScale"))
                    {
                        Vector4 uv     = mat.GetVector("_GlobalEmisUVScale");
                        Vector2 scale  = new Vector2(uv.x, uv.y);
                        Vector2 offset = new Vector2(uv.z, uv.w);

                        EditorGUI.BeginChangeCheck();
                        scale  = EditorGUILayout.Vector2Field(CUVScale, scale);
                        offset = EditorGUILayout.Vector2Field(CUVOffset, offset);

                        if (EditorGUI.EndChangeCheck())
                        {
                            uv = new Vector4(scale.x, scale.y, offset.x, offset.y);
                            mat.SetVector("_GlobalEmisUVScale", uv);
                            EditorUtility.SetDirty(mat);
                        }
                    }
                }
            }
        }