private static bool FindMinSepAxis(
            VoltPolygon poly1,
            VoltPolygon poly2,
            out Axis axis)
        {
            axis = new Axis(VoltVector2.zero, Fix64.MinValue);

            for (int i = 0; i < poly1.countWorld; i++)
            {
                Axis  a   = poly1.worldAxes[i];
                Fix64 min = Fix64.MaxValue;
                for (int j = 0; j < poly2.countWorld; j++)
                {
                    VoltVector2 v = poly2.worldVertices[j];
                    min = VoltMath.Min(min, VoltVector2.Dot(a.Normal, v));
                }
                min -= a.Width;

                if (min > Fix64.Zero)
                {
                    return(false);
                }
                if (min > axis.Width)
                {
                    axis = new Axis(a.Normal, min);
                }
            }

            return(true);
        }
        /// <summary>
        /// Workhorse for circle-circle collisions, compares origin distance
        /// to the sum of the two circles' radii, returns a Manifold.
        /// </summary>
        ///
        private static Manifold TestCircles(
            VoltWorld world,
            VoltCircle shapeA,
            VoltShape shapeB,
            VoltVector2 overrideBCenter, // For testing vertices in circles
            Fix64 overrideBRadius)
        {
            VoltVector2 r      = overrideBCenter - shapeA.worldSpaceOrigin;
            Fix64       min    = shapeA.radius + overrideBRadius;
            Fix64       distSq = r.sqrMagnitude;

            if (distSq >= min * min)
            {
                return(null);
            }

            Fix64 dist = VoltMath.Sqrt(distSq);

            // 최소값을 지정하여 divide by zero 방지
            Fix64 distInv = Fix64.One / VoltMath.Max(dist, min / (Fix64)10);

            VoltVector2 pos =
                shapeA.worldSpaceOrigin +
                (Fix64.One / (Fix64)2 + distInv * (shapeA.radius - min / (Fix64)2)) * r;

            // Build the collision Manifold
            Manifold manifold =
                world.AllocateManifold().Assign(world, shapeA, shapeB);

            manifold.AddContact(pos, distInv * r, dist - min);
            return(manifold);
        }
Example #3
0
 public void Set(TSVector2 position, FP radians)
 {
     this.Position = position;
     this.Angle    = radians;
     this.Facing   = VoltMath.Polar(radians);
     this.OnPositionUpdated();
 }
Example #4
0
        private void Initialize(
            TSVector2 position,
            FP radians,
            VoltShape[] shapesToAdd)
        {
            this.Position = position;
            this.Angle    = radians;
            this.Facing   = VoltMath.Polar(radians);

#if DEBUG
            for (int i = 0; i < shapesToAdd.Length; i++)
            {
                VoltDebug.Assert(shapesToAdd[i].IsInitialized);
            }
#endif

            if ((this.shapes == null) || (this.shapes.Length < shapesToAdd.Length))
            {
                this.shapes = new VoltShape[shapesToAdd.Length];
            }
            Array.Copy(shapesToAdd, this.shapes, shapesToAdd.Length);
            this.shapeCount = shapesToAdd.Length;
            for (int i = 0; i < this.shapeCount; i++)
            {
                this.shapes[i].AssignBody(this);
            }

#if DEBUG
            this.IsInitialized = true;
#endif
        }
Example #5
0
 public static VoltAABB CreateMerged(VoltAABB aabb1, VoltAABB aabb2)
 {
     return(new VoltAABB(
                VoltMath.Max(aabb1.top, aabb2.top),
                VoltMath.Min(aabb1.bottom, aabb2.bottom),
                VoltMath.Min(aabb1.left, aabb2.left),
                VoltMath.Max(aabb1.right, aabb2.right)));
 }
Example #6
0
 private void IntegrateVelocity()
 {
     this.Position +=
         this.World.DeltaTime * this.LinearVelocity + this.BiasVelocity;
     this.Angle +=
         this.World.DeltaTime * this.AngularVelocity + this.BiasRotation;
     this.Facing = VoltMath.Polar(this.Angle);
 }
        private Fix64 KScalar(
            VoltBody bodyA,
            VoltBody bodyB,
            VoltVector2 normal)
        {
            Fix64 massSum = bodyA.InvMass + bodyB.InvMass;
            Fix64 r1cnSqr = VoltMath.Square(VoltMath.Cross(this.toA, normal));
            Fix64 r2cnSqr = VoltMath.Square(VoltMath.Cross(this.toB, normal));

            return
                (massSum +
                 bodyA.InvInertia * r1cnSqr +
                 bodyB.InvInertia * r2cnSqr);
        }
