/// <summary>
        /// Computes the intersection, if any, between a ray and the objects in the character's bounding box.
        /// </summary>
        /// <param name="ray">Ray to test.</param>
        /// <param name="length">Length of the ray to use in units of the ray's length.</param>
        /// <param name="earliestHit">Earliest intersection location and information.</param>
        /// <returns>Whether or not the ray hit anything.</returns>
        public bool RayCast(Ray ray, float length, out RayHit earliestHit)
        {
            earliestHit = new RayHit();
            earliestHit.T = float.MaxValue;
            foreach (var collidable in character.Body.CollisionInformation.OverlappedCollidables)
            {
                //Check to see if the collidable is hit by the ray.
                float? t = ray.Intersects(collidable.BoundingBox);
                if (t != null && t < length)
                {
                    //Is it an earlier hit than the current earliest?
                    RayHit hit;
                    if (collidable.RayCast(ray, length, SupportRayFilter, out hit) && hit.T < earliestHit.T)
                    {
                        earliestHit = hit;
                    }
                }
            }
            if (earliestHit.T == float.MaxValue)
                return false;
            return true;

        }
Exemple #2
0
        /// <summary>
        /// Find the position and object in a ray.
        /// </summary>
        /// <param name="ray">The ray.</param>
        /// <param name="maximumDistance">The maximum distance to look from the ray origin.</param>
        /// <param name="castResult">Tuple containing the rayHit result and the DummyObject if any (can be null).</param>
        /// <returns>Whether the ray hit anything.</returns>
        public bool RayCast(Ray ray, float maximumDistance, out Tuple<RayHit, DummyObject> castResult)
        {
            // Fill out default failed hit data
            castResult = new Tuple<RayHit, DummyObject>(new RayHit(), null);

            DummyObject selectedObject = GetDummyObjectFromID(GraphicsManager.GetPickingObject(ray));
            RayHit rayHit = new RayHit();

            if (selectedObject == null || !selectedObject.RayCast(ray, maximumDistance, out rayHit))
            {
                RayCastResult rayResult = new RayCastResult();
                if (!Space.RayCast(ray, maximumDistance, out rayResult))
                {
                    return false;
                }
                rayHit = rayResult.HitData;
            }

            castResult = new Tuple<RayHit, DummyObject>(rayHit, selectedObject);
            return true;
        }
Exemple #3
0
 ///<summary>
 /// Constructs a new ray cast result.
 ///</summary>
 ///<param name="hitData">Ray cast hit data.</param>
 ///<param name="hitObject">Object hit by the ray.</param>
 public RayCastResult(RayHit hitData, BroadPhaseEntry hitObject)
 {
     HitData = hitData;
     HitObject = hitObject;
 }
Exemple #4
0
        ///<summary>
        /// Tests a ray against a sphere.
        ///</summary>
        ///<param name="ray">Ray to test.</param>
        ///<param name="spherePosition">Position of the sphere.</param>
        ///<param name="radius">Radius of the sphere.</param>
        ///<param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        ///<param name="hit">Hit data of the ray, if any.</param>
        ///<returns>Whether or not the ray hits the sphere.</returns>
        public static bool RayCastSphere(ref Ray ray, ref Vector3 spherePosition, float radius, float maximumLength, out RayHit hit)
        {
            Vector3 normalizedDirection;
            float length = ray.Direction.Length();
            Vector3.Divide(ref ray.Direction, length, out normalizedDirection);
            maximumLength *= length;
            hit = new RayHit();
            Vector3 m;
            Vector3.Subtract(ref ray.Position, ref spherePosition, out m);
            float b = Vector3.Dot(m, normalizedDirection);
            float c = m.LengthSquared() - radius * radius;

            if (c > 0 && b > 0)
                return false;
            float discriminant = b * b - c;
            if (discriminant < 0)
                return false;

            hit.T = -b - (float)Math.Sqrt(discriminant);
            if (hit.T < 0)
                hit.T = 0;
            if (hit.T > maximumLength)
                return false;
            hit.T /= length;
            Vector3.Multiply(ref normalizedDirection, hit.T, out hit.Location);
            Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
            Vector3.Subtract(ref hit.Location, ref spherePosition, out hit.Normal);
            hit.Normal.Normalize();
            return true;
        }
