Пример #1
0
        // Solve the Jacobian
        public void Solve(ref MotionVelocity velocityA, ref MotionVelocity velocityB, sfloat timestep, sfloat invTimestep)
        {
            // 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);
            sfloat jacLengthSq  = math.lengthsq(jacB0);
            sfloat invJacLength = Math.RSqrtSafe(jacLengthSq);
            sfloat futureAngle;
            {
                sfloat sinAngle = jacLengthSq * invJacLength;
                sfloat 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
                sfloat invEffMassDiag0   = math.csum(jacA0 * jacA0 * velocityA.InverseInertia + jacB0 * jacB0 * velocityB.InverseInertia);
                sfloat invEffMassDiag1   = math.csum(jacA1 * jacA1 * velocityA.InverseInertia + jacB1 * jacB1 * velocityB.InverseInertia);
                sfloat invEffMassOffDiag = math.csum(jacA0 * jacA1 * velocityA.InverseInertia + jacB0 * jacB1 * velocityB.InverseInertia);
                sfloat det    = invEffMassDiag0 * invEffMassDiag1 - invEffMassOffDiag * invEffMassOffDiag;
                sfloat invDet = math.select(jacLengthSq / det, sfloat.Zero, det.IsZero()); // 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
            sfloat futureError = JacobianUtilities.CalculateError(futureAngle, MinAngle, MaxAngle);
            sfloat solveError  = JacobianUtilities.CalculateCorrection(futureError, InitialError, Tau, Damping);
            float2 impulse     = -effectiveMass * solveError * invTimestep;

            velocityA.ApplyAngularImpulse(impulse.x * jacA0 + impulse.y * jacA1);
            velocityB.ApplyAngularImpulse(impulse.x * jacB0 + impulse.y * jacB1);
        }
Пример #2
0
        public static bool IsTriangleDegenerate(float3 a, float3 b, float3 c)
        {
            sfloat defaultTriangleDegeneracyTolerance = sfloat.FromRaw(0x33d6bf95);

            // Small area check
            {
                float3 edge1 = a - b;
                float3 edge2 = a - c;
                float3 cross = math.cross(edge1, edge2);

                float3 edge1B = b - a;
                float3 edge2B = b - c;
                float3 crossB = math.cross(edge1B, edge2B);

                bool cmp0 = defaultTriangleDegeneracyTolerance > math.lengthsq(cross);
                bool cmp1 = defaultTriangleDegeneracyTolerance > math.lengthsq(crossB);
                if (cmp0 || cmp1)
                {
                    return(true);
                }
            }

            // Point triangle distance check
            {
                float3 q = a - b;
                float3 r = c - b;

                sfloat qq = math.dot(q, q);
                sfloat rr = math.dot(r, r);
                sfloat qr = math.dot(q, r);

                sfloat qqrr = qq * rr;
                sfloat qrqr = qr * qr;
                sfloat det  = (qqrr - qrqr);

                return(det.IsZero());
            }
        }
Пример #3
0
        // Solve the Jacobian
        public void Solve(ref MotionVelocity velocityA, ref MotionVelocity velocityB, sfloat timestep, sfloat 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);
            sfloat effectiveMass;
            {
                sfloat invEffectiveMass = math.csum(AxisInMotionA * AxisInMotionA * velocityA.InverseInertia +
                                                    axisInMotionB * axisInMotionB * velocityB.InverseInertia);
                effectiveMass = math.select(sfloat.One / invEffectiveMass, sfloat.Zero, invEffectiveMass.IsZero());
            }

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

            velocityA.ApplyAngularImpulse(impulse * AxisInMotionA);
            velocityB.ApplyAngularImpulse(impulse * axisInMotionB);
        }
