public static void updateObbFromSegment(TgcBoundingOrientedBox obb, TGCVector3 a, TGCVector3 b, float thickness) { var lineDiff = b - a; var lineLength = lineDiff.Length(); var lineVec = TGCVector3.Normalize(lineDiff); //Obtener angulo y vector de rotacion var upVec = TGCVector3.Up; var angle = FastMath.Acos(TGCVector3.Dot(upVec, lineVec)); var axisRotation = TGCVector3.Cross(upVec, lineVec); axisRotation.Normalize(); //Obtener matriz de rotacion para este eje y angulo var rotM = TGCMatrix.RotationAxis(axisRotation, angle); //Actualizar orientacion de OBB en base a matriz de rotacion obb.Orientation[0] = new TGCVector3(rotM.M11, rotM.M12, rotM.M13); obb.Orientation[1] = new TGCVector3(rotM.M21, rotM.M22, rotM.M23); obb.Orientation[2] = new TGCVector3(rotM.M31, rotM.M32, rotM.M33); //Actualizar extent de OBB segun el thickness del segmento obb.Extents = new TGCVector3(thickness, lineLength / 2, thickness); //Actualizar centro del OBB segun centro del segmento obb.Center = a + TGCVector3.Scale(lineDiff, 0.5f); //Regenerar OBB obb.updateValues(); }
/// <summary> /// Actualizar parámetros del plano en base a los valores configurados /// </summary> public void updateValues() { var vertices = new CustomVertex.PositionColored[6]; //Crear un Quad con dos triángulos sobre XZ con normal default (0, 1, 0) var min = new TGCVector3(-Size.X / 2, 0, -Size.Y / 2); var max = new TGCVector3(Size.X / 2, 0, Size.Y / 2); var c = Color.ToArgb(); vertices[0] = new CustomVertex.PositionColored(min, c); vertices[1] = new CustomVertex.PositionColored(min.X, 0, max.Z, c); vertices[2] = new CustomVertex.PositionColored(max, c); vertices[3] = new CustomVertex.PositionColored(min, c); vertices[4] = new CustomVertex.PositionColored(max, c); vertices[5] = new CustomVertex.PositionColored(max.X, 0, min.Z, c); //Obtener matriz de rotacion respecto de la normal del plano Normal.Normalize(); var angle = FastMath.Acos(TGCVector3.Dot(ORIGINAL_DIR, Normal)); var axisRotation = TGCVector3.Cross(ORIGINAL_DIR, Normal); axisRotation.Normalize(); var t = TGCMatrix.RotationAxis(axisRotation, angle) * TGCMatrix.Translation(Center); //Transformar todos los puntos for (var i = 0; i < vertices.Length; i++) { vertices[i].Position = TGCVector3.TransformCoordinate(TGCVector3.FromVector3(vertices[i].Position), t); } //Cargar vertexBuffer vertexBuffer.SetData(vertices, 0, LockFlags.None); }
private void updateMesh() { mesh.Transform = TGCMatrix.RotationAxis(TGCVector3.Cross(lookAt, lookin), -(float)Math.Acos(TGCVector3.Dot(lookAt, lookin))) * TGCMatrix.Scaling(TGCVector3.One * 30) * TGCMatrix.Translation(pos); }
private bool CumpleCondicionRender(ListaMeshPosicionada listaM) { TGCVector3 vectorDistancia = CommonHelper.SumarVectores(target.GetPosition(), -listaM.posicion); float largoDistancia = vectorDistancia.Length(); return(TGCVector3.Dot(-vectorDistancia, target.coordenadaDireccionCartesiana) > 0 || largoDistancia < 2000); }
/// <summary> /// Devuelve la rotacion que se debe aplicar a la matriz de transformacion para que la entidad apunte hacia una direccion. /// </summary> /// <param name="lookDir">Vector normalizado que define la direccion a la que debe mirar la entidad.</param> private void LookAt(TGCVector3 lookDir) { float angle = FastMath.Acos(TGCVector3.Dot(defaultLookDir, lookDir)); TGCVector3 rotVector = TGCVector3.Cross(defaultLookDir, lookDir); rotVector.Z = 0; rotation = TGCQuaternion.RotationAxis(rotVector, angle); }
private float CheckFrontVector(float rotation) { if (TGCVector3.Dot(this.vectorCostado, this.vectorAdelante) > 0) { return(rotation); } else { return(-rotation); } }
public static float anguloEntreVectores(TGCVector3 vector1, TGCVector3 vector2) { if ((TGCVector3.Length(vector1) * TGCVector3.Length(vector2)) == 0) { return(0); } float cos = TGCVector3.Dot(vector1, vector2) / (TGCVector3.Length(vector1) * TGCVector3.Length(vector2)); return(FastMath.Acos(cos)); }
/// <summary> /// Compute indices to the two most separated points of the (up to) six points /// defining the AABB encompassing the point set. Return these as min and max. /// </summary> private static void mostSeparatedPointsOnAABB(TGCVector3[] pt, out int min, out int max) { // First find most extreme points along principal axes int minx = 0, maxx = 0, miny = 0, maxy = 0, minz = 0, maxz = 0; for (var i = 0; i < pt.Length; i++) { if (pt[i].X < pt[minx].X) { minx = i; } if (pt[i].X > pt[maxx].X) { maxx = i; } if (pt[i].Y < pt[miny].Y) { miny = i; } if (pt[i].Y > pt[maxy].Y) { maxy = i; } if (pt[i].Z < pt[minz].Z) { minz = i; } if (pt[i].Z > pt[maxz].Z) { maxz = i; } } // Compute the squared distances for the three pairs of points var dist2x = TGCVector3.Dot(pt[maxx] - pt[minx], pt[maxx] - pt[minx]); var dist2y = TGCVector3.Dot(pt[maxy] - pt[miny], pt[maxy] - pt[miny]); var dist2z = TGCVector3.Dot(pt[maxz] - pt[minz], pt[maxz] - pt[minz]); // Pick the pair (min,max) of points most distant min = minx; max = maxx; if (dist2y > dist2x && dist2y > dist2z) { max = maxy; min = miny; } if (dist2z > dist2x && dist2z > dist2y) { max = maxz; min = minz; } }
private void TellIfCameraIsLookingAtThing(Thing thing) { TGCVector3 dist = thing.Center - Camera.Position; bool isClose = Math.Abs(dist.Length()) - D3DDevice.Instance.ZNearPlaneDistance < 500; dist.Normalize(); TGCVector3 normalLookAt = TGCVector3.Normalize(Camera.LookAt - Camera.Position); float dot = TGCVector3.Dot(dist, normalLookAt); thing.Looked = isClose && dot > 0.985; }
/// <summary> /// Crear esfera a partir de los puntos mas distintas /// </summary> private static SphereStruct sphereFromDistantPoints(TGCVector3[] pt) { // Find the most separated point pair defining the encompassing AABB int min, max; mostSeparatedPointsOnAABB(pt, out min, out max); // Set up sphere to just encompass these two points var s = new SphereStruct(); s.center = (pt[min] + pt[max]) * 0.5f; s.radius = TGCVector3.Dot(pt[max] - s.center, pt[max] - s.center); s.radius = FastMath.Sqrt(s.radius); return(s); }
private void updateViewMatrix() { m_orientation.Normalize(); m_viewMatrix = TGCMatrix.RotationTGCQuaternion(m_orientation); var m_xAxis = new TGCVector3(m_viewMatrix.M11, m_viewMatrix.M21, m_viewMatrix.M31); var m_yAxis = new TGCVector3(m_viewMatrix.M12, m_viewMatrix.M22, m_viewMatrix.M32); var m_zAxis = new TGCVector3(m_viewMatrix.M13, m_viewMatrix.M23, m_viewMatrix.M33); Eye = Target + m_zAxis * -m_offsetDistance; m_viewMatrix.M41 = -TGCVector3.Dot(m_xAxis, Eye); m_viewMatrix.M42 = -TGCVector3.Dot(m_yAxis, Eye); m_viewMatrix.M43 = -TGCVector3.Dot(m_zAxis, Eye); }
public static TGCVector3 rotate_vector_by_quaternion(TGCVector3 v, Quaternion q) { // Extract the vector part of the quaternion TGCVector3 u = new TGCVector3(q.X, q.Y, q.Z); // Extract the scalar part of the quaternion float s = q.W; // Do the math var vprime = 2.0f * TGCVector3.Dot(u, v) * u + (s * s - TGCVector3.Dot(u, u)) * v + 2.0f * s * TGCVector3.Cross(u, v); return(vprime); }
/// <summary> /// Given Sphere s and Point p, update s (if needed) to just encompass p /// </summary> private static void sphereOfSphereAndPt(ref SphereStruct s, TGCVector3 p) { // Compute squared distance between point and sphere center var d = p - s.center; var dist2 = TGCVector3.Dot(d, d); // Only update s if point p is outside it if (dist2 > s.radius * s.radius) { var dist = FastMath.Sqrt(dist2); var newRadius = (s.radius + dist) * 0.5f; var k = (newRadius - s.radius) / dist; s.radius = newRadius; s.center += d * k; } }
/// <summary> /// Recorrer el BSP desde un punto de inicio hasta un punto de fin y detectar colisiones /// </summary> private TGCVector3 Trace(TGCVector3 vStart, TGCVector3 vEnd) { // Initially we set our trace ratio to 1.0f, which means that we don't have // a collision or intersection point, so we can move freely. traceRatio = 1.0f; vCollisionNormal = TGCVector3.Empty; // We start out with the first node (0), setting our start and end ratio to 0 and 1. // We will recursively go through all of the nodes to see which brushes we should check. CheckNode(0, 0.0f, 1.0f, vStart, vEnd); // If the traceRatio is STILL 1.0f, then we never collided and just return our end position if (traceRatio == 1.0f) { return(vEnd); } // If we get here then it's assumed that we collided and need to move the position // the correct distance from the starting position a position around the intersection // point. This is done by the cool equation below (described in detail at top of page). // Set our new position to a position that is right up to the brush we collided with var vNewPosition = vStart + (vEnd - vStart) * traceRatio; //Aplico el Sliding var vMove = vEnd - vNewPosition; var distance = TGCVector3.Dot(vMove, vCollisionNormal); var vEndPosition = vEnd - vCollisionNormal * distance; //como me movi, Hay que detectar si hubo otra colision vNewPosition = Trace(vNewPosition, vEndPosition); if (vCollisionNormal.Y > 0.2f || OnGround) { OnGround = true; } else { OnGround = false; } // Return the new position to be used by our camera (or player)) return(vNewPosition); }
public void UpdateRotationY(float rotation) { //Si entra es porque la rueda está clavada en el centro, no hace nada. if (TGCVector3.Dot(vectorCostado, vectorAdelante) == 0) { return; } var updatedRotation = this.CheckFrontVector(rotation); this.rotationY = TGCMatrix.RotationY(updatedRotation) * this.rotationY; this.vectorAdelante.TransformCoordinate(TGCMatrix.RotationY(updatedRotation)); if (updatedRotation > 0 && TGCVector3.Dot(this.vectorCostado, this.vectorAdelante) < 0) { this.vectorAdelante = new TGCVector3(0, 0, 1); this.rotationY = TGCMatrix.Identity; } }
public override void Render() { PreRender(); // 1) Crear un vector en 3D var v = new TGCVector3(0, 19, -1759.21f); // 2) Producto escalar entre dos vectores (dot product) var v1 = new TGCVector3(0, 19, -1759.21f); var v2 = new TGCVector3(0, 19, -1759.21f); var dotResult = TGCVector3.Dot(v1, v2); // 3) Producto vectorial entre dos vectores (cross product). El orden de v1 y v2 influye en la orientacion del resultado var crossResultVec = TGCVector3.Cross(v1, v2); // 4) Distancia entre dos puntos var p1 = new TGCVector3(100, 200, 300); var p2 = new TGCVector3(1000, 2000, 3000); var distancia = TGCVector3.Length(p2 - p1); var distanciaCuadrada = TGCVector3.LengthSq(p2 - p1); //Es mas eficiente porque evita la raiz cuadrada (pero te da el valor al cuadrado) // 5) Normalizar vector var norm = TGCVector3.Normalize(v1); // 6) Obtener el angulo que hay entre dos vectores que estan en XZ, expresion A.B=|A||B|cos(a) var v3 = new TGCVector3(-1, 0, 19); var v4 = new TGCVector3(3, 0, -5); var angle = FastMath.Acos(TGCVector3.Dot(TGCVector3.Normalize(v3), TGCVector3.Normalize(v4))); //Tienen que estar normalizados // 7) Tenemos un objeto que rota un cierto angulo en Y (ej: un auto) y queremos saber los componentes X,Z para donde tiene que avanzar al moverse var rotacionY = FastMath.PI_HALF; var componenteX = FastMath.Sin(rotacionY); var componenteZ = FastMath.Cos(rotacionY); float velocidadMovimiento = 100; //Ojo que este valor deberia siempre multiplicarse por el elapsedTime var movimientoAdelante = new TGCVector3(componenteX * velocidadMovimiento, 0, componenteZ * velocidadMovimiento); DrawText.drawText( "Este ejemplo no muestra nada por pantalla. Sino que es para leer el codigo y sus comentarios.", 5, 50, Color.Yellow); PostRender(); }
/// <summary> /// Setear la camara con una determinada posicion y lookAt /// </summary> public void lookAt(TGCVector3 pos, TGCVector3 lookAt) { //TODO: solo funciona bien para hacer un TopView var v = pos - lookAt; var length = TGCVector3.Length(v); v.Scale(1 / length); CameraDistance = length; upVector = TGCVector3.Up; diffX = 0; diffY = 0.01f; diffZ = 1; BaseRotX = -FastMath.Acos(TGCVector3.Dot(new TGCVector3(0, 0, -1), v)); //baseRotY = FastMath.Acos(TGCVector3.Dot(new TGCVector3(0, 0, -1), v)); BaseRotY = 0; CameraCenter = lookAt; }
/** * Chequea dado el radio de apertura y la direccion en que mira la nave enemigo, si esta viendo al xwing * principal. **/ public bool XwingSeEncuentraEnRadioDeVisibilidad() { vectorDistancia = CommonHelper.SumarVectores(target.GetPosition(), -posicion); float largoDistancia = vectorDistancia.Length(); visible = TGCVector3.Dot(-vectorDistancia, target.coordenadaDireccionCartesiana) > 0 && largoDistancia < 2000; if (largoDistancia > DistanciaMinimaPersecucion && target.GetPosition().Y > AlturaMinimaChequeoXwing) { coordenadaAXwing = new CoordenadaEsferica(vectorDistancia.X, vectorDistancia.Y, vectorDistancia.Z); CoordenadaEsferica dif = this.coordenadaEsferica.Diferencia(coordenadaAXwing); return(dif.acimutal < radioAperturaVisibilidad && dif.polar < radioAperturaVisibilidad && vectorDistancia.Length() < distanciaLejanaVisibilidad); } else { return(false); } }
public void RotateY(float rotacion) { if (rotacion < 0) { var dot = TGCVector3.Dot(this.vectorAdelante, this.vectorLimiteDerecho); if (dot > 0) { this.rotationY = TGCMatrix.RotationY(rotacion) * this.rotationY; this.vectorAdelante.TransformCoordinate(TGCMatrix.RotationY(rotacion)); } } else { var dot = TGCVector3.Dot(this.vectorLimiteIzquierdo, this.vectorAdelante); if (dot > 0) { this.rotationY = TGCMatrix.RotationY(rotacion) * this.rotationY; this.vectorAdelante.TransformCoordinate(TGCMatrix.RotationY(rotacion)); } } }
/// <summary> /// Reconstruct the view TGCMatrix. /// </summary> private void reconstructViewTGCMatrix(bool orthogonalizeAxes) { if (orthogonalizeAxes) { // Regenerate the camera's local axes to orthogonalize them. zAxis.Normalize(); yAxis = TGCVector3.Cross(zAxis, xAxis); yAxis.Normalize(); xAxis = TGCVector3.Cross(yAxis, zAxis); xAxis.Normalize(); viewDir = zAxis; LookAt = eye + viewDir; } // Reconstruct the view TGCMatrix. viewTGCMatrix.M11 = xAxis.X; viewTGCMatrix.M21 = xAxis.Y; viewTGCMatrix.M31 = xAxis.Z; viewTGCMatrix.M41 = -TGCVector3.Dot(xAxis, eye); viewTGCMatrix.M12 = yAxis.X; viewTGCMatrix.M22 = yAxis.Y; viewTGCMatrix.M32 = yAxis.Z; viewTGCMatrix.M42 = -TGCVector3.Dot(yAxis, eye); viewTGCMatrix.M13 = zAxis.X; viewTGCMatrix.M23 = zAxis.Y; viewTGCMatrix.M33 = zAxis.Z; viewTGCMatrix.M43 = -TGCVector3.Dot(zAxis, eye); viewTGCMatrix.M14 = 0.0f; viewTGCMatrix.M24 = 0.0f; viewTGCMatrix.M34 = 0.0f; viewTGCMatrix.M44 = 1.0f; }
public void UpdateInternalValues() { frontVector = new TGCVector3(Vector3.TransformNormal(-Vector3.UnitZ, RigidBody.InterpolationWorldTransform)); var velocityVector = new TGCVector3(RigidBody.InterpolationLinearVelocity.X, 0, RigidBody.InterpolationLinearVelocity.Z); if (velocityVector.Length() < 0.12f) { velocityVector = TGCVector3.Empty; } var speedAngle = FastMath.Acos(TGCVector3.Dot(frontVector, velocityVector) / (frontVector.Length() * velocityVector.Length())); velocityVector.Multiply(2f); currentSpeed = (int)velocityVector.Length(); if (speedAngle >= FastMath.PI_HALF) { currentSpeed *= -1; } yawPitchRoll = Quat.ToEulerAngles(RigidBody.Orientation); }
virtual public void Collide(Vehicle car) { var dot = TGCVector3.Dot(car.vectorAdelante, this.outDirection); var modulusProduct = car.vectorAdelante.Length() * this.outDirection.Length(); var angle = (float)Math.Acos(dot / modulusProduct); var yCross = TGCVector3.Cross(car.vectorAdelante, this.outDirection).Y; var giro = (yCross > 0) ? angle : -angle; car.Girar(giro); if (!(car is ArtificialIntelligence)) { Scene.GetInstance().camera.rotateY(giro); } if (car.GetVelocidadActual() < 0) { car.Girar(FastMath.PI); if (!(car is ArtificialIntelligence)) { Scene.GetInstance().camera.rotateY(FastMath.PI); } } }
/// <summary> /// Configura la posicion de la cámara /// </summary> private void setCamera(TGCVector3 eye, TGCVector3 target, TGCVector3 up) { this.eye = eye; zAxis = target - eye; zAxis.Normalize(); viewDir = zAxis; LookAt = eye + viewDir; xAxis = TGCVector3.Cross(up, zAxis); xAxis.Normalize(); yAxis = TGCVector3.Cross(zAxis, xAxis); yAxis.Normalize(); //xAxis.Normalize(); viewTGCMatrix = TGCMatrix.Identity; viewTGCMatrix.M11 = xAxis.X; viewTGCMatrix.M21 = xAxis.Y; viewTGCMatrix.M31 = xAxis.Z; viewTGCMatrix.M41 = -TGCVector3.Dot(xAxis, eye); viewTGCMatrix.M12 = yAxis.X; viewTGCMatrix.M22 = yAxis.Y; viewTGCMatrix.M32 = yAxis.Z; viewTGCMatrix.M42 = -TGCVector3.Dot(yAxis, eye); viewTGCMatrix.M13 = zAxis.X; viewTGCMatrix.M23 = zAxis.Y; viewTGCMatrix.M33 = zAxis.Z; viewTGCMatrix.M43 = -TGCVector3.Dot(zAxis, eye); // Extract the pitch angle from the view TGCMatrix. accumPitchDegrees = Geometry.RadianToDegree((float)-Math.Asin(viewTGCMatrix.M23)); }
private void updateViewMatrix(float elapsedTimeSec) { m_orientation.Normalize(); m_viewMatrix = TGCMatrix.RotationTGCQuaternion(m_orientation); var m_xAxis = new TGCVector3(m_viewMatrix.M11, m_viewMatrix.M21, m_viewMatrix.M31); var m_yAxis = new TGCVector3(m_viewMatrix.M12, m_viewMatrix.M22, m_viewMatrix.M32); var m_zAxis = new TGCVector3(m_viewMatrix.M13, m_viewMatrix.M23, m_viewMatrix.M33); // Calculate the new camera position. The 'idealPosition' is where the // camera should be position. The camera should be positioned directly // behind the target at the required offset distance. What we're doing here // is rather than have the camera immediately snap to the 'idealPosition' // we slowly move the camera towards the 'idealPosition' using a spring // system. // // References: // Stone, Jonathan, "Third-Person Camera Navigation," Game Programming // Gems 4, Andrew Kirmse, Editor, Charles River Media, Inc., 2004. var idealPosition = Target + m_zAxis * -m_offsetDistance; var displacement = Eye - idealPosition; var springAcceleration = -Spring * displacement - Damping * m_velocity; m_velocity += springAcceleration * elapsedTimeSec; Eye += m_velocity * elapsedTimeSec; // The view matrix is always relative to the camera's current position // 'm_eye'. Since a spring system is being used here 'm_eye' will be // relative to 'desiredPosition'. When the camera is no longer being moved // 'm_eye' will become the same as 'desiredPosition'. The local x, y, and // z axes that were extracted from the camera's orientation 'm_orienation' // is correct for the 'desiredPosition' only. We need to recompute these // axes so that they're relative to 'm_eye'. Once that's done we can use // those axes to reconstruct the view matrix. m_zAxis = Target - Eye; m_zAxis.Normalize(); m_xAxis = TGCVector3.Cross(m_targetYAxis, m_zAxis); m_xAxis.Normalize(); m_yAxis = TGCVector3.Cross(m_zAxis, m_xAxis); m_yAxis.Normalize(); m_viewMatrix = TGCMatrix.Identity; m_viewMatrix.M11 = m_xAxis.X; m_viewMatrix.M21 = m_xAxis.Y; m_viewMatrix.M31 = m_xAxis.Z; m_viewMatrix.M41 = -TGCVector3.Dot(m_xAxis, Eye); m_viewMatrix.M12 = m_yAxis.X; m_viewMatrix.M22 = m_yAxis.Y; m_viewMatrix.M32 = m_yAxis.Z; m_viewMatrix.M42 = -TGCVector3.Dot(m_yAxis, Eye); m_viewMatrix.M13 = m_zAxis.X; m_viewMatrix.M23 = m_zAxis.Y; m_viewMatrix.M33 = m_zAxis.Z; m_viewMatrix.M43 = -TGCVector3.Dot(m_zAxis, Eye); }
/// <summary> /// Indica si un cilindro colisiona con un segmento. /// El cilindro se especifica con dos puntos centrales "cylinderInit" y "cylinderEnd" que forman una recta y con un /// radio "radius". /// Si hay colision se devuelve el instante de colision "t". /// No chequear EndCaps /// </summary> /// <param name="segmentInit">Punto de inicio del segmento</param> /// <param name="segmentEnd">Punto de fin del segmento</param> /// <param name="cylinderInit">Punto inicial del cilindro</param> /// <param name="cylinderEnd">Punto final del cilindro</param> /// <param name="radius">Radio del cilindro</param> /// <param name="t">Instante de colision</param> /// <returns>True si hay colision</returns> private static bool intersectSegmentCylinderNoEndcap(TGCVector3 segmentInit, TGCVector3 segmentEnd, TGCVector3 cylinderInit, TGCVector3 cylinderEnd, float radius, out float t) { t = -1; TGCVector3 d = cylinderEnd - cylinderInit, m = segmentInit - cylinderInit, n = segmentEnd - segmentInit; var md = TGCVector3.Dot(m, d); var nd = TGCVector3.Dot(n, d); var dd = TGCVector3.Dot(d, d); // Test if segment fully outside either endcap of cylinder if (md < 0.0f && md + nd < 0.0f) { return(false); // Segment outside ’p’ side of cylinder } if (md > dd && md + nd > dd) { return(false); // Segment outside ’q’ side of cylinder } var nn = TGCVector3.Dot(n, n); var mn = TGCVector3.Dot(m, n); var a = dd * nn - nd * nd; var k = TGCVector3.Dot(m, m) - radius * radius; var c = dd * k - md * md; if (FastMath.Abs(a) < float.Epsilon) { // Segment runs parallel to cylinder axis if (c > 0.0f) { return(false); // 'a' and thus the segment lie outside cylinder } // Now known that segment intersects cylinder; figure out how it intersects if (md < 0.0f) { t = -mn / nn; // Intersect segment against 'p' endcap } else if (md > dd) { t = (nd - mn) / nn; // Intersect segment against ’q’ endcap } else { t = 0.0f; // ’a’ lies inside cylinder } return(true); } var b = dd * mn - nd * md; var discr = b * b - a * c; if (discr < 0.0f) { return(false); // No real roots; no intersection } t = (-b - FastMath.Sqrt(discr)) / a; if (t < 0.0f || t > 1.0f) { return(false); // Intersection lies outside segment } /* No chequear EndCaps * * if (md + t * nd < 0.0f) { * // Intersection outside cylinder on 'p' side * if (nd <= 0.0f) return false; // Segment pointing away from endcap * t = -md / nd; * // Keep intersection if Dot(S(t) - p, S(t) - p) <= r^2 * return k + t * (2.0f * mn + t * nn) <= 0.0f; * } else if (md + t * nd > dd) { * // Intersection outside cylinder on 'q' side * if (nd >= 0.0f) return false; // Segment pointing away from endcap * t = (dd - md) / nd; * // Keep intersection if Dot(S(t) - q, S(t) - q) <= r^2 * return k + dd - 2.0f * md + t * (2.0f * (mn - nd) + t * nn) <= 0.0f; * } */ // Segment intersects cylinder between the endcaps; t is correct return(true); }
private TGCQuaternion QuaternionDireccion(TGCVector3 direccionDisparoNormalizado) { TGCVector3 DireccionA = new TGCVector3(0, 0, -1); TGCVector3 cross = TGCVector3.Cross(DireccionA, direccionDisparoNormalizado); TGCQuaternion newRotation = TGCQuaternion.RotationAxis(cross, FastMath.Acos(TGCVector3.Dot(DireccionA, direccionDisparoNormalizado))); return(newRotation); }
public override void Render() { PreRender(); //Si hacen clic con el mouse, ver si hay colision con el suelo if (Input.buttonPressed(TgcD3dInput.MouseButtons.BUTTON_LEFT)) { //Actualizar Ray de colisión en base a posición del mouse pickingRay.updateRay(); //Detectar colisión Ray-AABB if (TgcCollisionUtils.intersectRayAABB(pickingRay.Ray, suelo.BoundingBox, out newPosition)) { //Fijar nueva posición destino applyMovement = true; collisionPointMesh.Position = newPosition; directionArrow.PEnd = new TGCVector3(newPosition.X, 30f, newPosition.Z); //Rotar modelo en base a la nueva dirección a la que apunta var direction = TGCVector3.Normalize(newPosition - mesh.Position); var angle = FastMath.Acos(TGCVector3.Dot(originalMeshRot, direction)); var axisRotation = TGCVector3.Cross(originalMeshRot, direction); meshRotationMatrix = TGCMatrix.RotationAxis(axisRotation, angle); } } var speed = speedModifier.Value; //Interporlar movimiento, si hay que mover if (applyMovement) { //Ver si queda algo de distancia para mover var posDiff = newPosition - mesh.Position; var posDiffLength = posDiff.LengthSq(); if (posDiffLength > float.Epsilon) { //Movemos el mesh interpolando por la velocidad var currentVelocity = speed * ElapsedTime; posDiff.Normalize(); posDiff.Multiply(currentVelocity); //Ajustar cuando llegamos al final del recorrido var newPos = mesh.Position + posDiff; if (posDiff.LengthSq() > posDiffLength) { newPos = newPosition; } //Actualizar flecha de movimiento directionArrow.PStart = new TGCVector3(mesh.Position.X, 30f, mesh.Position.Z); directionArrow.updateValues(); //Actualizar posicion del mesh mesh.Position = newPos; //Como desactivamos la transformacion automatica, tenemos que armar nosotros la matriz de transformacion mesh.Transform = meshRotationMatrix * TGCMatrix.Translation(mesh.Position); //Actualizar camara camaraInterna.Target = mesh.Position; } //Se acabo el movimiento else { applyMovement = false; } } //Mostrar caja con lugar en el que se hizo click, solo si hay movimiento if (applyMovement) { collisionPointMesh.Render(); directionArrow.Render(); } suelo.Render(); mesh.Render(); PostRender(); }
/// <summary> /// Checks our movement vector against all the planes of the brush /// </summary> private void CheckBrush(QBrush pBrush, TGCVector3 vStart, TGCVector3 vEnd) { var startRatio = -1.0f; // Like in BrushCollision.htm, start a ratio at -1 var endRatio = 1.0f; // Set the end ratio to 1 var startsOut = false; // This tells us if we starting outside the brush // This function actually does the collision detection between our movement // vector and the brushes in the world data. We will go through all of the // brush sides and check our start and end ratio against the planes to see if // they pass each other. We start the startRatio at -1 and the endRatio at // 1, but as we set the ratios to their intersection points (ratios), then // they slowly move toward each other. If they pass each other, then there // is definitely not a collision. // Go through all of the brush sides and check collision against each plane for (var i = 0; i < pBrush.numSides; i++) { // Here we grab the current brush side and plane in this brush var pBrushSide = bspMap.Data.brushSides[pBrush.firstSide + i]; var pPlane = bspMap.Data.planes[pBrushSide.planeNum]; // Let's store a variable for the offset (like for sphere collision) var offset = 0.0f; // If we are testing sphere collision we need to add the sphere radius if (traceType == TYPE_SPHERE) { offset = traceRadius; } // Test the start and end points against the current plane of the brush side. // Notice that we add an offset to the distance from the origin, which makes // our sphere collision work. var startDistance = TGCVector3.Dot(vStart, pPlane.normal) - (pPlane.dist + offset); var endDistance = TGCVector3.Dot(vEnd, pPlane.normal) - (pPlane.dist + offset); // This is the last beefy part of code in this tutorial. In this // section we need to do a few special checks to see which extents // we should use. We do this by checking the x,y,z of the normal. // If the vNormal.x is less than zero, we want to use the Max.x // value, otherwise use the Min.x value. We do these checks because // we want the corner of the bounding box that is closest to the plane to // test for collision. Write it down on paper and see how this works. // We want to grab the closest corner (x, y, or z value that is...) so we // dont go through the wall. This works because the bounding box is axis aligned. // Store the offset that we will check against the plane // If we are using AABB collision if (traceType == TYPE_BOX) { var vOffset = TGCVector3.Empty; // Grab the closest corner (x, y, or z value) that is closest to the plane vOffset.X = pPlane.normal.X < 0 ? vTraceMaxs.X : vTraceMins.X; vOffset.Y = pPlane.normal.Y < 0 ? vTraceMaxs.Y : vTraceMins.Y; vOffset.Z = pPlane.normal.Z < 0 ? vTraceMaxs.Z : vTraceMins.Z; // Use the plane equation to grab the distance our start position is from the plane. // We need to add the offset to this to see if the box collides with the plane, // even if the position doesn't. startDistance = TGCVector3.Dot(vStart + vOffset, pPlane.normal) - pPlane.dist; // Get the distance our end position is from this current brush plane endDistance = TGCVector3.Dot(vEnd + vOffset, pPlane.normal) - pPlane.dist; } // Make sure we start outside of the brush's volume if (startDistance > 0) { startsOut = true; } // Stop checking since both the start and end position are in front of the plane if (startDistance > 0 && endDistance > 0) { return; } // Continue on to the next brush side if both points are behind or on the plane if (startDistance <= 0 && endDistance <= 0) { continue; } // If the distance of the start point is greater than the end point, we have a collision! if (startDistance > endDistance) { // This gets a ratio from our starting point to the approximate collision spot var Ratio1 = (startDistance - kEpsilon) / (startDistance - endDistance); // If this is the first time coming here, then this will always be true, // since startRatio starts at -1.0f. We want to find the closest collision, // so we still continue to check all of the brushes before quitting. if (Ratio1 > startRatio) { // Set the startRatio (currently the closest collision distance from start) startRatio = Ratio1; bCollided = true; // Let us know we collided! vCollisionNormal = pPlane.normal; if (vCollisionNormal.Y >= 0.2f) { OnGround = true; } // This checks first tests if we actually moved along the x or z-axis, // meaning that we went in a direction somewhere. The next check makes // sure that we don't always check to step every time we collide. If // the normal of the plane has a Y value of 1, that means it's just the // flat ground and we don't need to check if we can step over it, it's flat! if ((vStart.X != vEnd.X || vStart.Z != vEnd.Z) && pPlane.normal.Y != 1) { // We can try and step over the wall we collided with bTryStep = true; } } } else { // Get the ratio of the current brush side for the endRatio var Ratio = (startDistance + kEpsilon) / (startDistance - endDistance); // If the ratio is less than the current endRatio, assign a new endRatio. // This will usually always be true when starting out. if (Ratio < endRatio) { endRatio = Ratio; } } } // If we didn't start outside of the brush we don't want to count this collision - return; if (startsOut == false) { return; } // If our startRatio is less than the endRatio there was a collision!!! if (startRatio < endRatio) { // Make sure the startRatio moved from the start and check if the collision // ratio we just got is less than the current ratio stored in m_traceRatio. // We want the closest collision to our original starting position. if (startRatio > -1 && startRatio < traceRatio) { // If the startRatio is less than 0, just set it to 0 if (startRatio < 0) { startRatio = 0; } // Store the new ratio in our member variable for later traceRatio = startRatio; } } }
/// <summary> /// Detectar colision entre un Ray y una Capsula /// Basado en: http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrLine3Capsule3.cpp /// </summary> /// <param name="ray">Ray</param> /// <param name="capsule">Capsula</param> /// <param name="t">Menor instante de colision</param> /// <returns>True si hay colision</returns> private bool intersectRayCapsule(TgcRay.RayStruct ray, Capsule capsule, out float t) { t = -1; var origin = ray.origin; var dir = ray.direction; // Create a coordinate system for the capsule. In this system, the // capsule segment center C is the origin and the capsule axis direction // W is the z-axis. U and V are the other coordinate axis directions. // If P = x*U+y*V+z*W, the cylinder containing the capsule plane is // x^2 + y^2 = r^2, where r is the capsule radius. The finite cylinder // that makes up the capsule minus its hemispherical end caps has z-values // |z| <= e, where e is the extent of the capsule segment. The top // hemisphere cap is x^2+y^2+(z-e)^2 = r^2 for z >= e, and the bottom // hemisphere cap is x^2+y^2+(z+e)^2 = r^2 for z <= -e. var U = capsule.segment.dir; var V = U; var W = U; generateComplementBasis(ref U, ref V, W); var rSqr = capsule.radius * capsule.radius; var extent = capsule.segment.extent; // Convert incoming line origin to capsule coordinates. var diff = origin - capsule.segment.center; var P = new TGCVector3(TGCVector3.Dot(U, diff), TGCVector3.Dot(V, diff), TGCVector3.Dot(W, diff)); // Get the z-value, in capsule coordinates, of the incoming line's unit-length direction. var dz = TGCVector3.Dot(W, dir); if (FastMath.Abs(dz) >= 1f - float.Epsilon) { // The line is parallel to the capsule axis. Determine whether the line intersects the capsule hemispheres. var radialSqrDist = rSqr - P.X * P.X - P.Y * P.Y; if (radialSqrDist < 0f) { // Line outside the cylinder of the capsule, no intersection. return(false); } // line intersects the hemispherical caps var zOffset = FastMath.Sqrt(radialSqrDist) + extent; if (dz > 0f) { t = -P.Z - zOffset; } else { t = P.Z - zOffset; } return(true); } // Convert incoming line unit-length direction to capsule coordinates. var D = new TGCVector3(TGCVector3.Dot(U, dir), TGCVector3.Dot(V, dir), dz); // Test intersection of line P+t*D with infinite cylinder x^2+y^2 = r^2. // This reduces to computing the roots of a quadratic equation. If // P = (px,py,pz) and D = (dx,dy,dz), then the quadratic equation is // (dx^2+dy^2)*t^2 + 2*(px*dx+py*dy)*t + (px^2+py^2-r^2) = 0 var a0 = P.X * P.X + P.Y * P.Y - rSqr; var a1 = P.X * D.X + P.Y * D.Y; var a2 = D.X * D.X + D.Y * D.Y; var discr = a1 * a1 - a0 * a2; if (discr < 0f) { // Line does not intersect infinite cylinder. return(false); } float root, inv, tValue, zValue; var quantity = 0; if (discr > float.Epsilon) { // Line intersects infinite cylinder in two places. root = FastMath.Sqrt(discr); inv = 1f / a2; tValue = (-a1 - root) * inv; zValue = P.Z + tValue * D.Z; if (FastMath.Abs(zValue) <= extent) { quantity++; t = tValue; } tValue = (-a1 + root) * inv; zValue = P.Z + tValue * D.Z; if (FastMath.Abs(zValue) <= extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); } if (quantity == 2) { // Line intersects capsule plane in two places. return(true); } } else { // Line is tangent to infinite cylinder. tValue = -a1 / a2; zValue = P.Z + tValue * D.Z; if (FastMath.Abs(zValue) <= extent) { t = tValue; return(true); } } // Test intersection with bottom hemisphere. The quadratic equation is // t^2 + 2*(px*dx+py*dy+(pz+e)*dz)*t + (px^2+py^2+(pz+e)^2-r^2) = 0 // Use the fact that currently a1 = px*dx+py*dy and a0 = px^2+py^2-r^2. // The leading coefficient is a2 = 1, so no need to include in the // construction. var PZpE = P.Z + extent; a1 += PZpE * D.Z; a0 += PZpE * PZpE; discr = a1 * a1 - a0; if (discr > float.Epsilon) { root = FastMath.Sqrt(discr); tValue = -a1 - root; zValue = P.Z + tValue * D.Z; if (zValue <= -extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); if (quantity == 2) { return(true); } } tValue = -a1 + root; zValue = P.Z + tValue * D.Z; if (zValue <= -extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); if (quantity == 2) { return(true); } } } else if (FastMath.Abs(discr) <= float.Epsilon) { tValue = -a1; zValue = P.Z + tValue * D.Z; if (zValue <= -extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); if (quantity == 2) { return(true); } } } // Test intersection with top hemisphere. The quadratic equation is // t^2 + 2*(px*dx+py*dy+(pz-e)*dz)*t + (px^2+py^2+(pz-e)^2-r^2) = 0 // Use the fact that currently a1 = px*dx+py*dy+(pz+e)*dz and // a0 = px^2+py^2+(pz+e)^2-r^2. The leading coefficient is a2 = 1, so // no need to include in the construction. a1 -= 2f * extent * D.Z; a0 -= 4 * extent * P.Z; discr = a1 * a1 - a0; if (discr > float.Epsilon) { root = FastMath.Sqrt(discr); tValue = -a1 - root; zValue = P.Z + tValue * D.Z; if (zValue >= extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); if (quantity == 2) { return(true); } } tValue = -a1 + root; zValue = P.Z + tValue * D.Z; if (zValue >= extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); if (quantity == 2) { return(true); } } } else if (FastMath.Abs(discr) <= float.Epsilon) { tValue = -a1; zValue = P.Z + tValue * D.Z; if (zValue >= extent) { quantity++; t = TgcCollisionUtils.min(t, tValue); if (quantity == 2) { return(true); } } } return(quantity > 0); }
/// <summary> /// Traverses the BSP to find the brushes closest to our position /// </summary> private void CheckNode(int nodeIndex, float startRatio, float endRatio, TGCVector3 vStart, TGCVector3 vEnd) { // Remember, the nodeIndices are stored as negative numbers when we get to a leaf, so we // check if the current node is a leaf, which holds brushes. If the nodeIndex is negative, // the next index is a leaf (note the: nodeIndex + 1) if (nodeIndex < 0) { // If this node in the BSP is a leaf, we need to negate and add 1 to offset // the real node index into the m_pLeafs[] array. You could also do [~nodeIndex]. var pLeaf = bspMap.Data.leafs[~nodeIndex]; // We have a leaf, so let's go through all of the brushes for that leaf for (var i = 0; i < pLeaf.numLeafBrushes; i++) { // Get the current brush that we going to check var pBrush = bspMap.Data.brushes[bspMap.Data.leafbrushes[pLeaf.firstLeafBrush + i]]; // This is kind of an important line. First, we check if there is actually // and brush sides (which store indices to the normal and plane data for the brush). // If not, then go to the next one. Otherwise, we also check to see if the brush // is something that we want to collide with. For instance, there are brushes for // water, lava, bushes, misc. sprites, etc... We don't want to collide with water // and other things like bushes, so we check the texture type to see if it's a solid. // If the textureType can be binary-anded (&) and still be 1, then it's solid, // otherwise it's something that can be walked through. That's how Quake chose to // do it. // Check if we have brush sides and the current brush is solid and collidable if ((pBrush.numSides > 0) && (bspMap.Data.shaders[pBrush.shaderNum].contentFlags & 1) > 0) { // Now we delve into the dark depths of the real calculations for collision. // We can now check the movement vector against our brush planes. CheckBrush(pBrush, vStart, vEnd); } } // Since we found the brushes, we can go back up and stop recursing at this level return; } // If we haven't found a leaf in the node, then we need to keep doing some dirty work // until we find the leafs which store the brush information for collision detection. // Grad the next node to work with and grab this node's plane data var pNode = bspMap.Data.nodes[nodeIndex]; var pPlane = bspMap.Data.planes[pNode.planeNum]; // Now we do some quick tests to see which side we fall on of the node in the BSP // Here we use the plane equation to find out where our initial start position is // according the the node that we are checking. We then grab the same info for the end pos. var startDistance = TGCVector3.Dot(vStart, pPlane.normal) - pPlane.dist; var endDistance = TGCVector3.Dot(vEnd, pPlane.normal) - pPlane.dist; var offset = 0.0f; // If we are doing any type of collision detection besides a ray, we need to change // the offset for which we are testing collision against the brushes. If we are testing // a sphere against the brushes, we need to add the sphere's offset when we do the plane // equation for testing our movement vector (start and end position). * More Info * For // more info on sphere collision, check out our tutorials on this subject. // If we are doing sphere collision, include an offset for our collision tests below if (traceType == TYPE_SPHERE) { offset = traceRadius; } else if (traceType == TYPE_BOX) { // This equation does a dot product to see how far our // AABB is away from the current plane we are checking. // Since this is a distance, we need to make it an absolute // value, which calls for the fabs() function (abs() for floats). // Get the distance our AABB is from the current splitter plane offset = Math.Abs(vExtents.X * pPlane.normal.X) + Math.Abs(vExtents.Y * pPlane.normal.Y) + Math.Abs(vExtents.Z * pPlane.normal.Z); } // Below we just do a basic traversal down the BSP tree. If the points are in // front of the current splitter plane, then only check the nodes in front of // that splitter plane. Otherwise, if both are behind, check the nodes that are // behind the current splitter plane. The next case is that the movement vector // is on both sides of the splitter plane, which makes it a bit more tricky because we now // need to check both the front and the back and split up the movement vector for both sides. // Here we check to see if the start and end point are both in front of the current node. // If so, we want to check all of the nodes in front of this current splitter plane. if (startDistance >= offset && endDistance >= offset) { // Traverse the BSP tree on all the nodes in front of this current splitter plane CheckNode(pNode.children[0], startDistance, endDistance, vStart, vEnd); } // If both points are behind the current splitter plane, traverse down the back nodes else if (startDistance < -offset && endDistance < -offset) { // Traverse the BSP tree on all the nodes in back of this current splitter plane CheckNode(pNode.children[1], startDistance, endDistance, vStart, vEnd); } else { // If we get here, then our ray needs to be split in half to check the nodes // on both sides of the current splitter plane. Thus we create 2 ratios. float Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f; TGCVector3 vMiddle; // This stores the middle point for our split ray // Start of the side as the front side to check var side = pNode.children[0]; // Here we check to see if the start point is in back of the plane (negative) if (startDistance < endDistance) { // Since the start position is in back, let's check the back nodes side = pNode.children[1]; // Here we create 2 ratios that hold a distance from the start to the // extent closest to the start (take into account a sphere and epsilon). // We use epsilon like Quake does to compensate for float errors. The second // ratio holds a distance from the other size of the extents on the other side // of the plane. This essential splits the ray for both sides of the splitter plane. var inverseDistance = 1.0f / (startDistance - endDistance); Ratio1 = (startDistance - offset - kEpsilon) * inverseDistance; Ratio2 = (startDistance + offset + kEpsilon) * inverseDistance; } // Check if the starting point is greater than the end point (positive) else if (startDistance > endDistance) { // This means that we are going to recurse down the front nodes first. // We do the same thing as above and get 2 ratios for split ray. // Ratio 1 and 2 are switched in contrast to the last if statement. // This is because the start is starting in the front of the splitter plane. var inverseDistance = 1.0f / (startDistance - endDistance); Ratio1 = (startDistance + offset + kEpsilon) * inverseDistance; Ratio2 = (startDistance - offset - kEpsilon) * inverseDistance; } // Make sure that we have valid numbers and not some weird float problems. // This ensures that we have a value from 0 to 1 as a good ratio should be :) if (Ratio1 < 0.0f) { Ratio1 = 0.0f; } else if (Ratio1 > 1.0f) { Ratio1 = 1.0f; } if (Ratio2 < 0.0f) { Ratio2 = 0.0f; } else if (Ratio2 > 1.0f) { Ratio2 = 1.0f; } // Just like we do in the Trace() function, we find the desired middle // point on the ray, but instead of a point we get a middleRatio percentage. // This isn't the true middle point since we are using offset's and the epsilon value. // We also grab the middle point to go with the ratio. middleRatio = startRatio + (endRatio - startRatio) * Ratio1; vMiddle = vStart + (vEnd - vStart) * Ratio1; // Now we recurse on the current side with only the first half of the ray CheckNode(side, startRatio, middleRatio, vStart, vMiddle); // Now we need to make a middle point and ratio for the other side of the node middleRatio = startRatio + (endRatio - startRatio) * Ratio2; vMiddle = vStart + (vEnd - vStart) * Ratio2; // Depending on which side should go last, traverse the bsp with the // other side of the split ray (movement vector). if (side == pNode.children[1]) { CheckNode(pNode.children[0], middleRatio, endRatio, vMiddle, vEnd); } else { CheckNode(pNode.children[1], middleRatio, endRatio, vMiddle, vEnd); } } }