Exemple #5
0
        /// <summary>
        /// Determines the intersection between a ray and a triangle.
        /// </summary>
        /// <param name="ray">Ray to test.</param>
        /// <param name="maximumLength">Maximum length to travel in units of the direction's length.</param>
        /// <param name="sidedness">Sidedness of the triangle to test.</param>
        /// <param name="a">First vertex of the triangle.</param>
        /// <param name="b">Second vertex of the triangle.</param>
        /// <param name="c">Third vertex of the triangle.</param>
        /// <param name="hit">Hit data of the ray, if any</param>
        /// <returns>Whether or not the ray and triangle intersect.</returns>
        public static bool FindRayTriangleIntersection(ref Ray ray, float maximumLength, TriangleSidedness sidedness, ref Vector3 a, ref Vector3 b, ref Vector3 c, out RayHit hit)
        {
            hit = new RayHit();
            Vector3 ab, ac;
            Vector3.Subtract(ref b, ref a, out ab);
            Vector3.Subtract(ref c, ref a, out ac);

            Vector3.Cross(ref ab, ref ac, out hit.Normal);
            if (hit.Normal.LengthSquared() < Epsilon)
                return false; //Degenerate triangle!

            float d;
            Vector3.Dot(ref ray.Direction, ref hit.Normal, out d);
            d = -d;
            switch (sidedness)
            {
                case TriangleSidedness.DoubleSided:
                    if (d <= 0) //Pointing the wrong way.  Flip the normal.
                    {
                        Vector3.Negate(ref hit.Normal, out hit.Normal);
                        d = -d;
                    }
                    break;
                case TriangleSidedness.Clockwise:
                    if (d <= 0) //Pointing the wrong way.  Can't hit.
                        return false;

                    break;
                case TriangleSidedness.Counterclockwise:
                    if (d >= 0) //Pointing the wrong way.  Can't hit.
                        return false;

                    Vector3.Negate(ref hit.Normal, out hit.Normal);
                    d = -d;
                    break;
            }

            Vector3 ap;
            Vector3.Subtract(ref ray.Position, ref a, out ap);

            Vector3.Dot(ref ap, ref hit.Normal, out hit.T);
            hit.T /= d;
            if (hit.T < 0 || hit.T > maximumLength)
                return false;//Hit is behind origin, or too far away.

            Vector3.Multiply(ref ray.Direction, hit.T, out hit.Location);
            Vector3.Add(ref ray.Position, ref hit.Location, out hit.Location);

            // Compute barycentric coordinates
            Vector3.Subtract(ref hit.Location, ref a, out ap);
            float ABdotAB, ABdotAC, ABdotAP;
            float ACdotAC, ACdotAP;
            Vector3.Dot(ref ab, ref ab, out ABdotAB);
            Vector3.Dot(ref ab, ref ac, out ABdotAC);
            Vector3.Dot(ref ab, ref ap, out ABdotAP);
            Vector3.Dot(ref ac, ref ac, out ACdotAC);
            Vector3.Dot(ref ac, ref ap, out ACdotAP);

            float denom = 1 / (ABdotAB * ACdotAC - ABdotAC * ABdotAC);
            float u = (ACdotAC * ABdotAP - ABdotAC * ACdotAP) * denom;
            float v = (ABdotAB * ACdotAP - ABdotAC * ABdotAP) * denom;

            return (u >= -Toolbox.BigEpsilon) && (v >= -Toolbox.BigEpsilon) && (u + v <= 1 + Toolbox.BigEpsilon);

        }
        void IBeforeNarrowPhaseUpdateable.Update(float dt)
        {
            //Cast down to find the support.

            //In a 6DOF character, the starting transform will vary.
            //For now, just assume it's locked to Up.
            RigidTransform startingTransform = new RigidTransform();
            startingTransform.Position = Body.Position;
            startingTransform.Position.Y -= Body.Length * .5f;
            startingTransform.Orientation = Quaternion.Identity;

            //Compute the sweep direction.  This too will change in a 6DOF character (Body.OrientationMatrix.Down instead of Vector3.Down).
            //If the body had traction last frame, then do a full-length sweep; this will detect step-downs.
            //If it didn't have traction, just cast down by one step height to try and find a support.
            //The reason why Body.Radius is added is that the starting transform is embedded in the capsule.
            sweepLength = hasTraction ? StepHeight * 2 + Body.Radius : StepHeight + Body.Radius + supportMargin;
            sweep = new Vector3(0, -sweepLength, 0);

            //Cycle through every collidable and find the first time of impact for the convex cast.

            support = null;
            supportData = new RayHit() { T = float.MaxValue }; //Set the T value to an extremely high value to start with so that any result will be lower.
            foreach (var collidable in Body.CollisionInformation.OverlappedCollidables)
            {
                if (convexCastDownVolume.Intersects(collidable.BoundingBox))
                {
                    RayHit hit;
                    if (collidable.ConvexCast(castShape, ref startingTransform, ref sweep, out hit) && hit.T < supportData.T)
                    {
                        supportData = hit;
                        support = collidable;
                    }

                }
            }

            goalSupportT = (Body.Radius + StepHeight) / sweepLength;

            //If there's any support, analyze it.
            //Check the slope of the hit against the maximum.
            //This is a dot product of the normal against the body up vector (in this implementation, always {0, 1, 0}).
            //For a fixed up vector of {0,1,0}, the dot product result is just the Y component of the normal.
            //The following slope test is equivalent to MaximumTractionSlope > Math.Acos(Vector3.Dot(-firstHitData.Normal, Vector3.Up))
            hasTraction = support != null & cosMaximumTractionSlope < -supportData.Normal.Y;

            //Compute the relative velocity between the capsule and its support, if any.
            //The relative velocity will be updated as impulses are applied.
            Vector3 relativeVelocity = Body.LinearVelocity;
            if (IsSupported)
            {
                //Only entities has velocity.
                var entityCollidable = support as EntityCollidable;
                if (entityCollidable != null)
                {
                    Vector3 entityVelocity = Toolbox.GetVelocityOfPoint(supportData.Location, entityCollidable.Entity);
                    Vector3.Subtract(ref relativeVelocity, ref entityVelocity, out relativeVelocity);
                    //TODO: Multithreaded safety.  If characters are running in parallel, ensure that this operation will not corrupt anything.
                    if (entityCollidable.Entity.IsDynamic)
                        entityCollidable.Entity.ActivityInformation.Activate();
                }
            }

            //Attempt to jump.
            if (tryToJump)
            {
                //In the following, note that the jumping velocity changes are computed such that the separating velocity is specifically achieved,
                //rather than just adding some speed along an arbitrary direction.  This avoids some cases where the character could otherwise increase
                //the jump speed, which may not be desired.
                if (hasTraction)
                {
                    //The character has traction, so jump straight up.
                    float currentUpVelocity = Vector3.Dot(Body.OrientationMatrix.Up, relativeVelocity);
                    //Target velocity is JumpSpeed.
                    float velocityChange = JumpSpeed - currentUpVelocity;
                    ChangeVelocity(Body.OrientationMatrix.Up * velocityChange, ref relativeVelocity);
                }
                else if (IsSupported)
                {
                    //The character does not have traction, so jump along the surface normal instead.
                    float currentNormalVelocity = Vector3.Dot(supportData.Normal, relativeVelocity);
                    //Target velocity is JumpSpeed.
                    float velocityChange = SlidingJumpSpeed - currentNormalVelocity;
                    ChangeVelocity(supportData.Normal * -velocityChange, ref relativeVelocity);
                }
                hasTraction = false;
                support = null;
                tryToJump = false;
            }

            //Update the vertical motion of the character.
            if (hasTraction)
            {
                //Remove all velocity, penetrating or separating, along the contact normal if it is below a threshold.
                float separatingVelocity = -Vector3.Dot(relativeVelocity, supportData.Normal);
                if (separatingVelocity < GlueSpeed)
                {
                    ChangeVelocity(supportData.Normal * separatingVelocity, ref relativeVelocity);
                }
                else
                    hasTraction = false;

            }
            else if (IsSupported)
            {
                //The character has no traction, so just stop penetrating velocity.
                float dot = -Vector3.Dot(relativeVelocity, supportData.Normal);
                if (dot < 0)
                    ChangeVelocity(supportData.Normal * dot, ref relativeVelocity);

            }

            //Update the horizontal motion of the character.
            if (IsSupported)
            {
                //If the object has traction, it has a lot more control over its motion.  If it is sliding, then use the sliding coefficients.
                float accelerationToUse = hasTraction ? Acceleration : SlidingAcceleration;
                float decelerationToUse = hasTraction ? Deceleration : SlidingDeceleration;
                Vector3 velocityDirection;
                Vector3 violatingVelocity;
                if (MovementDirection.LengthSquared() > 0)
                {
                    //Project the movement direction onto the support plane defined by the support normal.
                    //This projection is NOT along the support normal to the plane; that would cause the character to veer off course when moving on slopes.
                    //Instead, project along the sweep direction to the plane.
                    //For a 6DOF character controller, the lineStart would be different; it must be perpendicular to the local up.
                    Vector3 lineStart = new Vector3(MovementDirection.X, 0, MovementDirection.Y);
                    Vector3 lineEnd;
                    Vector3.Add(ref lineStart, ref sweep, out lineEnd);
                    Plane plane = new Plane(supportData.Normal, 0);
                    float t;
                    //This method can return false when the line is parallel to the plane, but previous tests and the slope limit guarantee that it won't happen.
                    Toolbox.GetLinePlaneIntersection(ref lineStart, ref lineEnd, ref plane, out t, out velocityDirection);

                    //The origin->intersection line direction defines the horizontal velocity direction in 3d space.
                    velocityDirection.Normalize();

                    //Compare the current velocity to the goal velocity.
                    float currentVelocity;
                    Vector3.Dot(ref velocityDirection, ref relativeVelocity, out currentVelocity);

                    //Violating velocity is velocity which is not in the direction of the goal direction.
                    violatingVelocity = relativeVelocity - velocityDirection * currentVelocity;

                    //Compute the acceleration component.
                    float speedUpNecessary = Speed - currentVelocity;
                    float velocityChange = MathHelper.Clamp(speedUpNecessary, 0, accelerationToUse * dt);

                    //Apply the change.

                    ChangeVelocity(velocityDirection * velocityChange, ref relativeVelocity);

                }
                else
                {
                    velocityDirection = new Vector3();
                    violatingVelocity = relativeVelocity;
                }

                //Compute the deceleration component.
                float lengthSquared = violatingVelocity.LengthSquared();
                if (lengthSquared > 0)
                {
                    Vector3 violatingVelocityDirection;
                    float violatingVelocityMagnitude = (float)Math.Sqrt(lengthSquared);
                    Vector3.Divide(ref violatingVelocity, violatingVelocityMagnitude, out violatingVelocityDirection);

                    //We need to get rid of the violating velocity magnitude, but don't go under zero (that would cause nasty oscillations).
                    float velocityChange = -Math.Min(decelerationToUse * dt, violatingVelocityMagnitude);
                    //Apply the change.
                    ChangeVelocity(violatingVelocityDirection * velocityChange, ref relativeVelocity);
                }

            }
            else
            {
                //The character is in the air.  Still allow a little air control!
                //6DOF character controllers will likely completely replace this; if it doesn't,
                //use an oriented velocity direction instead of 2d movement direction.
                var velocityDirection = new Vector3(MovementDirection.X, 0, MovementDirection.Y);

                //Compare the current velocity to the goal velocity.
                float currentVelocity;
                Vector3.Dot(ref velocityDirection, ref relativeVelocity, out currentVelocity);

                //Compute the acceleration component.
                float speedUpNecessary = AirSpeed - currentVelocity;
                float velocityChange = MathHelper.Clamp(speedUpNecessary, 0, AirAcceleration * dt);

                //Apply the change.
                ChangeVelocity(velocityDirection * velocityChange, ref relativeVelocity);
            }
        }
 public override void OnRemovalFromSpace(ISpace oldSpace)
 {
     //Remove any supplements from the space too.
     oldSpace.Remove(Body);
     //This character controller requires the standard implementation of Space.
     ((Space)oldSpace).BoundingBoxUpdater.Finishing -= ExpandBoundingBox;
     support = null;
     supportData = new RayHit();
     hasTraction = false;
     Body.AngularVelocity = new Vector3();
     Body.LinearVelocity = new Vector3();
 }
