using System; using System.Collections.Generic; using UnityEngine; public class FastLineDrawer : MonoBehaviour { private static List localPoints = new List(4096); public OnlineMapsTileSetControl control; public Shader shader; private OnlineMaps map; private List lines = new List(); public void Add(OnlineMapsDrawingLine line) { lines.Add(line); line.manager = map.drawingElementManager; line["fastDrawerData"] = new Data(this, line); map.Redraw(); } private void Draw(OnlineMapsDrawingLine line) { Data data = line["fastDrawerData"] as Data; if (data == null) return; if (!line.visible) { data.SetActive(false); return; } if (line.range != null && !line.range.InRange(control.map.floatZoom)) { data.SetActive(false); return; } if (!data.Contains(map)) { data.SetActive(false); return; } data.SetActive(true); double tlx, tly, brx, bry; map.GetTileCorners(out tlx, out tly, out brx, out bry, 0); if (tlx > brx) brx += 1; double rx = brx - tlx; double ry = bry - tly; Vector2 sizeInScene = control.sizeInScene; double sx = -sizeInScene.x / rx; double sy = sizeInScene.y / ry; double[] mercatorPoints = data.mercatorPoints; int i = 0; localPoints.Clear(); while (i < mercatorPoints.Length) { double mx = (mercatorPoints[i++] - tlx) * sx; double my = (mercatorPoints[i++] - tly) * sy; localPoints.Add(new Vector3((float)mx, 0, (float)my)); } double cx = ((tlx + brx) * sx + sizeInScene.x) / 2; float ox = 0; if (rx > 0.5 && cx < sizeInScene.x / -2) ox = -sizeInScene.x; Vector3 offset = new Vector3(ox, 0, 0); Vector3 lastPosition = localPoints[0] + offset; float size = line.width / 2; Vector3[] vertices = data.vertices; for (i = 1; i < localPoints.Count; i++) { Vector3 p = localPoints[i] + offset; float a = OnlineMapsUtils.Angle2DRad(lastPosition, p, 90); Vector3 off = new Vector3(Mathf.Cos(a) * size, 0, Mathf.Sin(a) * size); int vi = (i - 1) * 4; vertices[vi] = lastPosition + off; vertices[vi + 1] = lastPosition - off; vertices[vi + 2] = p + off; vertices[vi + 3] = p - off; lastPosition = p; } Mesh mesh = data.mesh; mesh.vertices = vertices; mesh.RecalculateBounds(); } private void OnMeshUpdated() { foreach (OnlineMapsDrawingLine line in lines) { Draw(line); } } public void Refresh(OnlineMapsDrawingLine line) { Data data = line["fastDrawerData"] as Data; data?.Refresh(); map.Redraw(); } public void Remove(OnlineMapsDrawingLine line) { if (!lines.Remove(line)) return; Data data = line["fastDrawerData"] as Data; data?.Dispose(); line.Dispose(); map.Redraw(); } private void Start() { if (!control) control = OnlineMapsTileSetControl.instance; map = control.map; control.OnMeshUpdated += OnMeshUpdated; } private class Data { public double[] mercatorPoints; public Mesh mesh; public Vector3[] vertices; private Vector2[] uvs; private Vector3[] normals; private int[] triangles; private bool active; private FastLineDrawer drawer; private GameObject gameObject; private OnlineMapsDrawingLine line; private double top, bottom, left, right; public Data(FastLineDrawer drawer, OnlineMapsDrawingLine line) { this.drawer = drawer; this.line = line; Refresh(); } public bool Contains(OnlineMaps map) { double tlx, tly, brx, bry; map.GetTileCorners(out tlx, out tly, out brx, out bry, 0); if (tlx > brx) brx += 1; return (right > tlx && left < brx || right > tlx - 1 && left < brx - 1) && bottom > tly && top < bry; } public void Dispose() { if (gameObject) { Destroy(gameObject); gameObject = null; } mesh = null; line = null; drawer = null; } private void InitializeArrays() { int countPoints = mercatorPoints.Length / 2 - 1; vertices = new Vector3[countPoints * 4]; uvs = new Vector2[countPoints * 4]; normals = new Vector3[countPoints * 4]; triangles = new int[countPoints * 6]; Array.Fill(uvs, Vector2.zero); Array.Fill(normals, Vector3.up); for (int i = 0; i < countPoints; i++) { int v = i * 4; int ti = i * 6; triangles[ti] = v; triangles[ti + 1] = v + 3; triangles[ti + 2] = v + 1; triangles[ti + 3] = v; triangles[ti + 4] = v + 2; triangles[ti + 5] = v + 3; } if (mesh) { mesh.vertices = vertices; mesh.uv = uvs; mesh.normals = normals; mesh.triangles = triangles; } } private void InitializeMesh() { gameObject = new GameObject("FastLineDrawer"); gameObject.transform.SetParent(line.manager.map.transform, false); gameObject.transform.localPosition = Vector3.up * drawer.control.sizeInScene.magnitude / 1024f; gameObject.transform.localRotation = Quaternion.identity; gameObject.transform.localScale = Vector3.one; mesh = new Mesh(); mesh.MarkDynamic(); mesh.vertices = vertices; mesh.uv = uvs; mesh.normals = normals; mesh.triangles = triangles; MeshFilter meshFilter = gameObject.AddComponent(); meshFilter.mesh = mesh; Material material = new Material(drawer.shader) { color = line.color }; MeshRenderer renderer = gameObject.AddComponent(); renderer.sharedMaterial = material; } public void Refresh() { double[] points = line.points as double[]; if (points == null || points.Length % 2 != 0) { throw new Exception("Points must be of type double[]: longitude, latitude pairs."); } top = double.MaxValue; bottom = double.MinValue; left = double.MaxValue; right = double.MinValue; mercatorPoints = new double[points.Length]; OnlineMapsProjection projection = line.manager.map.projection; for (int i = 0; i < points.Length; i += 2) { double lng = points[i]; double lat = points[i + 1]; double mx, my; projection.CoordinatesToTile(lng, lat, 0, out mx, out my); mercatorPoints[i] = mx; mercatorPoints[i + 1] = my; if (mx < left) left = mx; if (mx > right) right = mx; if (my < top) top = my; if (my > bottom) bottom = my; } InitializeArrays(); } public void SetActive(bool value) { if (active == value) return; if (!value || gameObject) { gameObject.SetActive(value); active = value; return; } InitializeMesh(); active = true; } } }