Exemplo n.º 1
0
        /// <summary>
        /// Llena la información de colisión entre dos cajas, una vez se conoce que hay contacto del tipo vértice - cara
        /// </summary>
        /// <param name="one">Caja uno</param>
        /// <param name="two">Caja dos</param>
        /// <param name="toCentre">Distancia entre centros</param>
        /// <param name="data">Información de colisión</param>
        /// <param name="best">Eje de penetración menor</param>
        /// <param name="pen">Pentración menor</param>
        private static void FillPointFaceBoxBox(CollisionBox one, CollisionBox two, Vector3 toCentre, uint best, float pen, ref CollisionData data)
        {
            // Sabemos cual es el eje de la colisión, pero tenemos que conocer con qué cara tenemos que trabjar
            Vector3 normal = one.GetAxis((TransformAxis)best);

            if (Vector3.Dot(one.GetAxis((TransformAxis)best), toCentre) > 0f)
            {
                normal = normal * -1.0f;
            }

            // Obtenemos el vértice
            Vector3 vertex = two.HalfSize;

            if (Vector3.Dot(two.XAxis, normal) < 0f)
            {
                vertex.X = -vertex.X;
            }
            if (Vector3.Dot(two.YAxis, normal) < 0f)
            {
                vertex.Y = -vertex.Y;
            }
            if (Vector3.Dot(two.ZAxis, normal) < 0f)
            {
                vertex.Z = -vertex.Z;
            }

            Contact contact = data.CurrentContact;

            contact.ContactNormal = normal;
            contact.Penetration   = pen;
            contact.ContactPoint  = Vector3.Transform(vertex, two.Transform);
            contact.SetBodyData(ref one.Body, ref two.Body, data.Friction, data.Restitution);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Detecta la colisión entre cajas
        /// </summary>
        /// <param name="one">Caja uno</param>
        /// <param name="two">Caja dos</param>
        /// <param name="data">Datos de colisión</param>
        /// <returns>Devuelve verdadero si hay colisión, o falso en el resto de los casos</returns>
        public static bool BoxAndBox(CollisionBox one, CollisionBox two, ref CollisionData data)
        {
            if (data.ContactsLeft <= 0)
            {
                // Si no hay más contactos disponibles se sale de la función.
                return(false);
            }

            // Encontrar el vector entre los dos centros
            Vector3 toCentre = two.Position - one.Position;

            // Se asume que no hay contacto
            float pen  = float.MaxValue;
            uint  best = uint.MaxValue;

            // Chequear cada eje, almacenando penetración y el mejor eje
            if (!TryAxis(one, two, one.XAxis, toCentre, 0, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, one.YAxis, toCentre, 1, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, one.ZAxis, toCentre, 2, ref pen, ref best))
            {
                return(false);
            }

            if (!TryAxis(one, two, two.XAxis, toCentre, 3, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, two.YAxis, toCentre, 4, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, two.ZAxis, toCentre, 5, ref pen, ref best))
            {
                return(false);
            }

            // Almacenar el mejor eje hasta ahora, en el caso de estar en una colisión de ejes paralelos más adelante.
            uint bestSingleAxis = best;

            if (!TryAxis(one, two, Vector3.Cross(one.XAxis, two.XAxis), toCentre, 6, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.XAxis, two.YAxis), toCentre, 7, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.XAxis, two.ZAxis), toCentre, 8, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.YAxis, two.XAxis), toCentre, 9, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.YAxis, two.YAxis), toCentre, 10, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.YAxis, two.ZAxis), toCentre, 11, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.ZAxis, two.XAxis), toCentre, 12, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.ZAxis, two.YAxis), toCentre, 13, ref pen, ref best))
            {
                return(false);
            }
            if (!TryAxis(one, two, Vector3.Cross(one.ZAxis, two.ZAxis), toCentre, 14, ref pen, ref best))
            {
                return(false);
            }

            // Asegurarse de que tenemos un resultado.
            if (best != uint.MaxValue)
            {
                // Tenemos colisión, y tenemos el eje de colisión con menor penetración
                if (best < 3)
                {
                    // Hay un vértice la caja dos en una cara de la caja uno.
                    FillPointFaceBoxBox(one, two, toCentre, best, pen, ref data);

                    data.AddContact();

                    return(true);
                }
                else if (best < 6)
                {
                    // Hay un vértice de la caja uno en una cara de la caja dos.
                    FillPointFaceBoxBox(two, one, toCentre * -1.0f, best - 3, pen, ref data);

                    data.AddContact();

                    return(true);
                }
                else
                {
                    // Contacto canto a canto. Obtener el eje común.
                    best -= 6;
                    uint    oneAxisIndex = best / 3;
                    uint    twoAxisIndex = best % 3;
                    Vector3 oneAxis      = one.GetAxis((TransformAxis)oneAxisIndex);
                    Vector3 twoAxis      = two.GetAxis((TransformAxis)twoAxisIndex);
                    Vector3 axis         = Vector3.Cross(oneAxis, twoAxis);
                    axis.Normalize();

                    // El eje debería apuntar desde la caja uno a la dos.
                    if (Vector3.Dot(axis, toCentre) > 0f)
                    {
                        axis = axis * -1.0f;
                    }

                    // Tenemos los ejes, pero no los cantos.

                    // Cada eje tiene 4 cantos paralelos a él, tenemos que encontrar los 4 de cada caja.
                    // Buscaremos el punto en el centro del canto. Sabemos que su componente en el eje de colisión es 0 y
                    // determinamos cual de los extremos en cada uno de los otros ejes es el más cercano.
                    Vector3 vOne        = one.HalfSize;
                    Vector3 vTwo        = two.HalfSize;
                    float[] ptOnOneEdge = new float[] { vOne.X, vOne.Y, vOne.Z };
                    float[] ptOnTwoEdge = new float[] { vTwo.X, vTwo.Y, vTwo.Z };
                    for (uint i = 0; i < 3; i++)
                    {
                        if (i == oneAxisIndex)
                        {
                            ptOnOneEdge[i] = 0;
                        }
                        else if (Vector3.Dot(one.GetAxis((TransformAxis)i), axis) > 0f)
                        {
                            ptOnOneEdge[i] = -ptOnOneEdge[i];
                        }

                        if (i == twoAxisIndex)
                        {
                            ptOnTwoEdge[i] = 0;
                        }
                        else if (Vector3.Dot(two.GetAxis((TransformAxis)i), axis) < 0f)
                        {
                            ptOnTwoEdge[i] = -ptOnTwoEdge[i];
                        }
                    }

                    vOne.X = ptOnOneEdge[0];
                    vOne.Y = ptOnOneEdge[1];
                    vOne.Z = ptOnOneEdge[2];

                    vTwo.X = ptOnTwoEdge[0];
                    vTwo.Y = ptOnTwoEdge[1];
                    vTwo.Z = ptOnTwoEdge[2];

                    // Pasar a coordenadas del mundo
                    vOne = Vector3.Transform(vOne, one.Transform);
                    vTwo = Vector3.Transform(vTwo, two.Transform);

                    // Tenemos un punto y una dirección para los cantos que colisionan.
                    // Necesitamos encontrar el punto de mayor cercanía de los dos segmentos.
                    float[] vOneAxis = new float[] { one.HalfSize.X, one.HalfSize.Y, one.HalfSize.Z };
                    float[] vTwoAxis = new float[] { two.HalfSize.X, two.HalfSize.Y, two.HalfSize.Z };
                    Vector3 vertex   = ContactPoint(
                        vOne, oneAxis, vOneAxis[oneAxisIndex],
                        vTwo, twoAxis, vTwoAxis[twoAxisIndex],
                        bestSingleAxis > 2);

                    // Llenar el contacto.
                    Contact contact = data.CurrentContact;
                    contact.Penetration   = pen;
                    contact.ContactNormal = axis;
                    contact.ContactPoint  = vertex;
                    contact.SetBodyData(ref one.Body, ref two.Body, data.Friction, data.Restitution);

                    data.AddContact();

                    return(true);
                }
            }

            return(false);
        }