Exemple #8
0
        bool TryToStepUsingContact(ref ContactData contact, out Vector3 newPosition)
        {
            Vector3 down = character.Body.OrientationMatrix.Down;
            Vector3 position = character.Body.Position;
            //The normal of the contact may not be facing perfectly out to the side.
            //The detection process allows a bit of slop.
            //Correct it by removing any component of the normal along the local up vector.
            Vector3 normal = contact.Normal;
            float dot;
            Vector3.Dot(ref normal, ref down, out dot);
            Vector3 error;
            Vector3.Multiply(ref down, dot, out error);
            Vector3.Subtract(ref normal, ref error, out normal);
            normal.Normalize();

            //Now we need to ray cast out from the center of the character in the direction of this normal to check for obstructions.
            //Compute the ray origin location.  Fire it out of the top of the character; if we're stepping, this must be a valid location.
            //Putting it as high as possible helps to reject more invalid step geometry.
            Ray ray;
            float downRayLength = character.Body.Height;// MaximumStepHeight + upStepMargin;
            Vector3.Multiply(ref down, character.Body.Height * .5f - downRayLength, out ray.Position);
            Vector3.Add(ref ray.Position, ref position, out ray.Position);
            ray.Direction = normal;
            //Include a little margin in the length.
            //Technically, the character only needs to teleport horizontally by the complicated commented expression.
            //That puts it just far enough to have traction on the new surface.
            //In practice, the current contact refreshing approach used for many pair types causes contacts to persist horizontally a bit,
            //which can cause side effects for the character.
            float horizontalOffsetAmount = character.Body.CollisionInformation.Shape.CollisionMargin;// (float)((1 - character.SupportFinder.sinMaximumSlope) * character.Body.CollisionInformation.Shape.CollisionMargin + 0);
            float length = character.Body.Radius + horizontalOffsetAmount;// -contact.PenetrationDepth;

            if (character.QueryManager.RayCastHitAnything(ray, length))
            {
                //The step is obstructed!
                newPosition = new Vector3();
                return false;
            }

            //The down-cast ray origin has been verified by the previous ray cast.
            //Let's look for a support!
            Vector3 horizontalOffset;
            Vector3.Multiply(ref normal, length, out horizontalOffset);
            Vector3.Add(ref ray.Position, ref horizontalOffset, out ray.Position);
            ray.Direction = down;

            //Find the earliest hit, if any.
            RayHit earliestHit = new RayHit();
            if (!character.QueryManager.RayCast(ray, downRayLength, out earliestHit) || //Can't do anything if it didn't hit.
                earliestHit.T <= 0 || //Can't do anything if the hit was invalid.
                earliestHit.T - downRayLength > -minimumUpStepHeight || //Don't bother doing anything if the step is too small.
                earliestHit.T - downRayLength < -maximumStepHeight - upStepMargin) //Can't do anything if the step is too tall.
            {
                //No valid hit was detected.
                newPosition = new Vector3();
                return false;
            }

            //Ensure the candidate surface supports traction.
            Vector3 supportNormal;
            Vector3.Normalize(ref earliestHit.Normal, out supportNormal);
            //Calibrate the normal to face in the same direction as the down vector for consistency.
            Vector3.Dot(ref supportNormal, ref down, out dot);
            if (dot < 0)
            {
                Vector3.Negate(ref supportNormal, out supportNormal);
                dot = -dot;
            }

            //If the new surface does not have traction, do not attempt to step up.
            if (dot < character.SupportFinder.cosMaximumSlope)
            {
                newPosition = new Vector3();
                return false;
            }

            //Since contact queries are frequently expensive compared to ray cast tests,
            //do one more ray cast test.  This time, starting from the same position, cast upwards.
            //In order to step up, the previous down-ray hit must be at least a character height away from the result of the up-ray.
            Vector3.Negate(ref down, out ray.Direction);
            //Find the earliest hit, if any.
            //RayHit earliestHitUp = new RayHit();
            //earliestHitUp.T = float.MaxValue;
            float upLength = character.Body.Height - earliestHit.T;

            //If the sum of the up and down distances is less than the height, the character can't fit.
            if (character.QueryManager.RayCastHitAnything(ray, upLength))
            {
                newPosition = new Vector3();
                return false;
            }

            //By now, a valid ray hit has been found.  Now we need to validate it using contact queries.
            //This process is very similar in concept to the down step verification, but it has some extra
            //requirements.

            //Predict a hit location based on the time of impact and the normal at the intersection.
            //Take into account the radius of the character (don't forget the collision margin!)

            RigidTransform transform = character.Body.CollisionInformation.WorldTransform;
            //The transform must be modified to position the query body at the right location.
            //The horizontal offset of the queries ensures that a tractionable part of the character will be put onto the new support.
            Vector3.Multiply(ref normal, horizontalOffsetAmount, out horizontalOffset);
            Vector3.Add(ref transform.Position, ref horizontalOffset, out transform.Position);
            Vector3 verticalOffset;
            Vector3.Multiply(ref down, -downRayLength, out verticalOffset);
            Vector3.Add(ref transform.Position, ref verticalOffset, out transform.Position);

            //We know that the closest point to the plane will be the extreme point in the plane's direction.
            //Use it as the ray origin.
            Ray downRay;
            character.Body.CollisionInformation.Shape.GetExtremePoint(supportNormal, ref transform, out downRay.Position);
            downRay.Direction = down;

            //Intersect the ray against the plane defined by the support hit.
            Vector3 intersection;
            Vector3.Dot(ref earliestHit.Location, ref supportNormal, out dot);
            Plane plane = new Plane(supportNormal, dot);
            Vector3 candidatePosition;

            //Define the interval bounds to be used later.

            //The words 'highest' and 'lowest' here refer to the position relative to the character's body.
            //The ray cast points downward relative to the character's body.
            float highestBound = -maximumStepHeight;
            float lowestBound = character.Body.CollisionInformation.Shape.CollisionMargin - downRayLength + earliestHit.T;
            float currentOffset = lowestBound;
            float hintOffset;

            //This guess may either win immediately, or at least give us a better idea of where to search.
            float hitT;
            if (Toolbox.GetRayPlaneIntersection(ref downRay, ref plane, out hitT, out intersection))
            {
                hitT = -downRayLength + hitT + CollisionDetectionSettings.AllowedPenetration;
                if (hitT < highestBound)
                {
                    //Don't try a location known to be too high.
                    hitT = highestBound;
                }
                currentOffset = hitT;
                if (currentOffset > lowestBound)
                    lowestBound = currentOffset;
                candidatePosition = character.Body.Position + down * currentOffset + horizontalOffset;
                switch (TryUpStepPosition(ref normal, ref candidatePosition, out hintOffset))
                {
                    case PositionState.Accepted:
                        currentOffset += hintOffset;
                        //Only use the new position location if the movement distance was the right size.
                        if (currentOffset < 0 && currentOffset > -maximumStepHeight - CollisionDetectionSettings.AllowedPenetration)
                        {
                            //It's possible that we let a just-barely-too-high step occur, limited by the allowed penetration.
                            //Just clamp the overall motion and let it penetrate a bit.
                            newPosition = character.Body.Position + Math.Max(-maximumStepHeight, currentOffset) * down + horizontalOffset;
                            return true;
                        }
                        else
                        {
                            newPosition = new Vector3();
                            return false;
                        }
                    case PositionState.Rejected:
                        newPosition = new Vector3();
                        return false;
                    case PositionState.NoHit:
                        highestBound = currentOffset + hintOffset;
                        currentOffset = (lowestBound + currentOffset) * .5f;
                        break;
                    case PositionState.Obstructed:
                        lowestBound = currentOffset;
                        currentOffset = (highestBound + currentOffset) * .5f;
                        break;
                    case PositionState.HeadObstructed:
                        highestBound = currentOffset + hintOffset;
                        currentOffset = (lowestBound + currentOffset) * .5f;
                        break;
                    case PositionState.TooDeep:
                        currentOffset += hintOffset;
                        lowestBound = currentOffset;
                        break;

                }

            }//TODO: If the ray cast doesn't hit, that could be used to early out...  Then again, it pretty much can't happen.

            //Our guesses failed.
            //Begin the regular process.  Start at the time of impact of the ray itself.
            //How about trying the time of impact of the ray itself?

            //Since we wouldn't be here unless there were no contacts at the body's current position,
            //testing the ray cast location gives us the second bound we need to do an informed binary search.

            int attempts = 0;
            //Don't keep querying indefinitely.  If we fail to reach it in a few informed steps, it's probably not worth continuing.
            //The bound size check prevents the system from continuing to search a meaninglessly tiny interval.
            while (attempts++ < 5 && lowestBound - highestBound > Toolbox.BigEpsilon)
            {
                candidatePosition = character.Body.Position + currentOffset * down + horizontalOffset;
                switch (TryUpStepPosition(ref normal, ref candidatePosition, out hintOffset))
                {
                    case PositionState.Accepted:
                        currentOffset += hintOffset;
                        //Only use the new position location if the movement distance was the right size.
                        if (currentOffset < 0 && currentOffset > -maximumStepHeight - CollisionDetectionSettings.AllowedPenetration)
                        {
                            //It's possible that we let a just-barely-too-high step occur, limited by the allowed penetration.
                            //Just clamp the overall motion and let it penetrate a bit.
                            newPosition = character.Body.Position + Math.Max(-maximumStepHeight, currentOffset) * down + horizontalOffset;
                            return true;
                        }
                        else
                        {
                            newPosition = new Vector3();
                            return false;
                        }
                    case PositionState.Rejected:
                        newPosition = new Vector3();
                        return false;
                    case PositionState.NoHit:
                        highestBound = currentOffset + hintOffset;
                        currentOffset = (lowestBound + highestBound) * .5f;
                        break;
                    case PositionState.Obstructed:
                        lowestBound = currentOffset;
                        currentOffset = (highestBound + lowestBound) * .5f;
                        break;
                    case PositionState.HeadObstructed:
                        highestBound = currentOffset + hintOffset;
                        currentOffset = (lowestBound + currentOffset) * .5f;
                        break;
                    case PositionState.TooDeep:
                        currentOffset += hintOffset;
                        lowestBound = currentOffset;
                        break;
                }
            }
            //Couldn't find a candidate.
            newPosition = new Vector3();
            return false;
        }
