예제 #1
0
        /// <summary>
        /// Evaluate collisions with the moving sphere.
        /// If returns true, there was a collision as described in collPrim,
        /// else no collision and collPrim untouched.
        /// </summary>
        /// <param name="startPos"></param>
        /// <param name="endPos"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public override bool Collide(Vector3 startPos, Vector3 endPos, float radius, ref CollInfo collPrim)
        {
            bool hit = false;

            float combinedRadius = WorldSphere.Radius + radius + Vector3.Distance(startPos, endPos) * 0.5f;
            float distance       = Vector3.Distance(WorldSphere.Center, (startPos + endPos) * 0.5f);

            if (distance <= combinedRadius)
            {
                CollInfo test = new CollInfo();
                test.DistSq = Single.MaxValue;

                for (int i = 0; i < children.Count; ++i)
                {
                    if (children[i].Collide(startPos, endPos, radius, ref test))
                    {
                        if (test.DistSq <= collPrim.DistSq)
                        {
                            collPrim = test;
                            collPrim.TopPrimitive = this;
                            hit = true;
                        }
                    }
                }
            }
            else
            {
                /// Just a spot to put a breakpoint.
                hit = false;
            }
            return(hit);
        }
예제 #2
0
        /// <summary>
        /// Check for initial condition of contact. If so, fill in the collPrim
        /// and return true, else return false and leave collPrim alone.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        protected bool CheckTouching(Vector3 p0, float radius, ref CollInfo collPrim)
        {
            Vector3 p0Local = Vector3.Transform(p0, WorldToLocal);

            Vector3 closestLocal = p0Local;
            float   lengthSq     = p0Local.X * p0Local.X / (Radii.X * Radii.X)
                                   + p0Local.Y * p0Local.Y / (Radii.Y * Radii.Y)
                                   + p0Local.Z * p0Local.Z / (Radii.Z * Radii.Z);

            if (lengthSq > 1.0f)
            {
                float invLength = (float)(1.0 / Math.Sqrt(lengthSq));
                closestLocal *= invLength;
            }
            Vector3 closest = Vector3.Transform(closestLocal, LocalToWorld);

            if (Vector3.DistanceSquared(closest, p0) <= radius * radius)
            {
                Vector3 normal = lengthSq < 1.0f
                    ? p0 - LocalToWorld.Translation
                    : p0 - closest;
                return(SetCollPrimTouching(
                           p0,
                           p0,
                           closest,
                           normal,
                           Vector3.Zero,
                           ref collPrim));
            }
            return(false);
        }
예제 #3
0
        /// <summary>
        /// Helper func to set up a collision reporting struct.
        /// </summary>
        /// <param name="from"></param>
        /// <param name="center"></param>
        /// <param name="contact"></param>
        /// <param name="normal"></param>
        /// <param name="struck"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public bool SetCollPrim(
            Vector3 from,
            Vector3 center,
            Vector3 contact,
            Vector3 normal,
            Vector3 struck,
            ref CollInfo collPrim)
        {
            collPrim.DistSq  = Vector3.DistanceSquared(from, contact);
            collPrim.Center  = center;
            collPrim.Contact = contact;
            if (normal.LengthSquared() == 0.0f)
            {
                collPrim.Normal = Vector3.UnitZ;
            }
            else
            {
                collPrim.Normal = normal;
            }
            collPrim.Struck       = struck;
            collPrim.Touching     = false;
            collPrim.LowPrimitive = this;
            collPrim.TopPrimitive = this;

            return(true);
        }
