本篇將繼續(xù)上篇的內容筹煮,對導出的數(shù)據(jù)進行讀取资昧,并還原編輯的區(qū)域塊,效果如下所示:
1.讀取導出數(shù)據(jù)
private void ReadAndCreate()
{
if (meshList.Count > 0)
{
EditorUtility.DisplayDialog("提示", "請清空所有生成的面片幔烛!", "確定");
return;
}
try
{
string filePath = outputPath + map.name + ".txt";
if (File.Exists(Path.GetFullPath(filePath)))
{
List<string> list = new List<string>(File.ReadAllLines(filePath));
List<Block> bList = new List<Block>();
int idx = 0;
while (idx < list.Count)
{
Block block = new Block();
if (block.linePosList == null)
{
block.linePosList = new List<Vector3>();
}
int cnt = int.Parse(list[idx]);
for (int i = idx + 1; i < idx + 1 + cnt; ++i)
{
string[] strs = list[i].Split(',');
Vector3 pos = new Vector3(float.Parse(strs[0]), float.Parse(strs[1]), float.Parse(strs[2]));
block.linePosList.Add(pos);
}
idx = idx + 1 + cnt;
bList.Add(block);
}
CreateMesh(bList);
}
}
catch (Exception ex)
{
LogMgr.LogError(ex);
}
}
2.生成區(qū)域塊
根據(jù)得到的順時針結點list隙畜, 還原編輯的區(qū)域塊,當多邊形為凹多邊形且凹點(即角度大于180的頂點)不止一個時说贝,需分割成多個多邊形來生成mesh。若凹點只有一個慎颗,生成mesh時乡恕,將凹點作為固定點,即可生成所需mesh俯萎; 若凹點大于一個傲宜,則需要分割多邊形成凸多邊形或者只有一個凹點的凹多邊形,所以這里牽扯到了如何利用頂點信息分割凹多邊形夫啊。我的處理方法是函卒,記錄下凹點,連接兩個凹點撇眯,將圖形分割成兩塊报嵌,若兩個子塊滿足要求則不再分割,若不滿足熊榛,則重復之前操作锚国,將不滿足的子塊繼續(xù)分割直至滿足條件。當存在兩個相鄰且角度大于180的頂點時玄坦,若直接連接則無法分割血筑,所以這里和最近的非凹點連接進行強行分割。
meshList: 存儲生成的區(qū)域塊
private void CreateMesh(List<Block> blist)
{
for (int k = 0; k < blist.Count; ++k)
{
List<Vector3> list = blist[k].linePosList;
Vector3[] vertices = new Vector3[list.Count - 1];
bool[] IsObtuse = new bool[list.Count - 1]; //標志該頂點兩邊夾角是否大于180
int[] triangles = new int[3 * (list.Count - 3)];
for (int i = 0; i < list.Count - 1; ++i)
{
vertices[i] = list[i];
}
int startIdx = -1; //記錄第一個角度大于180的頂點索引
for (int i = 0; i < list.Count - 1; ++i)
{
Vector3 a = list[(i + list.Count - 2) % (list.Count - 1)] - list[i];
Vector3 b = list[(i + 1) % (list.Count - 1)] - list[i];
if (Vector3.Cross(a.normalized, b.normalized).y > 0) //順時針編輯頂點煎楣,故可如此判斷
{
IsObtuse[i] = true;
if (startIdx == -1)
{
startIdx = i;
}
}
else
{
IsObtuse[i] = false;
}
}
startIdx = startIdx > 0 ? startIdx : 0;
Queue<int> queue = new Queue<int>(); //頂點隊列
for (int i = 0; i < list.Count - 1; ++i)
{
queue.Enqueue((startIdx + i) % (list.Count - 1));
}
int flagCnt = 0;
int tIdx = 0;
int lastIdx = -1;
List<int> vlist = new List<int>();
while (queue.Count > 0)
{
int idx = queue.Dequeue();
if (IsObtuse[idx])
{
if (vlist.Count > 0 && flagCnt == 0) //若存在頂點角度大于180豺总,保證vlist[0]頂點角度大于180
{
for (int q = 0; q < vlist.Count; q++)
{
queue.Enqueue(vlist[q]);
}
vlist.Clear();
}
queue.Enqueue(idx);
IsObtuse[idx] = false;
++flagCnt;
if (flagCnt == 1)
{
lastIdx = idx;
}
else if (Math.Abs(idx - lastIdx) == 1) //相鄰凹點處理, 進行強行分割
{
if (vlist.Count == 1) //連續(xù)兩個凹點择懂,則移除并重置第一個凹點
{
IsObtuse[lastIdx] = true;
vlist.Clear();
flagCnt = 1;
lastIdx = idx;
}
else if(queue.Count == 2) //第二個凹點是繞了一圈后的點喻喳,理論上來說queue.Count = 2,這里就是加個保護
{
Vector3 a = list[idx] - list[lastIdx];
Vector3 b = list[(lastIdx + 2) % (list.Count - 1)] - list[lastIdx];
if (Vector3.Cross(a.normalized, b.normalized).y > 0)
{
IsObtuse[lastIdx] = true;
}
IsObtuse[idx] = true;
int aver = vlist[1];
int bver = vlist[2];
vlist.RemoveAt(1);
queue.Clear();
queue.Enqueue(lastIdx);
queue.Enqueue(aver);
queue.Enqueue(bver);
}
}
else
{
//連接兩個滿足要求的頂點(故連續(xù)的兩個角度大于180的圖形不能處理)休蟹,對多邊形進行分割沸枯,并重新標記被分割的頂點
Vector3 a = list[(lastIdx - 1) % (list.Count - 1)] - list[lastIdx];
Vector3 b = list[idx] - list[lastIdx];
if (Vector3.Cross(a.normalized, b.normalized).y > 0)
{
IsObtuse[lastIdx] = true;
}
a = list[lastIdx] - list[idx];
b = list[(idx + 1) % (list.Count - 1)] - list[idx];
if (Vector3.Cross(a.normalized, b.normalized).y > 0)
{
IsObtuse[idx] = true;
}
}
}
vlist.Add(idx);
if (flagCnt > 0 && flagCnt % 2 == 0 || queue.Count == 0)
{
//若最后只剩一個角度大于180的頂點日矫,則移除開始時的重復添加
if (flagCnt == 1 && queue.Count == 0)
{
vlist.RemoveAt(0);
}
for (int i = 0; i < vlist.Count - 2; ++i)
{
triangles[3 * (i + tIdx)] = vlist[vlist.Count - 1];//固定第一個點
triangles[3 * (i + tIdx) + 1] = vlist[i];
triangles[3 * (i + tIdx) + 2] = vlist[i + 1];
}
tIdx += (vlist.Count - 2);
flagCnt = 0;
lastIdx = -1;
vlist.Clear();
}
}
GameObject target = new GameObject();
target.name = "BlockMesh_" + k;
target.transform.position = new Vector3(0, 30, 0);
MeshFilter filter = target.AddComponent<MeshFilter>();
target.AddComponent<MeshRenderer>();
Material material = new Material(Shader.Find("Diffuse"));
material.color = blockColors[k % 9];
target.GetComponent<MeshRenderer>().material = material;
Mesh mesh = new Mesh();
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
List<Color> colors = new List<Color>();
for (int i = 0; i < mesh.vertexCount; ++i)
{
colors.Add(blockColors[k % 9]);
}
mesh.SetColors(colors);
//mesh.uv = GetComponent<MeshFilter>().sharedMesh.uv;
mesh.name = "mesh";
mesh.RecalculateNormals();
mesh.RecalculateBounds();
filter.sharedMesh = mesh;
meshList.Add(target);
}
}
當前方法我進行了測試還木發(fā)現(xiàn)什么問題,有可能我測試不足绑榴,或者有什么思考不足的方面哪轿,如果發(fā)現(xiàn)此方法有問題,歡迎大家指出翔怎。
源碼地址:https://github.com/gtgt154/MapSplit