Example #1
0
        public unsafe void TestExamineActivePlanes()
        {
            float3 up = new float3(0, 1, 0);

            SurfaceConstraintInfo plane1 = CreateConstraint(ColliderKey.Empty, float3.zero, new float3(0, 1, 0), 1, 0, 0, false, float3.zero);
            SurfaceConstraintInfo plane2 = CreateConstraint(ColliderKey.Empty, float3.zero, new float3(0, -1, 0), 1, 0, 0, false, float3.zero);
            SurfaceConstraintInfo plane3 = CreateConstraint(ColliderKey.Empty, float3.zero, new float3(1, 0, 0), 1, 0, 0, false, float3.zero);
            SurfaceConstraintInfo plane4 = CreateConstraint(ColliderKey.Empty, float3.zero, math.normalize(new float3(1, 1, 0)), 1, 0, 0, false, float3.zero);
            SurfaceConstraintInfo plane5 = CreateConstraint(ColliderKey.Empty, float3.zero, math.normalize(new float3(-1, 1, 0)), 1, 0, 0, false, float3.zero);
            SurfaceConstraintInfo plane6 = CreateConstraint(ColliderKey.Empty, float3.zero, new float3(0, 0, 1), 1, 0, 0, false, float3.zero);
            SurfaceConstraintInfo plane7 = CreateConstraint(ColliderKey.Empty, float3.zero, new float3(0, 0, -1), 1, 0, 0, false, new float3(0, 0, 1));

            // Test the single plane
            SurfaceConstraintInfo *supportPlanes = stackalloc SurfaceConstraintInfo[4];
            int numSupportPlanes = 1;

            supportPlanes[0] = plane1;
            float3 velocity = new float3(0, -1, 0);

            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(1, numSupportPlanes);

            // 2 planes, one unnecessary
            numSupportPlanes = 2;
            supportPlanes[0] = plane1;
            supportPlanes[1] = plane1;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(1, numSupportPlanes);

            // 2 planes, both necessary
            numSupportPlanes = 2;
            supportPlanes[0] = plane5;
            supportPlanes[1] = plane4;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(2, numSupportPlanes);

            // 3 planes, 2 unnecessary
            numSupportPlanes = 3;
            supportPlanes[0] = plane1;
            supportPlanes[1] = plane1;
            supportPlanes[2] = plane1;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(1, numSupportPlanes);

            // 3 planes, 1 unnecessary
            numSupportPlanes = 3;
            supportPlanes[0] = plane1;
            supportPlanes[1] = plane1;
            supportPlanes[2] = plane3;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(2, numSupportPlanes);

            // 3 planes, all necessary
            numSupportPlanes = 3;
            supportPlanes[0] = plane4;
            supportPlanes[1] = plane5;
            supportPlanes[2] = plane6;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(3, numSupportPlanes);

            // 4 planes, 3 unnecessary
            numSupportPlanes = 4;
            supportPlanes[0] = plane1;
            supportPlanes[1] = plane1;
            supportPlanes[2] = plane1;
            supportPlanes[3] = plane1;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(1, numSupportPlanes);

            // 4 planes, 2 unnecessary
            numSupportPlanes = 4;
            supportPlanes[0] = plane1;
            supportPlanes[1] = plane1;
            supportPlanes[2] = plane1;
            supportPlanes[3] = plane3;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(2, numSupportPlanes);

            // 4 planes, 1 unnecessary
            numSupportPlanes = 4;
            supportPlanes[0] = plane1;
            supportPlanes[1] = plane4;
            supportPlanes[2] = plane5;
            supportPlanes[3] = plane6;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(3, numSupportPlanes);

            // 4 planes, all necessary
            numSupportPlanes = 4;
            supportPlanes[0] = plane4;
            supportPlanes[1] = plane5;
            supportPlanes[2] = plane6;
            supportPlanes[3] = plane7;
            velocity         = new float3(0, -1, 0);
            ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);
            Assert.AreApproximatelyEqual(velocity.x, 0);
            Assert.AreApproximatelyEqual(velocity.y, 0);
            Assert.AreApproximatelyEqual(velocity.z, 0);
            Assert.AreEqual(4, numSupportPlanes);
        }
