433 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.Collections.Generic;
 | ||
| using TMPro;
 | ||
| using UnityEngine;
 | ||
| using UnityEngine.SceneManagement;
 | ||
| using UnityEngine.XR.ARFoundation;
 | ||
| using UnityEngine.XR.Interaction.Toolkit.AR;
 | ||
| 
 | ||
| public class LineManager : MonoBehaviour
 | ||
| {
 | ||
|     //Creamos una referencia de 'LineaObjeto'
 | ||
|     public LineRenderer lineRenderer;
 | ||
|     //Creamos una referencia a nuestro AR Placement Interactable
 | ||
|     public ARPlacementInteractable aRPlacementInteractable;
 | ||
|     //Creamos una referencia al TextoDistancia
 | ||
|     public TextMeshPro mText;
 | ||
|     //Creamos unbooleano para delimitar al usuario cuando cierre la forma de los poligonos
 | ||
|     private bool isPolygonClosed = false;
 | ||
| 
 | ||
|     // Listas para los objetos y textos colocados
 | ||
|     private List<GameObject> placedObjects = new List<GameObject>();
 | ||
|     private List<TextMeshPro> distanceTexts = new List<TextMeshPro>();
 | ||
| 
 | ||
|     //Referencias a los botones
 | ||
|     public GameObject cleanButton; // Bot<6F>n de Limpiar
 | ||
|     public GameObject undoButton; // Bot<6F>n de Deshacer
 | ||
|     public GameObject confirmButton; // Bot<6F>n de confirmaci<63>n
 | ||
| 
 | ||
|     //Referencia
 | ||
|     public ObjectPlacementManager objectPlacementManager; // Referencia al script ObjectPlacementManager.cs
 | ||
| 
 | ||
|     [SerializeField] private ARPlaneManager arPlaneManager; // Referencia al ARPlaneManager
 | ||
|     [SerializeField] private Material fillMaterial; // Material con la textura
 | ||
|     private MeshFilter meshFilter;
 | ||
|     private MeshRenderer meshRenderer;
 | ||
| 
 | ||
| 
 | ||
|     public ARRaycastManager arRaycastManager; // Aseg<65>rate de asignar esto en el Inspector
 | ||
| 
 | ||
|     // Start is called before the first frame update
 | ||
|     void Start()
 | ||
|     {
 | ||
|         //aRPlacementInteractable.objectPlaced.AddListener(DrawLine);
 | ||
| 
 | ||
|         // Configurar MeshFilter y MeshRenderer din<69>micamente
 | ||
|         // meshFilter = gameObject.AddComponent<MeshFilter>();
 | ||
|         // meshRenderer = gameObject.AddComponent<MeshRenderer>();
 | ||
|         // meshRenderer.material = fillMaterial; // Asignar el material en el Inspector
 | ||
|         // meshRenderer.enabled = false; // Ocultar el MeshRenderer hasta que se confirme
 | ||
|         SceneManager.LoadScene("ARScreen", LoadSceneMode.Additive);
 | ||
|         StartCoroutine(AssignManagersToARScreen());
 | ||
|     }
 | ||
|     void DrawLine(ARObjectPlacementEventArgs args)
 | ||
|     {
 | ||
|         //Si esta cerrado el poligono entonces dejamos de dibujar lineas
 | ||
|         if (isPolygonClosed) return; // Detenemos si el pol<6F>gono est<73> cerrado
 | ||
| 
 | ||
|         // Colocar punto
 | ||
|         placedObjects.Add(args.placementObject); // Registrar el objeto colocado
 | ||
| 
 | ||
| 
 | ||
|         //increase point count
 | ||
|         lineRenderer.positionCount++;
 | ||
| 
 | ||
|         //2. let the points location in the line renderer
 | ||
|         lineRenderer.SetPosition(
 | ||
|             index: lineRenderer.positionCount - 1,//La linea se crea desde el objeto colocado anteriormente
 | ||
|             args.placementObject.transform.position//Hasta el nuevo objeto colocado
 | ||
|         );
 | ||
|         //Mientras tengamos una linea o mas de un punto, entonces se puede crear el texto de medicion
 | ||
|         if (lineRenderer.positionCount > 1)
 | ||
|         {
 | ||
|             //Distancia del ultimo punto colocado
 | ||
|             Vector3 pointA = lineRenderer.GetPosition(index: lineRenderer.positionCount - 1);
 | ||
|             //Distancia del punto anterior al ultimo colocado (penultimo punto)
 | ||
|             Vector3 pointB = lineRenderer.GetPosition(index: lineRenderer.positionCount - 2);
 | ||
|             //Objtenem,os la distancia entre los dos puntos
 | ||
|             float distancia = Vector3.Distance(pointA, pointB);
 | ||
|             //Colocamos el texto de la distancia en TextoDistancia se mostrara en cm ejemplo de salida: 10.009 cm
 | ||
|             //mText.text = (distancia * 100).ToString("0.000") + " cm";
 | ||
|             //crearemos un TextoDistancia en cada linea cada vez que se genere una. 
 | ||
|             TextMeshPro textoDistanciaTemp = Instantiate(mText);
 | ||
|             textoDistanciaTemp.text = (distancia * 100).ToString("0.000") + " cm";
 | ||
|             //aplicamos un formato al texto para que se coloque encima de la linea
 | ||
|             Vector3 directionVector = (pointB - pointA);
 | ||
|             Vector3 normal = args.placementObject.transform.up;
 | ||
| 
 | ||
|             Vector3 upd = Vector3.Cross(
 | ||
|                 lhs: directionVector,
 | ||
|                 rhs: normal).normalized;
 | ||
|             //calculamos la rotacion del texto
 | ||
|             Quaternion rotation = Quaternion.LookRotation(forward: -normal, upwards: upd);
 | ||
|             //aplicamos la posicion y rotacion al texto
 | ||
|             textoDistanciaTemp.transform.position = (pointA + directionVector * 0.5f) + upd * 0.05f;
 | ||
|             textoDistanciaTemp.transform.rotation = rotation;
 | ||
| 
 | ||
|             distanceTexts.Add(textoDistanciaTemp); // Registrar el texto de distancia
 | ||
|         }
 | ||
|         //Si los botones de limpiar y deshacer estan desactivados alcolocar una linea u objeto, entonces los activamos
 | ||
|         if (!cleanButton.activeSelf && !undoButton.activeSelf)
 | ||
|         {
 | ||
|             //Agregamos los botones a la pantalla al agregar un punto
 | ||
|             cleanButton.SetActive(true);
 | ||
|             undoButton.SetActive(true);
 | ||
|         }
 | ||
|         //Verificamos si es posiblie cerrar el poligono
 | ||
|         CheckPolygonClosure();
 | ||
|     }
 | ||
| 
 | ||
|     //Este metodo determina si se pueden continuar insertando o no mas lineas
 | ||
|     private void CheckPolygonClosure()
 | ||
|     {
 | ||
|         if (lineRenderer.positionCount > 2)
 | ||
|         {
 | ||
|             Vector3 firstPoint = lineRenderer.GetPosition(0);
 | ||
|             Vector3 lastPoint = lineRenderer.GetPosition(lineRenderer.positionCount - 1);
 | ||
| 
 | ||
|             float distanceToFirst = Vector3.Distance(lastPoint, firstPoint);
 | ||
| 
 | ||
|             if (distanceToFirst < 0.03f) // Define el margen de cierre en 3 cm
 | ||
|             {
 | ||
|                 isPolygonClosed = true;//En caso de que la proxima linea este cerca de la primera entonces volvemos true
 | ||
| 
 | ||
|                 // Cambiar color del pol<6F>gono para indicar cierre
 | ||
|                 lineRenderer.material.color = Color.green;
 | ||
| 
 | ||
|                 // Deshabilitar el ARPlacementInteractable para evitar m<>s colocaciones
 | ||
|                 aRPlacementInteractable.enabled = false;
 | ||
| 
 | ||
|                 // Activar el bot<6F>n de confirmaci<63>n
 | ||
|                 confirmButton.SetActive(true);
 | ||
| 
 | ||
| 
 | ||
|                 // Mostrar mensaje al usuario
 | ||
|                 Debug.Log("Pol<6F>gono cerrado. No se pueden agregar m<>s puntos.");
 | ||
|                 //FindObjectOfType<DebugLoggerUI>().AddMessage("Pol<6F>gono cerrado. No se pueden agregar m<>s puntos.");
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //Este metodo limpia los poligonos de la camara
 | ||
|     public void ResetPolygon()
 | ||
|     {
 | ||
|         if (isPolygonClosed)
 | ||
|         {
 | ||
|             //Quitamos de pantalla boton confirmar
 | ||
|             confirmButton.SetActive(false);
 | ||
|         }
 | ||
|         // Reiniciar la l<>gica del pol<6F>gono
 | ||
|         isPolygonClosed = false;
 | ||
|         aRPlacementInteractable.enabled = true;
 | ||
| 
 | ||
|         // Limpiar las l<>neas
 | ||
|         lineRenderer.positionCount = 0;
 | ||
|         lineRenderer.material.color = Color.red;
 | ||
| 
 | ||
|         // Destruir objetos colocados
 | ||
|         foreach (GameObject obj in placedObjects)
 | ||
|         {
 | ||
|             Destroy(obj);
 | ||
|         }
 | ||
|         placedObjects.Clear();
 | ||
| 
 | ||
|         // Destruir textos de distancia
 | ||
|         foreach (TextMeshPro text in distanceTexts)
 | ||
|         {
 | ||
|             Destroy(text.gameObject);
 | ||
|         }
 | ||
|         distanceTexts.Clear();
 | ||
|         //Quitamos los botones de limpiar y deshacer de la pantalla
 | ||
|         cleanButton.SetActive(false);
 | ||
|         undoButton.SetActive(false);
 | ||
| 
 | ||
|         Debug.Log("Pol<6F>gono reiniciado.");
 | ||
|     }
 | ||
| 
 | ||
|     //Este metodo se encarga de deshacer los cambios del usuario
 | ||
|     public void UndoLastAction()
 | ||
|     {
 | ||
|         // Verificar que haya puntos para deshacer
 | ||
|         if (placedObjects.Count > 0)
 | ||
|         {
 | ||
|             // Eliminar el <20>ltimo punto colocado
 | ||
|             GameObject lastObject = placedObjects[placedObjects.Count - 1];
 | ||
|             Destroy(lastObject);
 | ||
|             placedObjects.RemoveAt(placedObjects.Count - 1);
 | ||
| 
 | ||
|             // Eliminar el <20>ltimo texto de distancia, si existe
 | ||
|             if (distanceTexts.Count > 0)
 | ||
|             {
 | ||
|                 TextMeshPro lastText = distanceTexts[distanceTexts.Count - 1];
 | ||
|                 Destroy(lastText.gameObject);
 | ||
|                 distanceTexts.RemoveAt(distanceTexts.Count - 1);
 | ||
|             }
 | ||
| 
 | ||
|             // Actualizar el LineRenderer
 | ||
|             if (lineRenderer.positionCount > 0)
 | ||
|             {
 | ||
|                 lineRenderer.positionCount--; // Reduce el conteo de puntos
 | ||
| 
 | ||
|                 // Si el poligono ya estaba cerrado lo volvemos a habilitar
 | ||
|                 if (isPolygonClosed)
 | ||
|                 {
 | ||
|                     isPolygonClosed = false;
 | ||
|                     aRPlacementInteractable.enabled = true;
 | ||
|                     lineRenderer.material.color = Color.red;// Cambiar color del pol<6F>gono
 | ||
|                     //Quitamos de pantalla boton confirmar
 | ||
|                     confirmButton.SetActive(false);
 | ||
|                 }
 | ||
| 
 | ||
|             }
 | ||
|             //Si no tenemos ningun objeto entonces quitamos los botones de limpiar
 | ||
|             if (placedObjects.Count == 0)
 | ||
|             {
 | ||
|                 //Agregamos los botones a la pantalla al agregar un punto
 | ||
|                 cleanButton.SetActive(false);
 | ||
|                 undoButton.SetActive(false);
 | ||
|             }
 | ||
|             Debug.Log("<22>ltima acci<63>n deshecha.");
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             Debug.Log("No hay acciones para deshacer.");
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //Confirmar el seteo de objetos
 | ||
|     public void ConfirmPlacement()
 | ||
|     {
 | ||
|         if (!isPolygonClosed)
 | ||
|         {
 | ||
|             Debug.Log("No se puede confirmar: el pol<6F>gono no est<73> cerrado.");
 | ||
|             return;
 | ||
|         }
 | ||
| 
 | ||
|         Debug.Log("Confirmaci<63>n completada: Solo se pueden colocar objetos dentro del <20>rea seleccionada.");
 | ||
| 
 | ||
|         // Generar el Mesh del pol<6F>gono
 | ||
|         GeneratePolygonMesh(GetPolygonPoints());
 | ||
| 
 | ||
|         // Cambiar la l<>gica del ARPlacementInteractable
 | ||
|         aRPlacementInteractable.objectPlaced.RemoveListener(DrawLine); // Quitar la l<>gica de dibujo
 | ||
|         aRPlacementInteractable.objectPlaced.AddListener(ValidatePlacement); // Agregar la validaci<63>n
 | ||
|         //aRPlacementInteractable.enabled = true;
 | ||
| 
 | ||
|         // Detener la detecci<63>n de planos y ocultar los existentes
 | ||
|         if (arPlaneManager != null)
 | ||
|         {
 | ||
|             arPlaneManager.enabled = false;
 | ||
| 
 | ||
|             foreach (var plane in arPlaneManager.trackables)
 | ||
|             {
 | ||
|                 plane.gameObject.SetActive(false);
 | ||
|             }
 | ||
|             Debug.Log("Detecci<63>n de planos detenida y planos ocultos.");
 | ||
|         }
 | ||
| 
 | ||
|         //Quitamos los botones de la pantalla
 | ||
|         cleanButton.SetActive(false);
 | ||
|         undoButton.SetActive(false);
 | ||
|         confirmButton.SetActive(false);
 | ||
| 
 | ||
|         // Cargar ARScreen en modo aditivo
 | ||
|         SceneManager.LoadScene("ARScreen", LoadSceneMode.Additive);
 | ||
|         Debug.Log("ARScreen cargado en modo aditivo.");
 | ||
|         //FindObjectOfType<DebugLoggerUI>().AddMessage("ARScreen cargado en modo aditivo.");
 | ||
| 
 | ||
|         // Asignar la c<>mara y el ARRaycastManager a los scripts en ARScreen
 | ||
|         StartCoroutine(AssignManagersToARScreen());
 | ||
|     }
 | ||
| 
 | ||
|     private System.Collections.IEnumerator AssignManagersToARScreen()
 | ||
|     {
 | ||
|         yield return null; // Esperar un frame para que ARScreen se cargue
 | ||
| 
 | ||
|         Camera measuringCamera = Camera.main;
 | ||
| 
 | ||
|         if (measuringCamera == null)
 | ||
|         {
 | ||
|             Debug.LogError("C<>mara principal no encontrada en la nueva escena.");
 | ||
|             yield break;
 | ||
|         }
 | ||
| 
 | ||
|         ARRaycastManager raycastManager = FindObjectOfType<ARRaycastManager>();
 | ||
|         if (raycastManager == null)
 | ||
|         {
 | ||
|             Debug.LogError("ARRaycastManager no encontrado en la nueva escena.");
 | ||
|             yield break;
 | ||
|         }
 | ||
| 
 | ||
|         foreach (var dragManager in FindObjectsOfType<DragAndDropManager>())
 | ||
|         {
 | ||
|             dragManager.SetCamera(measuringCamera);
 | ||
|             dragManager.SetARRaycastManager(raycastManager);
 | ||
|         }
 | ||
| 
 | ||
|         foreach (var objectSelector in FindObjectsOfType<ObjectSelector>())
 | ||
|         {
 | ||
|             objectSelector.SetCamera(measuringCamera);
 | ||
|             objectSelector.SetARRaycastManager(raycastManager);
 | ||
|         }
 | ||
| 
 | ||
|         Debug.Log("C<>mara y ARRaycastManager asignados a ARScreen.");
 | ||
|         //FindObjectOfType<DebugLoggerUI>().AddMessage("C<>mara y ARRaycastManager asignados a ARScreen.");
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|     #region Limitar area de seto de objetos
 | ||
|     void ValidatePlacement(ARObjectPlacementEventArgs args)
 | ||
|     {
 | ||
|         Debug.Log($"Intentando colocar objeto en: {args.placementObject.transform.position}");
 | ||
|         // Validar la posici<63>n del objeto antes de colocarlo
 | ||
|         objectPlacementManager.PlaceObject(args.placementObject.transform.position);
 | ||
|         Destroy(args.placementObject); // Eliminar el objeto temporal si est<73> fuera del <20>rea
 | ||
|     }
 | ||
|     /**
 | ||
|      * 
 | ||
|      * Metodos publicos para compartir datos a otros scripts
 | ||
|      * 
 | ||
|      * **/
 | ||
|     public bool IsPolygonClosed()
 | ||
|     {
 | ||
|         return isPolygonClosed;
 | ||
|     }
 | ||
| 
 | ||
|     public List<Vector3> GetPolygonPoints()
 | ||
|     {
 | ||
|         List<Vector3> points = new List<Vector3>();
 | ||
|         for (int i = 0; i < lineRenderer.positionCount; i++)
 | ||
|         {
 | ||
|             points.Add(lineRenderer.GetPosition(i));
 | ||
|         }
 | ||
|         return points;
 | ||
|     }
 | ||
| 
 | ||
|     public bool IsPointInPolygon(Vector2 point, List<Vector3> polygon)
 | ||
|     {
 | ||
|         int intersections = 0;
 | ||
|         for (int i = 0; i < polygon.Count; i++)
 | ||
|         {
 | ||
|             Vector3 vertex1 = polygon[i];
 | ||
|             Vector3 vertex2 = polygon[(i + 1) % polygon.Count];
 | ||
| 
 | ||
|             if (RayIntersectsSegment(point, new Vector2(vertex1.x, vertex1.z), new Vector2(vertex2.x, vertex2.z)))
 | ||
|             {
 | ||
|                 intersections++;
 | ||
|             }
 | ||
|         }
 | ||
|         Debug.DrawLine(new Vector3(point.x, 0, point.y), new Vector3(point.x, 1, point.y), Color.red, 5f);
 | ||
| 
 | ||
|         return (intersections % 2) != 0; // Punto est<73> dentro si intersecciones es impar
 | ||
|     }
 | ||
| 
 | ||
|     private bool RayIntersectsSegment(Vector2 point, Vector2 vertex1, Vector2 vertex2)
 | ||
|     {
 | ||
|         // Checa si el rayo desde el punto cruza el segmento
 | ||
|         if (vertex1.y > vertex2.y)
 | ||
|         {
 | ||
|             Vector2 temp = vertex1;
 | ||
|             vertex1 = vertex2;
 | ||
|             vertex2 = temp;
 | ||
|         }
 | ||
| 
 | ||
|         if (point.y == vertex1.y || point.y == vertex2.y)
 | ||
|         {
 | ||
|             //point.y += 0.0001f; // Evita bordes exactos
 | ||
|             point.y += 0.001f; // Ajusta este valor para evitar problemas en bordes
 | ||
|         }
 | ||
| 
 | ||
|         if (point.y < vertex1.y || point.y > vertex2.y || point.x > Mathf.Max(vertex1.x, vertex2.x))
 | ||
|         {
 | ||
|             return false;
 | ||
|         }
 | ||
| 
 | ||
|         if (point.x < Mathf.Min(vertex1.x, vertex2.x))
 | ||
|         {
 | ||
|             return true;
 | ||
|         }
 | ||
| 
 | ||
|         float red = (point.y - vertex1.y) / (vertex2.y - vertex1.y);
 | ||
|         float blue = (point.x - vertex1.x) / (vertex2.x - vertex1.x);
 | ||
|         return red >= blue;
 | ||
|     }
 | ||
|     #endregion
 | ||
| 
 | ||
|     #region pintar poligono
 | ||
|     private void GeneratePolygonMesh(List<Vector3> polygonPoints)
 | ||
|     {
 | ||
|         if (polygonPoints.Count < 3)
 | ||
|         {
 | ||
|             Debug.LogError("El pol<6F>gono debe tener al menos 3 puntos.");
 | ||
|             return;
 | ||
|         }
 | ||
| 
 | ||
|         // Crear un nuevo Mesh
 | ||
|         Mesh mesh = new Mesh();
 | ||
| 
 | ||
|         // Convertir los puntos del pol<6F>gono a un array de Vector3
 | ||
|         Vector3[] vertices = polygonPoints.ToArray();
 | ||
| 
 | ||
|         // Generar <20>ndices para triangulaci<63>n (simple para pol<6F>gonos convexos)
 | ||
|         List<int> indices = new List<int>();
 | ||
|         for (int i = 1; i < polygonPoints.Count - 1; i++)
 | ||
|         {
 | ||
|             indices.Add(0);
 | ||
|             indices.Add(i);
 | ||
|             indices.Add(i + 1);
 | ||
|         }
 | ||
| 
 | ||
|         // Asignar los v<>rtices y tri<72>ngulos al Mesh
 | ||
|         mesh.vertices = vertices;
 | ||
|         mesh.triangles = indices.ToArray();
 | ||
| 
 | ||
|         // Generar UVs para aplicar la textura
 | ||
|         Vector2[] uvs = new Vector2[vertices.Length];
 | ||
|         for (int i = 0; i < vertices.Length; i++)
 | ||
|         {
 | ||
|             uvs[i] = new Vector2(vertices[i].x, vertices[i].z); // UV mapping en XZ
 | ||
|         }
 | ||
|         mesh.uv = uvs;
 | ||
| 
 | ||
|         // Asignar el Mesh al MeshFilter
 | ||
|         mesh.RecalculateNormals();
 | ||
|         mesh.RecalculateBounds();
 | ||
|         meshFilter.mesh = mesh;
 | ||
| 
 | ||
|         // Activar el MeshRenderer para mostrar la textura
 | ||
|         meshRenderer.enabled = true;
 | ||
|     }
 | ||
|     #endregion
 | ||
| 
 | ||
| }
 | 
