Main/Assets/Editor/SplitTerrain/SplitTerrain.cs
2025-01-25 04:38:09 +08:00

892 lines
38 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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]);
}
}
}
}