Example #8
0
        private float KScalar(
            VoltBody bodyA,
            VoltBody bodyB,
            Vector2 normal)
        {
            float massSum = bodyA.InvMass + bodyB.InvMass;
            float r1cnSqr = VoltMath.Square(VoltMath.Cross(this.toA, normal));
            float r2cnSqr = VoltMath.Square(VoltMath.Cross(this.toB, normal));

            return
                (massSum +
                 bodyA.InvInertia * r1cnSqr +
                 bodyB.InvInertia * r2cnSqr);
        }
        internal Manifold Assign(
            VoltWorld world,
            VoltShape shapeA,
            VoltShape shapeB)
        {
            this.world  = world;
            this.ShapeA = shapeA;
            this.ShapeB = shapeB;

            this.Restitution = VoltMath.Sqrt(shapeA.Restitution * shapeB.Restitution);
            this.Friction    = VoltMath.Sqrt(shapeA.Friction * shapeB.Friction);
            this.used        = 0;

            return(this);
        }
Example #10
0
        public void PerformExplosion(
            TSVector2 origin,
            FP radius,
            VoltExplosionCallback callback,
            VoltBodyFilter targetFilter    = null,
            VoltBodyFilter occlusionFilter = null,
            int ticksBehind = 0,
            int rayCount    = 32)
        {
            if (ticksBehind < 0)
            {
                throw new ArgumentOutOfRangeException("ticksBehind");
            }

            // Get all target bodies
            this.PopulateFiltered(
                origin,
                radius,
                targetFilter,
                ticksBehind,
                ref this.targetBodies);

            // Get all occluding bodies
            this.PopulateFiltered(
                origin,
                radius,
                occlusionFilter,
                ticksBehind,
                ref this.occludingBodies);

            VoltRayCast ray;
            FP          rayWeight      = 1.0f / rayCount;
            FP          angleIncrement = (TSMath.Pi * 2.0f) * rayWeight;

            for (int i = 0; i < rayCount; i++)
            {
                TSVector2 normal = VoltMath.Polar(angleIncrement * i);
                ray = new VoltRayCast(origin, normal, radius);

                FP minDistance =
                    this.GetOccludingDistance(ray, ticksBehind);
                minDistance += VoltWorld.EXPLOSION_OCCLUDER_SLOP;

                this.TestTargets(ray, callback, ticksBehind, minDistance, rayWeight);
            }
        }
Example #11
0
        private static Manifold Circle_Polygon(
            VoltWorld world,
            VoltCircle circ,
            VoltPolygon poly)
        {
            // Get the axis on the polygon closest to the circle's origin
            float penetration;
            int   index =
                Collision.FindAxisMaxPenetration(
                    circ.worldSpaceOrigin,
                    circ.radius,
                    poly,
                    out penetration);

            if (index < 0)
            {
                return(null);
            }

            Vector2 a, b;

            poly.GetEdge(index, out a, out b);
            Axis axis = poly.GetWorldAxis(index);

            // If the circle is past one of the two vertices, check it like
            // a circle-circle intersection where the vertex has radius 0
            float d = VoltMath.Cross(axis.Normal, circ.worldSpaceOrigin);

            if (d > VoltMath.Cross(axis.Normal, a))
            {
                return(Collision.TestCircles(world, circ, poly, a, 0.0f));
            }
            if (d < VoltMath.Cross(axis.Normal, b))
            {
                return(Collision.TestCircles(world, circ, poly, b, 0.0f));
            }

            // Build the collision Manifold
            Manifold manifold = world.AllocateManifold().Assign(world, circ, poly);
            Vector2  pos      =
                circ.worldSpaceOrigin - (circ.radius + penetration / 2) * axis.Normal;

            manifold.AddContact(pos, -axis.Normal, penetration);
            return(manifold);
        }
