/// <summary>
        /// Detecta la colisión entre caja y plano
        /// </summary>
        /// <param name="box">Caja</param>
        /// <param name="plane">Plano</param>
        /// <param name="data">Datos de la colisión</param>
        /// <returns>Devuelve verdadero si hay colisión, o falso en el resto de los casos</returns>
        public static bool BoxAndHalfSpace(CollisionBox box, CollisionPlane plane, ref CollisionData data)
        {
            if (data.ContactsLeft <= 0)
            {
                // Si no hay más contactos disponibles se sale de la función.
                return(false);
            }

            // Comprobar la intersección
            if (!IntersectionTests.BoxAndHalfSpace(box, plane))
            {
                return(false);
            }

            // Hay intersección, ahora hay que encontrar los puntos de intersección.
            // Podemos hacerlo únicamente chequeando los vértices.
            // Si la caja está descansando sobre el plano o un eje, se reportarán cuatro o dos puntos de contacto.

            uint contactsUsed = 0;

            for (int i = 0; i < 8; i++)
            {
                // Calcular la positición de cada vértice
                Vector3 vertexPos = box.GetCorner(i);

                // Calcular la distancia al plano
                float vertexDistance = Vector3.Dot(vertexPos, plane.Normal);

                // Comparar las distancias
                if (vertexDistance <= plane.D)
                {
                    // Crear la información del contacto.

                    // El punto de contacto está a medio camino entre el vértice y el plano.
                    // Se obtiene multiplicando la dirección por la mitad de la distancia de separación, y añadiendo la posición del vértice.
                    Contact contact = data.CurrentContact;
                    contact.ContactPoint  = vertexPos;
                    contact.ContactNormal = plane.Normal;
                    contact.Penetration   = plane.D - vertexDistance;

                    // Establecer los datos del contacto
                    RigidBody one = box.Body;
                    RigidBody two = null;
                    contact.SetBodyData(ref one, ref two, data.Friction, data.Restitution);

                    // Añadir contacto
                    data.AddContact();

                    contactsUsed++;
                    if (contactsUsed == data.ContactsLeft)
                    {
                        return(true);
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Detecta la colisión entre caja y triángulo
        /// </summary>
        /// <param name="box">Caja</param>
        /// <param name="tri">Triángulo</param>
        /// <param name="data">Datos de la colisión</param>
        /// <returns>Devuelve verdadero si hay colisión, o falso en el resto de los casos</returns>
        private static bool BoxAndTriangle(CollisionBox box, Triangle tri, ref CollisionData data)
        {
            bool intersectionExists = false;

            if (data.ContactsLeft <= 0)
            {
                // Si no hay más contactos disponibles se sale de la función.
                return(false);
            }

            // Hay intersección, ahora hay que encontrar los puntos de intersección.
            // Podemos hacerlo únicamente chequeando los vértices.
            // Si la caja está descansando sobre el plano o un eje, se reportarán cuatro o dos puntos de contacto.
            for (int i = 0; i < 8; i++)
            {
                // Calcular la positición de cada vértice
                Vector3 vertexPos = box.GetCorner(i);

                // Calcular la distancia al plano
                float distanceToPlane = tri.Plane.Distance(vertexPos);
                if (distanceToPlane <= 0f)
                {
                    // Si la distancia es negativa está tras el plano. Si es 0, está en el plano

                    // Intersección entre línea y triángulo
                    Vector3 direction = Vector3.Normalize(box.Position - vertexPos);
                    Ray     r         = new Ray(vertexPos, direction);
                    if (IntersectionTests.TriAndRay(tri, r))
                    {
                        intersectionExists = true;

                        // Crear la información del contacto.

                        // El punto de contacto está a medio camino entre el vértice y el plano.
                        // Se obtiene multiplicando la dirección por la mitad de la distancia de separación, y añadiendo la posición del vértice.
                        Contact contact = data.CurrentContact;
                        contact.ContactPoint  = vertexPos;
                        contact.ContactNormal = tri.Plane.Normal;
                        contact.Penetration   = -distanceToPlane;

                        // Establecer los datos del contacto
                        RigidBody one = box;
                        RigidBody two = null;
                        contact.SetBodyData(ref one, ref two, data.Friction, data.Restitution);

                        // Añadir contacto
                        data.AddContact();

                        if (data.ContactsLeft <= 0)
                        {
                            return(true);
                        }
                    }
                }
            }

            return(intersectionExists);
        }
        /// <summary>
        /// Detecta la colisión entre una esfera y una lista de triángulos
        /// </summary>
        /// <param name="sphere">Esfera</param>
        /// <param name="triangleSoup">Lista de triángulos</param>
        /// <param name="data">Datos de la colisión</param>
        /// <returns>Devuelve verdadero si hay colisión, o falso en el resto de los casos</returns>
        public static bool SphereAndTriangleSoup(CollisionSphere sphere, CollisionTriangleSoup triangleSoup, ref CollisionData data)
        {
            if (data.ContactsLeft <= 0)
            {
                // Si no hay más contactos disponibles se sale de la función.
                return(false);
            }

            foreach (Triangle triangle in triangleSoup.Triangles)
            {
                // Comprobar la intersección
                if (IntersectionTests.SphereAndTri(sphere, triangle, true))
                {
                    // Informar la colisión
                    if (CollisionDetector.SphereAndHalfSpace(sphere, new CollisionPlane(triangle.Plane), ref data))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Obtiene si hay intersección entre el rayo y la lista de triángulos
        /// </summary>
        /// <param name="triangleList">Lista de triángulos</param>
        /// <param name="ray">Rayo</param>
        /// <param name="triangle">Devuelve el triángulo de intersección si existe</param>
        /// <param name="intersectionPoint">Devuelve el punto de intersección con el triángulo</param>
        /// <param name="distanceToPoint">Devuelve la distancia desde el origen del ray al punto de intersección</param>
        /// <param name="findNearest">Indica si se debe testear la intersección hasta encontrar el más cercano</param>
        /// <returns>Devuelve verdadero si hay intersección o falso en el resto de los casos</returns>
        public static bool TriangleListAndRay(Triangle[] triangleList, Ray ray, out Triangle?triangle, out Vector3?intersectionPoint, out float?distanceToPoint, bool findNearest)
        {
            triangle          = null;
            intersectionPoint = null;
            distanceToPoint   = null;

            // Indica si ha habido intersección
            bool intersectionOccur = false;

            // Variables para almacenar la interscción más cercana
            Triangle?closestTriangle          = null;
            Vector3? closestIntersectionPoint = null;
            float?   closestDistanceToPoint   = null;

            // Recorremos todos los triángulos de la lista realizando el test de intersección
            foreach (Triangle currentTriangle in triangleList)
            {
                // Variables para almacener la intersección actual
                Vector3?currentIntersectionPoint = null;
                float?  currentDistanceToPoint   = null;

                if (IntersectionTests.TriAndRay(currentTriangle, ray, out currentIntersectionPoint, out currentDistanceToPoint))
                {
                    bool update = false;

                    if (closestDistanceToPoint.HasValue)
                    {
                        // Comprobar si la distancia obtenida en la intersección es más cercana a la obtenida anteriormente
                        if (closestDistanceToPoint.Value > currentDistanceToPoint.Value)
                        {
                            // Actualizar la intersección más cercana
                            update = true;
                        }
                    }
                    else
                    {
                        // No hay intersección todavía, se debe actualizar la intersección más cercana con la obtenida
                        update = true;
                    }

                    if (update)
                    {
                        // Indicar que ha habido una intersección
                        intersectionOccur = true;

                        // Actualizar la información de intersección más cercana
                        closestTriangle          = currentTriangle;
                        closestIntersectionPoint = currentIntersectionPoint;
                        closestDistanceToPoint   = currentDistanceToPoint;

                        if (!findNearest)
                        {
                            // Salimos en la primera intersección
                            break;
                        }
                    }
                }
            }

            if (intersectionOccur)
            {
                // Si ha habido intersección se establece el resultado de la intersección más cercana
                triangle          = closestTriangle;
                intersectionPoint = closestIntersectionPoint;
                distanceToPoint   = closestDistanceToPoint;

                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Detecta la colisión entre una caja y una lista de triángulos
        /// </summary>
        /// <param name="box">Caja</param>
        /// <param name="triangleList">Lista de triángulos</param>
        /// <param name="data">Datos de colisión a llenar</param>
        /// <returns>Devuelve verdadero si existe colisión, falso en el resto de los casos</returns>
        private static bool BoxAndTriangleList(CollisionBox box, Triangle[] triangleList, ref CollisionData data)
        {
            if (data.ContactsLeft <= 0)
            {
                // Si no hay más contactos disponibles se sale de la función.
                return(false);
            }

            bool intersection = false;

            if (data.ContactsLeft > 0)
            {
                int firstContact = data.ContactCount;

                foreach (Triangle triangle in triangleList)
                {
                    // Comprobar la intersección con el triángulo
                    if (IntersectionTests.BoxAndTri(box, triangle))
                    {
                        if (data.ContactsLeft > 0)
                        {
                            if (CollisionDetector.BoxAndTriangle(box, triangle, ref data))
                            {
                                intersection = true;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                //int contactCount = data.ContactCount - firstContact;

                //if (intersection && contactCount > 1)
                //{
                //    //Agrupar los contactos
                //    Vector3 contactPoint = Vector3.Zero;
                //    Vector3 contactNormal = Vector3.Zero;
                //    float penetration = 0f;

                //    for (int i = firstContact; i < data.ContactCount; i++)
                //    {
                //        contactPoint += data.ContactArray[i].ContactPoint;
                //        contactNormal += data.ContactArray[i].ContactNormal;
                //        penetration += data.ContactArray[i].Penetration;
                //    }

                //    contactPoint /= contactCount;
                //    contactNormal /= contactCount;
                //    penetration /= contactCount;

                //    Contact newContact = new Contact();
                //    data.ContactArray[firstContact].ContactPoint = contactPoint;
                //    data.ContactArray[firstContact].ContactNormal = Vector3.Normalize(contactNormal);
                //    data.ContactArray[firstContact].Penetration = penetration;

                //    data.SetContactIndex(firstContact + 1);
                //}
            }

            return(intersection);
        }