예제 #4
0
        /// <summary>
        /// Check for initial condition of contact. If so, fill in the collPrim
        /// and return true, else return false and leave collPrim alone.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        protected bool CheckTouching(Vector3 p0, float radius, ref CollInfo collPrim)
        {
            Vector3 p0Local = Vector3.Transform(p0, WorldToLocal);

            Vector3 closestLocal = ClosestPointLocal(p0Local);

            Vector3 closest = Vector3.Transform(closestLocal, LocalToWorld);

            if (Vector3.DistanceSquared(closest, p0) <= radius * radius)
            {
                Vector3 localNorm = Vector3.Zero;
                if (p0Local.Z >= Length)
                {
                    localNorm = Vector3.UnitZ;
                }
                else if (p0Local.Z <= 0)
                {
                    localNorm = -Vector3.UnitZ;
                }
                else
                {
                    localNorm.X = p0Local.X;
                    localNorm.Y = p0Local.Y;
                }
                Vector3 norm = Vector3.TransformNormal(localNorm, LocalToWorld);

                /// Make sure the contact point is on the surface.
                if ((closestLocal.Z < Length) && (closestLocal.Z > 0))
                {
                    /// It's not on an end cap, check if it needs pushing out to the sides.
                    Vector2 xy = new Vector2(closestLocal.X, closestLocal.Y);
                    if (xy.LengthSquared() < Radius * Radius)
                    {
                        /// Yep, push it out.
                        xy.Normalize();
                        xy            *= Radius;
                        closestLocal.X = xy.X;
                        closestLocal.Y = xy.Y;
                        closest        = Vector3.Transform(closestLocal, LocalToWorld);
                    }
                }
                else
                {
                    // This case happens when we're within the infinite cylinder
                    // but outside of either end cap.
                    return(false);
                }
                return(SetCollPrimTouching(
                           p0,
                           p0,
                           closest,
                           p0 - closest,
                           Vector3.Zero,
                           ref collPrim));
            }
            return(false);
        }
예제 #5
0
        /// <summary>
        /// Fill in a collPrim for initial condition already in contact.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="del"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        private bool AlreadyTouching(Vector3 p0, Vector3 del, ref CollInfo collPrim)
        {
            Vector3 contact = Center + del;

            return(SetCollPrimTouching(
                       p0,
                       p0,
                       contact,
                       p0 - contact,
                       Center,
                       ref collPrim));
        }
예제 #6
0
 /// <summary>
 /// Helper func to set up a collision reporting struct.
 /// For use when two bodies already touching at beginning of frame.
 /// </summary>
 /// <param name="from"></param>
 /// <param name="center"></param>
 /// <param name="contact"></param>
 /// <param name="normal"></param>
 /// <param name="collPrim"></param>
 /// <returns></returns>
 public bool SetCollPrimTouching(
     Vector3 from,
     Vector3 center,
     Vector3 contact,
     Vector3 normal,
     Vector3 struck,
     ref CollInfo collPrim)
 {
     SetCollPrim(from, center, contact, normal, struck, ref collPrim);
     collPrim.Touching = true;
     return(true);
 }
예제 #7
0
        }   // end of Collide()

        /// <summary>
        /// Specialized check against other Movers
        /// </summary>
        /// <param name="other"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public bool Collide(Mover other, ref CollInfo collPrim)
        {
            float dt = Time.GameTimeFrameSeconds;

            Vector3 p0 = other.Owner.Movement.Position;
            Vector3 p1 = p0
                         - Owner.Movement.Velocity * dt
                         + other.Owner.Movement.Velocity * dt;

            return(Collide(
                       p0,
                       p1,
                       other.Radius,
                       ref collPrim));
        }
예제 #8
0
 /// <summary>
 /// Helper func to set up a collision reporting struct.
 /// </summary>
 /// <param name="from"></param>
 /// <param name="center"></param>
 /// <param name="contact"></param>
 /// <param name="normal"></param>
 /// <param name="collPrim"></param>
 /// <returns></returns>
 protected bool SetCollPrim(
     Vector3 from,
     Vector3 center,
     Vector3 contact,
     Vector3 normal,
     ref CollInfo collPrim)
 {
     return(SetCollPrim(
                from,
                center,
                contact,
                normal,
                Vector3.Zero,
                ref collPrim));
 }
