public void Compile(Material m, string shaderName = null) { int hash = 0; MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords(m); for (int i = 0; i < keywords.keywords.Count; ++i) { hash += 31 + keywords.keywords [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(keywords.keywords.ToArray(), nm, baseName); if (renderLoop != null) { keywords.EnableKeyword(renderLoop.GetRenderLoopKeyword()); } GenerateAuxShaders(nm, path, keywords.keywords); MicroSplatUtilities.Checkout(path); System.IO.File.WriteAllText(path, terrainShader); if (!keywords.IsKeywordEnabled("_MICROMESH") && !keywords.IsKeywordEnabled("_MICROVERTEXMESH") && !keywords.IsKeywordEnabled("_MEGASPLAT") && !keywords.IsKeywordEnabled("_MICROTERRAINMESH")) { // generate fallback string[] oldKeywords = new string[keywords.keywords.Count]; System.Array.Copy(keywords.keywords.ToArray(), oldKeywords, keywords.keywords.Count); keywords.DisableKeyword("_TESSDISTANCE"); keywords.DisableKeyword("_PARALLAX"); keywords.DisableKeyword("_DETAILNOISE"); keywords.EnableKeyword("_MICROSPLATBASEMAP"); string fallback = Compile(keywords.keywords.ToArray(), baseName); keywords.keywords = new List <string> (oldKeywords); string fallbackPath = path.Replace(".shader", "_Base.shader"); MicroSplatUtilities.Checkout(fallbackPath); System.IO.File.WriteAllText(fallbackPath, fallback); } EditorUtility.SetDirty(m); AssetDatabase.Refresh(); #if __MICROSPLAT_MESH__ MicroSplatMesh.ClearMaterialCache(); #endif MicroSplatObject.SyncAll(); }
void Undo_UndoRedoPerformed() { if (cachedMaterial != null && cachedKeywordCount > 0) { var keywordSO = MicroSplatUtilities.FindOrCreateKeywords(cachedMaterial); if (cachedKeywordCount != keywordSO.keywords.Count) { needsCompile = true; } } }
public static Material NewShaderAndMaterial(string path, string name, string[] keywords = null) { string shaderPath = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.shader"); string shaderBasePath = shaderPath.Replace(".shader", "_Base.shader"); string matPath = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.mat"); MicroSplatCompiler compiler = new MicroSplatCompiler(); compiler.Init(); if (keywords == null) { keywords = new string[0]; } string baseName = "Hidden/MicroSplat/" + name + "_Base"; string baseShader = compiler.Compile(keywords, baseName); string regularShader = compiler.Compile(keywords, name, baseName); System.IO.File.WriteAllText(shaderPath, regularShader); System.IO.File.WriteAllText(shaderBasePath, baseShader); if (keywords.Contains("_MESHOVERLAYSPLATS")) { string meshOverlayShader = compiler.Compile(keywords, name, null, true); System.IO.File.WriteAllText(shaderPath.Replace(".shader", "_MeshOverlay.shader"), meshOverlayShader); } AssetDatabase.Refresh(); Shader s = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath); Material m = new Material(s); AssetDatabase.CreateAsset(m, matPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); var kwds = MicroSplatUtilities.FindOrCreateKeywords(m); kwds.keywords = new List <string>(keywords); EditorUtility.SetDirty(kwds); return(AssetDatabase.LoadAssetAtPath <Material>(matPath)); }
public static Material NewShaderAndMaterial(string path, string name, string[] keywords = null) { string shaderPath = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.shader"); string shaderBasePath = shaderPath.Replace(".shader", "_Base.shader"); string matPath = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.mat"); shaderPath = shaderPath.Replace("//", "/"); shaderBasePath = shaderBasePath.Replace("//", "/"); matPath = matPath.Replace("//", "/"); MicroSplatCompiler compiler = new MicroSplatCompiler(); compiler.Init(); if (keywords == null) { keywords = new string[0]; } var pipeline = MicroSplatUtilities.DetectPipeline(); if (pipeline == MicroSplatUtilities.PipelineType.HDPipeline) { System.Array.Resize(ref keywords, keywords.Length + 1); keywords [keywords.Length - 1] = "_MSRENDERLOOP_UNITYHD"; } else if (pipeline == MicroSplatUtilities.PipelineType.UniversalPipeline) { System.Array.Resize(ref keywords, keywords.Length + 1); keywords [keywords.Length - 1] = "_MSRENDERLOOP_UNITYLD"; } string baseName = "Hidden/MicroSplat/" + name + "_Base"; string baseShader = compiler.Compile(keywords, baseName); string regularShader = compiler.Compile(keywords, name, baseName); System.IO.File.WriteAllText(shaderPath, regularShader); System.IO.File.WriteAllText(shaderBasePath, baseShader); if (keywords.Contains("_MESHOVERLAYSPLATS")) { string meshOverlayShader = compiler.Compile(keywords, name, null, true); System.IO.File.WriteAllText(shaderPath.Replace(".shader", "_MeshOverlay.shader"), meshOverlayShader); } AssetDatabase.Refresh(); Shader s = AssetDatabase.LoadAssetAtPath <Shader> (shaderPath); if (s == null) { Debug.LogError("Shader not found at path " + shaderPath); } Material m = new Material(s); AssetDatabase.CreateAsset(m, matPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); var kwds = MicroSplatUtilities.FindOrCreateKeywords(m); kwds.keywords = new List <string> (keywords); EditorUtility.SetDirty(kwds); var propData = MicroSplatShaderGUI.FindOrCreatePropTex(m); if (propData != null) { EditorUtility.SetDirty(propData); } return(AssetDatabase.LoadAssetAtPath <Material> (matPath)); }
public void Compile(Material m, string shaderName = null) { int hash = 0; MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords(m); for (int i = 0; i < keywords.keywords.Count; ++i) { hash += 31 + keywords.keywords [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(keywords.keywords.ToArray(), nm, baseName); if (renderLoop != null) { keywords.EnableKeyword(renderLoop.GetRenderLoopKeyword()); } string blendShader = null; // strip extra feature from terrain blending to make it cheaper if (keywords.IsKeywordEnabled("_TERRAINBLENDING")) { List <string> blendKeywords = new List <string> (keywords.keywords); if (keywords.IsKeywordEnabled("_TBDISABLE_DETAILNOISE") && blendKeywords.Contains("_DETAILNOISE")) { blendKeywords.Remove("_DETAILNOISE"); } if (keywords.IsKeywordEnabled("_TBDISABLE_DETAILNOISE") && blendKeywords.Contains("_ANTITILEARRAYDETAIL")) { blendKeywords.Remove("_ANTITILEARRAYDETAIL"); } if (keywords.IsKeywordEnabled("_TBDISABLE_DISTANCENOISE") && blendKeywords.Contains("_DISTANCENOISE")) { blendKeywords.Remove("_DISTANCENOISE"); } if (keywords.IsKeywordEnabled("_TBDISABLE_DISTANCENOISE") && blendKeywords.Contains("_ANTITILEARRAYDISTANCE")) { blendKeywords.Remove("_ANTITILEARRAYDISTANCE"); } if (keywords.IsKeywordEnabled("_TBDISABLE_DISTANCERESAMPLE") && blendKeywords.Contains("_DISTANCERESAMPLE")) { blendKeywords.Remove("_DISTANCERESAMPLE"); } blendShader = Compile(blendKeywords.ToArray(), nm, null, true); } string meshBlendShader = null; if (keywords.IsKeywordEnabled("_MESHOVERLAYSPLATS")) { List <string> blendKeywords = new List <string> (keywords.keywords); if (blendKeywords.Contains("_TESSDISTANCE")) { blendKeywords.Remove("_TESSDISTANCE"); } meshBlendShader = Compile(blendKeywords.ToArray(), nm, null, true); } MicroSplatUtilities.Checkout(path); System.IO.File.WriteAllText(path, terrainShader); if (!keywords.IsKeywordEnabled("_MICROMESH")) { // generate fallback string[] oldKeywords = new string[keywords.keywords.Count]; System.Array.Copy(keywords.keywords.ToArray(), oldKeywords, keywords.keywords.Count); keywords.DisableKeyword("_TESSDISTANCE"); keywords.DisableKeyword("_PARALLAX"); keywords.DisableKeyword("_DETAILNOISE"); keywords.EnableKeyword("_MICROSPLATBASEMAP"); string fallback = Compile(keywords.keywords.ToArray(), baseName); keywords.keywords = new List <string> (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(); MicroSplatObject.SyncAll(); }
public static Material NewShaderAndMaterial(string path, string name, string[] keywords = null) { // if no branch sampling is specified, go straight to aggressive. Usually defaults are not done this way, but this seems // to make the most sense.. if (!keywords.Contains("_BRANCHSAMPLES")) { System.Array.Resize(ref keywords, keywords.Length + 2); keywords [keywords.Length - 2] = "_BRANCHSAMPLES"; keywords [keywords.Length - 1] = "_BRANCHSAMPLESARG"; } string shaderPath = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.shader"); string shaderBasePath = shaderPath.Replace(".shader", "_Base.shader"); string matPath = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.mat"); shaderPath = shaderPath.Replace("//", "/"); shaderBasePath = shaderBasePath.Replace("//", "/"); matPath = matPath.Replace("//", "/"); MicroSplatCompiler compiler = new MicroSplatCompiler(); compiler.Init(); if (keywords == null) { keywords = new string[0]; } var pipeline = MicroSplatUtilities.DetectPipeline(); if (pipeline == MicroSplatUtilities.PipelineType.HDPipeline) { System.Array.Resize(ref keywords, keywords.Length + 1); keywords [keywords.Length - 1] = "_MSRENDERLOOP_UNITYHD"; } else if (pipeline == MicroSplatUtilities.PipelineType.UniversalPipeline) { System.Array.Resize(ref keywords, keywords.Length + 1); keywords [keywords.Length - 1] = "_MSRENDERLOOP_UNITYLD"; } string baseName = "Hidden/MicroSplat/" + name + "_Base"; string baseShader = compiler.Compile(keywords, baseName); string regularShader = compiler.Compile(keywords, name, baseName); System.IO.File.WriteAllText(shaderPath, regularShader); System.IO.File.WriteAllText(shaderBasePath, baseShader); compiler.GenerateAuxShaders(name, shaderPath, new List <string>(keywords)); AssetDatabase.Refresh(); Shader s = AssetDatabase.LoadAssetAtPath <Shader> (shaderPath); if (s == null) { Debug.LogError("Shader not found at path " + shaderPath); } Material m = new Material(s); AssetDatabase.CreateAsset(m, matPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); var kwds = MicroSplatUtilities.FindOrCreateKeywords(m); kwds.keywords = new List <string> (keywords); EditorUtility.SetDirty(kwds); var propData = MicroSplatShaderGUI.FindOrCreatePropTex(m); if (propData != null) { EditorUtility.SetDirty(propData); } AssetDatabase.SaveAssets(); return(AssetDatabase.LoadAssetAtPath <Material> (matPath)); }
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) { Undo.undoRedoPerformed -= Undo_UndoRedoPerformed; Undo.undoRedoPerformed += Undo_UndoRedoPerformed; Material targetMat = materialEditor.target as Material; if (cachedTitle == null) { cachedTitle = "Shader Generator v:" + MicroSplatVersion; } if (moduleLabelStyle == null) { moduleLabelStyle = new GUIStyle(EditorStyles.foldout); moduleLabelStyle.fontStyle = FontStyle.Bold; } if (GUI.enabled == false || string.IsNullOrEmpty(AssetDatabase.GetAssetPath(targetMat))) { EditorGUILayout.HelpBox("You must edit the template material, not the instance being used. You can find this in the MicroSplatData directory, or assigned on your MicroSplatTerrain component", MessageType.Info); return; } EditorGUI.BeginChangeCheck(); // sync materials var keywordSO = MicroSplatUtilities.FindOrCreateKeywords(targetMat); cachedKeywordCount = keywordSO.keywords.Count; // for undo cachedMaterial = 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 var propTex = FindOrCreatePropTex(targetMat); Undo.RecordObjects(new Object[3] { targetMat, keywordSO, propTex }, "MicroSplat Material Edit"); using (new GUILayout.VerticalScope(MicroSplatUtilities.boxStyle)) { if (MicroSplatUtilities.DrawRollup(cachedTitle)) { GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (bulkEditMode) { if (GUILayout.Button("Exit Bulk Shader Feature Edit Mode", GUILayout.Width(230))) { bulkEditMode = false; needsCompile = true; } } else { if (GUILayout.Button("Enter Bulk Shader Feature Edit Mode", GUILayout.Width(230))) { bulkEditMode = true; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (bulkEditMode) { EditorGUILayout.HelpBox("Shader is in bulk edit mode, allowing you to change many options without recompiling the shader. No material properties will be shown during bulk editing, and the shader will be recompiled and properties shown once you exit this mode", MessageType.Warning); } 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) { needsCompile = EditorGUI.EndChangeCheck() || needsCompile; if (!moduleFoldoutState.ContainsKey(e.ModuleName())) { moduleFoldoutState [e.ModuleName()] = true; } // hack for invisible modules- need to make this more formal somehow? bool empty = (e.ModuleName() == "Mesh" && (!keywordSO.IsKeywordEnabled("_MICROMESH") && !keywordSO.IsKeywordEnabled("_MICROVERTEXMESH")) || (e.ModuleName() == "MeshTerrain" && !keywordSO.IsKeywordEnabled("_MICROMESHTERRAIN"))); if (!empty) { using (new GUILayout.VerticalScope(MicroSplatUtilities.boxStyle)) { EditorGUI.indentLevel++; moduleFoldoutState [e.ModuleName()] = EditorGUILayout.Foldout(moduleFoldoutState [e.ModuleName()], e.ModuleName(), moduleLabelStyle); //EditorGUILayout.LabelField (e.ModuleName (), moduleLabelStyle); EditorGUI.BeginChangeCheck(); if (moduleFoldoutState [e.ModuleName()]) { //EditorGUI.indentLevel++; e.DrawFeatureGUI(keywordSO); //EditorGUI.indentLevel--; } EditorGUI.indentLevel--; } } else { EditorGUI.BeginChangeCheck(); } } else { EditorGUILayout.HelpBox("Extension : " + e.ModuleName() + " 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); } } } if (bulkEditMode) { if (!keywordSO.IsKeywordEnabled("_DISABLESPLATMAPS")) { Texture2DArray diff = targetMat.GetTexture("_Diffuse") as Texture2DArray; if (diff != null && MicroSplatUtilities.DrawRollup("Per Texture Properties")) { 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); } } } } needsCompile = needsCompile || EditorGUI.EndChangeCheck(); if (needsCompile) { 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()); } needsCompile = false; } return; // Don't draw rest of GUI } } 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.ModuleName() + " is version " + ext.GetVersion() + " and MicroSplat is version " + MicroSplatVersion + ", please update so that all modules are using the same version.", MessageType.Error); } } if (!keywordSO.IsKeywordEnabled("_DISABLESPLATMAPS")) { Texture2DArray diff = targetMat.GetTexture("_Diffuse") as Texture2DArray; if (diff != null && MicroSplatUtilities.DrawRollup("Per Texture Properties")) { 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) { MicroSplatObject.SyncAll(); } 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; } }
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 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; } }
public static Texture2D Bake(MicroSplatTerrain mst, BakingPasses p, int resolution, Texture2D worldPos, Texture2D worldNormal) { Camera cam = new GameObject("cam").AddComponent<Camera>(); cam.orthographic = true; cam.orthographicSize = 0.5f; cam.transform.position = new Vector3(0.5f, 10000.5f, -1); cam.nearClipPlane = 0.1f; cam.farClipPlane = 2.0f; cam.enabled = false; cam.depthTextureMode = DepthTextureMode.None; cam.clearFlags = CameraClearFlags.Color; cam.backgroundColor = Color.grey; var debugOutput = OutputFromPass(p); var readWrite = (debugOutput == MicroSplatBaseFeatures.DebugOutput.Albedo || debugOutput == MicroSplatBaseFeatures.DebugOutput.Emission) ? RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Linear; RenderTexture rt = RenderTexture.GetTemporary(resolution, resolution, 0, RenderTextureFormat.ARGB32, readWrite); RenderTexture.active = rt; cam.targetTexture = rt; GameObject go = GameObject.CreatePrimitive(PrimitiveType.Quad); go.transform.position = new Vector3(0, 10000, 0); cam.transform.position = new Vector3(0, 10000, -1); Material renderMat = SetupMaterial(MicroSplatUtilities.FindOrCreateKeywords(mst.templateMaterial), mst.matInstance, debugOutput, worldPos != null); renderMat.SetTexture("_DebugWorldPos", worldPos); renderMat.SetTexture("_DebugWorldNormal", worldNormal); go.GetComponent<MeshRenderer>().sharedMaterial = renderMat; bool fog = RenderSettings.fog; if (p == BakingPasses.Normal) { cam.backgroundColor = Color.blue; } else { cam.backgroundColor = Color.gray; } // this is a strange one, at 0,0,0 rotation the albedo won't render on Windows platforms, so parent the cam to the quad and rotate it a bit cam.transform.SetParent(go.transform); go.transform.rotation = Quaternion.Euler(0.01f, 0, 0); var ambInt = RenderSettings.ambientIntensity; var reflectInt = RenderSettings.reflectionIntensity; RenderSettings.ambientIntensity = 0; RenderSettings.reflectionIntensity = 0; Unsupported.SetRenderSettingsUseFogNoDirty(false); cam.Render(); Unsupported.SetRenderSettingsUseFogNoDirty(fog); RenderSettings.ambientIntensity = ambInt; RenderSettings.reflectionIntensity = reflectInt; Texture2D tex = new Texture2D(resolution, resolution, TextureFormat.ARGB32, false); tex.ReadPixels(new Rect(0, 0, resolution, resolution), 0, 0); RenderTexture.active = null; RenderTexture.ReleaseTemporary(rt); tex.Apply(); MeshRenderer mr = go.GetComponent<MeshRenderer>(); if (mr != null) { if (mr.sharedMaterial != null) { if (mr.sharedMaterial.shader != null) GameObject.DestroyImmediate(mr.sharedMaterial.shader); GameObject.DestroyImmediate(mr.sharedMaterial); } } GameObject.DestroyImmediate(go); // cam is child, so will be destroyed too return tex; }
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); } }
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); }