Example #2
0
        public static unsafe void ExamineActivePlanes(float3 up, SurfaceConstraintInfo *supportPlanes, ref int numSupportPlanes, ref float3 velocity)
        {
            switch (numSupportPlanes)
            {
            case 1:
            {
                Solve1d(supportPlanes[0], ref velocity);
                return;
            }

            case 2:
            {
                // Test whether we need plane 0 at all
                float3 tempVelocity = velocity;
                Solve1d(supportPlanes[1], ref tempVelocity);

                bool plane0Used = Test1d(supportPlanes[0], tempVelocity);
                if (!plane0Used)
                {
                    // Compact the buffer and reduce size
                    supportPlanes[0] = supportPlanes[1];
                    numSupportPlanes = 1;

                    // Write back the result
                    velocity = tempVelocity;
                }
                else
                {
                    Solve2d(up, supportPlanes[0], supportPlanes[1], ref velocity);
                }

                return;
            }

            case 3:
            {
                // Try to drop both planes
                float3 tempVelocity = velocity;
                Solve1d(supportPlanes[2], ref tempVelocity);

                bool plane0Used = Test1d(supportPlanes[0], tempVelocity);
                if (!plane0Used)
                {
                    bool plane1Used = Test1d(supportPlanes[1], tempVelocity);
                    if (!plane1Used)
                    {
                        // Compact the buffer and reduce size
                        supportPlanes[0] = supportPlanes[2];
                        numSupportPlanes = 1;
                        goto case 1;
                    }
                }

                // Try to drop plane 0 or 1
                for (int testPlane = 0; testPlane < 2; testPlane++)
                {
                    tempVelocity = velocity;
                    Solve2d(up, supportPlanes[testPlane], supportPlanes[2], ref tempVelocity);

                    bool planeUsed = Test1d(supportPlanes[1 - testPlane], tempVelocity);
                    if (!planeUsed)
                    {
                        supportPlanes[0] = supportPlanes[testPlane];
                        supportPlanes[1] = supportPlanes[2];
                        numSupportPlanes--;
                        goto case 2;
                    }
                }

                // Try solve all three
                Solve3d(up, supportPlanes[0], supportPlanes[1], supportPlanes[2], ref velocity);

                return;
            }

            case 4:
            {
                for (int i = 0; i < 3; i++)
                {
                    float3 tempVelocity = velocity;
                    Solve3d(up, supportPlanes[(i + 1) % 3], supportPlanes[(i + 2) % 3], supportPlanes[3], ref tempVelocity);
                    bool planeUsed = Test1d(supportPlanes[i], tempVelocity);
                    if (!planeUsed)
                    {
                        supportPlanes[i] = supportPlanes[2];
                        supportPlanes[2] = supportPlanes[3];
                        numSupportPlanes = 3;
                        goto case 3;
                    }
                }

                // Nothing can be dropped so we've failed to solve,
                // now we do all 3d combinations
                float3 tempVel            = velocity;
                SurfaceConstraintInfo sp0 = supportPlanes[0];
                SurfaceConstraintInfo sp1 = supportPlanes[1];
                SurfaceConstraintInfo sp2 = supportPlanes[2];
                SurfaceConstraintInfo sp3 = supportPlanes[3];
                Solve3d(up, sp0, sp1, sp2, ref tempVel);
                Solve3d(up, sp0, sp1, sp3, ref tempVel);
                Solve3d(up, sp0, sp2, sp3, ref tempVel);
                Solve3d(up, sp1, sp2, sp3, ref tempVel);

                velocity = tempVel;

                return;
            }

            default:
            {
                // Can't have more than 4 and less than 1 plane
                Assert.IsTrue(false);
                break;
            }
            }
        }