Exemple #9
0
        /// <summary>
        /// Calculate whether the AI should be accelerating or braking, and the magnitude it
        /// should be doing either.
        /// </summary>
        /// <returns>
        /// A float in the range -1.0f to 1.0f, describing whether the AI is accelerating or 
        /// braking by the sign of the returned value (positive is acceleration, negative
        /// decceleration). The value describes the value of the appropriate trigger on the AI's
        /// virtual controller.
        /// </returns>
        private float setAcceleration()
        {
            // Initially, just throw it round.
            // Half speed so it's easy to follow.
            float a;

            Matrix shipOrientation = parent.shipPhysics.ShipOrientationMatrix;

            Vector3 testVector = shipOrientation.Forward;
            Vector3 rayOrigin = parent.shipPhysics.ShipPosition;

            float rayLength = 120f;

            RayHit result;

            testRay.Position = rayOrigin;
            testRay.Direction = testVector;
            try
            {
                Physics.currentTrackInvisibleWall.RayCast(testRay, rayLength, out result);
            }
            catch (Exception e)
            {
                //Console.WriteLine("Error casting wall");
                result = new RayHit();
            }
            float distance = result.T == 0 ?  0 : rayLength - result.T;

            Physics.currentTrackFloor.RayCast(testRay, rayLength, out result);

            parent.shipDrawing.aiFrontRayArrow = testVector * rayLength;
            parent.shipDrawing.aiFrontRayHit = (result.T == 0)? false : true;

            if (distance > result.T)
            {
                closerBy = 0;
                closerByFraction = 0;
                a = 1f;
            }
            else
            {
                closerBy = rayLength - distance;
                closerByFraction = closerBy / rayLength;
                a = closerByFraction * closerByFraction;
            }
            
            return a;
        }
