/// <summary>
        /// Detección de colisiones recursiva
        /// </summary>
        public void doCollideWithWorld(TgcBoundingSphere characterSphere, Vector3 movementVector, List<IColisionable> obstaculos, int recursionDepth, ColisionInfo colisionInfo)
        {
            //Limitar recursividad
            if (recursionDepth > 5)
            {
                return;
            }

            //Ver si la distancia a recorrer es para tener en cuenta
            float distanceToTravelSq = movementVector.LengthSq();

            if (distanceToTravelSq < EPSILON)
            {
                return;
            }

            //Posicion deseada
            Vector3 originalSphereCenter = characterSphere.Center;
            Vector3 nextSphereCenter = originalSphereCenter + movementVector;

            //Buscar el punto de colision mas cercano de todos los objetos candidatos
            float minCollisionDistSq = float.MaxValue;
            Vector3 realMovementVector = movementVector;
            TgcBoundingAxisAlignBox.Face collisionFace = null;
            IColisionable collisionObstacle = null;
            Vector3 nearestPolygonIntersectionPoint = Vector3.Empty;
            foreach (IColisionable obstaculoBB in obstaculos)
            {
                //Obtener los polígonos que conforman las 6 caras del BoundingBox
                TgcBoundingAxisAlignBox.Face[] bbFaces = obstaculoBB.GetTgcBoundingBox().computeFaces();

                foreach (TgcBoundingAxisAlignBox.Face bbFace in bbFaces)
                {
                    Vector3 pNormal = TgcCollisionUtils.getPlaneNormal(bbFace.Plane);

                    TgcRay movementRay = new TgcRay(originalSphereCenter, movementVector);
                    float brutePlaneDist;
                    Vector3 brutePlaneIntersectionPoint;
                    if (!TgcCollisionUtils.intersectRayPlane(movementRay, bbFace.Plane, out brutePlaneDist, out brutePlaneIntersectionPoint))
                    {
                        continue;
                    }

                    float movementRadiusLengthSq = Vector3.Multiply(movementVector, characterSphere.Radius).LengthSq();
                    if (brutePlaneDist * brutePlaneDist > movementRadiusLengthSq)
                    {
                        continue;
                    }

                    //Obtener punto de colisión en el plano, según la normal del plano
                    float pDist;
                    Vector3 planeIntersectionPoint;
                    Vector3 sphereIntersectionPoint;
                    TgcRay planeNormalRay = new TgcRay(originalSphereCenter, -pNormal);
                    bool embebbed = false;
                    bool collisionFound = false;
                    if (TgcCollisionUtils.intersectRayPlane(planeNormalRay, bbFace.Plane, out pDist, out planeIntersectionPoint))
                    {
                        //Ver si el plano está embebido en la esfera
                        if (pDist <= characterSphere.Radius)
                        {
                            embebbed = true;

                            //TODO: REVISAR ESTO, caso embebido a analizar con más detalle
                            sphereIntersectionPoint = originalSphereCenter - pNormal * characterSphere.Radius;
                        }
                        //Esta fuera de la esfera
                        else
                        {
                            //Obtener punto de colisión del contorno de la esfera según la normal del plano
                            sphereIntersectionPoint = originalSphereCenter - Vector3.Multiply(pNormal, characterSphere.Radius);

                            //Disparar un rayo desde el contorno de la esfera hacia el plano, con el vector de movimiento
                            TgcRay sphereMovementRay = new TgcRay(sphereIntersectionPoint, movementVector);
                            if (!TgcCollisionUtils.intersectRayPlane(sphereMovementRay, bbFace.Plane, out pDist, out planeIntersectionPoint))
                            {
                                //no hay colisión
                                continue;
                            }
                        }

                        //Ver si planeIntersectionPoint pertenece al polígono
                        Vector3 newMovementVector;
                        float newMoveDistSq;
                        Vector3 polygonIntersectionPoint;
                        if (pointInBounbingBoxFace(planeIntersectionPoint, bbFace))
                        {
                            if (embebbed)
                            {
                                //TODO: REVISAR ESTO, nunca debería pasar
                                //throw new Exception("El polígono está dentro de la esfera");
                            }

                            polygonIntersectionPoint = planeIntersectionPoint;
                            collisionFound = true;
                        }
                        else
                        {
                            //Buscar el punto mas cercano planeIntersectionPoint que tiene el polígono real de esta cara
                            polygonIntersectionPoint = TgcCollisionUtils.closestPointRectangle3d(planeIntersectionPoint,
                                bbFace.Extremes[0], bbFace.Extremes[1], bbFace.Extremes[2]);

                            //Revertir el vector de velocidad desde el nuevo polygonIntersectionPoint para ver donde colisiona la esfera, si es que llega
                            Vector3 reversePointSeg = polygonIntersectionPoint - movementVector;
                            if (TgcCollisionUtils.intersectSegmentSphere(polygonIntersectionPoint, reversePointSeg, characterSphere, out pDist, out sphereIntersectionPoint))
                            {
                                collisionFound = true;
                            }
                        }

                        if (collisionFound)
                        {
                            //Nuevo vector de movimiento acotado
                            newMovementVector = polygonIntersectionPoint - sphereIntersectionPoint;
                            newMoveDistSq = newMovementVector.LengthSq();

                            //se colisiono con algo, lo agrego a la lista
                            colisionInfo.Add(obstaculoBB);

                            if (newMoveDistSq <= distanceToTravelSq && newMoveDistSq < minCollisionDistSq)
                            {
                                minCollisionDistSq = newMoveDistSq;
                                realMovementVector = newMovementVector;
                                nearestPolygonIntersectionPoint = polygonIntersectionPoint;
                                collisionFace = bbFace;
                                collisionObstacle = obstaculoBB;

                            }
                        }
                    }
                }
            }

            //Si nunca hubo colisión, avanzar todo lo requerido
            if (collisionFace == null)
            {
                //Avanzar hasta muy cerca
                float movementLength = movementVector.Length();
                movementVector.Multiply((movementLength - EPSILON) / movementLength);
                characterSphere.moveCenter(movementVector);
                return;
            }

            //Solo movernos si ya no estamos muy cerca
            if (minCollisionDistSq >= EPSILON)
            {
                //Mover el BoundingSphere hasta casi la nueva posición real
                float movementLength = realMovementVector.Length();
                realMovementVector.Multiply((movementLength - EPSILON) / movementLength);
                characterSphere.moveCenter(realMovementVector);
            }

            //Calcular plano de Sliding
            Vector3 slidePlaneOrigin = nearestPolygonIntersectionPoint;
            Vector3 slidePlaneNormal = characterSphere.Center - nearestPolygonIntersectionPoint;
            slidePlaneNormal.Normalize();

            Plane slidePlane = Plane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal);

            //Proyectamos el punto original de destino en el plano de sliding
            TgcRay slideRay = new TgcRay(nearestPolygonIntersectionPoint + Vector3.Multiply(movementVector, slideFactor), slidePlaneNormal);
            float slideT;
            Vector3 slideDestinationPoint;

            if (TgcCollisionUtils.intersectRayPlane(slideRay, slidePlane, out slideT, out slideDestinationPoint))
            {
                //Nuevo vector de movimiento
                Vector3 slideMovementVector = slideDestinationPoint - nearestPolygonIntersectionPoint;

                if (slideMovementVector.LengthSq() < EPSILON)
                {
                    return;
                }

                //Recursividad para aplicar sliding
                doCollideWithWorld(characterSphere, slideMovementVector, obstaculos, recursionDepth + 1, colisionInfo);
            }

            return;
        }
        /// <summary>
        /// Mover BoundingSphere con detección de colisiones, sliding y gravedad.
        /// Se actualiza la posición del centrodel BoundingSphere.
        /// </summary>
        /// <param name="characterSphere">BoundingSphere del cuerpo a mover</param>
        /// <param name="movementVector">Movimiento a realizar</param>
        /// <param name="obstaculos">BoundingBox de obstáculos contra los cuales se puede colisionar</param>
        /// <returns>Desplazamiento relativo final efecutado al BoundingSphere</returns> 
        public ColisionInfo moveCharacter(TgcBoundingSphere characterSphere, Vector3 movementVector)
        {
            Vector3 originalSphereCenter = characterSphere.Center;
            ColisionInfo colisionInfo = new ColisionInfo();
            //Realizar movimiento
            collideWithWorld(characterSphere, movementVector, obstaculos, colisionInfo);

            //Aplicar gravedad
            if (gravityEnabled)
            {
                collideWithWorld(characterSphere, gravityForce, obstaculos, colisionInfo);
            }

            colisionInfo.addMovimientoRelativo(characterSphere.Center - originalSphereCenter);
            return colisionInfo;
        }
        /// <summary>
        /// Detección de colisiones, filtrando los obstaculos que se encuentran dentro del radio de movimiento
        /// </summary>
        private void collideWithWorld(TgcBoundingSphere characterSphere, Vector3 movementVector, List<IColisionablePelota> obstaculos, ColisionInfo colisionInfo)
        {
            /*
            // DEJO que al menos se haga una iteracion aunque la pelota no se mueva, fix bug de colision con pelota quieta.
            if (movementVector.LengthSq() < EPSILON)
            {
                return;
            }
            */
            List<IColisionable> objetosCandidatos = new List<IColisionable>();
            Vector3 lastCenterSafePosition = characterSphere.Center;

            //Dejar solo los obstáculos que están dentro del radio de movimiento de la esfera
            Vector3 halfMovementVec = Vector3.Multiply(movementVector, 0.5f);
            TgcBoundingSphere testSphere = new TgcBoundingSphere(
                characterSphere.Center + halfMovementVec,
                halfMovementVec.Length() + characterSphere.Radius
                );
            objetosCandidatos.Clear();
            foreach (IColisionable obstaculo in obstaculos)
            {
                if (TgcCollisionUtils.testSphereAABB(testSphere, obstaculo.GetTgcBoundingBox()))
                {
                    //colisionInfo.Add(obstaculo);
                    objetosCandidatos.Add(obstaculo);
                }
            }

            //Detectar colisiones y deplazar con sliding
            doCollideWithWorld(characterSphere, movementVector, objetosCandidatos, 0, colisionInfo);

            //Manejo de error. No deberiamos colisionar con nadie si todo salio bien
            foreach (IColisionable obstaculo in objetosCandidatos)
            {
                if (TgcCollisionUtils.testSphereAABB(characterSphere, obstaculo.GetTgcBoundingBox()))
                {
                    //Hubo un error, volver a la posición original
                    characterSphere.setCenter(lastCenterSafePosition);
                    return;
                }
            }
        }
