Main/Assets/Editor/SplitTerrain/SplitTerrain.cs

892 lines
38 KiB
C#
Raw Permalink Normal View History

2025-01-25 04:38:09 +08:00
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<Vector3> ignoreVertices = new List<Vector3>();
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<TerrainData>(selectTerrainPath);
StartSplitTerrain(terrainData, vertexResolution, blockResolution, delExpandedMesh);
}
public void StartSplitTerrain(Transform currentSelect, float vertexResolution, float blockResolution, bool delExpandedMesh = false)
{
var terrainDat = currentSelect.GetComponent<Terrain>().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<string> SplitTerrainWithBounds(List<MyTerrainData> 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<string> configList = new List<string>();
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;
}
/// <summary>
/// 删除扩展的网格
/// </summary>
/// <param name="savePath"></param>
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;
}
/// <summary>
/// 保存网格信息到obj文件当有tNoramls数据时说明是读取的网格顶点保存这时需要反转顶点x方向和面的顶点需要逆时针方向
/// </summary>
/// <param name="name"></param>
/// <param name="tVertices"></param>
/// <param name="tUV"></param>
/// <param name="tPolys"></param>
/// <param name="tNormals"></param>
/// <param name="needReversX"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 删除之前扩展的网格
/// </summary>
/// <param name="meshPath"></param>
/// <param name="originalData">网格信息</param>
/// <param name="pixcelScale">像素缩放比</param>
/// <param name="verticesOut">删除后有效的顶点数据</param>
/// <param name="normalsOut"></param>
/// <param name="tangentsOut"></param>
/// <param name="uvsOut"></param>
/// <param name="trianglesOut"></param>
/// <returns></returns>
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<GameObject>(meshPath);
if (go == null)
return false;
var meshFilter = go.GetComponentInChildren<MeshFilter>();
if (meshFilter == null)
return false;
var mesh = meshFilter.sharedMesh;
{
var newVerticesList = new List<Vector3>();
var newVerticesNormals = new List<Vector3>();
var newVerticesTangents = new List<Vector4>();
var newUvs = new List<Vector2>();
var triangles = new List<int>();
//1. 无效顶点的索引
var invalidVericesIndexList = GetInvalidVerticeIndexs(mesh, originalData, pixcelScale);
var moreInvalidVerticeIndexs = new List<int>();
//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<int> 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<int>();
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<int> GetValidTriangles(Mesh mesh, List<int> invalidVericesIndexList, out List<int> noUseVertices)
{
var triangles = new List<int>();
//更多无效的顶点去除一个三角形会遗留2个无效顶点需要记录
noUseVertices = new List<int>();
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<int, int> PickValidDatas(Mesh mesh, MyTerrainData originalData, List<int> invalidVericesIndexList,
out List<Vector3> newVerticesList,
out List<Vector3> newVerticesNormals,
out List<Vector4> newVerticesTangents,
out List<Vector2> newUvs)
{
Dictionary<int, int> replaceIndexDict = new Dictionary<int, int>();
newVerticesList = new List<Vector3>();
newVerticesNormals = new List<Vector3>();
newVerticesTangents = new List<Vector4>();
newUvs = new List<Vector2>();
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<GameObject>(savedMeshPath);
var renderer = meshGo.GetComponentInChildren<MeshRenderer>();
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<GameObject>(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<MeshFilter>();
var renderer = go.AddComponent<MeshRenderer>();
renderer.sharedMaterial = mat;
var meshGo = AssetDatabase.LoadAssetAtPath<GameObject>(savedMeshPath);
mf.sharedMesh = meshGo.GetComponentInChildren<MeshFilter>().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<GameObject>(path);
go = PrefabUtility.InstantiatePrefab(go) as GameObject;
}
/// <summary>
/// 创建或加载对应mesh的材质
/// </summary>
/// <param name="savedDir">保存路径</param>
/// <param name="meshName">mesh的名字</param>
/// <returns></returns>
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<Material>(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;
}
/// <summary>
/// 保存terrain的control图
/// </summary>
/// <param name="path"></param>
/// <param name="sourceTerrain"></param>
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);
}
}
/// <summary>
/// 把terrain分成若干个块并且计算每个块的边界
/// </summary>
/// <param name="xVertexCount">x方向顶点个数</param>
/// <param name="yVertexCount">y方向顶点个数</param>
/// <param name="scale">像素缩放比</param>
/// <param name="blockSize">块大小</param>
/// <returns></returns>
private List<MyTerrainData> CalcBounds(int xVertexCount, int yVertexCount, float scale, float blockSize)
{
List<MyTerrainData> terrainDatas = new List<MyTerrainData>();
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<int> oldIndexList, List<int> 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]);
}
}
}
}