Example #3
0
        public static unsafe void Solve(
            float deltaTime, float minDeltaTime, float3 up, float maxVelocity,
            NativeList <SurfaceConstraintInfo> constraints, ref float3 position, ref float3 velocity, out float integratedTime, bool useConstraintVelocities = true
            )
        {
            // List of planes to solve against (up to 4)
            SurfaceConstraintInfo *supportPlanes = stackalloc SurfaceConstraintInfo[4];
            int numSupportPlanes = 0;

            float remainingTime = deltaTime;
            float currentTime   = 0.0f;

            // Clamp the input velocity to max movement speed
            ClampToMaxLength(maxVelocity, ref velocity);

            while (remainingTime > 0.0f)
            {
                int   hitIndex         = -1;
                float minCollisionTime = remainingTime;

                // Iterate over constraints and solve them
                for (int i = 0; i < constraints.Length; i++)
                {
                    if (constraints[i].Touched)
                    {
                        continue;
                    }

                    SurfaceConstraintInfo constraint = constraints[i];

                    float3 relVel     = velocity - (useConstraintVelocities ? constraint.Velocity : float3.zero);
                    float  relProjVel = -math.dot(relVel, constraint.Plane.Normal);
                    if (relProjVel < k_Epsilon)
                    {
                        continue;
                    }

                    // Clamp distance to 0, since penetration is handled by constraint.Velocity already
                    float distance = math.max(constraint.Plane.Distance, 0.0f);
                    if (distance < minCollisionTime * relProjVel)
                    {
                        minCollisionTime = distance / relProjVel;
                        hitIndex         = i;
                    }
                }

                // Integrate
                {
                    currentTime   += minCollisionTime;
                    remainingTime -= minCollisionTime;
                    position      += minCollisionTime * velocity;
                }

                if (hitIndex < 0 || currentTime > minDeltaTime)
                {
                    break;
                }

                // Mark constraint as touched
                {
                    var constraint = constraints[hitIndex];
                    constraint.Touched    = true;
                    constraints[hitIndex] = constraint;
                }

                // Add the hit to the current list of active planes
                supportPlanes[numSupportPlanes] = constraints[hitIndex];
                if (!useConstraintVelocities)
                {
                    supportPlanes[numSupportPlanes].Velocity = float3.zero;
                }
                numSupportPlanes++;

                // Solve support planes
                ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);

                // Clamp the solved velocity to max movement speed
                ClampToMaxLength(maxVelocity, ref velocity);

                // Can't handle more than 4 support planes
                if (numSupportPlanes == 4)
                {
                    break;
                }
            }

            integratedTime = currentTime;
        }
Example #4
0
        public static unsafe void Solve(PhysicsWorld world, float deltaTime, float3 up, int numConstraints,
                                        ref NativeArray <SurfaceConstraintInfo> constraints, ref float3 position, ref float3 velocity, out float integratedTime)
        {
            // List of planes to solve against (up to 4)
            SurfaceConstraintInfo *supportPlanes = stackalloc SurfaceConstraintInfo[4];
            int numSupportPlanes = 0;

            float remainingTime = deltaTime;
            float currentTime   = 0.0f;

            float minDeltaTime = 0.0f;

            if (math.lengthsq(velocity) > c_SimplexSolverEpsilon)
            {
                // Min delta time to travel at least 1cm
                minDeltaTime = 0.01f / math.length(velocity);
            }

            while (remainingTime > 0.0f)
            {
                int   hitIndex         = -1;
                float minCollisionTime = remainingTime;

                // Iterate over constraints and solve them
                for (int i = 0; i < numConstraints; i++)
                {
                    if (constraints[i].Touched)
                    {
                        continue;
                    }

                    SurfaceConstraintInfo constraint = constraints[i];

                    float3 relVel     = velocity - constraint.Velocity;
                    float  relProjVel = -math.dot(relVel, constraint.Plane.Normal);
                    if (relProjVel < c_SimplexSolverEpsilon)
                    {
                        continue;
                    }

                    // Clamp distance to 0, since penetration is handled by constraint.Velocity already
                    float distance = math.max(constraint.Plane.Distance, 0.0f);
                    if (distance < minCollisionTime * relProjVel)
                    {
                        minCollisionTime = distance / relProjVel;
                        hitIndex         = i;
                    }
                }

                // Integrate if at least 100 microseconds to hit
                if (minCollisionTime > 1e-4f)
                {
                    currentTime   += minCollisionTime;
                    remainingTime -= minCollisionTime;
                    position      += minCollisionTime * velocity;
                }

                if (hitIndex < 0 || currentTime > minDeltaTime)
                {
                    break;
                }

                // Mark constraint as touched
                {
                    var constraint = constraints[hitIndex];
                    constraint.Touched    = true;
                    constraints[hitIndex] = constraint;
                }

                // Add the hit to the current list of active planes
                supportPlanes[numSupportPlanes++] = constraints[hitIndex];

                // Solve support planes
                ExamineActivePlanes(up, supportPlanes, ref numSupportPlanes, ref velocity);

                // Can't handle more than 4 support planes
                if (numSupportPlanes == 4)
                {
                    break;
                }
            }

            integratedTime = currentTime;
        }