using Thousandto.Core.Asset; using Thousandto.Core.Base; using Thousandto.Package.Tool; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using UnityEditor; using UnityEngine; namespace Thousandto.Editor.Test { public class SplitTerrain { float tRes =4.100001f; static string SaveTerrainPath = ""; string _terrainName = "Terrain"; float progressUpdateInterval = 10000; float HeightmapWidth; float HeightmapHeight; float BlockSize = TerrainConfigDefine.BLOCK_SIZE; float DefaultVetexResolution = 90f; //顶点对应像素的缩放,顶点x缩放=顶点的世界坐标 float PixelScale = 0; bool _delExpandedMesh = false; //替换法线时,有些顶点需要忽略 List ignoreVertices = new List(); public void StartSplitTerrain(Transform currentSelect) { StartSplitTerrain(currentSelect, DefaultVetexResolution, BlockSize); } public void StartSplitTerrain(string selectTerrainPath, float vertexResolution, float blockResolution, bool delExpandedMesh = false) { var terrainData = AssetDatabase.LoadAssetAtPath(selectTerrainPath); StartSplitTerrain(terrainData, vertexResolution, blockResolution, delExpandedMesh); } public void StartSplitTerrain(Transform currentSelect, float vertexResolution, float blockResolution, bool delExpandedMesh = false) { var terrainDat = currentSelect.GetComponent().terrainData; StartSplitTerrain(terrainDat, vertexResolution, blockResolution, delExpandedMesh); } public void StartSplitTerrain(TerrainData terrainDat, float vertexResolution, float blockResolution, bool delExpandedMesh = false) { _terrainName = terrainDat.name; _delExpandedMesh = delExpandedMesh; SaveTerrainPath = ConfigDefine.GetSaveTerrainPath(_terrainName); HeightmapWidth = terrainDat.heightmapResolution - 1; HeightmapHeight = terrainDat.heightmapResolution - 1; //每个分割点的像素大小 tRes = (HeightmapWidth) / vertexResolution; var terrain = terrainDat; //高度图的长宽, 高度图长宽减1才是真实的size int w = terrain.heightmapResolution - 1; int h = terrain.heightmapResolution - 1; int collumns = (int)(vertexResolution); int rows = (int)(vertexResolution); //删除旧的mesh文件 DeleteOldMeshFiles(_terrainName); //生成正式的prefab,需要删除旧的prefab if (_delExpandedMesh) DeleteOldMeshPrefab(); //以地形size为基准的每个分割点的像素大小 PixelScale = terrain.size.x / collumns; var bounds = CalcBounds((int)(vertexResolution), (int)(vertexResolution), PixelScale, blockResolution); //将terrain按照划分的边界拆分成若干个mesh var configList = SplitTerrainWithBounds(bounds, terrain, (int)(vertexResolution), (int)(vertexResolution), blockResolution); //保存成配置文件,在Resources/Config目录下 if(configList.Count > 0) { var saveConfigPath = ConfigDefine.GetConfigPath(TerrainConfigDefine.MESH_CONFIG_FILENAME); File.WriteAllLines(saveConfigPath, configList.ToArray()); } } private List SplitTerrainWithBounds(List bounds, TerrainData terrain, int xVertexCount, int yVertexCount, float blockResolution) { //高度图的长宽, 高度图长宽减1才是真实的size int w = terrain.heightmapResolution - 1; int h = terrain.heightmapResolution - 1; //地形尺寸 Vector3 meshScale = terrain.size; //尺寸缩放比,y方向是高度,不做缩放 meshScale = new Vector3(meshScale.x / (h) * tRes, meshScale.y, meshScale.z / (w) * tRes); //uv缩放比,每一个像素对应的纹理坐标 Vector2 uvScale = new Vector2((float)(1.0 / (w)), (float)(1.0 / (h))); //地形的高度值 float[,] tData = terrain.GetHeights(0, 0, w, h); int blockCollumn = (int)(xVertexCount * tRes / blockResolution); if (blockCollumn == 0) blockCollumn = 1; //返回需要保存的配置信息 List configList = new List(); for (int i = 0; i < bounds.Count; ++i) { int curCollumn = i % blockCollumn; int curRow = i / blockCollumn; //bounds[i].Index = curRow + "x" + curCollumn; Vector2 offset = new Vector2(bounds[i].StartX * bounds[i].PixelScaleX1000 / 1000 / terrain.size.x, bounds[i].StartZ * bounds[i].PixelScaleX1000 / 1000 / terrain.size.z); bounds[i].Offset = offset; bounds[i].TerrainSize = terrain.size; bounds[i].Index = "" + Utils.CalcIndex(bounds[i].GetCenterPos(), (int)blockResolution); if (EditorUtility.DisplayCancelableProgressBar("Calc mesh", bounds[i].Index, i / (float)bounds.Count)) { break; } var savePath = CreateMesh(curRow + "" + curCollumn, bounds[i], meshScale, uvScale, tData); bool isFullMesh = bounds[i].StartX == 0 && bounds[i].StartZ == 0 && bounds[i].IsRightBound && bounds[i].IsBottomBound; if (_delExpandedMesh && !isFullMesh) { AssetDatabase.ImportAsset(savePath); var result = DeleteExpandedMesh(savePath); //删除老的mesh AssetDatabase.DeleteAsset(savePath); //SetMaterial2(terrain, result); var mat = GetSavedMeshMaterial(terrain, result); var index = Utils.CalcTerrainMeshIndex(result, (int)blockResolution); var savePrefabName = "Mesh_" + index; if (CreateNewUnityMesh(ConfigDefine.GetSavePrefabPath(savePrefabName + ".prefab"), result, mat)) configList.Add(string.Format("{0}", index)); } UnityEngine.Debug.Log(savePath); } AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); EditorUtility.ClearProgressBar(); return configList; } public void ShowMeshBlocks() { var terrainMeshArray = Directory.GetFiles(SaveTerrainPath, "*.obj"); for (int i = 0; i < terrainMeshArray.Length; ++i) { ShowMeshBlock(terrainMeshArray[i]); } } public static void ShowMeshBlock(string path) { if (GameObject.Find(Path.GetFileNameWithoutExtension(path))) return; var go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; if (go == null) return; var data = MyTerrainData.CreateTerrainDataFromFileName(path); float pixelScale = data.PixelScaleX1000 / 1000f; Vector3 position = new Vector3(data.StartX * pixelScale, 0, data.StartZ * pixelScale); var instance = PrefabUtility.InstantiatePrefab(go) as GameObject; instance.transform.position = position; instance.name = go.name; } //bound x,y,z,w 对应z,x,w,h public string CreateMesh(string boundIndex, MyTerrainData bound, Vector3 meshScale, Vector2 uvScale, float[,] tData) { int tDataRow = tData.GetLength(0); int tDataCollumn = tData.GetLength(1); //+1是把第一个点包含 int xLen = (int)(bound.Width - bound.StartZ) + 1; int yLen = (int)(bound.Height - bound.StartX) + 1; //边缘顶点,不做计算 if (xLen == 1 || yLen == 1) return ""; //是否单一网格,即将terrain分割成1个网格 bool isSingleMesh = bound.StartX == 0 && bound.StartZ == 0 && bound.IsRightBound && bound.IsBottomBound; #region 向4个方位拓展一个网格的范围 if(!isSingleMesh) { if (bound.StartZ > 0) { bound.StartZ -= 1; xLen += 1; } if (bound.StartX > 0) { bound.StartX -= 1; yLen += 1; } if (!bound.IsRightBound) { bound.Width += 1; xLen += 1; } if (!bound.IsBottomBound) { bound.Height += 1; yLen += 1; } } #endregion //顶点数,就是总共的格子数 Vector3[] tVertices = new Vector3[xLen * yLen]; Vector2[] tUV = new Vector2[(xLen) * (yLen)]; int y = 0; int x = 0; for (y = 0; y < yLen; y++) { for (x = 0; x < xLen; x++) { //tVertices[y*w + x] = Vector3.Scale(meshScale, new Vector3(x, tData[(int)(x*tRes),(int)(y*tRes)], y)); var hX = (int)((bound.StartZ + x) * tRes); var hY = (int)((bound.StartX + y) * tRes); if (hX >= tDataRow) hX = tDataRow - 1; if (hY >= tDataCollumn) hY = tDataCollumn - 1; try { tVertices[y * xLen + x] = Vector3.Scale(meshScale, new Vector3(-y, tData[hX, hY], x)); //Thank Cid Newman tUV[y * xLen + x] = Vector2.Scale(new Vector2(y * tRes, x * tRes), uvScale); } catch (Exception ex) { UnityEngine.Debug.LogException(ex); } } } y = 0; x = 0; //面数*2 int[] tPolys = new int[(xLen - 1) * (yLen - 1) * 6]; int index = 0; for (y = 0; y < yLen - 1; y++) { for (x = 0; x < xLen - 1; x++) { tPolys[index++] = (y * xLen) + x; tPolys[index++] = ((y + 1) * xLen) + x; tPolys[index++] = (y * xLen) + x + 1; tPolys[index++] = ((y + 1) * xLen) + x; tPolys[index++] = ((y + 1) * xLen) + x + 1; tPolys[index++] = (y * xLen) + x + 1; } } int pixelScaleX1000 = (int)(PixelScale * 1000); var savePath = SaveMesh(bound.CombineToFileName(), tVertices, tUV, tPolys); return savePath; } /// /// 删除扩展的网格 /// /// public static string DeleteExpandedMesh(string savePath) { if (!File.Exists(savePath)) return ""; var bound = MyTerrainData.CreateTerrainDataFromFileName(savePath); Vector3[] realVertices; Vector3[] realNormals; Vector4[] realTangents; Vector2[] realUvs; int[] realTriangles; if (DeleteExpandedMesh(savePath, bound, bound.PixelScaleX1000 / 1000f, out realVertices, out realNormals, out realTangents, out realUvs, out realTriangles)) { if (!Directory.Exists(SaveTerrainPath)) Directory.CreateDirectory(SaveTerrainPath); savePath = Path.Combine(SaveTerrainPath , bound.CombineToFileName(true) + ".obj"); savePath = SaveMesh(bound.CombineToFileName(true), realVertices, realUvs, realTriangles, realNormals, true); AssetDatabase.ImportAsset(savePath); } return savePath; } /// /// 保存网格信息到obj文件,当有tNoramls数据时,说明是读取的网格顶点保存,这时需要反转顶点x方向和面的顶点需要逆时针方向 /// /// /// /// /// /// /// /// public static string SaveMesh(string name, Vector3[] tVertices, Vector2[] tUV, int[] tPolys, Vector3[] tNormals = null, bool needReversX = false) { if (!Directory.Exists(SaveTerrainPath)) Directory.CreateDirectory(SaveTerrainPath); string savePath = Path.Combine(SaveTerrainPath, name + ".obj"); StreamWriter sw = new StreamWriter(savePath); try { sw.WriteLine("# T4M File"); System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); for (int i = 0; i < tVertices.Length; i++) { if (needReversX) { tVertices[i].x *= -1; } UpdateProgress(); StringBuilder sb = new StringBuilder("v ", 20); sb.Append(tVertices[i].x.ToString()).Append(" "). Append(tVertices[i].y.ToString()).Append(" "). Append(tVertices[i].z.ToString()); sw.WriteLine(sb); } for (int i = 0; i < tUV.Length; i++) { UpdateProgress(); StringBuilder sb = new StringBuilder("vt ", 22); sb.Append(tUV[i].x.ToString()).Append(" "). Append(tUV[i].y.ToString()); sw.WriteLine(sb); } if(tNormals != null) { for(int i = 0; i < tNormals.Length; ++i) { StringBuilder sb = new StringBuilder("vn ", 22); sb.Append(tNormals[i].x.ToString()).Append(" "). Append(tNormals[i].y.ToString()).Append(" "). Append(tNormals[i].z.ToString()); sw.WriteLine(sb); } } for (int i = 0; i < tPolys.Length; i += 3) { UpdateProgress(); StringBuilder sb = new StringBuilder("f ", 43); if (tNormals != null) { //面上顶点的顺序要改成逆时针 sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" "). Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1).Append(" "). Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1); } else { sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" "). Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" "). Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1); } sw.WriteLine(sb); } } catch (Exception err) { Debug.Log("Error saving file: " + err.Message); } sw.Close(); AssetDatabase.SaveAssets(); return savePath; } /// /// 删除之前扩展的网格 /// /// /// 网格信息 /// 像素缩放比 /// 删除后有效的顶点数据 /// /// /// /// /// public static bool DeleteExpandedMesh(string meshPath, MyTerrainData originalData, float pixcelScale, out Vector3[] verticesOut, out Vector3[] normalsOut, out Vector4[] tangentsOut, out Vector2[] uvsOut, out int[] trianglesOut) { verticesOut = new Vector3[0]; tangentsOut = new Vector4[0]; normalsOut = new Vector3[0]; uvsOut = new Vector2[0]; trianglesOut = new int[0]; var go = AssetDatabase.LoadAssetAtPath(meshPath); if (go == null) return false; var meshFilter = go.GetComponentInChildren(); if (meshFilter == null) return false; var mesh = meshFilter.sharedMesh; { var newVerticesList = new List(); var newVerticesNormals = new List(); var newVerticesTangents = new List(); var newUvs = new List(); var triangles = new List(); //1. 无效顶点的索引 var invalidVericesIndexList = GetInvalidVerticeIndexs(mesh, originalData, pixcelScale); var moreInvalidVerticeIndexs = new List(); //2. 获取有效的面 triangles = GetValidTriangles(mesh, invalidVericesIndexList, out moreInvalidVerticeIndexs); //这里考虑孤独点 //invalidVericesIndexList.AddRange(moreInvalidVerticeIndexs); //3. 取有效的顶点、法线等 var replaceIndexDict = PickValidDatas(mesh, originalData, invalidVericesIndexList, out newVerticesList, out newVerticesNormals, out newVerticesTangents, out newUvs); //4. 对三角面中的顶点索引进行新索引替换 for(int i = 0; i < triangles.Count; ++i) { EditorUtility.DisplayProgressBar("替换面的顶点索引", "替换面的顶点索引", i / (float)triangles.Count); if (replaceIndexDict.ContainsKey( triangles[i])) { triangles[i] = replaceIndexDict[triangles[i]]; } } verticesOut = newVerticesList.ToArray(); tangentsOut = newVerticesTangents.ToArray(); normalsOut = newVerticesNormals.ToArray(); uvsOut = newUvs.ToArray(); trianglesOut = triangles.ToArray(); Vector2 offset = new Vector2(originalData.StartX * originalData.PixelScaleX1000 / 1000 / originalData.TerrainSize.x, originalData.StartZ * originalData.PixelScaleX1000 / 1000 / originalData.TerrainSize.z); originalData.Offset = offset; return true; } } private static List GetInvalidVerticeIndexs(Mesh mesh, MyTerrainData originalData, float pixcelScale) { EditorUtility.DisplayProgressBar("获取无效的顶点", "", 0); //左侧顶点的坐标 var leftVerteicPos = originalData.StartZ * pixcelScale; var rightVerticesPos = (originalData.Width) * pixcelScale; var topVerticesPos = originalData.StartX * pixcelScale; var bottomVerticesPos = (originalData.Height) * pixcelScale; var vertices = mesh.vertices; //无效顶点的索引 var invalidVericesIndexList = new List(); for (int i = 0; i < vertices.Length; ++i) { EditorUtility.DisplayProgressBar("获取无效的顶点", "获取无效的顶点", i / (float)vertices.Length); //1. 第一次遍历,通过计算边缘顶点来获得需要去除的顶点索引 bool validate = (vertices[i].z > pixcelScale / 3) || !originalData.MoreLeft; validate &= (rightVerticesPos - leftVerteicPos - vertices[i].z > pixcelScale / 3) || !originalData.MoreRight; validate &= (vertices[i].x > pixcelScale / 3) || !originalData.MoreTop; validate &= (bottomVerticesPos - topVerticesPos - vertices[i].x > pixcelScale / 3) || !originalData.MoreBottom; if (!validate) { //无效的顶点索引 invalidVericesIndexList.Add(i); } } return invalidVericesIndexList; } private static List GetValidTriangles(Mesh mesh, List invalidVericesIndexList, out List noUseVertices) { var triangles = new List(); //更多无效的顶点,去除一个三角形会遗留2个无效顶点,需要记录 noUseVertices = new List(); for (int m = 0; m < mesh.triangles.Length; m += 3) { EditorUtility.DisplayProgressBar("获取有效的面", "获取有效的面", m / (float)mesh.triangles.Length); bool isValid = true; for (int i = 0; i < invalidVericesIndexList.Count; ++i) { //2. 通过无效顶点索引来计算无效的三角面片,同时, //一个三角面片包含3个顶点,其余两个顶点也判定无效,需要加到无效列表中 if (mesh.triangles[m] == invalidVericesIndexList[i] || mesh.triangles[m + 1] == invalidVericesIndexList[i] || mesh.triangles[m + 2] == invalidVericesIndexList[i]) { isValid = false; AddVerticeIndex(mesh.triangles[m], invalidVericesIndexList, noUseVertices); AddVerticeIndex(mesh.triangles[m + 1], invalidVericesIndexList, noUseVertices); AddVerticeIndex(mesh.triangles[m + 2], invalidVericesIndexList, noUseVertices); } } //剩余有效的三角面 if (isValid) { triangles.Add(mesh.triangles[m]); triangles.Add(mesh.triangles[m + 1]); triangles.Add(mesh.triangles[m + 2]); //如果多余顶点里面有三角面使用到,则把顶点从孤独点列表中去掉,剩下的才是真正的孤独点 for (int i = 0; i < noUseVertices.Count; ++i) { if (noUseVertices.IndexOf(mesh.triangles[m]) >= 0) { noUseVertices.Remove(mesh.triangles[m]); continue; } if (noUseVertices.IndexOf(mesh.triangles[m + 1]) >= 0) { noUseVertices.Remove(mesh.triangles[m + 1]); continue; } if (noUseVertices.IndexOf(mesh.triangles[m + 2]) >= 0) { noUseVertices.Remove(mesh.triangles[m + 2]); continue; } } } } return triangles; } private static Dictionary PickValidDatas(Mesh mesh, MyTerrainData originalData, List invalidVericesIndexList, out List newVerticesList, out List newVerticesNormals, out List newVerticesTangents, out List newUvs) { Dictionary replaceIndexDict = new Dictionary(); newVerticesList = new List(); newVerticesNormals = new List(); newVerticesTangents = new List(); newUvs = new List(); var vertices = mesh.vertices; //第一个顶点,其他顶点需要减去第一个顶点 Vector3 firstVertice = Vector3.zero; for (int i = 0; i < vertices.Length; ++i) { EditorUtility.DisplayProgressBar("获取有效的顶点、法线等", "获取有效的顶点、法线等", i / (float)vertices.Length); //获取有效的顶点等数据 if (invalidVericesIndexList.IndexOf(i) == -1) { if (newVerticesList.Count == 0) firstVertice = vertices[i]; //所有顶点要往x、z方向挪动一格 if (originalData.MoreLeft) vertices[i].z -= firstVertice.z; if (originalData.MoreTop) vertices[i].x -= firstVertice.x; //旧的顶点索引对应新的顶点索引 replaceIndexDict.Add(i, newVerticesList.Count); var vertice = vertices[i]; newVerticesList.Add(vertice); newVerticesNormals.Add(mesh.normals[i]); newVerticesTangents.Add(mesh.tangents[i]); newUvs.Add(mesh.uv[i]); } } return replaceIndexDict; } public static void SetMaterial(TerrainData sourceTerrain, string savedMeshPath) { SetMaterial2(sourceTerrain, savedMeshPath, false); } public static void SetMaterial2(TerrainData sourceTerrain, string savedMeshPath, bool load = true) { var meshGo = AssetDatabase.LoadAssetAtPath(savedMeshPath); var renderer = meshGo.GetComponentInChildren(); renderer.sharedMaterial = GetSavedMeshMaterial(sourceTerrain, savedMeshPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); if(load) ShowMeshBlock(savedMeshPath); } public static Material GetSavedMeshMaterial(TerrainData sourceTerrain, string savedMeshPath, bool replace = true) { string mapName = sourceTerrain.name; string savedDir = Path.GetDirectoryName(savedMeshPath); string meshName = Path.GetFileNameWithoutExtension(savedMeshPath); string heightMapDir = ConfigDefine.GetControlMapPath(mapName); if (!Directory.Exists(heightMapDir)) Directory.CreateDirectory(heightMapDir); string controlImgPath = heightMapDir + "/control.png"; SaveControlMap(controlImgPath, sourceTerrain); //Creation du Materiel Material Tmaterial = CreateOrLoadMaterial(mapName, meshName); if (!replace) return Tmaterial; var myTerrainData = MyTerrainData.CreateTerrainDataFromFileName(savedMeshPath); //Recuperation des Texture du terrain SplatPrototype[] texts = sourceTerrain.splatPrototypes; for (int e = 0; e < texts.Length; e++) { if (e < 4) { var tileSize = texts[e].tileSize * 4f; Tmaterial.SetTexture("_Splat" + e, texts[e].texture); Tmaterial.SetTextureScale("_Splat" + e, tileSize); //当前网格起始点对应平铺图的第几个,小数部分就是需要偏移的量 var tilePercentX = (myTerrainData.Offset.x * tileSize.x); var tilePercentY = (myTerrainData.Offset.y * tileSize.y); Tmaterial.SetTextureOffset("_Splat" + e, new Vector2(tilePercentX - (int)tilePercentX, tilePercentY - (int)tilePercentY)); } } //设置control图,已经其偏移 Texture heightMap = (Texture)AssetDatabase.LoadAssetAtPath(controlImgPath, typeof(Texture)); Tmaterial.SetTexture("_Control", heightMap); Tmaterial.SetTextureOffset("_Control", myTerrainData.Offset); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); return Tmaterial; } public static bool CreateNewUnityMesh(string path, string savedMeshPath, Material mat) { var meshObjGo = AssetDatabase.LoadAssetAtPath(savedMeshPath); if (!meshObjGo) return false; MyTerrainData myTd = MyTerrainData.CreateTerrainDataFromFileName(savedMeshPath); var position = new Vector3(myTd.StartX * myTd.PixelScaleX1000 / 1000, 0, myTd.StartZ * myTd.PixelScaleX1000 / 1000); GameObject rootGo = new GameObject(Path.GetFileNameWithoutExtension(path)); var go = new GameObject("default"); go.transform.parent = rootGo.transform; go.transform.position = position; var mf = go.AddComponent(); var renderer = go.AddComponent(); renderer.sharedMaterial = mat; var meshGo = AssetDatabase.LoadAssetAtPath(savedMeshPath); mf.sharedMesh = meshGo.GetComponentInChildren().sharedMesh; AssetDatabase.SaveAssets(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); PrefabUtility.CreatePrefab(path, rootGo, ReplacePrefabOptions.ConnectToPrefab); AssetDatabase.ImportAsset(path); GameObject.DestroyImmediate(rootGo); return true; } public static void ShowPrefab(string path) { var go = AssetDatabase.LoadAssetAtPath(path); go = PrefabUtility.InstantiatePrefab(go) as GameObject; } /// /// 创建或加载对应mesh的材质 /// /// 保存路径 /// mesh的名字 /// private static Material CreateOrLoadMaterial(string mapName, string meshName) { Material Tmaterial; string matPath = ConfigDefine.GetMateriaPath(mapName, meshName + ".mat");// string.Format(savedDir + "/{0}/{1}.mat", ConfigDefine.MatDirName, meshName); string matRootDir = matPath.Substring(0, matPath.LastIndexOf("/")); if (!Directory.Exists(matRootDir)) Directory.CreateDirectory(matRootDir); if (File.Exists(matPath)) { Tmaterial = AssetDatabase.LoadAssetAtPath(matPath); Tmaterial.shader = Shader.Find(ConfigDefine.ShaderName); } else { Tmaterial = new Material(Shader.Find(ConfigDefine.ShaderName)); AssetDatabase.CreateAsset(Tmaterial, matPath); AssetDatabase.ImportAsset(matPath, ImportAssetOptions.ForceUpdate); AssetDatabase.Refresh(); } return Tmaterial; } /// /// 保存terrain的control图 /// /// /// public static void SaveControlMap(string path, TerrainData sourceTerrain) { if (!File.Exists(path)) { //Control Texture Creation or Recuperation string AssetName = AssetDatabase.GetAssetPath(sourceTerrain) as string; UnityEngine.Object[] AssetName2 = AssetDatabase.LoadAllAssetsAtPath(AssetName); if (AssetName2 != null && AssetName2.Length > 1) { for (var b = 0; b < AssetName2.Length; b++) { if (AssetName2[b].name == "SplatAlpha 0") { Texture2D texture = AssetName2[b] as Texture2D; byte[] bytes = texture.EncodeToPNG(); File.WriteAllBytes(path, bytes); AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); } } } AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); //Modification de la Texture TextureImporter TextureI = AssetImporter.GetAtPath(path) as TextureImporter; TextureI.textureFormat = TextureImporterFormat.ARGB32; TextureI.isReadable = true; TextureI.anisoLevel = 9; TextureI.mipmapEnabled = false; TextureI.wrapMode = TextureWrapMode.Clamp; AssetDatabase.Refresh(); AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); } } private static void SetTextureReadable(Texture tex) { string path = AssetDatabase.GetAssetPath(tex); if(File.Exists(path)) { TextureImporter ti = TextureImporter.GetAtPath(path) as TextureImporter; if (ti.isReadable) return; ti.isReadable = true; AssetDatabase.Refresh(); AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); } } /// /// 把terrain分成若干个块,并且计算每个块的边界 /// /// x方向顶点个数 /// y方向顶点个数 /// 像素缩放比 /// 块大小 /// private List CalcBounds(int xVertexCount, int yVertexCount, float scale, float blockSize) { List terrainDatas = new List(); MyTerrainData bound = new MyTerrainData(_terrainName); bool needCreateBound = true; int startX = 0; int startY = 0; int blockCollumns = (int)((xVertexCount * scale) / blockSize) + 1; if (blockCollumns == 0) blockCollumns = 1; for(int y = 0; y <= yVertexCount; ++y) { int curCollumn = terrainDatas.Count % blockCollumns; int curRow = terrainDatas.Count / blockCollumns; for (int x = startX; x <= xVertexCount; ++x) { if (needCreateBound) { bound = new MyTerrainData(_terrainName); bound.StartZ = startX; bound.StartX = startY; needCreateBound = false; } //当前x方向坐标值大于第n列的块大小,则说明到临界值了 //x方向总是先达到临界值,然后等待y方向到临界值 if(x*scale >= blockSize * (curCollumn + 1)) { startX = x; break; } startX = x; } bound.Width = startX; if(startX == xVertexCount) { bound.IsRightBound = true; } //当前y方向坐标值大于第n行的块大小,则说明到临界值了 //或者到最后,已经没有能满足大于第n行的块大小了,则直接添加进来 if (y * scale >= blockSize * (curRow + 1) || y + 1 > yVertexCount) { bound.Height = y; if( y == yVertexCount) { bound.IsBottomBound = true; } needCreateBound = true; bound.PixelScaleX1000 = (PixelScale * 1000); terrainDatas.Add(bound); var newRow = terrainDatas.Count / blockCollumns; //换行了,则x从0开始 if (newRow > curRow) { startY = y; startX = 0; //到这里,已经换行了,但是y已经到头了,则退出计算 if (y + 1 > yVertexCount) break; } //这里减1是因为下次循环会自动加1 y = y - 1; } } return terrainDatas; } private static void AddVerticeIndex(int index, List oldIndexList, List newIndexList) { if (oldIndexList.IndexOf(index) >= 0) return; if (newIndexList.IndexOf(index) >= 0) return; newIndexList.Add(index); } static void UpdateProgress() { } private static void DeleteOldMeshPrefab() { var path = ConfigDefine.GetSavePrefabPath(); var fileList = Directory.GetFiles(path, "*.prefab"); for(int i = 0; i < fileList.Length; ++i) { if (fileList[i].StartsWith("Mesh_")) File.Delete(fileList[i]); } } private static void DeleteOldMeshFiles(string mapName) { var path = ConfigDefine.GetSaveTerrainPath(mapName); var fileList = Directory.GetFiles(path, "*"); for(int i = 0; i < fileList.Length; ++i) { File.Delete(fileList[i]); } } } }