예제 #9
0
        /// <summary>
        /// Fill in a hitInfo struct from the input.
        /// </summary>
        /// <param name="best"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        private HitInfo MakeHitScratch(CollInfo best, float radius)
        {
            GameActor other      = best.TopPrimitive.Owner;
            HitInfo   hitScratch = new HitInfo();

            hitScratch.Contact    = best.Contact;
            hitScratch.Center     = best.Center;
            hitScratch.Normal     = Vector3.Normalize(best.Normal);
            hitScratch.DistSq     = best.DistSq;
            hitScratch.Other      = other;
            hitScratch.Struck     = best.Struck;
            hitScratch.OtherMover = best.TopPrimitive as Mover;

            hitScratch.Offset = MakeOffset(hitScratch, radius);

            hitScratch.Touching  = best.Touching;
            hitScratch.Handled   = false;
            hitScratch.TimeStamp = Time.GameTimeTotalSeconds;

            return(hitScratch);
        }
예제 #10
0
        /// <summary>
        /// Check for initial condition of contact. If so, fill in the collPrim
        /// and return true, else return false and leave collPrim alone.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        private bool CheckTouching(Vector3 worldPos, float radius, ref CollInfo collPrim)
        {
            Vector3 closePos = ClosestPoint(worldPos);

            if (Vector3.DistanceSquared(worldPos, closePos) <= radius * radius)
            {
                Vector3 normal = worldPos - closePos;
                if (Vector3.Dot(normal, WorldNormal) < 0.0f)
                {
                    normal = WorldNormal;
                }
                return(SetCollPrimTouching(
                           worldPos,
                           worldPos,
                           closePos,
                           normal,
                           Vector3.Zero,
                           ref collPrim));
            }

            return(false);
        }
예제 #11
0
        /// <summary>
        /// Evaluate collisions with the moving sphere.
        /// If returns true, there was a collision as described in collPrim,
        /// else no collision and collPrim untouched.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="p1"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public override bool Collide(Vector3 p0, Vector3 p1, float radius, ref CollInfo collPrim)
        {
            ///Check sphere first.

            CollInfo testPrim = new CollInfo();

            testPrim.DistSq = Single.MaxValue;
            bool hit = false;

            foreach (Rectangle rect in sides)
            {
                if (rect.Collide(p0, p1, radius, ref testPrim))
                {
                    if (testPrim.DistSq < collPrim.DistSq)
                    {
                        hit      = true;
                        collPrim = testPrim;
                        collPrim.TopPrimitive = this;
                    }
                }
            }
            return(hit);
        }