Пример #4
0
            // Compute the barycentric coordinates of the closest point.
            public float4 ComputeBarycentricCoordinates(float3 closestPoint)
            {
                float4 coordinates = new float4(0);

                switch (NumVertices)
                {
                case 1:
                    coordinates.x = sfloat.One;
                    break;

                case 2:
                    sfloat distance = math.distance(A.Xyz, B.Xyz);
                    UnityEngine.Assertions.Assert.AreNotEqual(distance, sfloat.Zero); // TODO just checking if this happens in my tests
                    if (distance.IsZero())                                            // Very rare case, simplex is really 1D.
                    {
                        goto case 1;
                    }
                    coordinates.x = math.distance(B.Xyz, closestPoint) / distance;
                    coordinates.y = sfloat.One - coordinates.x;
                    break;

                case 3:
                {
                    coordinates.x = math.length(math.cross(B.Xyz - closestPoint, C.Xyz - closestPoint));
                    coordinates.y = math.length(math.cross(C.Xyz - closestPoint, A.Xyz - closestPoint));
                    coordinates.z = math.length(math.cross(A.Xyz - closestPoint, B.Xyz - closestPoint));
                    sfloat sum = math.csum(coordinates.xyz);
                    if (sum.IsZero())     // Very rare case, simplex is really 2D.  Happens because of int->float conversion from the hull builder.
                    {
                        // Choose the two farthest apart vertices to keep
                        float3 lengthsSq = new float3(math.lengthsq(A.Xyz - B.Xyz), math.lengthsq(B.Xyz - C.Xyz), math.lengthsq(C.Xyz - A.Xyz));
                        bool3  longest   = math.cmin(lengthsSq) == lengthsSq;
                        if (longest.y)
                        {
                            A.Xyz = C.Xyz;
                        }
                        else if (longest.z)
                        {
                            A.Xyz = B.Xyz;
                            B.Xyz = C.Xyz;
                        }
                        coordinates.z = sfloat.Zero;
                        NumVertices   = 2;
                        goto case 2;
                    }
                    coordinates /= sum;
                    break;
                }

                case 4:
                {
                    coordinates.x = Det(D.Xyz, C.Xyz, B.Xyz);
                    coordinates.y = Det(D.Xyz, A.Xyz, C.Xyz);
                    coordinates.z = Det(D.Xyz, B.Xyz, A.Xyz);
                    coordinates.w = Det(A.Xyz, B.Xyz, C.Xyz);
                    sfloat sum = math.csum(coordinates.xyzw);
                    UnityEngine.Assertions.Assert.AreNotEqual(sum, sfloat.Zero); // TODO just checking that this doesn't happen in my tests
                    if (sum.IsZero())                                            // Unexpected case, may introduce significant error by dropping a vertex but it's better than nan
                    {
                        coordinates.zw = sfloat.Zero;
                        NumVertices    = 3;
                        goto case 3;
                    }
                    coordinates /= sum;
                    break;
                }
                }

                return(coordinates);
            }
