using SharpKml.Dom; using Skytex.ThreadDispatcher; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UIElements; public class DrawTrailOfDrone : MonoBehaviour { [SerializeField] OnlineMaps map; [SerializeField] double distanceThreshold = 2.0; GeoCoordinates initialDronePosition, markerPos; List dronePathPoints = new(); List markerPoints = new(); public OnlineMapsDrawingLine greyMarkerLine; EDroneState currDroneState = EDroneState.DISCONNECTED, prevDroneState = EDroneState.TOTAL; public bool isCoroutineRunning = false; Coroutine redline; OnlineMapsDrawingLine lineSegment; List OdronePathPoints = new(); GameObject meshObject; //GameObject mesh2Object; int times = 1; public int segmentThreshold = 100; private Mesh combinedMesh; private Material lineMaterial; private OnlineMapsMarker3D marker; //private OnlineMapsMarker3D marker2; void Start() { if (map == null) map = OnlineMaps.instance; lineMaterial = new Material(Shader.Find("Unlit/Color")); lineMaterial.color = Color.red; } //Drone trail private void OnEnable() { EventManager.Instance.RegisterEvent(EEvent.GOTO_ENTER_CLICKED, GetMarkerLocation); EventManager.Instance.RegisterEvent(EEvent.DRONE_STATE, ChangeDroneState); EventManager.Instance.RegisterEvent(EEvent.DRONE_RETURN_STARTED, GetMarkerLocation); EventManager.Instance.RegisterEvent(EEvent.START_DRAW_REDLINE, EnableRedLine); } private void OnDisable() { EventManager.Instance.UnregisterEvent(EEvent.GOTO_ENTER_CLICKED, GetMarkerLocation); EventManager.Instance.UnregisterEvent(EEvent.DRONE_STATE, ChangeDroneState); EventManager.Instance.UnregisterEvent(EEvent.DRONE_RETURN_STARTED, GetMarkerLocation); EventManager.Instance.UnregisterEvent(EEvent.START_DRAW_REDLINE, EnableRedLine); } private void ChangeDroneState(EDroneState state) { currDroneState = state; if (currDroneState != prevDroneState) MainThreadDispatcher.Instance.Enqueue(DeleteAllLinesOnLand); prevDroneState = currDroneState; } private void DeleteAllLinesOnLand() { if (currDroneState == EDroneState.LANDED) { DroneStatusUIHandler.Instance.goToMarker.circleAroundMarker.currentCircle = null; if (MissionController.Instance.IsExecutionInProgress) MissionController.Instance.IsExecutionInProgress = false; else if (greyMarkerLine != null) map.drawingElementManager.Remove(greyMarkerLine); greyMarkerLine = null; markerPoints.Clear(); dronePathPoints.Clear(); isCoroutineRunning = false; if (lineSegment != null) { map.drawingElementManager.Remove(lineSegment); lineSegment?.Dispose(); lineSegment?.DestroyInstance(); lineSegment = null; times = 1; } // map.marker3DManager.RemoveAll(); DroneStatusUIHandler.Instance.goToMarker.circleAroundMarker.circleDrawn = false; } } public void GetMarkerLocation(GeoCoordinates geo) { if (MissionController.Instance.IsExecutionInProgress) { return; } if (geo != null) { if (greyMarkerLine != null) { if (greyMarkerLine.name.Equals("GreyLine")) { //map.drawingElementManager.Remove(greyMarkerLine); markerPoints.Clear(); } } geo = SkyGPS.GetCoordinates(geo.lat, geo.lon); markerPos = geo; initialDronePosition = DroneManager.Instance.currLocation; markerPoints.Add(new Vector2((float)initialDronePosition.lon, (float)initialDronePosition.lat)); markerPoints.Add(new Vector2((float)geo.lon, (float)geo.lat)); if (greyMarkerLine == null) { greyMarkerLine = new OnlineMapsDrawingLine(markerPoints, GCSUtils.GetColorUsingHexCode(HomeButtonColorCode.homeDroneMarkerLineColor), 2); greyMarkerLine.name = "GreyLine"; map.drawingElementManager.Add(greyMarkerLine); } else { map.needRedraw = true; } isCoroutineRunning = false; } } /*IEnumerator CheckDistanceInDelay() { isCoroutineRunning = true; while (DroneManager.Instance.currLocation != null && DroneManager.Instance.IsDroneFlying && initialDronePosition != null) { double? distance = OnlineMapsUtils.DistanceBetweenPoints(new Vector2((float)initialDronePosition.lon, (float)initialDronePosition.lat), new Vector2((float)DroneManager.Instance.currLocation.lon, (float)DroneManager.Instance.currLocation.lat)).magnitude; distance = SkyUtils.KmToMeter(distance); if (distance > distanceThreshold) { dronePathPoints.Add(new Vector2((float)initialDronePosition.lon, (float)initialDronePosition.lat)); dronePathPoints.Add(new Vector2((float)DroneManager.Instance.currLocation.lon, (float)DroneManager.Instance.currLocation.lat)); initialDronePosition = DroneManager.Instance.currLocation; OnlineMapsDrawingLine line = new OnlineMapsDrawingLine(dronePathPoints, Color.red, 2); map.drawingElementManager.Add(line); } double? distance2 = OnlineMapsUtils.DistanceBetweenPoints(new Vector2((float)markerPos.lon, (float)markerPos.lat), new Vector2((float)DroneManager.Instance.currLocation.lon, (float)DroneManager.Instance.currLocation.lat)).magnitude; distance2 = SkyUtils.KmToMeter(distance2); if (distance2 < 0.2) { dronePathPoints.Add(new Vector2((float)markerPos.lon, (float)markerPos.lat)); dronePathPoints.Add(new Vector2((float)DroneManager.Instance.currLocation.lon, (float)DroneManager.Instance.currLocation.lat)); OnlineMapsDrawingLine line = new OnlineMapsDrawingLine(dronePathPoints, Color.red, 2); map.drawingElementManager.Add(line); break; } yield return new WaitForSeconds(0.5f); } isCoroutineRunning = false; } *//// Not used public void EnableRedLine() { // Debug.Log("Red line operation" + isCoroutineRunning); if (!isCoroutineRunning) { if (redline != null) { StopCoroutine(redline); redline = null; } redline = StartCoroutine(DrawRedLine()); } } IEnumerator DrawRedLine() { while (DroneManager.Instance.currLocation == null) { yield return new WaitForSeconds(0.2f); } initialDronePosition = DroneManager.Instance.currLocation; isCoroutineRunning = true; Vector2 startPoint = new Vector2((float)initialDronePosition.lon, (float)initialDronePosition.lat); dronePathPoints.Add(startPoint); while (DroneManager.Instance.IsDroneFlying || DroneManager.Instance.droneState == EDroneState.LANDING || DroneStatusUIHandler.Instance.IsKillActivated) { double? distance = OnlineMapsUtils.DistanceBetweenPoints(new Vector2((float)initialDronePosition.lon, (float)initialDronePosition.lat), new Vector2((float)DroneManager.Instance.currLocation.lon, (float)DroneManager.Instance.currLocation.lat)).magnitude; distance = SkyUtils.KmToMeter(distance); if (distance > distanceThreshold) { Vector2 currPoint = new Vector2((float)DroneManager.Instance.currLocation.lon, (float)DroneManager.Instance.currLocation.lat); dronePathPoints.Add(currPoint); initialDronePosition = DroneManager.Instance.currLocation; // update for next iteration //if (lineSegment != null) //{ // map.drawingElementManager.Remove(lineSegment); // lineSegment?.Dispose(); // lineSegment?.DestroyInstance(); //} if (lineSegment == null) { lineSegment = new OnlineMapsDrawingLine(dronePathPoints, Color.red, 2); lineSegment.yOffset = 0.1f; lineSegment.OnInitMesh += tryOptimizeMsh; map.drawingElementManager.Add(lineSegment); } else { map.needRedraw = true; } if (dronePathPoints.Count >= segmentThreshold) { List segment = dronePathPoints.Take(segmentThreshold).ToList(); //for (int i = 0; i < 10000; i++) //{ // segment.AddRange(dronePathPoints); //} //lineSegment = new OnlineMapsDrawingLine(segment, Color.red, 2); //lineSegment.yOffset = 0.1f; //lineSegment.OnInitMesh += tryOptimizeMsh; //map.drawingElementManager.Add(lineSegment); dronePathPoints.RemoveRange(0, segmentThreshold - 1); Debug.Log(segment.Count + " segment count"); Mesh newSegmentMesh = CreateLineMesh(segment, 2f); if (combinedMesh == null) { // First segment, create base mesh combinedMesh = newSegmentMesh; CreateOrUpdateMarker(segment, combinedMesh); } else { CreateOrUpdateMarker2(segment, newSegmentMesh); } } } yield return new WaitForSeconds(0.5f); } isCoroutineRunning = false; // Debug.Log("Red line operation Ended" + isCoroutineRunning); } void UpdateMarkerMesh(Mesh mesh) { if (meshObject != null && marker != null) { meshObject.GetComponent().mesh = mesh; } } Vector2 centerLatLon; void CreateOrUpdateMarker(List segmentPoints, Mesh mesh) { if (meshObject != null) { Destroy(meshObject); } meshObject = new GameObject("DroneTrailMesh"); //meshObject.transform.rotation = Quaternion.identity; meshObject.AddComponent().mesh = mesh; meshObject.AddComponent().material = lineMaterial; meshObject.GetComponent().sortingOrder = 4; centerLatLon = GetMidpoint(segmentPoints); //meshObject.AddComponent().lat = centerLatLon.x; //meshObject.GetComponent().lng = centerLatLon.y; //meshObject.GetComponent().baseScale = meshObject.transform.localScale; meshObject.transform.position = LatLonToWorld(centerLatLon); //meshObject.gameObject.isStatic = true; // Anchor the mesh to the correct lat/lon marker = OnlineMapsMarker3DManager.CreateItemFromExistGameObject(centerLatLon.x, centerLatLon.y, meshObject); marker.sizeType = OnlineMapsMarker3D.SizeType.realWorld; marker.scale = 1f; marker.SetPosition(centerLatLon.x, centerLatLon.y); } public static Mesh CombineMeshesPreservePivot(Transform[] meshObjects, Transform pivotReference) { List combineInstances = new List(); foreach (Transform t in meshObjects) { MeshFilter mf = t.GetComponent(); if (mf == null || mf.sharedMesh == null) continue; CombineInstance ci = new CombineInstance(); ci.mesh = mf.sharedMesh; // Convert the mesh's transform into pivotReference's local space Matrix4x4 relativeMatrix = pivotReference.worldToLocalMatrix * t.localToWorldMatrix; ci.transform = relativeMatrix; combineInstances.Add(ci); } Mesh combinedMesh = new Mesh(); combinedMesh.CombineMeshes(combineInstances.ToArray(), true, true); return combinedMesh; } void CreateOrUpdateMarker2(List segmentPoints, Mesh mesh) { GameObject mesh2Object = new GameObject("DroneTrailMesh"); mesh2Object.transform.rotation = Quaternion.identity; mesh2Object.AddComponent().mesh = mesh; mesh2Object.AddComponent().material = lineMaterial; mesh2Object.GetComponent().sortingOrder = 4; Vector2 centerLatLon = GetMidpoint(segmentPoints); mesh2Object.transform.position = new Vector3 (LatLonToWorld(centerLatLon).x, LatLonToWorld(centerLatLon).y + 0.5f, LatLonToWorld(centerLatLon).z); mesh2Object.gameObject.isStatic = true; if (mesh2Object != null) { combinedMesh = CombineMeshesPreservePivot(new Transform[] { meshObject.transform, mesh2Object.transform }, meshObject.transform); UpdateMarkerMesh(combinedMesh); Destroy(mesh2Object); } } Vector2 GetMidpoint(List points) { double latSum = 0, lonSum = 0; foreach (var p in points) { latSum += p.y; lonSum += p.x; } return new Vector2((float)(lonSum / points.Count), (float)(latSum / points.Count)); } private void tryOptimizeMsh(OnlineMapsDrawingElement element, Renderer renderer) { renderer.sortingOrder = 4; renderer.gameObject.isStatic = true; } Mesh CreateLineMesh(List latLonPoints, float width = 1f) { int count = latLonPoints.Count; if (count < 2) return null; Vector2 centerLatLon = GetMidpoint(latLonPoints); Vector3 centerWorld = LatLonToWorld(centerLatLon); List vertices = new List(); List triangles = new List(); for (int i = 0; i < count - 1; i++) { Vector3 p1 = LatLonToWorld(latLonPoints[i]) - centerWorld; Vector3 p2 = LatLonToWorld(latLonPoints[i + 1]) - centerWorld; Vector3 dir = (p2 - p1).normalized; Vector3 normal = Vector3.Cross(dir, Vector3.up) * (width / 2); int v = vertices.Count; vertices.Add(p1 + normal); vertices.Add(p1 - normal); vertices.Add(p2 + normal); vertices.Add(p2 - normal); triangles.Add(v + 0); triangles.Add(v + 2); triangles.Add(v + 1); triangles.Add(v + 2); triangles.Add(v + 3); triangles.Add(v + 1); } Mesh mesh = new Mesh(); mesh.SetVertices(vertices); mesh.SetTriangles(triangles, 0); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } Vector3 LatLonToWorld(Vector2 latLon) { return OnlineMapsTileSetControl.instance.GetWorldPosition(latLon); } GameObject CreateStaticMeshObject(List latLonPoints) { GameObject meshObj = new GameObject("StaticTrailSegment"); MeshFilter mf = meshObj.AddComponent(); MeshRenderer mr = meshObj.AddComponent(); mf.mesh = CreateLineMesh(latLonPoints, 1f); mr.material = new Material(Shader.Find("Standard")) { color = Color.red }; return meshObj; } }