예제 #12
0
        /// <summary>
        /// Start, end and radii are passed in from a mover.  This method tests
        /// this mover's swept ellipsoid against all the non-moving things as
        /// well as all the other movers which appear after it in the list.
        /// The "first" param is the index of the next item in the list.
        ///
        /// The listAll param seems to determine whether we just keep the nearest hit
        /// or keep them all.  It appears that listAll=false is used for physics collision
        /// testing while listAll=true is used for WHEN Bumped testing.  But, of course,
        /// this is just a guess on my part since it's not documented.  Argh.
        /// </summary>
        /// <param name="startPos0">start position of swept sphere</param>
        /// <param name="endPos0">end position of swept sphere</param>
        /// <param name="radii0">Radii of swept ellipsoid.</param>
        /// <param name="first">This is the index of the first mover to check for collisions with.  Start, end, and radii come from the previous mover.  TODO (****) Change numbering so this is more clear.</param>
        /// <param name="listAll">When true, all hits are listed.  When false, only the nearest hit is listed.</param>
        /// <param name="hits"></param>
        /// <returns></returns>
        private bool CollisionCheck(Vector3 startPos0, Vector3 endPos0, Vector3 radii0, int first, bool listAll, List <HitInfo> hits)
        {
            /// These are structs, no real allocation done here.
            CollInfo curCollPrim = new CollInfo();

            curCollPrim.DistSq = Single.MaxValue;

            CollInfo best = new CollInfo();

            best.DistSq = Single.MaxValue;
            bool hit = false;

            // Test the current swept ellipsoid against all the non-moving things.
            List <CollisionPrimitive> relevants = things;

            for (int ithing = 0; ithing < relevants.Count; ++ithing)
            {
                CollisionPrimitive thing = relevants[ithing];

                if (thing.Owner.Ignored)
                {
                    continue;
                }

                // Note we used the Z part of the radii.  So, on squashed movers we
                // might get some intersection.
                // TODO (****) Actually figure out how to test a swept ellipsoid against
                // the Primitive based collision shapes.
                if (thing.Collide(startPos0, endPos0, radii0.Z, ref curCollPrim))
                {
                    if (listAll)
                    {
                        HitInfo hitScratch = MakeHitScratch(curCollPrim, radii0.X);
                        hits.Add(hitScratch);
                        curCollPrim.DistSq = Single.MaxValue;
                    }
                    else
                    if (curCollPrim.DistSq <= best.DistSq)
                    {
                        hit     = true;
                        best    = curCollPrim;
                        endPos0 = best.Center;

                        if (best.Touching)
                        {
                            HitInfo hitScratch = MakeHitScratch(best, radii0.X);
                            hits.Add(hitScratch);
                        }
                    }
                }
            }

            // Test the current mover against the remaining movers in the list.
            // Note that in the case of hit testing blips this index may be -1.
            int curMoverIndex = first - 1;

            // If listAll is true, then we're testing for bump so test the full
            // list since we have to worry about TouchCushions.
            if (listAll)
            {
                first = 0;
            }
            for (int second = first; second < movers.Count; ++second)
            {
                Mover sphere = movers[second];

                // Don't test against self.
                if (second == curMoverIndex)
                {
                    continue;
                }

                if (sphere.Owner.Ignored)
                {
                    continue;
                }

                // Don't collide missiles with their launcher.  Note that this assumes
                // that the launcher is always in the list _ahead_ of the missile.
                MissileChassis mc = movers[second].Owner.Chassis as MissileChassis;
                if (mc != null && curMoverIndex >= 0 && mc.Launcher == movers[curMoverIndex].Owner)
                {
                    continue;
                }

                Vector3 radii1 = sphere.Radius * sphere.Owner.SquashScale;

                Vector3 hitContactPosition = Vector3.Zero;
                Vector3 hitNormal          = Vector3.UnitZ;
                float   hitT    = -1;
                bool    hitTest = SweptPrims.SweptEllipsoidSweptEllipsoid(startPos0, endPos0, radii0, sphere.Center, sphere.Center + sphere.Delta, sphere.Radius * sphere.Owner.SquashScale, ref hitContactPosition, ref hitNormal, ref hitT);

                if (hitTest)
                {
                    Vector3 posAtCollision = MyMath.Lerp(startPos0, endPos0, hitT);
                    // Already touching?
                    if (hitT <= 0)
                    {
                        sphere.SetCollPrimTouching(startPos0, posAtCollision, hitContactPosition, -hitNormal, sphere.Center + hitT * sphere.Delta, ref curCollPrim);
                    }
                    else
                    {
                        sphere.SetCollPrim(startPos0, posAtCollision, hitContactPosition, -hitNormal, sphere.Center + hitT * sphere.Delta, ref curCollPrim);
                    }

                    if (listAll)
                    {
                        HitInfo hitScratch = MakeHitScratch(curCollPrim, radii0.X);
                        hits.Add(hitScratch);
                        curCollPrim.DistSq = Single.MaxValue;
                    }
                    else
                    if (curCollPrim.DistSq <= best.DistSq)
                    {
                        best = curCollPrim;
                        hit  = true;

                        if (best.Touching)
                        {
                            HitInfo hitScratch = MakeHitScratch(best, radii0.X);
                            hits.Add(hitScratch);
                        }
                    }
                } // end if hitTest
            }     // end of loop over other movers.

            if (hit)
            {
                Debug.Assert(!listAll, "Hit shouldn't get set when listing all hits");
                /// If it's touching, we've already applied, otherwise
                /// do it here.
                if (!best.Touching)
                {
                    HitInfo hitScratch = MakeHitScratch(best, radii0.X);
                    hits.Add(hitScratch);
                }
            }
            if (listAll && (hits.Count > 1))
            {
                hits.Sort(comparer);
            }
            return(hits.Count > 0);
        }
