コード例 #1
0
        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();
        }
コード例 #2
0
ファイル: TgcQuad.cs プロジェクト: ospocarpa/tgc-viewer
        /// <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);
        }
コード例 #3
0
ファイル: Mostro.cs プロジェクト: dadais216/2019_2C_3572_dani
 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);
 }
コード例 #4
0
        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);
        }
コード例 #5
0
        /// <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);
        }
コード例 #6
0
 private float CheckFrontVector(float rotation)
 {
     if (TGCVector3.Dot(this.vectorCostado, this.vectorAdelante) > 0)
     {
         return(rotation);
     }
     else
     {
         return(-rotation);
     }
 }
コード例 #7
0
        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));
        }
コード例 #8
0
        /// <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;
            }
        }
コード例 #9
0
        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;
        }
コード例 #10
0
        /// <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);
        }
コード例 #11
0
        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);
        }
コード例 #12
0
        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);
        }
コード例 #13
0
        /// <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;
            }
        }
コード例 #14
0
        /// <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);
        }
コード例 #15
0
        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;
            }
        }
コード例 #16
0
        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();
        }
コード例 #17
0
        /// <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;
        }
コード例 #18
0
        /**
         * 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);
            }
        }
コード例 #19
0
 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));
         }
     }
 }
コード例 #20
0
        /// <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;
        }
コード例 #21
0
        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);
        }
コード例 #22
0
        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);
                }
            }
        }
コード例 #23
0
        /// <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));
        }
コード例 #24
0
        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);
        }
コード例 #26
0
        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);
        }
コード例 #27
0
        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();
        }
コード例 #28
0
        /// <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;
                }
            }
        }
コード例 #29
0
        /// <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);
        }
コード例 #30
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);
                }
            }
        }