Example #12
0
        private Fix64 ComputeInertia()
        {
            Fix64 s1 = Fix64.Zero;
            Fix64 s2 = Fix64.Zero;

            for (int i = 0; i < this.countBody; i++)
            {
                VoltVector2 v = this.bodyVertices[i];
                VoltVector2 u = this.bodyVertices[(i + 1) % this.countBody];

                Fix64 a = VoltMath.Cross(u, v);
                Fix64 b = v.sqrMagnitude + u.sqrMagnitude + VoltVector2.Dot(v, u);
                s1 += a * b;
                s2 += a;
            }

            return(s1 / ((Fix64)6 * s2));
        }
Example #13
0
        private float ComputeInertia()
        {
            float s1 = 0.0f;
            float s2 = 0.0f;

            for (int i = 0; i < this.countBody; i++)
            {
                Vector2 v = this.bodyVertices[i];
                Vector2 u = this.bodyVertices[(i + 1) % this.countBody];

                float a = VoltMath.Cross(u, v);
                float b = v.sqrMagnitude + u.sqrMagnitude + Vector2.Dot(v, u);
                s1 += a * b;
                s2 += a;
            }

            return(s1 / (6.0f * s2));
        }
Example #14
0
        private FP ComputeInertia()
        {
            FP s1 = 0.0f;
            FP s2 = 0.0f;

            for (int i = 0; i < this.countBody; i++)
            {
                TSVector2 v = this.bodyVertices[i];
                TSVector2 u = this.bodyVertices[(i + 1) % this.countBody];

                FP a = VoltMath.Cross(u, v);
                FP b = v.sqrMagnitude + u.sqrMagnitude + TSVector2.Dot(v, u);
                s1 += a * b;
                s2 += a;
            }

            return(s1 / (6.0f * s2));
        }
        internal void Solve(Manifold manifold)
        {
            VoltBody bodyA      = manifold.ShapeA.Body;
            VoltBody bodyB      = manifold.ShapeB.Body;
            Fix64    elasticity = bodyA.World.Elasticity;

            // Calculate relative bias velocity
            VoltVector2 vb1 = bodyA.BiasVelocity + (bodyA.BiasRotation * this.toALeft);
            VoltVector2 vb2 = bodyB.BiasVelocity + (bodyB.BiasRotation * this.toBLeft);
            Fix64       vbn = VoltVector2.Dot((vb1 - vb2), this.normal);

            // Calculate and clamp the bias impulse
            Fix64 jbn = this.nMass * (vbn - this.bias);

            jbn         = VoltMath.Max(-this.jBias, jbn);
            this.jBias += jbn;

            // Apply the bias impulse
            this.ApplyNormalBiasImpulse(bodyA, bodyB, jbn);

            // Calculate relative velocity
            VoltVector2 vr  = this.RelativeVelocity(bodyA, bodyB);
            Fix64       vrn = VoltVector2.Dot(vr, this.normal);

            // Calculate and clamp the normal impulse
            Fix64 jn = nMass * (vrn + (this.restitution * elasticity));

            jn = VoltMath.Max(-this.cachedNormalImpulse, jn);
            this.cachedNormalImpulse += jn;

            // Calculate the relative tangent velocity
            Fix64 vrt = VoltVector2.Dot(vr, this.normal.Left());

            // Calculate and clamp the friction impulse
            Fix64 jtMax  = manifold.Friction * this.cachedNormalImpulse;
            Fix64 jt     = vrt * tMass;
            Fix64 result = VoltMath.Clamp(this.cachedTangentImpulse + jt, -jtMax, jtMax);

            jt = result - this.cachedTangentImpulse;
            this.cachedTangentImpulse = result;

            // Apply the normal and tangent impulse
            this.ApplyContactImpulse(bodyA, bodyB, jn, jt);
        }
        /// <summary>
        /// Checks a ray against a circle with a given origin and square radius.
        /// </summary>
        internal static bool CircleRayCast(
            VoltShape shape,
            VoltVector2 shapeOrigin,
            Fix64 sqrRadius,
            ref VoltRayCast ray,
            ref VoltRayResult result)
        {
            VoltVector2 toOrigin = shapeOrigin - ray.origin;

            if (toOrigin.sqrMagnitude < sqrRadius)
            {
                result.SetContained(shape);
                return(true);
            }

            Fix64 slope = VoltVector2.Dot(toOrigin, ray.direction);

            if (slope < Fix64.Zero)
            {
                return(false);
            }

            Fix64 sqrSlope = slope * slope;
            Fix64 d        = sqrRadius + sqrSlope - VoltVector2.Dot(toOrigin, toOrigin);

            if (d < Fix64.Zero)
            {
                return(false);
            }

            Fix64 dist = slope - VoltMath.Sqrt(d);

            if (dist < Fix64.Zero || dist > ray.distance)
            {
                return(false);
            }

            // N.B.: For historical raycasts this normal will be wrong!
            // Must be either transformed back to world or invalidated later.
            VoltVector2 normal = (dist * ray.direction - toOrigin).normalized;

            result.Set(shape, dist, normal);
            return(true);
        }
