using System.Collections.Generic; using System.Linq; using Assets.AnnotationPass; using Assets.Common; using UnityEngine; namespace Assets.RenderPass { public class LandmassRenderer : IRenderer { public float UVScale { get; set; } = 1.0f; private List _vertices; private List _uv; private List _normals; private List _colors; private List _triangles; private List> _edges; public void Render(Map map, Component component) { var points = map.Boundaries.Vertices; var locations = map.GetProperty>(LandmassPass.MapLocationsProperty); _vertices = new List(); _normals = new List(); _uv = new List(); _colors = new List(); _triangles = new List(); foreach (var location in locations.Vertices.Skip(1)) { GenerateLocationEdges(location, points); GenerateLocationMeshAlt(location, points); GenerateLocationWall(location, points); } Mesh mesh = new Mesh(); mesh.vertices = _vertices.ToArray(); mesh.uv = _uv.ToArray(); mesh.normals = _normals.ToArray(); mesh.triangles = _triangles.ToArray(); mesh.colors = _colors.ToArray(); var mf = component.GetComponent(); mf.sharedMesh = mesh; } private void GenerateLocationEdges(Location location, List points) { foreach (var (a, b) in location.BoundaryEdges.Select(x => (points[x.Item1], points[x.Item2]))) location.DetailedEdge.AddRange(this.SplitEdge(a, b, 3)); } private IEnumerable SplitEdge(Point a, Point b, int count) { var magnitude = Point.Dist(a, b); var d = new Point(-(b.y - a.y) / magnitude, (b.x - a.x) / magnitude); magnitude *= Random.value * 0.2 - 0.1; Point c = new Point((a.x + b.x) / 2.0 + d.x * magnitude, (a.y + b.y) / 2.0 + d.y * magnitude); if (count > 0) { foreach (var p in SplitEdge(a, c, count - 1)) yield return p; yield return c; foreach (var p in SplitEdge(c, b, count - 1)) yield return p; } else { yield return c; } } private void GenerateLocationMeshAlt(Location location, IList points) { var start = _vertices.Count; var count = location.DetailedEdge.Count; _vertices.AddRange(location.DetailedEdge.Select(p => new Vector3((float)p.x, location.Type.height, (float)p.y))); _normals.AddRange(Enumerable.Repeat(Vector3.up, count)); _colors.AddRange(Enumerable.Repeat(Color.blue, count)); _uv.AddRange(location.DetailedEdge.Select(p => new Vector2((float)p.x, (float)p.y) / UVScale)); var triangulator = new Triangulator(location.DetailedEdge.Select(v => new Vector2((float)v.x, (float)v.y)).ToArray()); _triangles.AddRange(triangulator.Triangulate().Select(x => x + start)); } private void GenerateLocationMesh(Location location, IList points) { foreach (var vertices in location.Sites.Select(site => site.Edges.Select(x => x.Item1).Reverse())) { int start = _vertices.Count; foreach (var i in vertices) { var isEdge = location.BoundaryPoints.Contains(i); var vertex = points[i]; var v = PointToVector(location, vertex); _vertices.Add(v); _normals.Add(Vector3.up); _uv.Add(new Vector2(v.x, v.z) / UVScale); _colors.Add(isEdge ? Color.red : Color.blue); } int end = _vertices.Count; var triangulator = new Triangulator(_vertices.Skip(start).Take(end - start).Select(v => new Vector2(v.x, v.z)).ToArray()); _triangles.AddRange(triangulator.Triangulate().Select(x => x + start)); } } private static Vector3 PointToVector(Location location, Point vertex) { return new Vector3((float)vertex.x, location.Type.height + Mathf.PerlinNoise((float)vertex.x, (float)vertex.y) / 4, (float)vertex.y); } private void GenerateLocationWall(Location location, IList points) { var length = 0.0; var collection = location.DetailedEdge.Append(location.DetailedEdge.First()); var edges = collection.Zip(collection.Skip(1), (a, b) => (a, b)).Zip(collection.Skip(2), (tuple, c) => (tuple.a, tuple.b, c)); foreach (var (p, c, n) in edges) { int start = _vertices.Count; var dist = Point.Dist(p, c); var veca = PointToVector(location, p); var vecb = PointToVector(location, c); _vertices.Add(veca); _vertices.Add(new Vector3((float)p.x, -10,(float)p.y)); _vertices.Add(vecb); _vertices.Add(new Vector3((float)c.x, -10,(float)c.y)); var normal = new Vector3((float)(p.x + n.x - c.x) / 2.0f, 0, (float)(p.y + n.y - c.y) / 2.0f).normalized; _normals.AddRange(Enumerable.Repeat(normal, 4)); _colors.AddRange(Enumerable.Repeat(Color.red, 4)); _triangles.AddRange(new [] { start, start + 2, start + 1, start + 1, start + 2, start + 3 }); _uv.AddRange(new [] { new Vector2((float)length, veca.y) / UVScale, new Vector2((float)length, -10) / UVScale, new Vector2((float)(length + dist), vecb.y) / UVScale, new Vector2((float)(length + dist), -10) / UVScale, }); length += dist; } } public void DrawGizmos(Map map, Component component) { } } }