Пример #5
0
        public void Solve(ref MotionVelocity velocityA, ref MotionVelocity velocityB, sfloat timestep, sfloat invTimestep)
        {
            // Predict the relative orientation at the end of the step
            quaternion futureBFromA = JacobianUtilities.IntegrateOrientationBFromA(BFromA, velocityA.AngularVelocity, velocityB.AngularVelocity, timestep);

            // Find the future axis and angle of rotation between the free axes
            float3 jacA0, jacA1, jacA2, jacB0, jacB1, jacB2;
            float3 effectiveMass; // first column of 3x3 effective mass matrix, don't need the others because only jac0 can have nonzero error
            sfloat futureAngle;
            {
                // Calculate the relative rotation between joint spaces
                quaternion jointOrientation = math.mul(math.inverse(RefBFromA), futureBFromA);

                // Find the axis and angle of rotation
                jacA0 = jointOrientation.value.xyz;
                sfloat sinHalfAngleSq  = math.lengthsq(jacA0);
                sfloat invSinHalfAngle = Math.RSqrtSafe(sinHalfAngleSq);
                sfloat sinHalfAngle    = sinHalfAngleSq * invSinHalfAngle;
                futureAngle = math.asin(sinHalfAngle) * (sfloat)2.0f;

                jacA0 = math.select(jacA0 * invSinHalfAngle, new float3(sfloat.One, sfloat.Zero, sfloat.Zero), invSinHalfAngle.IsZero());
                jacA0 = math.select(jacA0, -jacA0, jointOrientation.value.w < sfloat.Zero);
                Math.CalculatePerpendicularNormalized(jacA0, out jacA1, out jacA2);

                jacB0 = math.mul(futureBFromA, -jacA0);
                jacB1 = math.mul(futureBFromA, -jacA1);
                jacB2 = math.mul(futureBFromA, -jacA2);

                // Calculate the effective mass
                float3 invEffectiveMassDiag = new float3(
                    math.csum(jacA0 * jacA0 * velocityA.InverseInertia + jacB0 * jacB0 * velocityB.InverseInertia),
                    math.csum(jacA1 * jacA1 * velocityA.InverseInertia + jacB1 * jacB1 * velocityB.InverseInertia),
                    math.csum(jacA2 * jacA2 * velocityA.InverseInertia + jacB2 * jacB2 * velocityB.InverseInertia));
                float3 invEffectiveMassOffDiag = new float3(
                    math.csum(jacA0 * jacA1 * velocityA.InverseInertia + jacB0 * jacB1 * velocityB.InverseInertia),
                    math.csum(jacA0 * jacA2 * velocityA.InverseInertia + jacB0 * jacB2 * velocityB.InverseInertia),
                    math.csum(jacA1 * jacA2 * velocityA.InverseInertia + jacB1 * jacB2 * velocityB.InverseInertia));
                JacobianUtilities.InvertSymmetricMatrix(invEffectiveMassDiag, invEffectiveMassOffDiag, out float3 effectiveMassDiag, out float3 effectiveMassOffDiag);
                effectiveMass = JacobianUtilities.BuildSymmetricMatrix(effectiveMassDiag, effectiveMassOffDiag).c0;
            }

            // Calculate the error, adjust by tau and damping, and apply an impulse to correct it
            sfloat futureError   = JacobianUtilities.CalculateError(futureAngle, MinAngle, MaxAngle);
            sfloat solveError    = JacobianUtilities.CalculateCorrection(futureError, InitialError, Tau, Damping);
            sfloat solveVelocity = -solveError * invTimestep;
            float3 impulseA      = solveVelocity * (jacA0 * effectiveMass.x + jacA1 * effectiveMass.y + jacA2 * effectiveMass.z);
            float3 impulseB      = solveVelocity * (jacB0 * effectiveMass.x + jacB1 * effectiveMass.y + jacB2 * effectiveMass.z);

            velocityA.ApplyAngularImpulse(impulseA);
            velocityB.ApplyAngularImpulse(impulseB);
        }
Пример #6
0
        private sfloat CalculateError(MTransform worldFromA, MTransform worldFromB, out float3 direction)
        {
            // Find the direction from pivot A to B and the distance between them
            float3 pivotA = Mul(worldFromA, PivotAinA);
            float3 pivotB = Mul(worldFromB, PivotBinB);
            float3 axis   = math.mul(worldFromB.Rotation, AxisInB);

            direction = pivotB - pivotA;
            sfloat dot = math.dot(direction, axis);

            // Project for lower-dimension joints
            sfloat distance;

            if (Is1D)
            {
                // In 1D, distance is signed and measured along the axis
                distance  = -dot;
                direction = -axis;
            }
            else
            {
                // In 2D / 3D, distance is nonnegative.  In 2D it is measured perpendicular to the axis.
                direction -= axis * dot;
                sfloat futureDistanceSq  = math.lengthsq(direction);
                sfloat invFutureDistance = math.select(math.rsqrt(futureDistanceSq), sfloat.Zero, futureDistanceSq.IsZero());
                distance   = futureDistanceSq * invFutureDistance;
                direction *= invFutureDistance;
            }

            // Find the difference between the future distance and the limit range
            return(JacobianUtilities.CalculateError(distance, MinDistance, MaxDistance));
        }