Example #17
0
        private static VoltAABB ComputeBounds(
            VoltVector2[] vertices,
            int count)
        {
            Fix64 top    = vertices[0].y;
            Fix64 bottom = vertices[0].y;
            Fix64 left   = vertices[0].x;
            Fix64 right  = vertices[0].x;

            for (int i = 1; i < count; i++)
            {
                top    = VoltMath.Max(top, vertices[i].y);
                bottom = VoltMath.Min(bottom, vertices[i].y);
                left   = VoltMath.Min(left, vertices[i].x);
                right  = VoltMath.Max(right, vertices[i].x);
            }

            return(new VoltAABB(top, bottom, left, right));
        }
Example #18
0
        /// <summary>
        /// Builds the AABB by combining all the shape AABBs.
        /// </summary>
        private void UpdateAABB()
        {
            Fix64 top    = Fix64.MinValue;
            Fix64 right  = Fix64.MaxValue;
            Fix64 bottom = Fix64.MaxValue;
            Fix64 left   = Fix64.MinValue;

            for (int i = 0; i < this.shapeCount; i++)
            {
                VoltAABB aabb = this.shapes[i].AABB;

                top    = VoltMath.Max(top, aabb.Top);
                right  = VoltMath.Max(right, aabb.Right);
                bottom = VoltMath.Min(bottom, aabb.Bottom);
                left   = VoltMath.Min(left, aabb.Left);
            }

            this.AABB = new VoltAABB(top, bottom, left, right);
        }
Example #19
0
        protected override bool ShapeQueryCircle(
            Vector2 bodySpaceOrigin,
            float radius)
        {
            // Get the axis on the polygon closest to the circle's origin
            float penetration;
            int   foundIndex =
                Collision.FindAxisMaxPenetration(
                    bodySpaceOrigin,
                    radius,
                    this,
                    out penetration);

            if (foundIndex < 0)
            {
                return(false);
            }

            int     numVertices = this.countBody;
            Vector2 a           = this.bodyVertices[foundIndex];
            Vector2 b           = this.bodyVertices[(foundIndex + 1) % numVertices];
            Axis    axis        = this.bodyAxes[foundIndex];

            // If the circle is past one of the two vertices, check it like
            // a circle-circle intersection where the vertex has radius 0
            float d = VoltMath.Cross(axis.Normal, bodySpaceOrigin);

            if (d > VoltMath.Cross(axis.Normal, a))
            {
                return(Collision.TestPointCircleSimple(a, bodySpaceOrigin, radius));
            }
            if (d < VoltMath.Cross(axis.Normal, b))
            {
                return(Collision.TestPointCircleSimple(b, bodySpaceOrigin, radius));
            }
            return(true);
        }
 private static Fix64 BiasDist(Fix64 dist)
 {
     return(VoltConfig.ResolveRate * VoltMath.Min(Fix64.Zero, dist + VoltConfig.ResolveSlop));
 }
Example #21
0
 internal Vector2 WorldToBodyDirection(Vector2 vector)
 {
     return(VoltMath.WorldToBodyDirection(this.facing, vector));
 }