예제 #13
0
        /// <summary>
        /// Evaluate collisions with the moving sphere.
        /// If returns true, there was a collision as described in collPrim,
        /// else no collision and collPrim untouched.
        /// </summary>
        /// <param name="startPositionOther"></param>
        /// <param name="p1"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public override bool Collide(Vector3 startPositionOther, Vector3 endPositionOther, float radius, ref CollInfo collPrim)
        {
            Vector3 myRadii = Radius * Owner.SquashScale;

            /// Compensate for our motion.
            // We can then use this for "this" motion while "other" is now motionless.
            Vector3 endPositionThis = endPositionOther - Delta;

            Vector3 centerCenterDelta = startPositionOther - Center;
            float   sumOfRadiiSquared = radius + Radius;

            sumOfRadiiSquared *= sumOfRadiiSquared;
            float  centerCenterDistSquared = centerCenterDelta.LengthSquared();
            double C = centerCenterDistSquared - sumOfRadiiSquared;

            if (C <= 0.0)
            {
                /// Already touching
                return(AlreadyTouching(startPositionOther, centerCenterDelta * Radius / (radius + Radius), ref collPrim));
            }

            /// The aren't touching. The only degeneracies to worry about
            /// are that they aren't moving, they miss each other, or
            /// they hit outside range p0->p1.
            /// They aren't moving shows up as p1==p0.
            /// A miss will show up as a lack of real roots to the quadratic.
            /// Outside time range shows up as t < 0 || t > 1.

            Vector3 dir = endPositionThis - startPositionOther;
            double  A   = dir.LengthSquared();

            if (A < Single.Epsilon)
            {
                /// Not moving and not already touching.
                return(false);
            }
            double B = 2.0 * Vector3.Dot(dir, centerCenterDelta);

            double determ = B * B - 4.0 * A * C;

            if (determ < 0.0)
            {
                /// Missed.
                return(false);
            }
            double t = -B - Math.Sqrt(determ);

            t /= 2.0 * A;

            if ((t < 0) || (t > 1))
            {
                /// Outside interval
                return(false);
            }
            Vector3 centerOld  = startPositionOther + ((float)t) * (endPositionOther - startPositionOther);
            Vector3 normalOld  = centerOld - Center;
            Vector3 contactOld = Center + normalOld * Radius / (Radius + radius);

            return(SetCollPrim(
                       startPositionOther,
                       centerOld,
                       contactOld,
                       normalOld,
                       Center + delta * (float)t,
                       ref collPrim));
        }   // end of Collide()
