public CircleGeometry(float radius=1,int segments=8,float thetaStart = 0, float thetaLength = Mathf.Tau) { segments = Mathf.Max(3, segments); var uvs = new List<Vector2>(); vertices.Add(Vector3.Zero); uvs.Add(Vector2.Half); for(var i = 0f; i <= segments; i ++ ) { var segment = thetaStart + i / segments * thetaLength; var v = new Vector3(Mathf.Cos( segment ),radius * Mathf.Sin( segment ),0); v.Multiply(radius); vertices.Add(v); uvs.Add(new Vector2(( v.x / radius + 1 ) / 2, ( v.y / radius + 1 ) / 2 )); } var n = Vector3.UnitZ; for (var i = 1; i <= segments; i++) { faces.Add(new Face3(i, i + 1, 0, n)); var faceSet = new UVFaceSet(uvs[i], uvs[i + 1], Vector2.Half); faceVertexUvs[0].Add(faceSet); } ComputeNormals(); BoundingSphere = new Sphere(Vector3.Zero,radius ); }
/// <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); } }
// Setup the world, view, and projection matrices. private void SetupMatrices() { // World Matrix: const int TICKS_PER_REV = 10000; double angle = Environment.TickCount * (2 * Math.PI) / TICKS_PER_REV; m_Device.Transform.World = Matrix.RotationY((float)angle); // View Matrix: Vector3 camera_position = new Vector3(0, 10, -20); camera_position.Normalize(); camera_position.Multiply(m_Range); m_Device.Transform.View = Matrix.LookAtLH( camera_position, new Vector3(0, 0, 0), new Vector3(0, 1, 0)); // Projection Matrix: // Perspective transformation defined by: // Field of view Pi / 4 // Aspect ratio 1 // Near clipping plane Z = 1 // Far clipping plane Z = 100 m_Device.Transform.Projection = Matrix.PerspectiveFovLH((float)(Math.PI / 4), 1, 1, 100); }