Пример #4
0
        /// <summary>
        ///     Colisiona un BoundingSphere en movimiento contra el BoundingBox.
        ///     Si hay colision devuelve el instante t de colision mas proximo y el punto q de colision mas cercano
        /// </summary>
        /// <param name="sphere">BoundingSphere</param>
        /// <param name="movementVector">movimiento del BoundingSphere</param>
        /// <param name="t">Menor instante de colision</param>
        /// <param name="q">Punto mas cercano de colision</param>
        /// <param name="n">Normal de la cara colisionada</param>
        /// <returns>True si hay colision</returns>
        public override bool intersectMovingSphere(TgcBoundingSphere sphere, Vector3 movementVector,
                                                   TgcBoundingSphere movementSphere, out float t, out Vector3 q, out Vector3 n)
        {
            t = -1f;
            q = Vector3.Empty;
            n = Vector3.Empty;

            // Compute the AABB resulting from expanding b by sphere radius r
            var e = Aabb.toStruct();

            e.min.X -= sphere.Radius;
            e.min.Y -= sphere.Radius;
            e.min.Z -= sphere.Radius;
            e.max.X += sphere.Radius;
            e.max.Y += sphere.Radius;
            e.max.Z += sphere.Radius;

            // Intersect ray against expanded AABB e. Exit with no intersection if ray
            // misses e, else get intersection point p and time t as result
            Vector3 p;
            var     ray = new TgcRay.RayStruct();

            ray.origin    = sphere.Center;
            ray.direction = movementVector;
            if (!intersectRayAABB(ray, e, out t, out p) || t > 1.0f)
            {
                return(false);
            }

            // Compute which min and max faces of b the intersection point p lies
            // outside of. Note, u and v cannot have the same bits set and
            // they must have at least one bit set among them
            var i    = 0;
            var sign = new int[3];

            if (p.X < Aabb.PMin.X)
            {
                sign[0] = -1;
                i++;
            }
            if (p.X > Aabb.PMax.X)
            {
                sign[0] = 1;
                i++;
            }
            if (p.Y < Aabb.PMin.Y)
            {
                sign[1] = -1;
                i++;
            }
            if (p.Y > Aabb.PMax.Y)
            {
                sign[1] = 1;
                i++;
            }
            if (p.Z < Aabb.PMin.Z)
            {
                sign[2] = -1;
                i++;
            }
            if (p.Z > Aabb.PMax.Z)
            {
                sign[2] = 1;
                i++;
            }

            //Face
            if (i == 1)
            {
                n = new Vector3(sign[0], sign[1], sign[2]);
                q = sphere.Center + t * movementVector - sphere.Radius * n;
                return(true);
            }

            // Define line segment [c, c+d] specified by the sphere movement
            var seg = new Segment(sphere.Center, sphere.Center + movementVector);

            //Box extent and center
            var extent = Aabb.calculateAxisRadius();
            var center = Aabb.PMin + extent;

            //Edge
            if (i == 2)
            {
                //Generar los dos puntos extremos del Edge
                float[] extentDir = { sign[0], sign[1], sign[2] };
                var     zeroIndex = sign[0] == 0 ? 0 : (sign[1] == 0 ? 1 : 2);
                extentDir[zeroIndex] = 1;
                var capsuleA = center + new Vector3(extent.X * extentDir[0], extent.Y * extentDir[1], extent.Z * extentDir[2]);
                extentDir[zeroIndex] = -1;
                var capsuleB = center + new Vector3(extent.X * extentDir[0], extent.Y * extentDir[1], extent.Z * extentDir[2]);

                //Colision contra el Edge hecho Capsula
                if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, sphere.Radius), out t))
                {
                    n = new Vector3(sign[0], sign[1], sign[2]);
                    n.Normalize();
                    q = sphere.Center + t * movementVector - sphere.Radius * n;
                    return(true);
                }
            }

            //Vertex
            if (i == 3)
            {
                var     tmin     = float.MaxValue;
                var     capsuleA = center + new Vector3(extent.X * sign[0], extent.Y * sign[1], extent.Z * sign[2]);
                Vector3 capsuleB;

                capsuleB = center + new Vector3(extent.X * -sign[0], extent.Y * sign[1], extent.Z * sign[2]);
                if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, sphere.Radius), out t))
                {
                    tmin = TgcCollisionUtils.min(t, tmin);
                }

                capsuleB = center + new Vector3(extent.X * sign[0], extent.Y * -sign[1], extent.Z * sign[2]);
                if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, sphere.Radius), out t))
                {
                    tmin = TgcCollisionUtils.min(t, tmin);
                }

                capsuleB = center + new Vector3(extent.X * sign[0], extent.Y * sign[1], extent.Z * -sign[2]);
                if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, sphere.Radius), out t))
                {
                    tmin = TgcCollisionUtils.min(t, tmin);
                }

                if (tmin == float.MaxValue)
                {
                    return(false);                        // No intersection
                }
                t = tmin;
                n = new Vector3(sign[0], sign[1], sign[2]);
                n.Normalize();
                q = sphere.Center + t * movementVector - sphere.Radius * n;
                return(true); // Intersection at time t == tmin
            }

            return(false);
        }
