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(); }
void Bake(MicroSplatTerrain mst) { // for each pass int pass = 1; while (pass <= (int)(BakingPasses.Emissive)) { BakingPasses p = (BakingPasses)pass; pass *= 2; if (!IsEnabled(p)) { continue; } Texture2D worldPos, worldNormal; GenerateWorldData(mst.terrain, out worldNormal, out worldPos, (int)res); var debugOutput = OutputFromPass(p); var tex = Bake(mst, p, (int)res, worldPos, worldNormal); var bytes = tex.EncodeToPNG(); DestroyImmediate(worldPos, worldNormal); string texPath = MicroSplatUtilities.RelativePathFromAsset(mst.terrain) + "/" + mst.terrain.name + "_" + debugOutput.ToString(); System.IO.File.WriteAllBytes(texPath + ".png", bytes); } AssetDatabase.Refresh(); }
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 void GenerateAuxShaders(string name, string path, List <string> keywords) { var exts = new List <FeatureDescriptor> (extensions); // prevent recursive access due to init foreach (var e in exts) { var aux = e.GetAuxShader(); if (aux != null) { if (keywords.Contains(aux.trigger)) { var okeys = new List <string> (keywords); // remove all other trigger keywords foreach (var oe in exts) { if (oe != e) { var oaux = oe.GetAuxShader(); if (oaux != null) { okeys.Remove(oaux.trigger); } } } string opath = path.Replace(".shader", aux.extension + ".shader"); e.ModifyKeywordsForAuxShader(okeys); var shader = this.Compile(okeys.ToArray(), name, null, aux); MicroSplatUtilities.Checkout(opath); System.IO.File.WriteAllText(opath, shader); } } } }
private static void SetupMicroSplatMaterial(DiggerSystem diggerSystem) { var microSplat = diggerSystem.Terrain.GetComponent <MicroSplatTerrain>(); if (!microSplat) { Debug.LogError($"Could not find MicroSplatTerrain on terrain {diggerSystem.Terrain.name}"); return; } var microSplatShader = MicroSplatUtilities.GetDiggerShader(microSplat); if (microSplatShader == null) { Debug.LogError($"Could not find MicroSplat Digger shader"); return; } var material = new Material(microSplatShader); material.CopyPropertiesFromMaterial(microSplat.matInstance); var matPath = Path.Combine(diggerSystem.BasePathData, $"diggerMicroSplat.mat"); material = EditorUtils.CreateOrReplaceAsset(material, matPath); AssetDatabase.ImportAsset(matPath, ImportAssetOptions.ForceUpdate); diggerSystem.Materials[0] = material; }
public void DoPerPixelNormalGUI() { MicroSplatTerrain bt = target as MicroSplatTerrain; Terrain t = bt.GetComponent <Terrain>(); if (t == null || t.terrainData == null) { EditorGUILayout.HelpBox("No Terrain data found", MessageType.Error); return; } if (t.materialTemplate == null) { return; } if (bt.keywordSO == null) { return; } if (bt.terrain.drawInstanced && bt.perPixelNormal != null && !bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING")) { EditorGUILayout.HelpBox("Per Pixel Normal is assigned, but shader is using Instance rendering, which automatically provides per-pixel normal. You may turn off per pixel normal if it's on and clear the normal data to save memory.", MessageType.Warning); if (bt.perPixelNormal != null && GUILayout.Button("Clear")) { bt.perPixelNormal = null; EditorUtility.SetDirty(bt); EditorUtility.SetDirty(bt.terrain); } } MicroSplatUtilities.DrawTextureField(bt, CPerPixelNormal, ref bt.perPixelNormal, "_PERPIXNORMAL", "_TERRAINBLENDING", null, null, false); if (bt.perPixelNormal == null && (bt.keywordSO.IsKeywordEnabled("_PERPIXNORMAL") || bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING"))) { EditorGUILayout.HelpBox("Terrain Normal Data is not present, please generate", MessageType.Warning); } if (bt.keywordSO.IsKeywordEnabled("_PERPIXNORMAL") || bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING")) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Normal Data"); if (GUILayout.Button(bt.perPixelNormal == null ? "Generate" : "Update")) { GenerateTerrainNormalMap(bt); EditorUtility.SetDirty(bt); EditorUtility.SetDirty(bt.terrain); } if (bt.perPixelNormal != null && GUILayout.Button("Clear")) { bt.perPixelNormal = null; EditorUtility.SetDirty(bt); EditorUtility.SetDirty(bt.terrain); } EditorGUILayout.EndHorizontal(); } }
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 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 Shader NewShader() { string path = "Assets"; foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets)) { path = AssetDatabase.GetAssetPath(obj); if (System.IO.File.Exists(path)) { path = System.IO.Path.GetDirectoryName(path); } break; } path = path.Replace("\\", "/"); path = AssetDatabase.GenerateUniqueAssetPath(path + "/MicroSplat.shader"); string name = path.Substring(path.LastIndexOf("/")); name = name.Substring(0, name.IndexOf(".")); name = name.Replace("/", ""); ; string [] keywords = new string [0]; var pipeline = MicroSplatUtilities.DetectPipeline(); if (pipeline == MicroSplatUtilities.PipelineType.HDPipeline) { System.Array.Resize(ref keywords, keywords.Length + 2); keywords [keywords.Length - 2] = "_MSRENDERLOOP_UNITYHD"; keywords [keywords.Length - 1] = "_MSRENDERLOOP_UNITYHDRP2020"; } else if (pipeline == MicroSplatUtilities.PipelineType.UniversalPipeline) { System.Array.Resize(ref keywords, keywords.Length + 2); keywords [keywords.Length - 2] = "_MSRENDERLOOP_UNITYLD"; keywords [keywords.Length - 1] = "_MSRENDERLOOP_UNITYURP2020"; } MicroSplatCompiler compiler = new MicroSplatCompiler(); compiler.Init(); string ret = compiler.Compile(keywords, name, null); System.IO.File.WriteAllText(path, ret); AssetDatabase.Refresh(); return(AssetDatabase.LoadAssetAtPath <Shader> (path)); }
void WeightLimitingGUI(MicroSplatTerrain t) { if (MicroSplatUtilities.DrawRollup("Weight Limiting", false)) { weightLimit = EditorGUILayout.IntSlider("Weight Limit", weightLimit, 2, 4); multipassWeightLimit = EditorGUILayout.Toggle(CMultipassWeight, multipassWeightLimit); if (GUILayout.Button("Limit")) { WeightLimitTerrain(t, weightLimit, false); if (multipassWeightLimit) { WeightLimitTerrain(t, weightLimit, 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)); }
static void RegenAllShaders() { var mats = AssetDatabase.FindAssets("t:Material"); foreach (var m in mats) { Material mat = AssetDatabase.LoadAssetAtPath <Material> (AssetDatabase.GUIDToAssetPath(m)); if (mat != null && mat.shader != null) { if (MicroSplatUtilities.CanFindKeywords(mat)) { Debug.Log("Regenerating shader " + AssetDatabase.GetAssetPath(mat.shader)); MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler(); compiler.Compile(mat); } } } }
void ImportExportGUI() { if (MicroSplatUtilities.DrawRollup("Splat Import/Export", false)) { EditorGUI.BeginChangeCheck(); serializedObject.Update(); SerializedProperty prop = serializedObject.FindProperty("importSplatMaps"); EditorGUILayout.PropertyField(prop, true); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } if (GUILayout.Button("Import")) { ImportSplatMaps(); } if (GUILayout.Button("Export")) { ExportSplatMaps(); } } }
public void BakingGUI(MicroSplatTerrain t) { if (needsBake && Event.current.type == EventType.Repaint) { needsBake = false; Bake(t); } if (MicroSplatUtilities.DrawRollup("Render Baking", false)) { res = (BakingResolutions)EditorGUILayout.EnumPopup(new GUIContent("Resolution"), res); #if UNITY_2017_3_OR_NEWER passes = (BakingPasses)EditorGUILayout.EnumFlagsField(new GUIContent("Features"), passes); #else passes = (BakingPasses)EditorGUILayout.EnumMaskPopup(new GUIContent("Features"), passes); #endif if (GUILayout.Button("Export Selected")) { needsBake = true; } } }
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; } }
public void BakingGUI(MicroSplatTerrain t) { if (needsBake && Event.current.type == EventType.Repaint) { needsBake = false; Bake(t); } #if __MICROSPLAT_PROCTEX__ if (bakeSplats && Event.current.type == EventType.Repaint) { bakeSplats = false; int alphaLayerCount = t.terrain.terrainData.alphamapLayers; int splatRes = t.terrain.terrainData.alphamapResolution; int splatCount = t.terrain.terrainData.terrainLayers.Length; float[,,] splats = new float[splatRes, splatRes, splatCount]; // World/normals are used in texturing, so we have to make them match. Texture2D worldPos, worldNormal; GenerateWorldData(t.terrain, out worldNormal, out worldPos, splatRes); for (int i = 0; i < alphaLayerCount; i = i + 4) { Texture2D tex = Texture2D.blackTexture; Texture2D alpha = Texture2D.blackTexture; if (i == 0) { tex = Bake(t, BakingPasses.ProceduralSplatOutput0, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput0A, splatRes, worldPos, worldNormal); } if (i == 4) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput1, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput1A, splatRes, worldPos, worldNormal); } else if (i == 8) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput2, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput2A, splatRes, worldPos, worldNormal); } else if (i == 12) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput3, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput3A, splatRes, worldPos, worldNormal); } else if (i == 16) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput4, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput4A, splatRes, worldPos, worldNormal); } else if (i == 20) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput5, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput5A, splatRes, worldPos, worldNormal); } else if (i == 24) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput6, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput6A, splatRes, worldPos, worldNormal); } else if (i == 28) { DestroyTex(tex); DestroyTex(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput7, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput7A, splatRes, worldPos, worldNormal); } for (int x = 0; x < splatRes; ++x) { for (int y = 0; y < splatRes; ++y) { Color c = tex.GetPixel(x, y); Color a = alpha.GetPixel(x, y); if (i < splatCount) { splats[y, x, i] = c.r; } if (i + 1 < splatCount) { splats[y, x, i + 1] = c.g; } if (i + 2 < splatCount) { splats[y, x, i + 2] = c.b; } if (i + 3 < splatCount) { splats[y, x, i + 3] = a.g; } } } } DestroyImmediate(worldPos); DestroyImmediate(worldNormal); t.terrain.terrainData.SetAlphamaps(0, 0, splats); EditorUtility.SetDirty(t.terrain.terrainData); } #endif if (MicroSplatUtilities.DrawRollup("Render Baking", false)) { res = (BakingResolutions)EditorGUILayout.EnumPopup(new GUIContent("Resolution"), res); #if UNITY_2017_3_OR_NEWER passes = (BakingPasses)EditorGUILayout.EnumFlagsField(new GUIContent("Features"), passes); #else passes = (BakingPasses)EditorGUILayout.EnumMaskPopup(new GUIContent("Features"), passes); #endif if (GUILayout.Button("Export Selected")) { needsBake = true; } } #if __MICROSPLAT_PROCTEX__ if (t.templateMaterial != null && t.keywordSO != null && t.keywordSO.IsKeywordEnabled("_PROCEDURALTEXTURE")) { if (MicroSplatUtilities.DrawRollup("Procedural Baking", false)) { EditorGUILayout.Space(); if (GUILayout.Button("Bake Procedural to Terrain")) { bakeSplats = true; } EditorGUILayout.Space(); } } #endif MicroSplatCompressor.DrawGUI(t, compressOptions); }
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 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(); }
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); } }
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 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 void BakingGUI(MicroSplatTerrain t) { if (needsBake && Event.current.type == EventType.Repaint) { needsBake = false; Bake(t); } #if __MICROSPLAT_PROCTEX__ if (bakeSplats && Event.current.type == EventType.Repaint) { bakeSplats = false; int alphaLayerCount = t.terrain.terrainData.alphamapLayers; int splatRes = t.terrain.terrainData.alphamapResolution; #if UNITY_2019_3_OR_NEWER int splatCount = t.terrain.terrainData.terrainLayers.Length; #else int splatCount = t.terrain.terrainData.splatPrototypes.Length; #endif float[,,] splats = new float[splatRes, splatRes, splatCount]; // World/normals are used in texturing, so we have to make them match. Texture2D worldPos = new Texture2D(splatRes, splatRes, TextureFormat.RGBAFloat, true, true); Texture2D worldNormal = new Texture2D(splatRes, splatRes, TextureFormat.RGBAFloat, true, true); t.terrain.transform.rotation = Quaternion.identity; for (int x = 0; x < splatRes; ++x) { float u = (float)x / (float)splatRes; for (int y = 0; y < splatRes; ++y) { float v = (float)y / (float)splatRes; float h = t.terrain.terrainData.GetInterpolatedHeight(u, v); Vector3 n = t.terrain.terrainData.GetInterpolatedNormal(u, v); Vector3 wp = t.terrain.transform.localToWorldMatrix.MultiplyPoint(new Vector3(u * t.terrain.terrainData.size.x, h, v * t.terrain.terrainData.size.z)); worldPos.SetPixel(x, y, new Color(wp.x, wp.y, wp.z)); worldNormal.SetPixel(x, y, new Color(n.x, n.y, n.z)); } } worldPos.Apply(); worldNormal.Apply(); for (int i = 0; i < alphaLayerCount; i=i+4) { Texture2D tex = Texture2D.blackTexture; Texture2D alpha = Texture2D.blackTexture; if (i == 0) { tex = Bake(t, BakingPasses.ProceduralSplatOutput0, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput0A, splatRes, worldPos, worldNormal); } if (i == 4) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput1, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput1A, splatRes, worldPos, worldNormal); } else if (i == 8) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput2, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput2A, splatRes, worldPos, worldNormal); } else if (i == 12) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput3, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput3A, splatRes, worldPos, worldNormal); } else if (i == 16) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput4, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput4A, splatRes, worldPos, worldNormal); } else if (i == 20) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput5, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput5A, splatRes, worldPos, worldNormal); } else if (i == 24) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput6, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput6A, splatRes, worldPos, worldNormal); } else if (i == 28) { DestroyImmediate(tex); DestroyImmediate(alpha); tex = Bake(t, BakingPasses.ProceduralSplatOutput7, splatRes, worldPos, worldNormal); alpha = Bake(t, BakingPasses.ProceduralSplatOutput7A, splatRes, worldPos, worldNormal); } for (int x = 0; x < splatRes; ++x) { for (int y = 0; y < splatRes; ++y) { Color c = tex.GetPixel(x, y); Color a = alpha.GetPixel(x, y); if (i < splatCount) { splats[y, x, i] = c.r; } if (i + 1 < splatCount) { splats[y, x, i + 1] = c.g; } if (i + 2 < splatCount) { splats[y, x, i + 2] = c.b; } if (i + 3 < splatCount) { splats[y, x, i + 3] = a.g; } } } } DestroyImmediate(worldPos); DestroyImmediate(worldNormal); t.terrain.terrainData.SetAlphamaps(0, 0, splats); EditorUtility.SetDirty(t.terrain.terrainData); } #endif if (MicroSplatUtilities.DrawRollup("Render Baking", false)) { res = (BakingResolutions)EditorGUILayout.EnumPopup(new GUIContent("Resolution"), res); #if UNITY_2017_3_OR_NEWER passes = (BakingPasses)EditorGUILayout.EnumFlagsField(new GUIContent("Features"), passes); #else passes = (BakingPasses)EditorGUILayout.EnumMaskPopup(new GUIContent("Features"), passes); #endif if (GUILayout.Button("Export Selected")) { needsBake = true; } #if __MICROSPLAT_PROCTEX__ if (t.templateMaterial != null && t.keywordSO != null && t.keywordSO.IsKeywordEnabled("_PROCEDURALTEXTURE")) { EditorGUILayout.Space(); EditorGUILayout.Space(); if (GUILayout.Button("Bake Procedural to Terrain")) { bakeSplats = true; } EditorGUILayout.Space(); EditorGUILayout.Space(); } #endif } }
public override void OnInspectorGUI() { StreamManager sm = target as StreamManager; MicroSplatObject mso = sm.GetComponent <MicroSplatObject>(); if (mso == null) { EditorGUILayout.HelpBox("Must be on a MicroSplatObject (terrain, mesh terrain, etc)", MessageType.Error); return; } if (mso.keywordSO == null) { EditorGUILayout.HelpBox("MicroSplat Terrain is missing keywords", MessageType.Error); return; } if (!mso.keywordSO.IsKeywordEnabled("_DYNAMICFLOWS")) { EditorGUILayout.HelpBox("Must have Dynamic Flows ON in your terrain material", MessageType.Error); return; } EditorGUI.BeginChangeCheck(); if (MicroSplatUtilities.DrawRollup("Streams", true, false)) { sm.strength.x = EditorGUILayout.Slider(CStrength, sm.strength.x, 0, 1); sm.evaporation.x = EditorGUILayout.Slider(CEvaporation, sm.evaporation.x, 0, 0.5f); sm.speed.x = EditorGUILayout.Slider(CSpeed, sm.speed.x, 0, 10); sm.resistance.x = EditorGUILayout.Slider(CResistance, sm.resistance.x, 0.005f, 0.75f); sm.wetnessEvaporation = EditorGUILayout.Slider(CWetnessEvap, sm.wetnessEvaporation, 0.0f, 0.5f); if (Application.isPlaying) { GUILayout.BeginHorizontal(); GUILayout.Space(5); if (GUILayout.Button("Save State")) { SaveStream(sm); } GUILayout.Space(5); GUILayout.EndHorizontal(); } } if (MicroSplatUtilities.DrawRollup("Lava", true, false)) { sm.strength.y = EditorGUILayout.Slider(CStrength, sm.strength.y, 0, 1); sm.evaporation.y = EditorGUILayout.Slider(CEvaporation, sm.evaporation.y, 0, 0.5f); sm.speed.y = EditorGUILayout.Slider(CSpeed, sm.speed.y, 0, 10); sm.resistance.y = EditorGUILayout.Slider(CResistance, sm.resistance.y, 0.005f, 0.75f); sm.burnEvaporation = EditorGUILayout.Slider(CBurnEvap, sm.burnEvaporation, 0.0f, 0.5f); if (Application.isPlaying) { GUILayout.BeginHorizontal(); GUILayout.Space(5); if (GUILayout.Button("Save State")) { SaveLava(sm); } GUILayout.Space(5); GUILayout.EndHorizontal(); } } if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(sm); } if (sm.updateBuffer != null) { if (sm.updateBuffer.GetCurrent() != null) { EditorGUILayout.BeginHorizontal(); GUILayout.Label("Dynamic Buffer"); int mem = sm.updateBuffer.width * sm.updateBuffer.height * 2; mem /= 128; EditorGUILayout.LabelField("Buffer Memory: " + mem.ToString() + "kb"); EditorGUILayout.EndHorizontal(); GUILayout.Box(sm.updateBuffer.GetCurrent(), GUILayout.Width(256), GUILayout.Height(256)); } } }
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 static Material NewShaderAndMaterial(Terrain t, string [] keywords = null) { string path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData); return(NewShaderAndMaterial(path, t.name, keywords)); }
//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 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 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; } }