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(); } } } } }
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(); }
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); }
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(); } }
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(); } } }
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); }
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(); }
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); } }
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); } }
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); }
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; }
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(); }
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); } } } } }