Example #22
0
 internal Vector2 WorldToBodyPoint(Vector2 vector)
 {
     return(VoltMath.WorldToBodyPoint(this.position, this.facing, vector));
 }
 public static VoltVector2 Polar(Fix64 radians)
 {
     return(new VoltVector2(VoltMath.Cos(radians), VoltMath.Sin(radians)));
 }
 public static Fix64 Angle(this VoltVector2 v)
 {
     return(VoltMath.Atan2(v.y, v.x));
 }
Example #25
0
 internal Vector2 BodyToWorldDirection(Vector2 vector)
 {
     return(VoltMath.BodyToWorldDirection(this.facing, vector));
 }
Example #26
0
 internal void ApplyBias(TSVector2 j, TSVector2 r)
 {
     this.BiasVelocity += this.InvMass * j;
     this.BiasRotation -= this.InvInertia * VoltMath.Cross(j, r);
 }
Example #27
0
 internal void ApplyImpulse(TSVector2 j, TSVector2 r)
 {
     this.LinearVelocity  += this.InvMass * j;
     this.AngularVelocity -= this.InvInertia * VoltMath.Cross(j, r);
 }
Example #28
0
 public void AddForce(TSVector2 force, TSVector2 point)
 {
     this.Force  += force;
     this.Torque += VoltMath.Cross(this.Position - point, force);
 }
Example #29
0
        private bool CircleCastEdges(
            ref VoltRayCast bodySpaceRay,
            float radius,
            ref VoltRayResult result)
        {
            int  foundIndex       = -1;
            bool couldBeContained = true;

            // Pre-compute and initialize values
            float   shortestDist = float.MaxValue;
            Vector2 v3           = bodySpaceRay.direction.Left();

            // Check the edges -- this will be different from the raycast because
            // we care about staying within the ends of the edge line segment
            for (int i = 0; i < this.countBody; i++)
            {
                Axis curAxis = this.bodyAxes[i];

                // Push the edges out by the radius
                Vector2 extension = curAxis.Normal * radius;
                Vector2 a         = this.bodyVertices[i] + extension;
                Vector2 b         = this.bodyVertices[(i + 1) % this.countBody] + extension;

                // Update the check for containment
                if (couldBeContained == true)
                {
                    float proj =
                        Vector2.Dot(curAxis.Normal, bodySpaceRay.origin) - curAxis.Width;

                    // The point lies outside of the outer layer
                    if (proj > radius)
                    {
                        couldBeContained = false;
                    }
                    // The point lies between the outer and inner layer
                    else if (proj > 0.0f)
                    {
                        // See if the point is within the center Vornoi region of the edge
                        float d = VoltMath.Cross(curAxis.Normal, bodySpaceRay.origin);
                        if (d > VoltMath.Cross(curAxis.Normal, a))
                        {
                            couldBeContained = false;
                        }
                        if (d < VoltMath.Cross(curAxis.Normal, b))
                        {
                            couldBeContained = false;
                        }
                    }
                }

                // For the cast, only consider rays pointing towards the edge
                if (Vector2.Dot(curAxis.Normal, bodySpaceRay.direction) >= 0.0f)
                {
                    continue;
                }

                // See:
                // https://rootllama.wordpress.com/2014/06/20/ray-line-segment-intersection-test-in-2d/
                Vector2 v1 = bodySpaceRay.origin - a;
                Vector2 v2 = b - a;

                float denominator = Vector2.Dot(v2, v3);
                float t1          = VoltMath.Cross(v2, v1) / denominator;
                float t2          = Vector2.Dot(v1, v3) / denominator;

                if ((t2 >= 0.0f) && (t2 <= 1.0f) && (t1 > 0.0f) && (t1 < shortestDist))
                {
                    // See if the point is outside of any of the axes
                    shortestDist = t1;
                    foundIndex   = i;
                }
            }

            // Report results
            if (couldBeContained == true)
            {
                result.SetContained(this);
                return(true);
            }
            else if (foundIndex >= 0 && shortestDist <= bodySpaceRay.distance)
            {
                result.Set(
                    this,
                    shortestDist,
                    this.bodyAxes[foundIndex].Normal);
                return(true);
            }
            return(false);
        }
Example #30
0
 internal Vector2 BodyToWorldPoint(Vector2 vector)
 {
     return(VoltMath.BodyToWorldPoint(this.position, this.facing, vector));
 }