Пример #5
0
        /// <summary>
        /// Detección de colisiones recursiva
        /// </summary>
        public void doCollideWithWorld(TgcBoundingSphere characterSphere, Vector3 movementVector, List <TgcBoundingBox> obstaculos, int recursionDepth)
        {
            //Limitar recursividad
            if (recursionDepth > 5)
            {
                return;
            }

            //Ver si la distancia a recorrer es para tener en cuenta
            float distanceToTravelSq = movementVector.LengthSq();

            if (distanceToTravelSq < EPSILON)
            {
                return;
            }

            //Posicion deseada
            Vector3 originalSphereCenter = characterSphere.Center;
            Vector3 nextSphereCenter     = originalSphereCenter + movementVector;

            //Buscar el punto de colision mas cercano de todos los objetos candidatos
            float   minCollisionDistSq = float.MaxValue;
            Vector3 realMovementVector = movementVector;

            TgcBoundingBox.Face collisionFace     = null;
            TgcBoundingBox      collisionObstacle = null;
            Vector3             nearestPolygonIntersectionPoint = Vector3.Empty;

            foreach (TgcBoundingBox obstaculoBB in obstaculos)
            {
                //Obtener los polígonos que conforman las 6 caras del BoundingBox
                TgcBoundingBox.Face[] bbFaces = obstaculoBB.computeFaces();

                foreach (TgcBoundingBox.Face bbFace in bbFaces)
                {
                    Vector3 pNormal = TgcCollisionUtils.getPlaneNormal(bbFace.Plane);

                    TgcRay  movementRay = new TgcRay(originalSphereCenter, movementVector);
                    float   brutePlaneDist;
                    Vector3 brutePlaneIntersectionPoint;
                    if (!TgcCollisionUtils.intersectRayPlane(movementRay, bbFace.Plane, out brutePlaneDist, out brutePlaneIntersectionPoint))
                    {
                        continue;
                    }

                    float movementRadiusLengthSq = Vector3.Multiply(movementVector, characterSphere.Radius).LengthSq();
                    if (brutePlaneDist * brutePlaneDist > movementRadiusLengthSq)
                    {
                        continue;
                    }


                    //Obtener punto de colisión en el plano, según la normal del plano
                    float   pDist;
                    Vector3 planeIntersectionPoint;
                    Vector3 sphereIntersectionPoint;
                    TgcRay  planeNormalRay = new TgcRay(originalSphereCenter, -pNormal);
                    bool    embebbed       = false;
                    bool    collisionFound = false;
                    if (TgcCollisionUtils.intersectRayPlane(planeNormalRay, bbFace.Plane, out pDist, out planeIntersectionPoint))
                    {
                        //Ver si el plano está embebido en la esfera
                        if (pDist <= characterSphere.Radius)
                        {
                            embebbed = true;

                            //TODO: REVISAR ESTO, caso embebido a analizar con más detalle
                            sphereIntersectionPoint = originalSphereCenter - pNormal * characterSphere.Radius;
                        }
                        //Esta fuera de la esfera
                        else
                        {
                            //Obtener punto de colisión del contorno de la esfera según la normal del plano
                            sphereIntersectionPoint = originalSphereCenter - Vector3.Multiply(pNormal, characterSphere.Radius);

                            //Disparar un rayo desde el contorno de la esfera hacia el plano, con el vector de movimiento
                            TgcRay sphereMovementRay = new TgcRay(sphereIntersectionPoint, movementVector);
                            if (!TgcCollisionUtils.intersectRayPlane(sphereMovementRay, bbFace.Plane, out pDist, out planeIntersectionPoint))
                            {
                                //no hay colisión
                                continue;
                            }
                        }

                        //Ver si planeIntersectionPoint pertenece al polígono
                        Vector3 newMovementVector;
                        float   newMoveDistSq;
                        Vector3 polygonIntersectionPoint;
                        if (pointInBounbingBoxFace(planeIntersectionPoint, bbFace))
                        {
                            if (embebbed)
                            {
                                //TODO: REVISAR ESTO, nunca debería pasar
                                //throw new Exception("El polígono está dentro de la esfera");
                            }

                            polygonIntersectionPoint = planeIntersectionPoint;
                            collisionFound           = true;
                        }
                        else
                        {
                            //Buscar el punto mas cercano planeIntersectionPoint que tiene el polígono real de esta cara
                            polygonIntersectionPoint = TgcCollisionUtils.closestPointRectangle3d(planeIntersectionPoint,
                                                                                                 bbFace.Extremes[0], bbFace.Extremes[1], bbFace.Extremes[2]);

                            //Revertir el vector de velocidad desde el nuevo polygonIntersectionPoint para ver donde colisiona la esfera, si es que llega
                            Vector3 reversePointSeg = polygonIntersectionPoint - movementVector;
                            if (TgcCollisionUtils.intersectSegmentSphere(polygonIntersectionPoint, reversePointSeg, characterSphere, out pDist, out sphereIntersectionPoint))
                            {
                                collisionFound = true;
                            }
                        }

                        if (collisionFound)
                        {
                            //Nuevo vector de movimiento acotado
                            newMovementVector = polygonIntersectionPoint - sphereIntersectionPoint;
                            newMoveDistSq     = newMovementVector.LengthSq();

                            if (newMoveDistSq <= distanceToTravelSq && newMoveDistSq < minCollisionDistSq)
                            {
                                minCollisionDistSq = newMoveDistSq;
                                realMovementVector = newMovementVector;
                                nearestPolygonIntersectionPoint = polygonIntersectionPoint;
                                collisionFace     = bbFace;
                                collisionObstacle = obstaculoBB;
                            }
                        }
                    }
                }
            }

            //Si nunca hubo colisión, avanzar todo lo requerido
            if (collisionFace == null)
            {
                //Avanzar hasta muy cerca
                float movementLength = movementVector.Length();
                movementVector.Multiply((movementLength - EPSILON) / movementLength);
                characterSphere.moveCenter(movementVector);
                return;
            }

            //Solo movernos si ya no estamos muy cerca
            if (minCollisionDistSq >= EPSILON)
            {
                //Mover el BoundingSphere hasta casi la nueva posición real
                float movementLength = realMovementVector.Length();
                realMovementVector.Multiply((movementLength - EPSILON) / movementLength);
                characterSphere.moveCenter(realMovementVector);
            }



            //Calcular plano de Sliding
            Vector3 slidePlaneOrigin = nearestPolygonIntersectionPoint;
            Vector3 slidePlaneNormal = characterSphere.Center - nearestPolygonIntersectionPoint;

            slidePlaneNormal.Normalize();

            Plane slidePlane = Plane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal);

            //Proyectamos el punto original de destino en el plano de sliding
            TgcRay  slideRay = new TgcRay(nearestPolygonIntersectionPoint + Vector3.Multiply(movementVector, slideFactor), slidePlaneNormal);
            float   slideT;
            Vector3 slideDestinationPoint;

            if (TgcCollisionUtils.intersectRayPlane(slideRay, slidePlane, out slideT, out slideDestinationPoint))
            {
                //Nuevo vector de movimiento
                Vector3 slideMovementVector = slideDestinationPoint - nearestPolygonIntersectionPoint;

                if (slideMovementVector.LengthSq() < EPSILON)
                {
                    return;
                }

                //Recursividad para aplicar sliding
                doCollideWithWorld(characterSphere, slideMovementVector, obstaculos, recursionDepth + 1);
            }
        }
        /// <summary>
        ///     Detectar colision entre una esfera que se mueve y un triangulo
        /// </summary>
        /// <param name="sphere">BoundingSphere</param>
        /// <param name="movementVector">Vector de movimiento de la esferfa</param>
        /// <param name="triangle">Triangulo</param>
        /// <param name="collisionPoint">Menor punto de colision encontrado</param>
        /// <returns>True si hay colision</returns>
        private bool intersectMovingSphereTriangle(TgcBoundingSphere sphere, TGCVector3 movementVector, Triangle triangle,
                                                   out float minT, out TGCVector3 collisionPoint)
        {
            float      t;
            TGCVector3 q;

            collisionPoint = TGCVector3.Empty;
            minT           = float.MaxValue;

            //Ver si la esfera en movimiento colisiona con el plano del triangulo
            if (!TgcCollisionUtils.intersectMovingSpherePlane(sphere, movementVector, triangle.Plane, out t, out q))
            {
                return(false);
            }

            //Ver si la esfera ya esta dentro del Plano, hacer un chequeo Sphere-Triangle
            if (t == 0.0f)
            {
                if (TgcCollisionUtils.testSphereTriangle(sphere, triangle.A, triangle.B, triangle.C, out q))
                {
                    minT           = 0.0f;
                    collisionPoint = q;
                    return(true);
                }
            }
            else
            {
                //Si el punto de colision contra el plano pertenece al triangulo, entonces ya encontramos el punto de colision
                if (TgcCollisionUtils.testPointInTriangle(q, triangle.A, triangle.B, triangle.C))
                {
                    minT           = t;
                    collisionPoint = q;
                    return(true);
                }
            }

            //Ver de que lado del plano del triangulo esta la esfera
            var distPlane   = triangle.Plane.Dot(sphere.Center);
            var sphereRad   = distPlane >= 0.0f ? sphere.Radius : -sphere.Radius;
            var planeNormal = TgcCollisionUtils.getPlaneNormal(triangle.Plane);

            //Chequear colision entre la esfera en movimiento y los tres Edge y obtener el menor punto de colision
            //Es como un Ray del centro de la esfera contra un edge que se convierte en cilindro sin endcap
            var segmentEnd = sphere.Center + movementVector;

            if (intersectSegmentCylinderNoEndcap(sphere.Center, segmentEnd, triangle.A, triangle.B, sphere.Radius, out t))
            {
                minT = TgcCollisionUtils.min(t, minT);
            }
            if (intersectSegmentCylinderNoEndcap(sphere.Center, segmentEnd, triangle.B, triangle.C, sphere.Radius, out t))
            {
                minT = TgcCollisionUtils.min(t, minT);
            }
            if (intersectSegmentCylinderNoEndcap(sphere.Center, segmentEnd, triangle.C, triangle.A, sphere.Radius, out t))
            {
                minT = TgcCollisionUtils.min(t, minT);
            }
            //Si hubo colision, retornar la menor encontrada
            if (minT != float.MaxValue)
            {
                collisionPoint = sphere.Center + minT * movementVector - sphereRad * planeNormal;
                return(true);
            }

            //Sino, chequear colision entra la esfera y los tres vertices del triangulo y obtener la menor
            //Es como un Ray del centro de la esfera contra un vertice que se convierte en esfera
            var vertSphere = new TgcBoundingSphere();

            minT = float.MaxValue;
            vertSphere.setValues(triangle.A, sphere.Radius);
            if (TgcCollisionUtils.intersectSegmentSphere(sphere.Center, segmentEnd, vertSphere, out t, out q))
            {
                minT = TgcCollisionUtils.min(t, minT);
            }
            vertSphere.setValues(triangle.B, sphere.Radius);
            if (TgcCollisionUtils.intersectSegmentSphere(sphere.Center, segmentEnd, vertSphere, out t, out q))
            {
                minT = TgcCollisionUtils.min(t, minT);
            }
            vertSphere.setValues(triangle.C, sphere.Radius);
            if (TgcCollisionUtils.intersectSegmentSphere(sphere.Center, segmentEnd, vertSphere, out t, out q))
            {
                minT = TgcCollisionUtils.min(t, minT);
            }
            //Si hubo colision, retornar la menor encontrada
            if (minT != float.MaxValue)
            {
                collisionPoint = sphere.Center + minT * movementVector - sphereRad * planeNormal;
                return(true);
            }

            //No hay colision
            return(false);
        }
        /// <summary>
        ///     Detección de colisiones recursiva
        /// </summary>
        /// <param name="eSphere">Sphere de radio 1 pasada a Elipsoid space</param>
        /// <param name="eMovementVector">Movimiento pasado a Elipsoid space</param>
        /// <param name="eRadius">Radio de la elipsoide</param>
        /// <param name="colliders">Objetos contra los cuales colisionar</param>
        /// <param name="recursionDepth">Nivel de recursividad</param>
        /// <param name="movementSphere">Esfera real que representa el movimiento abarcado</param>
        /// <param name="slidingMinY">Minimo valor de normal Y de colision para hacer sliding</param>
        /// <returns>Resultado de colision</returns>
        public CollisionResult doCollideWithWorld(TgcBoundingSphere eSphere, Vector3 eMovementVector, Vector3 eRadius,
                                                  List <Collider> colliders, int recursionDepth, TgcBoundingSphere movementSphere, float slidingMinY)
        {
            var result = new CollisionResult();

            result.collisionFound = false;

            //Limitar recursividad
            if (recursionDepth > 5)
            {
                return(result);
            }

            //Posicion deseada
            var nextSphereCenter = eSphere.Center + eMovementVector;

            //Buscar el punto de colision mas cercano de todos los objetos candidatos
            Vector3 q;
            float   t;
            Vector3 n;
            var     minT = float.MaxValue;

            foreach (var collider in colliders)
            {
                //Colisionar Sphere en movimiento contra Collider (cada Collider resuelve la colision)
                if (collider.intersectMovingElipsoid(eSphere, eMovementVector, eRadius, movementSphere, out t, out q,
                                                     out n))
                {
                    //Quedarse con el menor instante de colision
                    if (t < minT)
                    {
                        minT = t;
                        result.collisionFound  = true;
                        result.collisionPoint  = q;
                        result.collisionNormal = n;
                        result.collider        = collider;
                    }
                }
            }

            //Si nunca hubo colisión, avanzar todo lo requerido
            if (!result.collisionFound)
            {
                //Avanzar todo lo pedido
                eSphere.moveCenter(eMovementVector);
                result.realMovmentVector = eMovementVector;
                result.collisionNormal   = Vector3.Empty;
                result.collisionPoint    = Vector3.Empty;
                result.collider          = null;
                return(result);
            }

            //Solo movernos si ya no estamos muy cerca
            if (minT >= EPSILON)
            {
                //Restar un poco al instante de colision, para movernos hasta casi esa distancia
                minT -= EPSILON;
                result.realMovmentVector = eMovementVector * minT;
                eSphere.moveCenter(result.realMovmentVector);

                //Quitarle al punto de colision el EPSILON restado al movimiento, para no afectar al plano de sliding
                var v = Vector3.Normalize(result.realMovmentVector);
                result.collisionPoint -= v * EPSILON;
            }

            //Calcular plano de Sliding, como un plano tangete al punto de colision con la esfera, apuntando hacia el centro de la esfera
            var slidePlaneOrigin = result.collisionPoint;
            var slidePlaneNormal = eSphere.Center - result.collisionPoint;

            slidePlaneNormal.Normalize();
            var slidePlane = Plane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal);

            //Calcular vector de movimiento para sliding, proyectando el punto de destino original sobre el plano de sliding
            var distance            = TgcCollisionUtils.distPointPlane(nextSphereCenter, slidePlane);
            var newDestinationPoint = nextSphereCenter - distance * slidePlaneNormal;
            var slideMovementVector = newDestinationPoint - result.collisionPoint;

            //No hacer recursividad si es muy pequeño
            slideMovementVector.Scale(SlideFactor);
            if (slideMovementVector.Length() < EPSILON)
            {
                return(result);
            }

            //Ver si posee la suficiente pendiente en Y para hacer sliding
            if (result.collisionNormal.Y <= slidingMinY)
            {
                //Recursividad para aplicar sliding
                doCollideWithWorld(eSphere, slideMovementVector, eRadius, colliders, recursionDepth + 1, movementSphere,
                                   slidingMinY);
            }

            return(result);
        }
