// Solve the Jacobian
        public void Solve(ref MotionVelocity velocityA, ref MotionVelocity velocityB, float timestep)
        {
            // Predict the relative orientation at the end of the step
            quaternion futureBFromA = JacobianUtilities.IntegrateOrientationBFromA(BFromA, velocityA.AngularVelocity, velocityB.AngularVelocity, timestep);

            // Calculate the jacobian axis and angle
            float3 axisAinB     = math.mul(futureBFromA, AxisAinA);
            float3 jacB0        = math.cross(axisAinB, AxisBinB);
            float3 jacA0        = math.mul(math.inverse(futureBFromA), -jacB0);
            float  jacLengthSq  = math.lengthsq(jacB0);
            float  invJacLength = Math.RSqrtSafe(jacLengthSq);
            float  futureAngle;
            {
                float sinAngle = jacLengthSq * invJacLength;
                float cosAngle = math.dot(axisAinB, AxisBinB);
                futureAngle = math.atan2(sinAngle, cosAngle);
            }

            // Choose a second jacobian axis perpendicular to A
            float3 jacB1 = math.cross(jacB0, axisAinB);
            float3 jacA1 = math.mul(math.inverse(futureBFromA), -jacB1);

            // Calculate effective mass
            float2 effectiveMass; // First column of the 2x2 matrix, we don't need the second column because the second component of error is zero

            {
                // Calculate the inverse effective mass matrix, then invert it
                float invEffMassDiag0   = math.csum(jacA0 * jacA0 * velocityA.InverseInertiaAndMass.xyz + jacB0 * jacB0 * velocityB.InverseInertiaAndMass.xyz);
                float invEffMassDiag1   = math.csum(jacA1 * jacA1 * velocityA.InverseInertiaAndMass.xyz + jacB1 * jacB1 * velocityB.InverseInertiaAndMass.xyz);
                float invEffMassOffDiag = math.csum(jacA0 * jacA1 * velocityA.InverseInertiaAndMass.xyz + jacB0 * jacB1 * velocityB.InverseInertiaAndMass.xyz);
                float det    = invEffMassDiag0 * invEffMassDiag1 - invEffMassOffDiag * invEffMassOffDiag;
                float invDet = math.select(jacLengthSq / det, 0.0f, det == 0.0f); // scale by jacLengthSq because the jacs were not normalized
                effectiveMass = invDet * new float2(invEffMassDiag1, -invEffMassOffDiag);
            }

            // Normalize the jacobians
            jacA0 *= invJacLength;
            jacB0 *= invJacLength;
            jacA1 *= invJacLength;
            jacB1 *= invJacLength;

            // Calculate the error, adjust by tau and damping, and apply an impulse to correct it
            float  futureError = JacobianUtilities.CalculateError(futureAngle, MinAngle, MaxAngle);
            float  solveError  = JacobianUtilities.CalculateCorrection(futureError, InitialError, Tau, Damping);
            float2 impulse     = -effectiveMass * solveError * (1.0f / timestep);

            velocityA.ApplyAngularImpulse(impulse.x * jacA0 + impulse.y * jacA1);
            velocityB.ApplyAngularImpulse(impulse.x * jacB0 + impulse.y * jacB1);
        }