예제 #14
0
        /// <summary>
        /// Evaluate collisions with the moving sphere.
        /// If returns true, there was a collision as described in collPrim,
        /// else no collision and collPrim untouched.
        /// </summary>
        /// <param name="startPos"></param>
        /// <param name="endPos"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public override bool Collide(Vector3 startPos, Vector3 endPos, float radius, ref CollInfo collPrim)
        {
            /// Three possibilities of a collision here.
            /// One is that the sphere hit our top or bottom cap
            /// Second is the sphere hits the side.
            /// Third is sphere hits edge between side and cap.
            ///
            if (disabled)
            {
                return(false);
            }

            if (CheckTouching(startPos, radius, ref collPrim))
            {
                return(true);
            }

            Vector3 p0loc = Vector3.Transform(startPos, WorldToLocal);
            Vector3 p1loc = Vector3.Transform(endPos, WorldToLocal);

            float localRadius = WorldToLocalRadius(radius);

            /// Check the end caps first.
            /// Note that the cap checks look only for the sphere's tangent
            /// hitting on the cap, not for further misses.
            float t = CheckCapTop(p0loc, p1loc, localRadius);

            if (IsHit(t))
            {
                Vector3 center = startPos + t * (endPos - startPos);

                Vector3 norm = Vector3.TransformNormal(Vector3.UnitZ, LocalToWorld);

                return(SetCollPrim(
                           startPos,
                           center,
                           center - norm * radius,
                           norm,
                           ref collPrim));
            }

            t = CheckCapBot(p0loc, p1loc, localRadius);
            if (IsHit(t))
            {
                Vector3 center = startPos + t * (endPos - startPos);
                Vector3 norm   = Vector3.TransformNormal(-Vector3.UnitZ, LocalToWorld);

                return(SetCollPrim(
                           startPos,
                           center,
                           center - norm * radius,
                           norm,
                           ref collPrim));
            }

            /// Now check the sides.
            t = CheckSides(p0loc, p1loc, localRadius);
            if (IsHit(t))
            {
                Vector3 localCenter = p0loc + t * (p1loc - p0loc);
                if ((localCenter.Z >= 0) && (localCenter.Z <= Length))
                {
                    Vector3 center    = startPos + t * (endPos - startPos);
                    Vector3 localNorm = new Vector3(localCenter.X, localCenter.Y, 0.0f);
                    Vector3 norm      = Vector3.TransformNormal(localNorm, LocalToWorld);

                    return(SetCollPrim(
                               startPos,
                               center,
                               center - norm * radius,
                               norm,
                               ref collPrim));
                }
            }

            return(false);
        }
예제 #15
0
        /// <summary>
        /// Evaluate collisions with the moving sphere.
        /// If returns true, there was a collision as described in collPrim,
        /// else no collision and collPrim untouched.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="p1"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public override bool Collide(Vector3 p0, Vector3 p1, float radius, ref CollInfo collPrim)
        {
            /// Bounding sphere test here!!!
            if (disabled)
            {
                return(false);
            }

            if (CheckTouching(p0, radius, ref collPrim))
            {
                return(true);
            }

            /// Find point on sphere tangent to plane parallel to this.
            Vector3 worldNormal = WorldNormal;
            Vector3 pTan0       = p0 - worldNormal * radius;
            Vector3 pTan1       = p1 - worldNormal * radius;

            /// Project it along velocity onto plane
            float stepDist = Vector3.Dot(pTan1 - pTan0, worldNormal);

            if (stepDist >= -Single.Epsilon) /// We're moving away from it
            {
                return(false);
            }

            Vector3 center = planeToWorld.Translation;
            /// Check if we're already inside the plane
            float centerDist = Vector3.Dot(center, worldNormal) - Vector3.Dot(pTan0, worldNormal);

            if (centerDist > 0)
            {
                if (centerDist > radius)
                {
                    /// We're more than halfway embedded from the start, skip out
                    return(false);
                }
                pTan0     += centerDist * worldNormal;
                pTan1     += centerDist * worldNormal;
                centerDist = 0.0f;
            }

            float t = centerDist / stepDist;

            if ((t < 0.0f) || (t > 1.0f))
            {
                return(false);
            }

            Vector3 pProj      = pTan0 + t * (pTan1 - pTan0);
            Vector3 pProjPlane = Vector3.Transform(pProj, worldToPlane);

            if (Inside(pProjPlane))
            {
                /// The tangent on the sphere smacked into a side.
                Vector3 centerAtT = p0 + t * (p1 - p0);

                return(SetCollPrim(p0, centerAtT, pProj, centerAtT - pProj, ref collPrim));
            }

            /// pProj is where the sphere hits the plane.
            /// Find the closest point on the rect to pProj
            pProjPlane.X = MathHelper.Clamp(pProjPlane.X, -HalfWidth, HalfWidth);
            pProjPlane.Y = MathHelper.Clamp(pProjPlane.Y, -HalfHeight, HalfHeight);
            Vector3 pHit = Vector3.Transform(pProjPlane, planeToWorld);

            /// Try for an early out.
            if (Vector3.DistanceSquared(pHit, pProj) > radius * radius)
            {
                return(false);
            }

            /// pHit now the point on the rect the sphere will make first contact,
            /// if indeed it's going to make contact.
            /// Now cast a ray back from pHit to see where on the sphere makes contact.
            if (RaySphere(p0, radius, pHit, pHit - (p1 - p0), ref t))
            {
                Vector3 centerAtT = p0 + t * (p1 - p0);
                return(SetCollPrim(p0, centerAtT, pHit, centerAtT - pHit, ref collPrim));
            }

            return(false);
        }