Пример #8
0
        public override void Init()
        {
            //Cargar escenario específico para este ejemplo
            var loader = new TgcSceneLoader();

            escenario = loader.loadSceneFromFile(MediaDir + "PatioDeJuegos\\PatioDeJuegos-TgcScene.xml");

            //Cargar personaje con animaciones
            var skeletalLoader = new TgcSkeletalLoader();

            personaje =
                skeletalLoader.loadMeshAndAnimationsFromFile(
                    MediaDir + "SkeletalAnimations\\Robot\\Robot-TgcSkeletalMesh.xml",
                    MediaDir + "SkeletalAnimations\\Robot\\",
                    new[]
            {
                MediaDir + "SkeletalAnimations\\Robot\\Caminando-TgcSkeletalAnim.xml",
                MediaDir + "SkeletalAnimations\\Robot\\Parado-TgcSkeletalAnim.xml"
            });

            //Le cambiamos la textura para diferenciarlo un poco
            personaje.changeDiffuseMaps(new[]
            {
                TgcTexture.createTexture(D3DDevice.Instance.Device,
                                         MediaDir + "SkeletalAnimations\\Robot\\Textures\\uvwGreen.jpg")
            });

            //Configurar animacion inicial
            personaje.playAnimation("Parado", true);
            //Escalarlo porque es muy grande
            personaje.Position = new Vector3(0, 500, -100);
            //Rotarlo 180° porque esta mirando para el otro lado
            personaje.rotateY(Geometry.DegreeToRadian(180f));

            //BoundingSphere que va a usar el personaje
            personaje.AutoUpdateBoundingBox = false;
            characterSphere = new TgcBoundingSphere(personaje.BoundingBox.calculateBoxCenter(),
                                                    personaje.BoundingBox.calculateBoxRadius());

            //Almacenar volumenes de colision del escenario
            objetosColisionables.Clear();
            foreach (var mesh in escenario.Meshes)
            {
                objetosColisionables.Add(mesh.BoundingBox);
            }

            //Crear linea para mostrar la direccion del movimiento del personaje
            directionArrow           = new TgcArrow();
            directionArrow.BodyColor = Color.Red;
            directionArrow.HeadColor = Color.Green;
            directionArrow.Thickness = 1;
            directionArrow.HeadSize  = new Vector2(10, 20);

            //Crear manejador de colisiones
            collisionManager = new SphereCollisionManager();
            collisionManager.GravityEnabled = true;

            //Configurar camara en Tercer Persona
            camaraInterna = new TgcThirdPersonCamera(personaje.Position, new Vector3(0, 100, 0), 100, -400);
            Camara        = camaraInterna;

            //Crear SkyBox
            skyBox        = new TgcSkyBox();
            skyBox.Center = new Vector3(0, 0, 0);
            skyBox.Size   = new Vector3(10000, 10000, 10000);
            var texturesPath = MediaDir + "Texturas\\Quake\\SkyBox3\\";

            skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Up, texturesPath + "Up.jpg");
            skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Down, texturesPath + "Down.jpg");
            skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Left, texturesPath + "Left.jpg");
            skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Right, texturesPath + "Right.jpg");
            skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Front, texturesPath + "Back.jpg");
            skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Back, texturesPath + "Front.jpg");
            skyBox.InitSkyBox();

            //Modifier para ver BoundingBox
            Modifiers.addBoolean("showBoundingBox", "Bouding Box", true);

            //Modifiers para desplazamiento del personaje
            Modifiers.addFloat("VelocidadCaminar", 0, 100, 16);
            Modifiers.addFloat("VelocidadRotacion", 1f, 360f, 150f);
            Modifiers.addBoolean("HabilitarGravedad", "Habilitar Gravedad", true);
            Modifiers.addVertex3f("Gravedad", new Vector3(-50, -50, -50), new Vector3(50, 50, 50),
                                  new Vector3(0, -10, 0));
            Modifiers.addFloat("SlideFactor", 1f, 2f, 1.3f);

            UserVars.addVar("Movement");
        }