Exemple #10
0
        /// <summary>
        /// Avoid walls in front of the ship by turning away from them.
        /// </summary>
        /// <returns></returns>
        private float avoidWallsInFront()
        {
            AiRay.Direction = parent.shipPhysics.ShipOrientationMatrix.Forward;
            AiRay.Position = parent.shipPhysics.ShipPosition;

            RayHit result;
            try
            {
                Physics.currentTrackInvisibleWall.RayCast(AiRay, 100f, out result);
            }
            catch (Exception e)
            {
                //Console.WriteLine("Error casting wall");
                result = new RayHit();
            }

            float wallAngle;

            if (result.T != 0)
            {
                wallAngle = (float) Vector3.Dot(Vector3.Normalize(result.Normal), parent.shipPhysics.ShipOrientationMatrix.Left);
                
                if (wallAngle > 0)
                {
                    return -1 * turnWalls(wallAngle, result.T);
                }
                else
                {
                    return turnWalls(wallAngle, result.T);
                }
            }

            return 0;

            AiRay.Direction = (parent.shipPhysics.ShipOrientationMatrix.Forward + parent.shipPhysics.ShipOrientationMatrix.Right * 2) / 3;
            Physics.currentTrackInvisibleWall.RayCast(AiRay, 30f, out result);
            if(result.T != 0){
                wallAngle = Vector3.Dot(Vector3.Normalize(result.Normal), parent.shipPhysics.ShipOrientationMatrix.Left);
                //if(wallAngle < 
            }
        }