Beispiel #2
0
        // Generic solve method that dispatches to specific ones
        public void Solve(
            ref JacobianHeader jacHeader, ref MotionVelocity velocityA, ref MotionVelocity velocityB,
            Solver.StepInput stepInput, ref BlockStream.Writer collisionEventsWriter)
        {
            bool bothBodiesWithInfInertiaAndMass = math.all(velocityA.InverseInertiaAndMass == float4.zero) && math.all(velocityB.InverseInertiaAndMass == float4.zero);

            if (bothBodiesWithInfInertiaAndMass)
            {
                SolveInfMassPair(ref jacHeader, velocityA, velocityB, stepInput, ref collisionEventsWriter);
            }
            else
            {
                SolveContact(ref jacHeader, ref velocityA, ref velocityB, stepInput, ref collisionEventsWriter);
            }
        }
        // Solve the Jacobian
        public void SolveContact(
            ref JacobianHeader jacHeader, ref MotionVelocity velocityA, ref MotionVelocity velocityB,
            Solver.StepInput stepInput, ref NativeStream.Writer collisionEventsWriter, bool enableFrictionVelocitiesHeuristic,
            Solver.MotionStabilizationInput motionStabilizationSolverInputA, Solver.MotionStabilizationInput motionStabilizationSolverInputB)
        {
            // Copy velocity data
            MotionVelocity tempVelocityA = velocityA;
            MotionVelocity tempVelocityB = velocityB;

            if (jacHeader.HasMassFactors)
            {
                MassFactors jacMod = jacHeader.AccessMassFactors();
                tempVelocityA.InverseInertia *= jacMod.InverseInertiaFactorA;
                tempVelocityA.InverseMass    *= jacMod.InverseMassFactorA;
                tempVelocityB.InverseInertia *= jacMod.InverseInertiaFactorB;
                tempVelocityB.InverseMass    *= jacMod.InverseMassFactorB;
            }

            // Solve normal impulses
            float sumImpulses             = 0.0f;
            float totalAccumulatedImpulse = 0.0f;
            bool  forceCollisionEvent     = false;

            for (int j = 0; j < BaseJacobian.NumContacts; j++)
            {
                ref ContactJacAngAndVelToReachCp jacAngular = ref jacHeader.AccessAngularJacobian(j);

                // Solve velocity so that predicted contact distance is greater than or equal to zero
                float relativeVelocity = BaseContactJacobian.GetJacVelocity(BaseJacobian.Normal, jacAngular.Jac,
                                                                            tempVelocityA.LinearVelocity, tempVelocityA.AngularVelocity, tempVelocityB.LinearVelocity, tempVelocityB.AngularVelocity);
                float dv = jacAngular.VelToReachCp - relativeVelocity;

                float impulse            = dv * jacAngular.Jac.EffectiveMass;
                float accumulatedImpulse = math.max(jacAngular.Jac.Impulse + impulse, 0.0f);
                if (accumulatedImpulse != jacAngular.Jac.Impulse)
                {
                    float deltaImpulse = accumulatedImpulse - jacAngular.Jac.Impulse;
                    ApplyImpulse(deltaImpulse, BaseJacobian.Normal, jacAngular.Jac, ref tempVelocityA, ref tempVelocityB,
                                 motionStabilizationSolverInputA.InverseInertiaScale, motionStabilizationSolverInputB.InverseInertiaScale);
                }

                jacAngular.Jac.Impulse   = accumulatedImpulse;
                sumImpulses             += accumulatedImpulse;
                totalAccumulatedImpulse += jacAngular.Jac.Impulse;

                // Force contact event even when no impulse is applied, but there is penetration.
                forceCollisionEvent |= jacAngular.VelToReachCp > 0.0f;
            }
Beispiel #4
0
 private static void BuildContactJacobian(
     int contactPointIndex,
     float3 normal,
     MTransform worldFromA,
     MTransform worldFromB,
     float invTimestep,
     MotionVelocity velocityA,
     MotionVelocity velocityB,
     float sumInvMass,
     float maxDepenetrationVelocity,
     ref JacobianHeader jacobianHeader,
     ref float3 centerA,
     ref float3 centerB,
     ref NativeStream.Reader contactReader)
 {
     ref ContactJacAngAndVelToReachCp jacAngular = ref jacobianHeader.AccessAngularJacobian(contactPointIndex);
        // Generic solve method that dispatches to specific ones
        public void Solve(
            ref JacobianHeader jacHeader, ref MotionVelocity velocityA, ref MotionVelocity velocityB,
            Solver.StepInput stepInput, ref NativeStream.Writer collisionEventsWriter, bool enableFrictionVelocitiesHeuristic,
            Solver.MotionStabilizationInput motionStabilizationSolverInputA, Solver.MotionStabilizationInput motionStabilizationSolverInputB)
        {
            bool bothBodiesWithInfInertiaAndMass = velocityA.HasInfiniteInertiaAndMass && velocityB.HasInfiniteInertiaAndMass;

            if (bothBodiesWithInfInertiaAndMass)
            {
                SolveInfMassPair(ref jacHeader, velocityA, velocityB, stepInput, ref collisionEventsWriter);
            }
            else
            {
                SolveContact(ref jacHeader, ref velocityA, ref velocityB, stepInput, ref collisionEventsWriter,
                             enableFrictionVelocitiesHeuristic, motionStabilizationSolverInputA, motionStabilizationSolverInputB);
            }
        }
Beispiel #6
0
            public void Execute(int i)
            {
                MotionData     motionData     = MotionDatas[i];
                MotionVelocity motionVelocity = MotionVelocities[i];

                // Apply gravity
                motionVelocity.LinearVelocity += GravityAcceleration * motionData.GravityFactor;

                // Write back
                MotionVelocities[i] = motionVelocity;

                // Make a copy
                InputVelocities[i] = new Velocity
                {
                    Linear  = motionVelocity.LinearVelocity,
                    Angular = motionVelocity.AngularVelocity
                };
            }
Beispiel #7
0
        // Build the Jacobian
        public void Build(
            MTransform aFromConstraint, MTransform bFromConstraint,
            MotionVelocity velocityA, MotionVelocity velocityB,
            MotionData motionA, MotionData motionB,
            Constraint constraint, float tau, float damping)
        {
            BFromA    = math.mul(math.inverse(motionB.WorldFromMotion.rot), motionA.WorldFromMotion.rot);
            RefBFromA = new quaternion(math.mul(bFromConstraint.Rotation, aFromConstraint.InverseRotation));
            MinAngle  = constraint.Min;
            MaxAngle  = constraint.Max;
            Tau       = tau;
            Damping   = damping;

            quaternion jointOrientation = math.mul(math.inverse(RefBFromA), BFromA);
            float      initialAngle     = math.asin(math.length(jointOrientation.value.xyz)) * 2.0f;

            InitialError = JacobianUtilities.CalculateError(initialAngle, MinAngle, MaxAngle);
        }
Beispiel #8
0
            internal static void ExecuteImpl(int i, float3 gravityAcceleration,
                                             NativeSlice <MotionData> motionDatas, NativeSlice <MotionVelocity> motionVelocities, NativeSlice <Velocity> inputVelocities)
            {
                MotionData     motionData     = motionDatas[i];
                MotionVelocity motionVelocity = motionVelocities[i];

                // Apply gravity
                motionVelocity.LinearVelocity += gravityAcceleration * motionData.GravityFactor;

                // Write back
                motionVelocities[i] = motionVelocity;

                // Make a copy
                inputVelocities[i] = new Velocity
                {
                    Linear  = motionVelocity.LinearVelocity,
                    Angular = motionVelocity.AngularVelocity
                };
            }
Beispiel #9
0
        // Build the Jacobian
        public void Build(
            MTransform aFromConstraint, MTransform bFromConstraint,
            MotionVelocity velocityA, MotionVelocity velocityB,
            MotionData motionA, MotionData motionB,
            Constraint constraint, float tau, float damping)
        {
            // Copy the constraint into the jacobian
            AxisIndex        = constraint.ConstrainedAxis1D;
            AxisInMotionA    = aFromConstraint.Rotation[AxisIndex];
            MinAngle         = constraint.Min;
            MaxAngle         = constraint.Max;
            Tau              = tau;
            Damping          = damping;
            MotionBFromA     = math.mul(math.inverse(motionB.WorldFromMotion.rot), motionA.WorldFromMotion.rot);
            MotionAFromJoint = new quaternion(aFromConstraint.Rotation);
            MotionBFromJoint = new quaternion(bFromConstraint.Rotation);

            // Calculate the current error
            InitialError = CalculateError(MotionBFromA);
        }
Beispiel #10
0
        // Build the Jacobian
        public void Build(
            MTransform aFromConstraint, MTransform bFromConstraint,
            MotionVelocity velocityA, MotionVelocity velocityB,
            MotionData motionA, MotionData motionB,
            Constraint constraint, float tau, float damping)
        {
            this = default(AngularLimit3DJacobian);

            BFromA           = math.mul(math.inverse(motionB.WorldFromMotion.rot), motionA.WorldFromMotion.rot);
            MotionAFromJoint = new quaternion(aFromConstraint.Rotation);
            MotionBFromJoint = new quaternion(bFromConstraint.Rotation);
            MinAngle         = constraint.Min;
            MaxAngle         = constraint.Max;
            Tau     = tau;
            Damping = damping;

            float initialAngle = math.atan2(math.length(BFromA.value.xyz), BFromA.value.w) * 2.0f;

            InitialError = JacobianUtilities.CalculateError(initialAngle, MinAngle, MaxAngle);
        }
Beispiel #11
0
 // Gets a body's motion, even if the body is static
 // TODO - share code with Solver.GetMotions()?
 private static void GetMotion(ref PhysicsWorld world, int bodyIndex, out MotionVelocity velocity, out MotionData motion)
 {
     if (bodyIndex >= world.MotionVelocities.Length)
     {
         // Body is static
         RigidBody body = world.Bodies[bodyIndex];
         velocity = MotionVelocity.Zero;
         motion   = new MotionData
         {
             WorldFromMotion = body.WorldFromBody,
             BodyFromMotion  = RigidTransform.identity
                               // remaining fields all zero
         };
     }
     else
     {
         // Body is dynamic
         velocity = world.MotionVelocities[bodyIndex];
         motion   = world.MotionDatas[bodyIndex];
     }
 }
Beispiel #12
0
        // Solve the Jacobian
        public void Solve(ref MotionVelocity velocityA, ref MotionVelocity velocityB, float timestep, float invTimestep)
        {
            // Predict the relative orientation at the end of the step
            quaternion futureMotionBFromA = JacobianUtilities.IntegrateOrientationBFromA(MotionBFromA, velocityA.AngularVelocity, velocityB.AngularVelocity, timestep);

            // Calculate the effective mass
            float3 axisInMotionB = math.mul(futureMotionBFromA, -AxisInMotionA);
            float  effectiveMass;
            {
                float invEffectiveMass = math.csum(AxisInMotionA * AxisInMotionA * velocityA.InverseInertia +
                                                   axisInMotionB * axisInMotionB * velocityB.InverseInertia);
                effectiveMass = math.select(1.0f / invEffectiveMass, 0.0f, invEffectiveMass == 0.0f);
            }

            // Calculate the error, adjust by tau and damping, and apply an impulse to correct it
            float futureError = CalculateError(futureMotionBFromA);
            float solveError  = JacobianUtilities.CalculateCorrection(futureError, InitialError, Tau, Damping);
            float impulse     = math.mul(effectiveMass, -solveError) * invTimestep;

            velocityA.ApplyAngularImpulse(impulse * AxisInMotionA);
            velocityB.ApplyAngularImpulse(impulse * axisInMotionB);
        }
 private static void ApplyImpulse(float3 impulse, float3 ang0, float3 ang1, float3 ang2, ref MotionVelocity velocity)
 {
     velocity.ApplyLinearImpulse(impulse);
     velocity.ApplyAngularImpulse(impulse.x * ang0 + impulse.y * ang1 + impulse.z * ang2);
 }
Beispiel #14
0
        // Write a set of contact manifolds for a pair of bodies to the given stream.
        public static unsafe void BodyBody(RigidBody rigidBodyA, RigidBody rigidBodyB, MotionVelocity motionVelocityA, MotionVelocity motionVelocityB,
                                           float collisionTolerance, float timeStep, BodyIndexPair pair, ref NativeStream.Writer contactWriter)
        {
            Collider *colliderA = rigidBodyA.Collider;
            Collider *colliderB = rigidBodyB.Collider;

            if (colliderA == null || colliderB == null || !CollisionFilter.IsCollisionEnabled(colliderA->Filter, colliderB->Filter))
            {
                return;
            }

            // Build combined motion expansion
            MotionExpansion expansion;
            {
                MotionExpansion expansionA = motionVelocityA.CalculateExpansion(timeStep);
                MotionExpansion expansionB = motionVelocityB.CalculateExpansion(timeStep);
                expansion = new MotionExpansion
                {
                    Linear  = expansionA.Linear - expansionB.Linear,
                    Uniform = expansionA.Uniform + expansionB.Uniform + collisionTolerance
                };
            }

            var context = new Context
            {
                BodyIndices    = pair,
                BodyCustomTags = new CustomTagsPair {
                    CustomTagsA = rigidBodyA.CustomTags, CustomTagsB = rigidBodyB.CustomTags
                },
                BodiesHaveInfiniteMass =
                    !math.any(motionVelocityA.InverseInertiaAndMass) &&
                    !math.any(motionVelocityB.InverseInertiaAndMass),
                ContactWriter = (NativeStream.Writer *)UnsafeUtility.AddressOf(ref contactWriter)
            };

            var worldFromA = new MTransform(rigidBodyA.WorldFromBody);
            var worldFromB = new MTransform(rigidBodyB.WorldFromBody);

            // Dispatch to appropriate manifold generator
            switch (colliderA->CollisionType)
            {
            case CollisionType.Convex:
                switch (colliderB->CollisionType)
                {
                case CollisionType.Convex:
                    ConvexConvex(context, ColliderKeyPair.Empty, colliderA, colliderB, worldFromA, worldFromB, expansion.MaxDistance, false);
                    break;

                case CollisionType.Composite:
                    ConvexComposite(context, ColliderKey.Empty, colliderA, colliderB, worldFromA, worldFromB, expansion, false);
                    break;

                case CollisionType.Terrain:
                    ConvexTerrain(context, ColliderKeyPair.Empty, colliderA, colliderB, worldFromA, worldFromB, expansion.MaxDistance, false);
                    break;
                }
                break;

            case CollisionType.Composite:
                switch (colliderB->CollisionType)
                {
                case CollisionType.Convex:
                    CompositeConvex(context, colliderA, colliderB, worldFromA, worldFromB, expansion, false);
                    break;

                case CollisionType.Composite:
                    CompositeComposite(context, colliderA, colliderB, worldFromA, worldFromB, expansion, false);
                    break;

                case CollisionType.Terrain:
                    CompositeTerrain(context, colliderA, colliderB, worldFromA, worldFromB, expansion.MaxDistance, false);
                    break;
                }
                break;

            case CollisionType.Terrain:
                switch (colliderB->CollisionType)
                {
                case CollisionType.Convex:
                    TerrainConvex(context, ColliderKeyPair.Empty, colliderA, colliderB, worldFromA, worldFromB, expansion.MaxDistance, false);
                    break;

                case CollisionType.Composite:
                    TerrainComposite(context, colliderA, colliderB, worldFromA, worldFromB, expansion.MaxDistance, false);
                    break;

                case CollisionType.Terrain:
                    UnityEngine.Assertions.Assert.IsTrue(false);
                    break;
                }
                break;
            }
        }
        public float3                 FrictionEffectiveMassOffDiag; // Effective mass matrix (0, 1), (0, 2), (1, 2) == (1, 0), (2, 0), (2, 1)

        // Solve the Jacobian
        public void Solve(
            ref JacobianHeader jacHeader, ref MotionVelocity velocityA, ref MotionVelocity velocityB,
            Solver.StepInput stepInput, ref BlockStream.Writer collisionEventsWriter)
        {
            // Copy velocity data
            MotionVelocity tempVelocityA = velocityA;
            MotionVelocity tempVelocityB = velocityB;

            if (jacHeader.HasMassFactors)
            {
                MassFactors jacMod = jacHeader.AccessMassFactors();
                tempVelocityA.InverseInertiaAndMass *= jacMod.InvInertiaAndMassFactorA;
                tempVelocityB.InverseInertiaAndMass *= jacMod.InvInertiaAndMassFactorB;
            }

            // Calculate maximum impulse per sub step
            float maxImpulseToApply;

            if (jacHeader.HasMaxImpulse)
            {
                maxImpulseToApply = jacHeader.AccessMaxImpulse() * stepInput.Timestep * stepInput.InvNumSolverIterations;
            }
            else
            {
                maxImpulseToApply = float.MaxValue;
            }

            // Solve normal impulses
            float  sumImpulses              = 0.0f;
            float  frictionFactor           = 1.0f;
            float4 totalAccumulatedImpulses = float4.zero;
            bool   forceCollisionEvent      = false;

            for (int j = 0; j < BaseJacobian.NumContacts; j++)
            {
                ref ContactJacAngAndVelToReachCp jacAngular = ref jacHeader.AccessAngularJacobian(j);

                // Solve velocity so that predicted contact distance is greater than or equal to zero
                float relativeVelocity = BaseContactJacobian.GetJacVelocity(BaseJacobian.Normal, jacAngular.Jac, tempVelocityA, tempVelocityB);
                float dv = jacAngular.VelToReachCp - relativeVelocity;

                // Restitution (typically set to zero)
                if (dv > 0.0f && CoefficientOfRestitution > 0.0f)
                {
                    float negContactRestingVelocity = -stepInput.GravityLength * stepInput.Timestep * 1.5f;
                    if (relativeVelocity < negContactRestingVelocity)
                    {
                        float invMassA           = tempVelocityA.InverseInertiaAndMass.w;
                        float invMassB           = tempVelocityB.InverseInertiaAndMass.w;
                        float effInvMassAtCenter = invMassA + invMassB;
                        jacAngular.VelToReachCp = -(CoefficientOfRestitution * (relativeVelocity - negContactRestingVelocity)) *
                                                  (jacAngular.Jac.EffectiveMass * effInvMassAtCenter);
                        dv = jacAngular.VelToReachCp - relativeVelocity;

                        // reduce friction to 1/4 of the impulse
                        frictionFactor = 0.25f;
                    }
                }

                float impulse = dv * jacAngular.Jac.EffectiveMass;
                bool  clipped = impulse > maxImpulseToApply;
                impulse = math.min(impulse, maxImpulseToApply);
                float accumulatedImpulse = math.max(jacAngular.Jac.Impulse + impulse, 0.0f);
                if (accumulatedImpulse != jacAngular.Jac.Impulse)
                {
                    float deltaImpulse = accumulatedImpulse - jacAngular.Jac.Impulse;
                    ApplyImpulse(deltaImpulse, BaseJacobian.Normal, jacAngular.Jac, ref tempVelocityA, ref tempVelocityB);
                }

                jacAngular.Jac.Impulse = accumulatedImpulse;
                sumImpulses           += accumulatedImpulse;

                // If there are more than 4 contacts, accumulate their impulses to the last contact point
                totalAccumulatedImpulses[math.min(j, 3)] += jacAngular.Jac.Impulse;

                // Force contact event even when no impulse is applied, but there is penetration.
                // Also force when impulse was clipped, even if clipped to 0.
                forceCollisionEvent |= jacAngular.VelToReachCp > 0.0f || clipped;
            }
        internal static float GetJacVelocity(float3 linear, ContactJacobianAngular jacAngular, MotionVelocity velocityA, MotionVelocity velocityB)
        {
            float3 temp = (velocityA.LinearVelocity - velocityB.LinearVelocity) * linear;

            temp += velocityA.AngularVelocity * jacAngular.AngularA;
            temp += velocityB.AngularVelocity * jacAngular.AngularB;
            return(math.csum(temp));
        }