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(); }
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { var cfgs = AssetDatabase.FindAssets("t:TextureArrayConfig"); for (int i = 0; i < cfgs.Length; ++i) { var asset = AssetDatabase.GUIDToAssetPath(cfgs[i]); var cfg = AssetDatabase.LoadAssetAtPath <TextureArrayConfig>(asset); if (cfg != null) { int hash = GetNewHash(cfg); if (hash != cfg.hash) { cfg.hash = hash; EditorUtility.SetDirty(cfg); try { sIsPostProcessing = true; TextureArrayConfigEditor.CompileConfig(cfg); } finally { sIsPostProcessing = false; AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); MicroSplatTerrain.SyncAll(); } } } } }
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 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 DoTerrainDescGUI() { MicroSplatTerrain bt = target as MicroSplatTerrain; Terrain t = bt.GetComponent <Terrain>(); if (t == null || t.terrainData == null) { return; } if (t.materialTemplate == null) { return; } if (bt.keywordSO == null) { return; } if (!bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING") && !bt.keywordSO.IsKeywordEnabled("_DYNAMICFLOWS")) { return; } EditorGUILayout.Space(); if (bt.blendMat == null && bt.templateMaterial != null && bt.keywordSO != null && bt.keywordSO.IsKeywordEnabled("_TERRAINBLENDING")) { var path = AssetDatabase.GetAssetPath(bt.templateMaterial); path = path.Replace(".mat", "_TerrainObjectBlend.mat"); bt.blendMat = AssetDatabase.LoadAssetAtPath <Material>(path); if (bt.blendMat == null) { string shaderPath = path.Replace(".mat", ".shader"); Shader shader = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath); if (shader == null) { shaderPath = path.Replace(".shader", ".surfshader"); shader = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath); } if (shader == null) { shaderPath = AssetDatabase.GetAssetPath(bt.templateMaterial.shader); shaderPath = shaderPath.Replace(".shader", "_TerrainObjectBlend.shader"); shaderPath = shaderPath.Replace(".surfshader", "_TerrainObjectBlend.surfshader"); shader = AssetDatabase.LoadAssetAtPath <Shader>(shaderPath); } if (shader != null) { Material mat = new Material(shader); AssetDatabase.CreateAsset(mat, path); AssetDatabase.SaveAssets(); MicroSplatTerrain.SyncAll(); } } } EditorUtility.SetDirty(bt); EditorUtility.SetDirty(bt.terrain); }
void InitTerrains() { Object[] objs = Selection.GetFiltered(typeof(Terrain), SelectionMode.Editable | SelectionMode.OnlyUserModifiable | SelectionMode.Deep); List <MicroSplatTerrainJob> ts = new List <MicroSplatTerrainJob>(); rawTerrains.Clear(); for (int i = 0; i < objs.Length; ++i) { Terrain t = objs[i] as Terrain; MicroSplatTerrain mst = t.GetComponent <MicroSplatTerrain>(); if (mst == null) { continue; } rawTerrains.Add(t); if (t.materialType == Terrain.MaterialType.Custom && t.materialTemplate != null) { if (!t.materialTemplate.HasProperty("_StreamControl")) { continue; } if (mst.streamTexture == null) { CreateTexture(t); } var tj = FindJob(t); if (tj != null) { tj.collider = t.GetComponent <Collider>(); tj.terrainTex = mst.streamTexture; ts.Add(tj); } else { tj = MicroSplatTerrainJob.CreateInstance <MicroSplatTerrainJob>(); tj.terrain = t; tj.collider = t.GetComponent <Collider>(); tj.terrainTex = mst.streamTexture; ts.Add(tj); } } } if (terrains != null) { // clear out old terrains for (int i = 0; i < terrains.Length; ++i) { if (!ts.Contains(terrains[i])) { DestroyImmediate(terrains[i]); } } } terrains = ts.ToArray(); jobEdits = new bool[ts.Count]; }
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.materialType != Terrain.MaterialType.Custom || t.materialTemplate == null) { return; } if (bt.keywordSO == null) { return; } if (!bt.keywordSO.IsKeywordEnabled("_PERPIXNORMAL")) { bt.perPixelNormal = null; return; } #if UNITY_2018_3_OR_NEWER if (bt.terrain.drawInstanced && bt.perPixelNormal) { 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 and clear the normal data to save memory.", MessageType.Warning); } #endif if (bt.perPixelNormal == null) { EditorGUILayout.HelpBox("Terrain Normal Data is not present, please generate", MessageType.Warning); } if (bt.perPixelNormal != null && bt.sTerrainDirty) { EditorGUILayout.HelpBox("Terrain Normal data is out of date, please update", MessageType.Warning); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Normal Data"); if (GUILayout.Button(bt.perPixelNormal == null ? "Generate" : "Update")) { GenerateTerrainNormalMap(bt); } if (bt.perPixelNormal != null && GUILayout.Button("Clear")) { bt.perPixelNormal = null; } EditorGUILayout.EndHorizontal(); }
public static void SyncAll() { MicroSplatTerrain.SyncAll(); #if __MICROSPLAT_MESHTERRAIN__ MicroSplatMeshTerrain.SyncAll(); #endif #if __MICROSPLAT_MESH__ MicroSplatMesh.SyncAll(); #endif }
void ExportSplatMaps() { var path = EditorUtility.SaveFolderPanel("Save textures to directory", "", ""); if (string.IsNullOrEmpty(path)) { return; } path = path.Replace("\\", "/"); if (!path.EndsWith("/")) { path += "/"; } MicroSplatTerrain mst = target as MicroSplatTerrain; var terrain = mst.terrain; if (terrain == null) { return; } var tdata = terrain.terrainData; if (tdata == null) { return; } var data = tdata.GetAlphamaps(0, 0, tdata.alphamapWidth, tdata.alphamapHeight); int textureCount = tdata.alphamapLayers / 4 + 1; for (int i = 0; i < textureCount; ++i) { Texture2D tex = new Texture2D(tdata.alphamapWidth, tdata.alphamapHeight, TextureFormat.ARGB32, false, true); for (int x = 0; x < tdata.alphamapWidth; ++x) { for (int y = 0; y < tdata.alphamapHeight; ++y) { Color c; c.r = data[x, y, i * 4]; c.g = tdata.alphamapLayers > i * 4 + 1 ? data[x, y, i * 4 + 1] : 0; c.b = tdata.alphamapLayers > i * 4 + 2 ? data[x, y, i * 4 + 2] : 0; c.a = tdata.alphamapLayers > i * 4 + 3 ? data[x, y, i * 4 + 3] : 0; tex.SetPixel(x, y, c); } } tex.Apply(); var bytes = tex.EncodeToPNG(); System.IO.File.WriteAllBytes(path + "SplatControl" + i + ".png", bytes); DestroyImmediate(tex); } }
bool VerifySnow() { #if __MICROSPLAT_SNOW__ for (int i = 0; i < rawTerrains.Count; ++i) { Terrain t = rawTerrains [i]; MicroSplatTerrain mst = t.GetComponent <MicroSplatTerrain> (); if (mst != null) { var tex = mst.snowMaskOverride; if (tex != null) { AssetImporter ai = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(tex)); TextureImporter ti = ai as TextureImporter; if (ti == null || !ti.isReadable) { EditorGUILayout.HelpBox("Snow Mask texture is not read/write", MessageType.Error); if (GUILayout.Button("Fix it!")) { ti.isReadable = true; ti.SaveAndReimport(); } return(false); } bool isLinear = ti.sRGBTexture == false; bool isRGB32 = ti.textureCompression == TextureImporterCompression.Uncompressed && ti.GetDefaultPlatformTextureSettings().format == TextureImporterFormat.RGBA32; if (isRGB32 == false || isLinear == false || ti.wrapMode == TextureWrapMode.Repeat) { EditorGUILayout.HelpBox("Snow Mask is not in the correct format (Uncompressed, linear, clamp, RGBA32)", MessageType.Error); if (GUILayout.Button("Fix it!")) { ti.sRGBTexture = false; ti.textureCompression = TextureImporterCompression.Uncompressed; var ftm = ti.GetDefaultPlatformTextureSettings(); ftm.format = TextureImporterFormat.RGBA32; ti.SetPlatformTextureSettings(ftm); ti.mipmapEnabled = true; ti.wrapMode = TextureWrapMode.Clamp; ti.SaveAndReimport(); } return(false); } } } } #endif return(true); }
void RevertTerrainSplats(MicroSplatTerrain t) { MicroSplatKeywords keywords = MicroSplatUtilities.FindOrCreateKeywords(t.templateMaterial); if (keywords.IsKeywordEnabled("_CUSTOMSPLATTEXTURES")) { if (t.customControl0 == null) { Debug.LogError("Could not revert terrain because textures are missing!"); return; } UncompressTexture(t.customControl0); UncompressTexture(t.customControl1); UncompressTexture(t.customControl2); UncompressTexture(t.customControl3); UncompressTexture(t.customControl4); UncompressTexture(t.customControl5); UncompressTexture(t.customControl6); UncompressTexture(t.customControl7); int size = t.customControl0.width; int layers = t.terrain.terrainData.alphamapLayers; t.terrain.terrainData.alphamapResolution = size; var maps = t.terrain.terrainData.GetAlphamaps(0, 0, size, size); ExtractSplats(ref maps, t.customControl0, 0, layers); ExtractSplats(ref maps, t.customControl1, 4, layers); ExtractSplats(ref maps, t.customControl2, 8, layers); ExtractSplats(ref maps, t.customControl3, 12, layers); ExtractSplats(ref maps, t.customControl4, 16, layers); ExtractSplats(ref maps, t.customControl5, 20, layers); ExtractSplats(ref maps, t.customControl6, 24, layers); ExtractSplats(ref maps, t.customControl7, 28, layers); t.terrain.terrainData.SetAlphamaps(0, 0, maps); EditorUtility.SetDirty(t.terrain.terrainData); keywords.DisableKeyword("_CUSTOMSPLATTEXTURES"); MicroSplatShaderGUI.MicroSplatCompiler compiler = new MicroSplatShaderGUI.MicroSplatCompiler(); compiler.Compile(t.templateMaterial); t.customControl0 = null; t.customControl1 = null; t.customControl2 = null; t.customControl3 = null; t.customControl4 = null; t.customControl5 = null; t.customControl6 = null; t.customControl7 = null; EditorUtility.SetDirty(t); MicroSplatTerrain.SyncAll(); } }
public void ApplyTmp(Terrain terrain) { //checking microsplat component MicroSplatTerrain mso = terrain.GetComponent <MicroSplatTerrain>(); if (mso == null) { mso = terrain.gameObject.AddComponent <MicroSplatTerrain>(); } mso.terrain = terrain; //otherwise nullref on newly created tiles mso.templateMaterial = terrain.materialTemplate; int numTextures = colors.Length; if (numTextures == 0) { return; } int resolution = (int)Mathf.Sqrt(colors[0].Length); for (int t = 0; t < numTextures; t++) { if (colors[t] == null) { continue; } Texture2D tex = GetTex(mso, t); if (tex == null || tex.width != resolution || tex.height != resolution || tex.format != TextureFormat.RGBA32) { if (tex != null) { #if UNITY_EDITOR if (!UnityEditor.AssetDatabase.Contains(tex)) #endif GameObject.DestroyImmediate(tex); } tex = new Texture2D(resolution, resolution, TextureFormat.RGBA32, false, true); tex.wrapMode = TextureWrapMode.Mirror; //to avoid border seams tex.name = "CustomControl " + t; SetTex(mso, t, tex); //tex.hideFlags = HideFlags.DontSave; } tex.SetPixels(0, 0, tex.width, tex.height, colors[t]); tex.Apply(); } mso.Sync(); //terrain.basemapDistance = 1000000; }
// #if __MICROSPLAT_DIGGER__ public static Shader GetDiggerShader(MicroSplatTerrain mst) { if (mst.templateMaterial == null) { Debug.LogError("Digger: Template Material is not assigned to MicroSplatTerrain component"); return(null); } var path = AssetDatabase.GetAssetPath(mst.templateMaterial.shader); string diggerPath = path.Replace(".shader", "_Digger.shader"); return(AssetDatabase.LoadAssetAtPath <Shader> (diggerPath)); }
public static void GenerateTerrainNormalMap(MicroSplatTerrain bt) { Terrain t = bt.GetComponent <Terrain>(); int w = t.terrainData.heightmapResolution; int h = t.terrainData.heightmapResolution; Texture2D data = new Texture2D(w, h, TextureFormat.RGBA32, true, true); for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { Vector3 normal = t.terrainData.GetInterpolatedNormal((float)x / w, (float)y / h); data.SetPixel(x, y, new Color(normal.x * 0.5f + 0.5f, normal.z * 0.5f + 0.5f, normal.y)); } } data.Apply(); var path = MicroSplatUtilities.RelativePathFromAsset(t.terrainData); path += "/" + t.name + "_normal.png"; var bytes = data.EncodeToPNG(); System.IO.File.WriteAllBytes(path, bytes); GameObject.DestroyImmediate(data); AssetDatabase.Refresh(); bt.perPixelNormal = AssetDatabase.LoadAssetAtPath <Texture2D>(path); var ai = AssetImporter.GetAtPath(path); var ti = ai as TextureImporter; var ps = ti.GetDefaultPlatformTextureSettings(); if (ti.isReadable == true || ti.wrapMode != TextureWrapMode.Clamp || ps.overridden != true || ti.textureType != TextureImporterType.NormalMap) { ti.textureType = TextureImporterType.NormalMap; ti.mipmapEnabled = true; ti.wrapMode = TextureWrapMode.Clamp; ti.isReadable = false; ps.overridden = true; ti.SetPlatformTextureSettings(ps); ti.SaveAndReimport(); } bt.sTerrainDirty = false; EditorUtility.SetDirty(bt); EditorUtility.SetDirty(bt.terrain); MicroSplatTerrain.SyncAll(); AssetDatabase.SaveAssets(); }
public static void SyncAll() { MicroSplatTerrain.SyncAll(); #if __MICROSPLAT_MESHTERRAIN__ MicroSplatMeshTerrain.SyncAll(); #endif #if __MICROSPLAT_MESH__ MicroSplatMesh.SyncAll(); MicroSplatVertexMesh.SyncAll(); #endif #if __MICROSPLAT_POLARIS__ MicroSplatPolarisMesh.SyncAll(); #endif }
public static void DrawTextureField(MicroSplatTerrain t, GUIContent content, ref Texture2D tex, string keword) { if (t.templateMaterial.IsKeywordEnabled(keword)) { EditorGUI.BeginChangeCheck(); tex = EditorGUILayout.ObjectField(content, tex, typeof(Texture2D), false) as Texture2D; if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(t); } } }
public void Revert(MicroSplatObject mso) { MicroSplatTerrain t = mso as MicroSplatTerrain; if (t != null) { if (t.templateMaterial == null) { Debug.LogError("MicroSplatTerrain " + mso.gameObject.name + " does not have template material"); } else { RevertTerrainSplats(t); } } #if __MICROSPLAT_MESH__ MicroSplatMesh msm = mso as MicroSplatMesh; if (msm != null) { foreach (var subMesh in msm.subMeshEntries) { foreach (var tex in subMesh.subMeshOverride.controlTextures) { UncompressTexture(tex); } } } #endif #if __MICROSPLAT_PROCTEX__ UncompressTexture(mso.procBiomeMask); UncompressTexture(mso.procBiomeMask2); UncompressTexture(mso.templateMaterial, "_ProcTexBiomeMask"); UncompressTexture(mso.templateMaterial, "_ProcTexBiomeMask2"); #endif #if __MICROSPLAT_SNOW__ UncompressTexture(mso.snowMaskOverride); UncompressTexture(mso.templateMaterial, "_SnowMask"); #endif #if __MICROSPLAT_STREAMS__ UncompressTexture(mso.streamTexture); UncompressTexture(mso.templateMaterial, "_StreamControl"); #endif #if __MICROSPLAT_GLOBALTEXTURE__ UncompressTexture(mso.streamTexture); UncompressTexture(mso.templateMaterial, "_GlobalTintTex"); #endif }
//private Loading _loader; // Start is called before the first frame update private void Start() { DontDestroyOnLoad(this.gameObject); Terrain[] terrains = GameObject.FindObjectsOfType <Terrain>(); foreach (Terrain terrain in terrains) { MicroSplatTerrain msTerrain = terrain.GetComponent <MicroSplatTerrain>(); msTerrain.Sync(); } terrainData = new TerrainMap(terrains, scene); pathFinderData = new PathfinderData(terrainData); }
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 Material materialTemplate; //source material assigned. Can't use terrain.materialTemplate since it will be changed with copy public override void Apply(Terrain terrain) { //checking microsplat component //this should be done before applying control since //microsplat removes template from terrain on disable (lod switch), so ensuring we have a material before base.Apply MicroSplatTerrain mso = null; if (assignComponent) { mso = terrain.GetComponent <MicroSplatTerrain>(); if (mso == null) { mso = terrain.gameObject.AddComponent <MicroSplatTerrain>(); } mso.terrain = terrain; //otherwise nullref on newly created tiles MapMagic.Core.MapMagicObject mapMagic = terrain.transform.parent.parent.GetComponent <MapMagic.Core.MapMagicObject>(); mso.templateMaterial = mapMagic.terrainSettings.material; if (terrain.materialTemplate == mso.templateMaterial || terrain.materialTemplate == null) //if material instance assigned (first run) { mso.matInstance = new Material(mapMagic.terrainSettings.material); terrain.materialTemplate = mso.matInstance; } else { mso.matInstance = terrain.materialTemplate; } mso.propData = propData; } else if (terrain.materialTemplate == null) //prevents an error (materialTemplate is null) on disabling "Set Component" { MapMagic.Core.MapMagicObject mapMagic = terrain.transform.parent.parent.GetComponent <MapMagic.Core.MapMagicObject>(); terrain.materialTemplate = mapMagic.terrainSettings.material; } base.Apply(terrain); if (assignComponent) { mso.Sync(); } }
public void DoCavityMapGUI() { 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.cavityMap == null) { EditorGUILayout.HelpBox("Cavity Map Data is not present, please generate or provide", MessageType.Warning); } bt.cavityMap = (Texture2D)EditorGUILayout.ObjectField("Cavity Map", bt.cavityMap, typeof(Texture2D), false); if (bt.cavityMap != null && bt.sTerrainDirty) { EditorGUILayout.HelpBox("Cavity data may be out of date as terrain has changed", MessageType.Warning); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Cavity Data"); if (GUILayout.Button(bt.cavityMap == null ? "Generate" : "Update")) { ComputeCavityFlowMap(bt); } if (bt.cavityMap != null && GUILayout.Button("Clear")) { bt.cavityMap = null; } EditorGUILayout.EndHorizontal(); }
private void Awake() { _terrain = Terrain.activeTerrain; if (_microSplatTerrain == null) { _microSplatTerrain = _terrain.GetComponent <MicroSplatTerrain>(); } if (_microSplatTerrain == null) { throw new Exception("Camera not set up correctly, microsplat reference missing!"); } if (_terrainMaterials == null || _terrainMaterials.Count == 0) { throw new Exception("Camera not set up correctly, terrain materials missing!"); } _cam = GetComponent <Camera>(); }
public void SetTex(MicroSplatTerrain mso, int num, Texture2D tex) { switch (num) { case 0: mso.customControl0 = tex; break; case 1: mso.customControl1 = tex; break; case 2: mso.customControl2 = tex; break; case 3: mso.customControl3 = tex; break; case 4: mso.customControl4 = tex; break; case 5: mso.customControl5 = tex; break; case 6: mso.customControl6 = tex; break; case 7: mso.customControl7 = tex; break; } }
void SaveStream(StreamManager sm) { MicroSplatTerrain mt = sm.gameObject.GetComponent <MicroSplatTerrain>(); var streamTex = mt.streamTexture; var buffer = sm.updateBuffer.GetCurrent(); if (mt == null || streamTex == null || buffer == null || buffer.width != streamTex.width || buffer.height != streamTex.height) { return; } RenderTexture.active = buffer; Texture2D tex = new Texture2D(buffer.width, buffer.height, TextureFormat.ARGB32, false, true); tex.ReadPixels(new Rect(0, 0, buffer.width, buffer.height), 0, 0); tex.Apply(); RenderTexture.active = null; var srcClrs = tex.GetPixels(); var trgClrs = streamTex.GetPixels(); for (int i = 0; i < srcClrs.Length; ++i) { Color t = trgClrs[i]; Color d = srcClrs[i]; t.b = d.b; trgClrs[i] = t; } streamTex.SetPixels(trgClrs); streamTex.Apply(); DestroyImmediate(tex); var path = AssetDatabase.GetAssetPath(streamTex); path = path.Replace("\\", "/"); path = Application.dataPath.Substring(0, Application.dataPath.Length - 6) + "/" + path; var bytes = streamTex.EncodeToPNG(); System.IO.File.WriteAllBytes(path, bytes); AssetDatabase.Refresh(); }
private static void CheckConfigForUpdates(TextureArrayConfig cfg) { int hash = GetNewHash(cfg); if (hash != cfg.hash) { cfg.hash = hash; EditorUtility.SetDirty(cfg); try { sIsPostProcessing = true; TextureArrayConfigEditor.CompileConfig(cfg); } finally { sIsPostProcessing = false; AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); MicroSplatTerrain.SyncAll(); } } }
public void 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 Texture2D GetTex(MicroSplatTerrain mso, int num) { switch (num) { case 0: return(mso.customControl0); case 1: return(mso.customControl1); case 2: return(mso.customControl2); case 3: return(mso.customControl3); case 4: return(mso.customControl4); case 5: return(mso.customControl5); case 6: return(mso.customControl6); case 7: return(mso.customControl7); default: return(null); } }
void ExportSplatMaps() { var path = EditorUtility.SaveFolderPanel("Save textures to directory", "", ""); if (string.IsNullOrEmpty(path)) { return; } path = path.Replace("\\", "/"); if (!path.EndsWith("/")) { path += "/"; } MicroSplatTerrain mst = target as MicroSplatTerrain; var terrain = mst.terrain; if (terrain == null) { return; } var tdata = terrain.terrainData; if (tdata == null) { return; } for (int i = 0; i < tdata.alphamapTextures.Length; ++i) { var bytes = tdata.alphamapTextures[i].EncodeToTGA(); System.IO.File.WriteAllBytes(path + "SplatControl" + i + ".tga", bytes); } AssetDatabase.Refresh(); }
/// <summary> /// Preprocess the terrain to clamp down on the number of splat maps which have weights on each control point. First pass /// limits the number of weights to the specified amount per control point. Since each rendered pixel is a blend of 4 possible /// control points, this still means a given pixel may need up to 4 weights even if the control point is clamped to 1 weight. /// In the second pass, we check all of the neighoring pixels to see if they have different weights- if they do, we clamp /// down to one less weight on this control point. The idea here is to create some extra headroom for the blend, but since /// you can still need 4 blend weights in some cases, there is no perfect solution to this issue when running with less than /// 4 blend weights. It does, however, greatly help when running under those constraints. /// /// </summary> /// <param name="bt">Bt.</param> /// <param name="maxWeights">Max weights.</param> /// <param name="secondPass">If set to <c>true</c> second pass.</param> public static void WeightLimitTerrain(MicroSplatTerrain bt, int maxWeights, bool secondPass = false) { Terrain t = bt.GetComponent <Terrain>(); if (t == null) { return; } UnityEngine.TerrainData td = t.terrainData; if (td == null) { return; } int w = td.alphamapWidth; int h = td.alphamapHeight; int l = td.alphamapLayers; Undo.RegisterCompleteObjectUndo(t, "Weight Limit Terrain"); var splats = td.GetAlphamaps(0, 0, w, h); float[] data = new float[16]; List <WeightPair> sorted = new List <WeightPair>(); List <int> validIndexes = new List <int>(); for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { // gather all weights for (int i = 0; i < l; ++i) { data[i] = splats[x, y, i]; } sorted.Clear(); for (int i = 0; i < 16; ++i) { var wp = new WeightPair(); wp.index = i; wp.weight = data[i]; sorted.Add(wp); } sorted.Sort((w0, w1) => w1.weight.CompareTo(w0.weight)); // remove lower weights int allowedWeights = maxWeights; while (sorted.Count > allowedWeights) { sorted.RemoveAt(sorted.Count - 1); } // generate valid index list validIndexes.Clear(); for (int i = 0; i < sorted.Count; ++i) { if (sorted[i].weight > 0) { validIndexes.Add(sorted[i].index); } } // figure out if our neighbors have weights which we don't have- if so, clamp down harder to make room for blending.. // if not, allow us to blend fully. We do this in a second pass so that small weights are reduced before we make // this decision.. if (secondPass) { for (int xm = -1; xm < 2; ++xm) { for (int ym = -1; ym < 2; ++ym) { int nx = x + xm; int ny = y + ym; if (nx >= 0 && ny >= 0 && nx < w && ny < y) { for (int layer = 0; layer < l; ++layer) { float weight = splats[nx, ny, layer]; if (weight > 0 && !validIndexes.Contains(layer)) { allowedWeights = maxWeights - 1; } } } } } while (sorted.Count > allowedWeights) { sorted.RemoveAt(sorted.Count - 1); } // generate valid index list validIndexes.Clear(); for (int i = 0; i < sorted.Count; ++i) { if (sorted[i].weight > 0) { validIndexes.Add(sorted[i].index); } } } // clear non-valid indexes for (int j = 0; j < 16; ++j) { if (!validIndexes.Contains(j)) { data[j] = 0; } } // now normalize weights so that they total one on each pixel float total = 0; for (int j = 0; j < 16; ++j) { total += data[j]; } float scale = 1.0f / total; for (int j = 0; j < 16; ++j) { data[j] *= scale; } // now map back to splat data array for (int i = 0; i < l; ++i) { splats[x, y, i] = data[i]; } } } td.SetAlphamaps(0, 0, splats); }