/// <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; } } }
/// <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); }
/// <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); }
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"); }