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 placedObjects = new List(); private List distanceTexts = new List(); //Referencias a los botones public GameObject cleanButton; // Bot�n de Limpiar public GameObject undoButton; // Bot�n de Deshacer public GameObject confirmButton; // Bot�n de confirmaci�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�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�micamente // meshFilter = gameObject.AddComponent(); // meshRenderer = gameObject.AddComponent(); // 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�gono est� 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�gono para indicar cierre lineRenderer.material.color = Color.green; // Deshabilitar el ARPlacementInteractable para evitar m�s colocaciones aRPlacementInteractable.enabled = false; // Activar el bot�n de confirmaci�n confirmButton.SetActive(true); // Mostrar mensaje al usuario Debug.Log("Pol�gono cerrado. No se pueden agregar m�s puntos."); //FindObjectOfType().AddMessage("Pol�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�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�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 �ltimo punto colocado GameObject lastObject = placedObjects[placedObjects.Count - 1]; Destroy(lastObject); placedObjects.RemoveAt(placedObjects.Count - 1); // Eliminar el �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�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("�ltima acci�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�gono no est� cerrado."); return; } Debug.Log("Confirmaci�n completada: Solo se pueden colocar objetos dentro del �rea seleccionada."); // Generar el Mesh del pol�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�n //aRPlacementInteractable.enabled = true; // Detener la detecci�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�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().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(); if (raycastManager == null) { Debug.LogError("ARRaycastManager no encontrado en la nueva escena."); yield break; } foreach (var dragManager in FindObjectsOfType()) { dragManager.SetCamera(measuringCamera); dragManager.SetARRaycastManager(raycastManager); } foreach (var objectSelector in FindObjectsOfType()) { objectSelector.SetCamera(measuringCamera); objectSelector.SetARRaycastManager(raycastManager); } Debug.Log("C�mara y ARRaycastManager asignados a ARScreen."); //FindObjectOfType().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�n del objeto antes de colocarlo objectPlacementManager.PlaceObject(args.placementObject.transform.position); Destroy(args.placementObject); // Eliminar el objeto temporal si est� fuera del �rea } /** * * Metodos publicos para compartir datos a otros scripts * * **/ public bool IsPolygonClosed() { return isPolygonClosed; } public List GetPolygonPoints() { List points = new List(); for (int i = 0; i < lineRenderer.positionCount; i++) { points.Add(lineRenderer.GetPosition(i)); } return points; } public bool IsPointInPolygon(Vector2 point, List 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� 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 polygonPoints) { if (polygonPoints.Count < 3) { Debug.LogError("El pol�gono debe tener al menos 3 puntos."); return; } // Crear un nuevo Mesh Mesh mesh = new Mesh(); // Convertir los puntos del pol�gono a un array de Vector3 Vector3[] vertices = polygonPoints.ToArray(); // Generar �ndices para triangulaci�n (simple para pol�gonos convexos) List indices = new List(); 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�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 }