Exemple #11
0
        /// <summary>
        /// Casts a ray sideways from the ship to determine how near the wall is and how to respond.
        /// </summary>
        /// <returns>Returns a value to represent how urgently the ship must turn to avoid a wall</returns>
        private float avoidSideWalls()
        {
            float t = 0;

            if (parent.shipPhysics.getForwardSpeed() == 0)
            {
                return 0f;
            }

            Matrix shipOrientation = parent.shipPhysics.ShipOrientationMatrix;

            if (float.IsNaN(shipOrientation.Forward.X))
                return 0;

            //MapPoint lastPoint = parent.shipPhysics.nearestMapPoint;
            //MapPoint nextPoint = parent.shipPhysics.mapData.nextPoint(lastPoint);
            MapPoint nearPoint = parent.shipPhysics.nearestMapPoint;

            // partially take current orientation into account, but not too much
            //Vector3 guessUp = (shipOrientation.Up + nextPoint.trackUp * 9) / 10;

            //Vector3 relativeTrackHeading = Vector3.Normalize(Vector3.Cross(nextPoint.roadSurface, guessUp));
            //Vector3 relativeShipRight = Vector3.Normalize(Vector3.Cross(lastPoint.tangent, guessUp));

            //float direction = Vector3.Dot(relativeTrackHeading, relativeShipRight);
            
            // Find if left/right of centre of track by determining which side of
            // the plane defined by the roadSurface vector the ship is on
                //If the Vector from the waypoint to the ship is within 90 degrees of roadSurface
                //it is on the side the track the roadSurface arrow faces
            float direction = Vector3.Dot(nearPoint.roadSurface, (parent.shipPhysics.ShipPosition - nearPoint.position));
            if(Single.IsNaN(direction)||Single.IsInfinity(direction)) direction= 1f;
            Vector3 testVector =nearPoint.roadSurface * Math.Sign(direction);

            Vector3 rayOrigin = parent.shipPhysics.ShipPosition;

            float shipWidth = 3f;
            float rayLength = shipWidth + parent.shipPhysics.ShipSpeed / 8;

            RayHit result;

            AiRay.Position = rayOrigin;
            AiRay.Direction = testVector;
            try
            {
                Physics.currentTrackInvisibleWall.RayCast(AiRay, rayLength, out result);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error casting wall");
                result = new RayHit();
            }
            float distance = result.T < shipWidth ? rayLength - shipWidth : rayLength - result.T;

            //Setup arrows for drawing
            parent.shipDrawing.aiWallRayArrow = testVector * rayLength;
            if (result.T == 0)
            {
                distance = 0;
                parent.shipDrawing.aiWallRayHit = false;
            }
            else
            {
                parent.shipDrawing.aiWallRayHit = true;
            }

            t = distance / (rayLength - shipWidth);

            float retVal = t * -1 * Math.Sign(direction);

            return float.IsNaN(retVal) ? 0f : retVal;
        }
Exemple #12
0
 public bool RayCast(Ray ray, float maximumLength, out RayHit rayHit)
 {
     return mPhysicsMesh.RayCast(ray, maximumLength, out rayHit);
 }