inz-00/Assets/Scripts/Underground/CaveMeshGenerator.cs
2019-11-10 20:23:07 +01:00

410 lines
13 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CaveMeshGenerator : MonoBehaviour
{
public SquareGrid squareGrid;
public MeshFilter walls;
List<Vector3> vertices;
List<int> triangles;
Dictionary<int, List<Triangle>> triangleDic = new Dictionary<int, List<Triangle>>();
List<List<int>> outlines = new List<List<int>>();
HashSet<int> checkedVertices = new HashSet<int>();
public void GenerateMesh(int[,] map, float squareSize)
{
outlines.Clear();
checkedVertices.Clear();
triangleDic.Clear();
squareGrid = new SquareGrid(map, squareSize);
vertices = new List<Vector3>();
triangles = new List<int>();
for (int x = 0; x < squareGrid.squares.GetLength(0); ++x)
{
for (int y = 0; y < squareGrid.squares.GetLength(1); ++y)
{
TriangulateSquare(squareGrid.squares[x, y]);
}
}
Mesh mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
CreateWallMesh();
}
void CreateWallMesh()
{
CalculateMeshOutlines();
List<Vector3> wallVertices = new List<Vector3>();
List<int> wallTriangles = new List<int>();
Mesh wallMesh = new Mesh();
float wallHeight = 5;
foreach (List<int> outline in outlines)
{
for(int i = 0; i < outline.Count - 1; ++i)
{
int startIndex = wallVertices.Count;
wallVertices.Add(vertices[outline[i]]);
wallVertices.Add(vertices[outline[i + 1]]);
wallVertices.Add(vertices[outline[i]] - Vector3.up * wallHeight);
wallVertices.Add(vertices[outline[i + 1]] - Vector3.up * wallHeight);
wallTriangles.Add(startIndex);
wallTriangles.Add(startIndex + 2);
wallTriangles.Add(startIndex + 3);
wallTriangles.Add(startIndex + 3);
wallTriangles.Add(startIndex + 1);
wallTriangles.Add(startIndex);
}
}
wallMesh.vertices = wallVertices.ToArray();
wallMesh.triangles = wallTriangles.ToArray();
walls.mesh = wallMesh;
walls.sharedMesh = wallMesh;
}
void TriangulateSquare(Square square)
{
switch(square.m_Config)
{
case 0:
break;
case 1:
MeshFromPoints(square.m_CentreLeft, square.m_CentreBottom, square.m_BottomLeft);
break;
case 2:
MeshFromPoints(square.m_BottomRight, square.m_CentreBottom, square.m_CentreRight);
break;
case 4:
MeshFromPoints(square.m_TopRight, square.m_CentreRight, square.m_CentreTop);
break;
case 8:
MeshFromPoints(square.m_TopLeft, square.m_CentreTop, square.m_CentreLeft);
break;
case 3:
MeshFromPoints(square.m_CentreRight, square.m_BottomRight, square.m_BottomLeft, square.m_CentreLeft);
break;
case 6:
MeshFromPoints(square.m_CentreTop, square.m_TopRight, square.m_BottomRight, square.m_CentreBottom);
break;
case 9:
MeshFromPoints(square.m_TopLeft, square.m_CentreTop, square.m_CentreBottom, square.m_BottomLeft);
break;
case 12:
MeshFromPoints(square.m_TopLeft, square.m_TopRight, square.m_CentreRight, square.m_CentreLeft);
break;
case 5:
MeshFromPoints(square.m_CentreTop, square.m_TopRight, square.m_CentreRight, square.m_CentreBottom, square.m_BottomLeft, square.m_CentreLeft);
break;
case 10:
MeshFromPoints(square.m_TopLeft, square.m_CentreTop, square.m_CentreRight, square.m_BottomRight, square.m_CentreBottom, square.m_CentreLeft);
break;
case 7:
MeshFromPoints(square.m_CentreTop, square.m_TopRight, square.m_BottomRight, square.m_BottomLeft, square.m_CentreLeft);
break;
case 11:
MeshFromPoints(square.m_TopLeft, square.m_CentreTop, square.m_CentreRight, square.m_BottomRight, square.m_BottomLeft);
break;
case 13:
MeshFromPoints(square.m_TopLeft, square.m_TopRight, square.m_CentreRight, square.m_CentreBottom, square.m_BottomLeft);
break;
case 14:
MeshFromPoints(square.m_TopLeft, square.m_TopRight, square.m_BottomRight, square.m_CentreBottom, square.m_CentreLeft);
break;
case 15:
MeshFromPoints(square.m_TopLeft, square.m_TopRight, square.m_BottomRight, square.m_BottomLeft);
checkedVertices.Add(square.m_TopLeft.m_VertexIndex);
checkedVertices.Add(square.m_TopRight.m_VertexIndex);
checkedVertices.Add(square.m_BottomRight.m_VertexIndex);
checkedVertices.Add(square.m_BottomLeft.m_VertexIndex);
break;
}
}
void MeshFromPoints(params Node[] points)
{
AssignVertices(points);
if(points.Length >= 3)
{
CreateTriangle(points[0], points[1], points[2]);
}
if(points.Length >= 4)
{
CreateTriangle(points[0], points[2], points[3]);
}
if (points.Length >= 5)
{
CreateTriangle(points[0], points[3], points[4]);
}
if (points.Length >= 6)
{
CreateTriangle(points[0], points[4], points[5]);
}
}
void AssignVertices(Node[] points)
{
for(int i = 0; i < points.Length; ++i)
{
if(points[i].m_VertexIndex == -1)
{
points[i].m_VertexIndex = vertices.Count;
vertices.Add(points[i].m_Position);
}
}
}
void CreateTriangle(Node a, Node b, Node c)
{
triangles.Add(a.m_VertexIndex);
triangles.Add(b.m_VertexIndex);
triangles.Add(c.m_VertexIndex);
Triangle triangle = new Triangle(a.m_VertexIndex, b.m_VertexIndex, c.m_VertexIndex);
AddTriangleToDictionary(triangle.vertexIndexA, triangle);
AddTriangleToDictionary(triangle.vertexIndexB, triangle);
AddTriangleToDictionary(triangle.vertexIndexC, triangle);
}
void AddTriangleToDictionary(int vertexIndexKey, Triangle triangle)
{
if (triangleDic.ContainsKey(vertexIndexKey))
{
triangleDic[vertexIndexKey].Add(triangle);
}
else
{
List<Triangle> triangles = new List<Triangle>();
triangles.Add(triangle);
triangleDic.Add(vertexIndexKey, triangles);
}
}
void CalculateMeshOutlines()
{
for(int vertexIndex = 0; vertexIndex < vertices.Count; ++vertexIndex)
{
if (!checkedVertices.Contains(vertexIndex))
{
int newOutlineVertex = GetConnectedOutlineVertex(vertexIndex);
if (newOutlineVertex != -1)
{
checkedVertices.Add(vertexIndex);
List<int> newOutline = new List<int>();
newOutline.Add(vertexIndex);
outlines.Add(newOutline);
FollowOutline(newOutlineVertex, outlines.Count - 1);
outlines[outlines.Count - 1].Add(vertexIndex);
}
}
}
}
// TODO: change to non recursive
void FollowOutline(int vertexIndex, int outlineIndex)
{
outlines[outlineIndex].Add(vertexIndex);
checkedVertices.Add(vertexIndex);
int nextVertexIndex = GetConnectedOutlineVertex(vertexIndex);
if (nextVertexIndex != -1)
{
FollowOutline(nextVertexIndex, outlineIndex);
}
}
int GetConnectedOutlineVertex(int vertexIndex)
{
List<Triangle> trianglesContainingVertex = triangleDic[vertexIndex];
for (int i = 0; i < trianglesContainingVertex.Count; ++i)
{
Triangle triangle = trianglesContainingVertex[i];
for (int j = 0; j < 3; ++j)
{
int vertexB = triangle[j];
if (vertexB == vertexIndex || checkedVertices.Contains(vertexB))
{
continue;
}
if (IsOutlineEdge(vertexIndex, vertexB))
{
return vertexB;
}
}
}
return -1;
}
bool IsOutlineEdge(int vertexA, int vertexB)
{
List<Triangle> trianglesContainingVertexA = triangleDic[vertexA];
int sharedTriangleCount = 0;
for (int i = 0; i < trianglesContainingVertexA.Count; ++i)
{
if (trianglesContainingVertexA[i].Contains(vertexB))
{
++sharedTriangleCount;
if (sharedTriangleCount == 2)
{
return false;
}
}
}
return true;
}
struct Triangle
{
public int vertexIndexA;
public int vertexIndexB;
public int vertexIndexC;
int[] vertices;
public Triangle(int a, int b, int c)
{
vertexIndexA = a;
vertexIndexB = b;
vertexIndexC = c;
vertices = new int[3];
vertices[0] = a;
vertices[1] = b;
vertices[2] = c;
}
public int this[int i]
{
get
{
return vertices[i];
}
}
public bool Contains(int vertexIndex)
{
return vertexIndex == vertexIndexA ||
vertexIndex == vertexIndexB ||
vertexIndex == vertexIndexC;
}
}
public class SquareGrid
{
public Square[,] squares;
public SquareGrid(int[,] map, float squareSize)
{
int nodeCountX = map.GetLength(0);
int nodeCountY = map.GetLength(1);
float mapWidth = nodeCountX * squareSize;
float mapHeight = nodeCountY * squareSize;
ControlNode[,] controlNodes = new ControlNode[nodeCountX, nodeCountY];
for(int x = 0; x < nodeCountX; ++x)
{
for (int y = 0; y < nodeCountY; ++y)
{
Vector3 position = new Vector3(-mapWidth / 2 + x * squareSize + squareSize / 2,
0,
-mapHeight / 2 + y * squareSize + squareSize / 2);
controlNodes[x, y] = new ControlNode(position, map[x, y] == 1, squareSize);
}
}
squares = new Square[nodeCountX - 1, nodeCountY - 1];
for (int x = 0; x < nodeCountX - 1; ++x)
{
for (int y = 0; y < nodeCountY - 1; ++y)
{
squares[x, y] = new Square(controlNodes[x, y + 1],
controlNodes[x + 1, y + 1],
controlNodes[x + 1, y],
controlNodes[x, y]);
}
}
}
}
public class Square
{
public ControlNode m_TopLeft, m_TopRight, m_BottomLeft, m_BottomRight;
public Node m_CentreTop, m_CentreRight, m_CentreBottom, m_CentreLeft;
public int m_Config;
public Square(
ControlNode topLeft,
ControlNode topRight,
ControlNode bottomRight,
ControlNode bottomLeft
)
{
m_TopLeft = topLeft;
m_TopRight = topRight;
m_BottomLeft = bottomLeft;
m_BottomRight = bottomRight;
m_CentreTop = m_TopLeft.m_RightNode;
m_CentreRight = m_BottomRight.m_AboveNode;
m_CentreBottom = m_BottomLeft.m_RightNode;
m_CentreLeft = m_BottomLeft.m_AboveNode;
m_Config = ((m_TopLeft.m_IsActive) ? 8 : 0) |
((m_TopRight.m_IsActive) ? 4 : 0) |
((m_BottomRight.m_IsActive) ? 2 : 0) |
(m_BottomLeft.m_IsActive ? 1 : 0);
}
}
public class Node
{
public Vector3 m_Position;
public int m_VertexIndex = -1;
public Node(Vector3 pos)
{
m_Position = pos;
}
}
public class ControlNode : Node
{
public bool m_IsActive;
public Node m_AboveNode, m_RightNode;
public ControlNode(Vector3 pos, bool isActive, float squareSize) : base(pos)
{
m_IsActive = isActive;
m_AboveNode = new Node(pos + Vector3.forward * squareSize / 2f);
m_RightNode = new Node(pos + Vector3.right * squareSize / 2f);
}
}
}