public void move(TGCVector3 movement) { // deshago el movimiento del manager boundingSphere.moveCenter(-movement); // limite de velocidad if (movement.X > WALK_SPEED * MULT_CORRER) { movement.X = WALK_SPEED * 1.5f; } if (movement.Z > WALK_SPEED * MULT_CORRER) { movement.Z = WALK_SPEED * 1.5f; } if (movement.X < WALK_SPEED * MULT_CORRER * -1) { movement.X = WALK_SPEED * 1.5f * -1; } if (movement.Z < WALK_SPEED * MULT_CORRER * -1) { movement.Z = WALK_SPEED * 1.5f * -1; } mesh.Move(movement); pies.moveCenter(movement); boundingSphere.moveCenter(movement); vel = movement; }
/// <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); }
/// <summary> /// Detección de colisiones recursiva /// </summary> public void doCollideWithWorld(TgcBoundingSphere characterSphere, Vector3 movementVector, List <TgcBoundingAxisAlignBox> obstaculos, int recursionDepth) { //Limitar recursividad if (recursionDepth > 5) { return; } //Ver si la distancia a recorrer es para tener en cuenta var distanceToTravelSq = movementVector.LengthSq(); if (distanceToTravelSq < EPSILON) { return; } //Posicion deseada var originalSphereCenter = characterSphere.Center; var nextSphereCenter = originalSphereCenter + movementVector; //Buscar el punto de colision mas cercano de todos los objetos candidatos var minCollisionDistSq = float.MaxValue; var realMovementVector = movementVector; TgcBoundingAxisAlignBox.Face collisionFace = null; TgcBoundingAxisAlignBox collisionObstacle = null; var nearestPolygonIntersectionPoint = Vector3.Empty; foreach (var obstaculoBB in obstaculos) { //Obtener los polígonos que conforman las 6 caras del BoundingBox var bbFaces = obstaculoBB.computeFaces(); foreach (var bbFace in bbFaces) { var pNormal = TgcCollisionUtils.getPlaneNormal(bbFace.Plane); var movementRay = new TgcRay(originalSphereCenter, movementVector); float brutePlaneDist; Vector3 brutePlaneIntersectionPoint; if (!TgcCollisionUtils.intersectRayPlane(movementRay, bbFace.Plane, out brutePlaneDist, out brutePlaneIntersectionPoint)) { continue; } var 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; var planeNormalRay = new TgcRay(originalSphereCenter, -pNormal); var embebbed = false; var collisionFound = false; if (TgcCollisionUtils.intersectRayPlane(planeNormalRay, bbFace.Plane, out pDist, out planeIntersectionPoint)) { //Ver si el plano está embebido en la esfera if (isEmbebbed(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 var 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"); characterSphere.moveCenter(new Vector3(originalSphereCenter.X, 0, originalSphereCenter.Z) - new Vector3(pNormal.X, 0, pNormal.Z) * characterSphere.Radius); } 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 var 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 var 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 var movementLength = realMovementVector.Length(); realMovementVector.Multiply((movementLength - EPSILON) / movementLength); characterSphere.moveCenter(realMovementVector); } //Calcular plano de Sliding var slidePlaneOrigin = nearestPolygonIntersectionPoint; var slidePlaneNormal = characterSphere.Center - nearestPolygonIntersectionPoint; slidePlaneNormal.Normalize(); var slidePlane = Plane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal); //Proyectamos el punto original de destino en el plano de sliding var 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 var slideMovementVector = slideDestinationPoint - nearestPolygonIntersectionPoint; if (slideMovementVector.LengthSq() < EPSILON) { return; } //Recursividad para aplicar sliding doCollideWithWorld(characterSphere, slideMovementVector, obstaculos, recursionDepth + 1); } }
/// <summary> /// Detección de colisiones recursiva /// </summary> public void doCollideWithWorld(TgcBoundingSphere characterSphere, Vector3 movementVector, List <Colisionador> colliders, int recursionDepth, TgcBoundingSphere movementSphere, bool sliding, float slidingMinY) { //Limitar recursividad if (recursionDepth > 5) { return; } //Posicion deseada var originalSphereCenter = characterSphere.Center; var nextSphereCenter = originalSphereCenter + movementVector; //Buscar el punto de colision mas cercano de todos los objetos candidatos Collision = false; 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.intersectMovingSphere(characterSphere, movementVector, movementSphere, out t, out q, out n)) { //Quedarse con el menor instante de colision if (t < minT) { minT = t; Collision = true; LastCollisionPoint = q; LastCollisionNormal = n; lastCollider = collider; } } } //Si nunca hubo colisión, avanzar todo lo requerido if (!Collision) { //Avanzar todo lo pedido //lastCollisionDistance = movementVector.Length(); characterSphere.moveCenter(movementVector); return; } //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; var realMovementVector = movementVector * minT; //Mover el BoundingSphere characterSphere.moveCenter(realMovementVector); //Quitarle al punto de colision el EPSILON restado al movimiento, para no afectar al plano de sliding var v = Vector3.Normalize(realMovementVector); LastCollisionPoint -= v * EPSILON; } if (sliding) { //Calcular plano de Sliding, como un plano tangete al punto de colision con la esfera, apuntando hacia el centro de la esfera var slidePlaneOrigin = LastCollisionPoint; var slidePlaneNormal = characterSphere.Center - LastCollisionPoint; 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 - LastCollisionPoint; //No hacer recursividad si es muy pequeño slideMovementVector.Scale(SlideFactor); if (slideMovementVector.Length() < EPSILON) { return; } if (LastCollisionNormal.Y <= slidingMinY) { //Recursividad para aplicar sliding doCollideWithWorld(characterSphere, slideMovementVector, colliders, recursionDepth + 1, movementSphere, sliding, slidingMinY); } } }