예제 #16
0
        /// <summary>
        /// Evaluate collisions with the moving sphere.
        /// If returns true, there was a collision as described in collPrim,
        /// else no collision and collPrim untouched.
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="p1"></param>
        /// <param name="radius"></param>
        /// <param name="collPrim"></param>
        /// <returns></returns>
        public override bool Collide(Vector3 p0, Vector3 p1, float radius, ref CollInfo collPrim)
        {
            if (disabled)
            {
                return(false);
            }

            //p1 = p0;
            //p1.Y -= 1.0f;

            /// Check against bounding sphere.

            /// Check degenerate case where we are already in contact
            if (CheckTouching(p0, radius, ref collPrim))
            {
                return(true);
            }

            /// Move the problem to the space in which the expanded ellipse is a unit sphere
            /// centered at the origin.
            ///
            /// Expand our radii by radius
            float  localRadius     = WorldToLocalRadius(radius);
            Matrix ellipseToSphere = Matrix.CreateScale(
                1.0f / (Radii.X + localRadius),
                1.0f / (Radii.X + localRadius),
                1.0f / (Radii.Z + localRadius));

            Matrix worldToSphere = WorldToLocal * ellipseToSphere;

            /// Convert ray test to expanded ellipsoid space
            Vector3 p0sph = Vector3.Transform(p0, worldToSphere);
            Vector3 p1sph = Vector3.Transform(p1, worldToSphere);

            /// If we have a hit...
            float t   = 0.0f;
            bool  hit = RaySphere(Vector3.Zero, 1.0f, p0sph, p1sph, ref t);

            if (hit)
            {
                Matrix sphereToEllipse = Matrix.CreateScale(
                    Radii.X + localRadius,
                    Radii.Y + localRadius,
                    Radii.Z + localRadius);

                Matrix sphereToWorld = sphereToEllipse * LocalToWorld;

                Vector3 hitWorld = p0 + t * (p1 - p0);

                /// Get the hit point and normal in sphere space
                Vector3 hitsph            = Vector3.Transform(hitWorld, worldToSphere);
                Matrix  normSphereToLocal = Matrix.CreateScale(
                    ellipseToSphere.M11 * ellipseToSphere.M11,
                    ellipseToSphere.M22 * ellipseToSphere.M22,
                    ellipseToSphere.M33 * ellipseToSphere.M33);
                Vector3 localNormal = Vector3.TransformNormal(hitsph, normSphereToLocal);

                /// Transform the hit point and normal back to world space
                Vector3 normWorld = Vector3.TransformNormal(localNormal, LocalToWorld);
                normWorld.Normalize();

                /// Move the hit point into the ellipse by the normalized hit normal
                /// times the radius.

                SetCollPrim(
                    p0,
                    hitWorld,
                    hitWorld - normWorld * radius,
                    normWorld,
                    ref collPrim);

                return(true);
            }

            return(false);
        }
예제 #17
0
 /// <summary>
 /// Evaluate collisions with the moving sphere.
 /// If returns true, there was a collision as described in collPrim,
 /// else no collision and collPrim untouched.
 /// </summary>
 /// <param name="startPos"></param>
 /// <param name="endPos"></param>
 /// <param name="radius"></param>
 /// <param name="collPrim"></param>
 /// <returns></returns>
 public abstract bool Collide(Vector3 startPos, Vector3 endPos, float radius, ref CollInfo collPrim);