Exemplo n.º 1
0
        /// <summary>
        /// Constructs a new constraint which restricts three degrees of linear freedom and two degrees of angular freedom between two entities.
        /// </summary>
        /// <param name="connectionA">First entity of the constraint pair.</param>
        /// <param name="connectionB">Second entity of the constraint pair.</param>
        /// <param name="anchor">Point around which both entities rotate.</param>
        /// <param name="freeAxis">Axis around which the hinge can rotate.</param>
        public RevoluteJoint(Entity connectionA, Entity connectionB, System.Numerics.Vector3 anchor, System.Numerics.Vector3 freeAxis)
        {
            if (connectionA == null)
            {
                connectionA = TwoEntityConstraint.WorldEntity;
            }
            if (connectionB == null)
            {
                connectionB = TwoEntityConstraint.WorldEntity;
            }
            BallSocketJoint = new BallSocketJoint(connectionA, connectionB, anchor);
            AngularJoint    = new RevoluteAngularJoint(connectionA, connectionB, freeAxis);
            Limit           = new RevoluteLimit(connectionA, connectionB);
            Motor           = new RevoluteMotor(connectionA, connectionB, freeAxis);
            Limit.IsActive  = false;
            Motor.IsActive  = false;

            //Ensure that the base and test direction is perpendicular to the free axis.
            System.Numerics.Vector3 baseAxis = anchor - connectionA.position;
            if (baseAxis.LengthSquared() < Toolbox.BigEpsilon) //anchor and connection a in same spot, so try the other way.
            {
                baseAxis = connectionB.position - anchor;
            }
            baseAxis -= Vector3Ex.Dot(baseAxis, freeAxis) * freeAxis;
            if (baseAxis.LengthSquared() < Toolbox.BigEpsilon)
            {
                //However, if the free axis is totally aligned (like in an axis constraint), pick another reasonable direction.
                baseAxis = System.Numerics.Vector3.Cross(freeAxis, Vector3Ex.Up);
                if (baseAxis.LengthSquared() < Toolbox.BigEpsilon)
                {
                    baseAxis = System.Numerics.Vector3.Cross(freeAxis, Vector3Ex.Right);
                }
            }
            Limit.Basis.SetWorldAxes(freeAxis, baseAxis, connectionA.orientationMatrix);
            Motor.Basis.SetWorldAxes(freeAxis, baseAxis, connectionA.orientationMatrix);

            baseAxis  = connectionB.position - anchor;
            baseAxis -= Vector3Ex.Dot(baseAxis, freeAxis) * freeAxis;
            if (baseAxis.LengthSquared() < Toolbox.BigEpsilon)
            {
                //However, if the free axis is totally aligned (like in an axis constraint), pick another reasonable direction.
                baseAxis = System.Numerics.Vector3.Cross(freeAxis, Vector3Ex.Up);
                if (baseAxis.LengthSquared() < Toolbox.BigEpsilon)
                {
                    baseAxis = System.Numerics.Vector3.Cross(freeAxis, Vector3Ex.Right);
                }
            }
            Limit.TestAxis = baseAxis;
            Motor.TestAxis = baseAxis;


            Add(BallSocketJoint);
            Add(AngularJoint);
            Add(Limit);
            Add(Motor);
        }
Exemplo n.º 2
0
 public static void Validate(this System.Numerics.Vector3 v)
 {
     if (IsInvalid(v.LengthSquared()))
     {
         throw new NotFiniteNumberException("Invalid value.");
     }
 }
Exemplo n.º 3
0
        void IForceUpdateable.UpdateForForces(float dt)
        {
            //Linear velocity
            if (IsAffectedByGravity)
            {
                Vector3Ex.Add(ref forceUpdater.gravityDt, ref linearVelocity, out linearVelocity);
            }

            //Boost damping at very low velocities.  This is a strong stabilizer; removes a ton of energy from the system.
            if (activityInformation.DeactivationManager.useStabilization && activityInformation.allowStabilization &&
                (activityInformation.isSlowing || activityInformation.velocityTimeBelowLimit > activityInformation.DeactivationManager.lowVelocityTimeMinimum))
            {
                float energy = linearVelocity.LengthSquared() + angularVelocity.LengthSquared();
                if (energy < activityInformation.DeactivationManager.velocityLowerLimitSquared)
                {
                    float boost = 1 - (float)(Math.Sqrt(energy) / (2f * activityInformation.DeactivationManager.velocityLowerLimit));
                    ModifyAngularDamping(boost);
                    ModifyLinearDamping(boost);
                }
            }

            //Damping
            float linear = LinearDamping + linearDampingBoost;

            if (linear > 0)
            {
                Vector3Ex.Multiply(ref linearVelocity, (float)Math.Pow(MathHelper.Clamp(1 - linear, 0, 1), dt), out linearVelocity);
            }
            //When applying angular damping, the momentum or velocity is damped depending on the conservation setting.
            float angular = AngularDamping + angularDampingBoost;

            if (angular > 0)
            {
#if CONSERVE
                Vector3Ex.Multiply(ref angularMomentum, (float)Math.Pow(MathHelper.Clamp(1 - angular, 0, 1), dt), out angularMomentum);
#else
                Vector3Ex.Multiply(ref angularVelocity, (float)Math.Pow(MathHelper.Clamp(1 - angular, 0, 1), dt), out angularVelocity);
#endif
            }

            linearDampingBoost  = 0;
            angularDampingBoost = 0;

            //Update world inertia tensors.
            Matrix3x3 multiplied;
            Matrix3x3.MultiplyTransposed(ref orientationMatrix, ref localInertiaTensorInverse, out multiplied);
            Matrix3x3.Multiply(ref multiplied, ref orientationMatrix, out inertiaTensorInverse);
            Matrix3x3.MultiplyTransposed(ref orientationMatrix, ref localInertiaTensor, out multiplied);
            Matrix3x3.Multiply(ref multiplied, ref orientationMatrix, out inertiaTensor);

#if CONSERVE
            //Update angular velocity.
            //Note that this doesn't play nice with singular inertia tensors.
            //Locked tensors result in zero angular velocity.
            Matrix3x3.Transform(ref angularMomentum, ref inertiaTensorInverse, out angularVelocity);
            MathChecker.Validate(angularMomentum);
#endif
            MathChecker.Validate(linearVelocity);
            MathChecker.Validate(angularVelocity);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Updates the movement basis of the horizontal motion constraint.
        /// Should be updated automatically by the character on each time step; other code should not need to call this.
        /// </summary>
        /// <param name="forward">Forward facing direction of the character.</param>
        public void UpdateMovementBasis(ref System.Numerics.Vector3 forward)
        {
            System.Numerics.Vector3 down = characterBody.orientationMatrix.Down;
            System.Numerics.Vector3 strafeDirection;
            System.Numerics.Vector3 horizontalForwardDirection = forward - down * Vector3Ex.Dot(down, forward);
            float forwardLengthSquared = horizontalForwardDirection.LengthSquared();

            if (forwardLengthSquared < Toolbox.Epsilon)
            {
                //Use an arbitrary direction to complete the basis.
                horizontalForwardDirection = characterBody.orientationMatrix.Forward;
                strafeDirection            = characterBody.orientationMatrix.Right;
            }
            else
            {
                Vector3Ex.Divide(ref horizontalForwardDirection, (float)Math.Sqrt(forwardLengthSquared), out horizontalForwardDirection);
                Vector3Ex.Cross(ref down, ref horizontalForwardDirection, out strafeDirection);
                //Don't need to normalize the strafe direction; it's the cross product of two normalized perpendicular vectors.
            }


            Vector3Ex.Multiply(ref horizontalForwardDirection, movementDirection.Y, out movementDirection3d);
            System.Numerics.Vector3 strafeComponent;
            Vector3Ex.Multiply(ref strafeDirection, movementDirection.X, out strafeComponent);
            Vector3Ex.Add(ref strafeComponent, ref movementDirection3d, out movementDirection3d);
        }
Exemplo n.º 5
0
        ///<summary>
        /// Gets the error tolerance of the simplex.
        ///</summary>
        ///<returns>Error tolerance of the simplex.</returns>
        public float GetErrorTolerance()
        {
            switch (State)
            {
            case SimplexState.Point:
                return(A.LengthSquared());

            case SimplexState.Segment:
                return(MathHelper.Max(A.LengthSquared(), B.LengthSquared()));

            case SimplexState.Triangle:
                return(MathHelper.Max(A.LengthSquared(), MathHelper.Max(B.LengthSquared(), C.LengthSquared())));

            case SimplexState.Tetrahedron:
                return(MathHelper.Max(A.LengthSquared(), MathHelper.Max(B.LengthSquared(), MathHelper.Max(C.LengthSquared(), D.LengthSquared()))));
            }
            return(1);
        }
Exemplo n.º 6
0
 private void UpdateRestrictedAxes()
 {
     localConstrainedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Up, localAxisA);
     if (localConstrainedAxis1.LengthSquared() < .001f)
     {
         localConstrainedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Right, localAxisA);
     }
     localConstrainedAxis2 = System.Numerics.Vector3.Cross(localAxisA, localConstrainedAxis1);
     localConstrainedAxis1.Normalize();
     localConstrainedAxis2.Normalize();
 }
Exemplo n.º 7
0
        ///<summary>
        /// Gets the extreme point of the shape in world space in a given direction with margin expansion.
        ///</summary>
        ///<param name="direction">Direction to find the extreme point in.</param>
        /// <param name="shapeTransform">Transform to use for the shape.</param>
        ///<param name="extremePoint">Extreme point on the shape.</param>
        public void GetExtremePoint(System.Numerics.Vector3 direction, ref RigidTransform shapeTransform, out System.Numerics.Vector3 extremePoint)
        {
            GetExtremePointWithoutMargin(direction, ref shapeTransform, out extremePoint);

            float directionLength = direction.LengthSquared();

            if (directionLength > Toolbox.Epsilon)
            {
                Vector3Ex.Multiply(ref direction, collisionMargin / (float)Math.Sqrt(directionLength), out direction);
                Vector3Ex.Add(ref extremePoint, ref direction, out extremePoint);
            }
        }
Exemplo n.º 8
0
        protected internal override void SolveVelocityIteration()
        {
            //Compute the 'relative' linear and angular velocities. For single bone constraints, it's based entirely on the one bone's velocities!
            //They have to be pulled into constraint space first to compute the necessary impulse, though.
            System.Numerics.Vector3 linearContribution;
            Matrix3x3.TransformTranspose(ref TargetBone.linearVelocity, ref linearJacobian, out linearContribution);
            System.Numerics.Vector3 angularContribution;
            Matrix3x3.TransformTranspose(ref TargetBone.angularVelocity, ref angularJacobian, out angularContribution);

            //The constraint velocity error will be the velocity we try to remove.
            System.Numerics.Vector3 constraintVelocityError;
            Vector3Ex.Add(ref linearContribution, ref angularContribution, out constraintVelocityError);
            //However, we need to take into account two extra sources of velocities which modify our target velocity away from zero.
            //First, the velocity bias from position correction:
            Vector3Ex.Subtract(ref constraintVelocityError, ref velocityBias, out constraintVelocityError);
            //And second, the bias from softness:
            System.Numerics.Vector3 softnessBias;
            Vector3Ex.Multiply(ref accumulatedImpulse, -softness, out softnessBias);
            Vector3Ex.Subtract(ref constraintVelocityError, ref softnessBias, out constraintVelocityError);

            //By now, the constraint velocity error contains all the velocity we want to get rid of.
            //Convert it into an impulse using the effective mass matrix.
            System.Numerics.Vector3 constraintSpaceImpulse;
            Matrix3x3.Transform(ref constraintVelocityError, ref effectiveMass, out constraintSpaceImpulse);

            Vector3Ex.Negate(ref constraintSpaceImpulse, out constraintSpaceImpulse);

            //Add the constraint space impulse to the accumulated impulse so that warm starting and softness work properly.
            System.Numerics.Vector3 preadd = accumulatedImpulse;
            Vector3Ex.Add(ref constraintSpaceImpulse, ref accumulatedImpulse, out accumulatedImpulse);
            //But wait! The accumulated impulse may exceed this constraint's capacity! Check to make sure!
            float impulseSquared = accumulatedImpulse.LengthSquared();

            if (impulseSquared > maximumImpulseSquared)
            {
                //Oops! Clamp that down.
                Vector3Ex.Multiply(ref accumulatedImpulse, maximumImpulse / (float)Math.Sqrt(impulseSquared), out accumulatedImpulse);
                //Update the impulse based upon the clamped accumulated impulse and the original, pre-add accumulated impulse.
                Vector3Ex.Subtract(ref accumulatedImpulse, ref preadd, out constraintSpaceImpulse);
            }

            //The constraint space impulse now represents the impulse we want to apply to the bone... but in constraint space.
            //Bring it out to world space using the transposed jacobian.
            System.Numerics.Vector3 linearImpulse;
            Matrix3x3.Transform(ref constraintSpaceImpulse, ref linearJacobian, out linearImpulse);
            System.Numerics.Vector3 angularImpulse;
            Matrix3x3.Transform(ref constraintSpaceImpulse, ref angularJacobian, out angularImpulse);

            //Apply them!
            TargetBone.ApplyLinearImpulse(ref linearImpulse);
            TargetBone.ApplyAngularImpulse(ref angularImpulse);
        }
Exemplo n.º 9
0
            public void Update()
            {
                Velocity     += _acceleration;
                Position     += Velocity;
                _acceleration = System.Numerics.Vector3.Zero;
                if (Velocity.LengthSquared() < 0.001f * 0.001f)
                {
                    Velocity = System.Numerics.Vector3.Zero;
                }

                Velocity *= _damping;
                _damping  = 0.98f;
            }
Exemplo n.º 10
0
        /// <summary>
        /// Applies the corrective impulses required by the constraint.
        /// </summary>
        public override float SolveIteration()
        {
#if !WINDOWS
            System.Numerics.Vector3 lambda = new System.Numerics.Vector3();
#else
            System.Numerics.Vector3 lambda;
#endif
            System.Numerics.Vector3 aVel = connectionA.angularVelocity;
            System.Numerics.Vector3 bVel = connectionB.angularVelocity;
            lambda.X = bVel.X - aVel.X - biasVelocity.X - usedSoftness * accumulatedImpulse.X;
            lambda.Y = bVel.Y - aVel.Y - biasVelocity.Y - usedSoftness * accumulatedImpulse.Y;
            lambda.Z = bVel.Z - aVel.Z - biasVelocity.Z - usedSoftness * accumulatedImpulse.Z;

            Matrix3x3.Transform(ref lambda, ref effectiveMassMatrix, out lambda);

            System.Numerics.Vector3 previousAccumulatedImpulse = accumulatedImpulse;
            accumulatedImpulse.X += lambda.X;
            accumulatedImpulse.Y += lambda.Y;
            accumulatedImpulse.Z += lambda.Z;
            float sumLengthSquared = accumulatedImpulse.LengthSquared();

            if (sumLengthSquared > maxForceDtSquared)
            {
                //max / impulse gives some value 0 < x < 1.  Basically, normalize the vector (divide by the length) and scale by the maximum.
                float multiplier = maxForceDt / (float)Math.Sqrt(sumLengthSquared);
                accumulatedImpulse.X *= multiplier;
                accumulatedImpulse.Y *= multiplier;
                accumulatedImpulse.Z *= multiplier;

                //Since the limit was exceeded by this corrective impulse, limit it so that the accumulated impulse remains constrained.
                lambda.X = accumulatedImpulse.X - previousAccumulatedImpulse.X;
                lambda.Y = accumulatedImpulse.Y - previousAccumulatedImpulse.Y;
                lambda.Z = accumulatedImpulse.Z - previousAccumulatedImpulse.Z;
            }


            if (connectionA.isDynamic)
            {
                connectionA.ApplyAngularImpulse(ref lambda);
            }
            if (connectionB.isDynamic)
            {
                System.Numerics.Vector3 torqueB;
                Vector3Ex.Negate(ref lambda, out torqueB);
                connectionB.ApplyAngularImpulse(ref torqueB);
            }

            return(Math.Abs(lambda.X) + Math.Abs(lambda.Y) + Math.Abs(lambda.Z));
        }
Exemplo n.º 11
0
        ///<summary>
        /// Computes the expansion of the minkowski sum due to margins in a given direction.
        ///</summary>
        ///<param name="marginA">First margin.</param>
        ///<param name="marginB">Second margin.</param>
        ///<param name="direction">Extreme point direction.</param>
        ///<param name="contribution">Margin contribution to the extreme point.</param>
        public static void ExpandMinkowskiSum(float marginA, float marginB, ref System.Numerics.Vector3 direction, out System.Numerics.Vector3 contribution)
        {
            float lengthSquared = direction.LengthSquared();

            if (lengthSquared > Toolbox.Epsilon)
            {
                //The contribution to the minkowski sum by the margin is:
                //direction * marginA - (-direction) * marginB.
                Vector3Ex.Multiply(ref direction, (marginA + marginB) / (float)Math.Sqrt(lengthSquared), out contribution);
            }
            else
            {
                contribution = new System.Numerics.Vector3();
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Calculates and applies corrective impulses.
        /// Called automatically by space.
        /// </summary>
        public override float SolveIteration()
        {
            float linearSpeed = entity.linearVelocity.LengthSquared();

            if (linearSpeed > maximumSpeedSquared)
            {
                linearSpeed = (float)Math.Sqrt(linearSpeed);
                System.Numerics.Vector3 impulse;
                //divide by linearSpeed to normalize the velocity.
                //Multiply by linearSpeed - maximumSpeed to get the 'velocity change vector.'
                Vector3Ex.Multiply(ref entity.linearVelocity, -(linearSpeed - maximumSpeed) / linearSpeed, out impulse);

                //incorporate softness
                System.Numerics.Vector3 softnessImpulse;
                Vector3Ex.Multiply(ref accumulatedImpulse, usedSoftness, out softnessImpulse);
                Vector3Ex.Subtract(ref impulse, ref softnessImpulse, out impulse);

                //Transform into impulse
                Vector3Ex.Multiply(ref impulse, effectiveMassMatrix, out impulse);


                //Accumulate
                System.Numerics.Vector3 previousAccumulatedImpulse = accumulatedImpulse;
                Vector3Ex.Add(ref accumulatedImpulse, ref impulse, out accumulatedImpulse);
                float forceMagnitude = accumulatedImpulse.LengthSquared();
                if (forceMagnitude > maxForceDtSquared)
                {
                    //max / impulse gives some value 0 < x < 1.  Basically, normalize the vector (divide by the length) and scale by the maximum.
                    float multiplier = maxForceDt / (float)Math.Sqrt(forceMagnitude);
                    accumulatedImpulse.X *= multiplier;
                    accumulatedImpulse.Y *= multiplier;
                    accumulatedImpulse.Z *= multiplier;

                    //Since the limit was exceeded by this corrective impulse, limit it so that the accumulated impulse remains constrained.
                    impulse.X = accumulatedImpulse.X - previousAccumulatedImpulse.X;
                    impulse.Y = accumulatedImpulse.Y - previousAccumulatedImpulse.Y;
                    impulse.Z = accumulatedImpulse.Z - previousAccumulatedImpulse.Z;
                }

                entity.ApplyLinearImpulse(ref impulse);


                return(Math.Abs(impulse.X) + Math.Abs(impulse.Y) + Math.Abs(impulse.Z));
            }


            return(0);
        }
Exemplo n.º 13
0
        ///<summary>
        /// Computes the expansion of the minkowski sum due to margins in a given direction.
        ///</summary>
        ///<param name="marginA">First margin.</param>
        ///<param name="marginB">Second margin.</param>
        ///<param name="direction">Extreme point direction.</param>
        ///<param name="toExpandA">Margin contribution to the shapeA.</param>
        ///<param name="toExpandB">Margin contribution to the shapeB.</param>
        public static void ExpandMinkowskiSum(float marginA, float marginB, System.Numerics.Vector3 direction, ref System.Numerics.Vector3 toExpandA, ref System.Numerics.Vector3 toExpandB)
        {
            float lengthSquared = direction.LengthSquared();

            if (lengthSquared > Toolbox.Epsilon)
            {
                lengthSquared = 1 / (float)Math.Sqrt(lengthSquared);
                //The contribution to the minkowski sum by the margin is:
                //direction * marginA - (-direction) * marginB.
                System.Numerics.Vector3 contribution;
                Vector3Ex.Multiply(ref direction, marginA * lengthSquared, out contribution);
                Vector3Ex.Add(ref toExpandA, ref contribution, out toExpandA);
                Vector3Ex.Multiply(ref direction, marginB * lengthSquared, out contribution);
                Vector3Ex.Subtract(ref toExpandB, ref contribution, out toExpandB);
            }
            //If the direction is too small, then the expansion values are left unchanged.
        }
Exemplo n.º 14
0
        /// <summary>
        /// Initializes the constraint for the current frame.
        /// </summary>
        /// <param name="dt">Time between frames.</param>
        public override void Update(float dt)
        {
            System.Numerics.Quaternion quaternionA;
            QuaternionEx.Multiply(ref connectionA.orientation, ref initialQuaternionConjugateA, out quaternionA);
            System.Numerics.Quaternion quaternionB;
            QuaternionEx.Multiply(ref connectionB.orientation, ref initialQuaternionConjugateB, out quaternionB);
            QuaternionEx.Conjugate(ref quaternionB, out quaternionB);
            System.Numerics.Quaternion intermediate;
            QuaternionEx.Multiply(ref quaternionA, ref quaternionB, out intermediate);


            float angle;

            System.Numerics.Vector3 axis;
            QuaternionEx.GetAxisAngleFromQuaternion(ref intermediate, out axis, out angle);

            error.X = axis.X * angle;
            error.Y = axis.Y * angle;
            error.Z = axis.Z * angle;

            float errorReduction;

            springSettings.ComputeErrorReductionAndSoftness(dt, 1 / dt, out errorReduction, out softness);
            errorReduction = -errorReduction;
            biasVelocity.X = errorReduction * error.X;
            biasVelocity.Y = errorReduction * error.Y;
            biasVelocity.Z = errorReduction * error.Z;

            //Ensure that the corrective velocity doesn't exceed the max.
            float length = biasVelocity.LengthSquared();

            if (length > maxCorrectiveVelocitySquared)
            {
                float multiplier = maxCorrectiveVelocity / (float)Math.Sqrt(length);
                biasVelocity.X *= multiplier;
                biasVelocity.Y *= multiplier;
                biasVelocity.Z *= multiplier;
            }

            Matrix3x3.Add(ref connectionA.inertiaTensorInverse, ref connectionB.inertiaTensorInverse, out effectiveMassMatrix);
            effectiveMassMatrix.M11 += softness;
            effectiveMassMatrix.M22 += softness;
            effectiveMassMatrix.M33 += softness;
            Matrix3x3.Invert(ref effectiveMassMatrix, out effectiveMassMatrix);
        }
Exemplo n.º 15
0
        private static void SetHandbrakeAndBoost(ref Controller controller, ref Vector3 carLocation, System.Numerics.Vector3 carVelocity, double botFrontToTargetAngle)
        {
            if (Math.Abs(botFrontToTargetAngle) > DegreesToRadians(100) && Math.Sqrt(carVelocity.LengthSquared()) > 0.6)
            {
                controller.Handbrake = true;
                controller.Boost     = false;
            }


            if (carLocation.Z > 50)
            {
                controller.Handbrake = false;
            }

            if (Math.Abs(botFrontToTargetAngle) < MinimumSteeringAngleRadians)
            {
                controller.Boost = true;
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Computes one iteration of the constraint to meet the solver updateable's goal.
        /// </summary>
        /// <returns>The rough applied impulse magnitude.</returns>
        public override float SolveIteration()
        {
            //Compute relative velocity
            System.Numerics.Vector3 lambda;
            Vector3Ex.Cross(ref r, ref entity.angularVelocity, out lambda);
            Vector3Ex.Subtract(ref lambda, ref entity.linearVelocity, out lambda);

            //Add in bias velocity
            Vector3Ex.Add(ref biasVelocity, ref lambda, out lambda);

            //Add in softness
            System.Numerics.Vector3 softnessVelocity;
            Vector3Ex.Multiply(ref accumulatedImpulse, usedSoftness, out softnessVelocity);
            Vector3Ex.Subtract(ref lambda, ref softnessVelocity, out lambda);

            //In terms of an impulse (an instantaneous change in momentum), what is it?
            Matrix3x3.Transform(ref lambda, ref effectiveMassMatrix, out lambda);

            //Sum the impulse.
            System.Numerics.Vector3 previousAccumulatedImpulse = accumulatedImpulse;
            accumulatedImpulse += lambda;

            //If the impulse it takes to get to the goal is too high for the motor to handle, scale it back.
            float sumImpulseLengthSquared = accumulatedImpulse.LengthSquared();

            if (sumImpulseLengthSquared > maxForceDtSquared)
            {
                //max / impulse gives some value 0 < x < 1.  Basically, normalize the vector (divide by the length) and scale by the maximum.
                accumulatedImpulse *= maxForceDt / (float)Math.Sqrt(sumImpulseLengthSquared);

                //Since the limit was exceeded by this corrective impulse, limit it so that the accumulated impulse remains constrained.
                lambda = accumulatedImpulse - previousAccumulatedImpulse;
            }


            entity.ApplyLinearImpulse(ref lambda);
            System.Numerics.Vector3 taImpulse;
            Vector3Ex.Cross(ref r, ref lambda, out taImpulse);
            entity.ApplyAngularImpulse(ref taImpulse);

            return(Math.Abs(lambda.X) + Math.Abs(lambda.Y) + Math.Abs(lambda.Z));
        }
Exemplo n.º 17
0
        protected void GetNormal(ref System.Numerics.Vector3 uncorrectedNormal, TriangleShape localTriangleShape, out System.Numerics.Vector3 normal)
        {
            //Compute the normal of the triangle in the current convex's local space.
            //Note its reliance on the local triangle shape.  It must be initialized to the correct values before this is called.
            System.Numerics.Vector3 AB, AC;
            Vector3Ex.Subtract(ref localTriangleShape.vB, ref localTriangleShape.vA, out AB);
            Vector3Ex.Subtract(ref localTriangleShape.vC, ref localTriangleShape.vA, out AC);
            //Compute the normal based on the sidedness.
            switch (localTriangleShape.sidedness)
            {
            case TriangleSidedness.DoubleSided:
                //If it's double sided, then pick the triangle normal which points in the same direction
                //as the contact normal that's going to be corrected.
                float dot;
                Vector3Ex.Cross(ref AB, ref AC, out normal);
                Vector3Ex.Dot(ref normal, ref uncorrectedNormal, out dot);
                if (dot < 0)
                {
                    Vector3Ex.Negate(ref normal, out normal);
                }
                break;

            case TriangleSidedness.Clockwise:
                //If it's clockwise, always use ACxAB.
                Vector3Ex.Cross(ref AC, ref AB, out normal);
                break;

            default:
                //If it's counterclockwise, always use ABxAC.
                Vector3Ex.Cross(ref AB, ref AC, out normal);
                break;
            }
            //If the normal is degenerate, just use the uncorrected normal.
            if (normal.LengthSquared() < Toolbox.Epsilon)
            {
                normal = uncorrectedNormal;
            }
        }
Exemplo n.º 18
0
        void ComputeConstrainedAxes()
        {
            System.Numerics.Vector3 worldAxisA = WorldFreeAxisA;
            System.Numerics.Vector3 error      = System.Numerics.Vector3.Cross(worldAxisA, WorldFreeAxisB);
            float lengthSquared = error.LengthSquared();

            System.Numerics.Vector3 worldConstrainedAxis1, worldConstrainedAxis2;
            //Find the first constrained axis.
            if (lengthSquared > Toolbox.Epsilon)
            {
                //The error direction can be used as the first axis!
                Vector3Ex.Divide(ref error, (float)Math.Sqrt(lengthSquared), out worldConstrainedAxis1);
            }
            else
            {
                //There's not enough error for it to be a good constrained axis.
                //We'll need to create the constrained axes arbitrarily.
                Vector3Ex.Cross(ref Toolbox.UpVector, ref worldAxisA, out worldConstrainedAxis1);
                lengthSquared = worldConstrainedAxis1.LengthSquared();
                if (lengthSquared > Toolbox.Epsilon)
                {
                    //The up vector worked!
                    Vector3Ex.Divide(ref worldConstrainedAxis1, (float)Math.Sqrt(lengthSquared), out worldConstrainedAxis1);
                }
                else
                {
                    //The up vector didn't work. Just try the right vector.
                    Vector3Ex.Cross(ref Toolbox.RightVector, ref worldAxisA, out worldConstrainedAxis1);
                    worldConstrainedAxis1.Normalize();
                }
            }
            //Don't have to normalize the second constraint axis; it's the cross product of two perpendicular normalized vectors.
            Vector3Ex.Cross(ref worldAxisA, ref worldConstrainedAxis1, out worldConstrainedAxis2);

            localConstrainedAxis1 = QuaternionEx.Transform(worldConstrainedAxis1, QuaternionEx.Conjugate(ConnectionA.Orientation));
            localConstrainedAxis2 = QuaternionEx.Transform(worldConstrainedAxis2, QuaternionEx.Conjugate(ConnectionA.Orientation));
        }
Exemplo n.º 19
0
        ///<summary>
        /// Gets the extreme point of the shape in local space in a given direction.
        ///</summary>
        ///<param name="direction">Direction to find the extreme point in.</param>
        ///<param name="extremePoint">Extreme point on the shape.</param>
        public override void GetLocalExtremePointWithoutMargin(ref System.Numerics.Vector3 direction, out System.Numerics.Vector3 extremePoint)
        {
            //Is it the tip of the cone?
            float sinThetaSquared = radius * radius / (radius * radius + height * height);

            //If d.Y * d.Y / d.LengthSquared >= sinthetaSquared
            if (direction.Y > 0 && direction.Y * direction.Y >= direction.LengthSquared() * sinThetaSquared)
            {
                extremePoint = new System.Numerics.Vector3(0, .75f * height, 0);
                return;
            }
            //Is it a bottom edge of the cone?
            float horizontalLengthSquared = direction.X * direction.X + direction.Z * direction.Z;

            if (horizontalLengthSquared > Toolbox.Epsilon)
            {
                var radOverSigma = radius / Math.Sqrt(horizontalLengthSquared);
                extremePoint = new System.Numerics.Vector3((float)(radOverSigma * direction.X), -.25f * height, (float)(radOverSigma * direction.Z));
            }
            else // It's pointing almost straight down...
            {
                extremePoint = new System.Numerics.Vector3(0, -.25f * height, 0);
            }
        }
Exemplo n.º 20
0
        /// <summary>
        /// Do any necessary computations to prepare the constraint for this frame.
        /// </summary>
        /// <param name="dt">Simulation step length.</param>
        public override void Update(float dt)
        {
            //Transform the axes into world space.
            Matrix3x3.Transform(ref localHingeAxis, ref connectionA.orientationMatrix, out worldHingeAxis);
            Matrix3x3.Transform(ref localTwistAxis, ref connectionB.orientationMatrix, out worldTwistAxis);

            //****** VELOCITY BIAS ******//
            Vector3Ex.Dot(ref worldHingeAxis, ref worldTwistAxis, out error);
            //Compute the correction velocity.

            float errorReduction;

            springSettings.ComputeErrorReductionAndSoftness(dt, 1 / dt, out errorReduction, out softness);

            biasVelocity = MathHelper.Clamp(error * errorReduction, -maxCorrectiveVelocity, maxCorrectiveVelocity);

            //Compute the jacobian
            Vector3Ex.Cross(ref worldHingeAxis, ref worldTwistAxis, out jacobianA);
            float length = jacobianA.LengthSquared();

            if (length > Toolbox.Epsilon)
            {
                Vector3Ex.Divide(ref jacobianA, (float)Math.Sqrt(length), out jacobianA);
            }
            else
            {
                jacobianA = new System.Numerics.Vector3();
            }
            jacobianB.X = -jacobianA.X;
            jacobianB.Y = -jacobianA.Y;
            jacobianB.Z = -jacobianA.Z;


            //****** EFFECTIVE MASS MATRIX ******//
            //Connection A's contribution to the mass matrix
            float entryA;

            System.Numerics.Vector3 transformedAxis;
            if (connectionA.isDynamic)
            {
                Matrix3x3.Transform(ref jacobianA, ref connectionA.inertiaTensorInverse, out transformedAxis);
                Vector3Ex.Dot(ref transformedAxis, ref jacobianA, out entryA);
            }
            else
            {
                entryA = 0;
            }

            //Connection B's contribution to the mass matrix
            float entryB;

            if (connectionB.isDynamic)
            {
                Matrix3x3.Transform(ref jacobianB, ref connectionB.inertiaTensorInverse, out transformedAxis);
                Vector3Ex.Dot(ref transformedAxis, ref jacobianB, out entryB);
            }
            else
            {
                entryB = 0;
            }

            //Compute the inverse mass matrix
            velocityToImpulse = 1 / (softness + entryA + entryB);
        }
Exemplo n.º 21
0
        //This works in the general case where there can be any  number of contacts and candidates.  Could specialize it as an optimization to single-contact added incremental manifolds.
        ///<summary>
        /// Reduces the contact manifold to a good subset.
        ///</summary>
        ///<param name="contacts">Contacts to reduce.</param>
        ///<param name="contactCandidates">Contact candidates to include in the reduction process.</param>
        ///<param name="contactsToRemove">Contacts that need to removed to reach the reduced state.</param>
        ///<param name="toAdd">Contact candidates that should be added to reach the reduced state.</param>
        ///<exception cref="InvalidOperationException">Thrown when the set being reduced is empty.</exception>
        public static void ReduceContacts(RawList <Contact> contacts, ref QuickList <ContactData> contactCandidates, RawList <int> contactsToRemove, ref QuickList <ContactData> toAdd)
        {
            //Find the deepest point of all contacts/candidates, as well as a compounded 'normal' vector.
            float maximumDepth = -float.MaxValue;
            int   deepestIndex = -1;

            System.Numerics.Vector3 normal = Toolbox.ZeroVector;
            for (int i = 0; i < contacts.Count; i++)
            {
                Vector3Ex.Add(ref normal, ref contacts.Elements[i].Normal, out normal);
                if (contacts.Elements[i].PenetrationDepth > maximumDepth)
                {
                    deepestIndex = i;
                    maximumDepth = contacts.Elements[i].PenetrationDepth;
                }
            }
            for (int i = 0; i < contactCandidates.Count; i++)
            {
                Vector3Ex.Add(ref normal, ref contactCandidates.Elements[i].Normal, out normal);
                if (contactCandidates.Elements[i].PenetrationDepth > maximumDepth)
                {
                    deepestIndex = contacts.Count + i;
                    maximumDepth = contactCandidates.Elements[i].PenetrationDepth;
                }
            }
            //If the normals oppose each other, this can happen.  It doesn't need to be normalized, but having SOME normal is necessary.
            if (normal.LengthSquared() < Toolbox.Epsilon)
            {
                if (contacts.Count > 0)
                {
                    normal = contacts.Elements[0].Normal;
                }
                else if (contactCandidates.Count > 0)
                {
                    normal = contactCandidates.Elements[0].Normal; //This method is only called when there's too many contacts, so if contacts is empty, the candidates must NOT be empty.
                }
                else //This method should not have been called at all if it gets here.
                {
                    throw new ArgumentException("Cannot reduce an empty contact set.");
                }
            }


            //Find the contact (candidate) that is furthest away from the deepest contact (candidate).
            System.Numerics.Vector3 deepestPosition;
            if (deepestIndex < contacts.Count)
            {
                deepestPosition = contacts.Elements[deepestIndex].Position;
            }
            else
            {
                deepestPosition = contactCandidates.Elements[deepestIndex - contacts.Count].Position;
            }
            float distanceSquared;
            float furthestDistance = 0;
            int   furthestIndex    = -1;

            for (int i = 0; i < contacts.Count; i++)
            {
                Vector3Ex.DistanceSquared(ref contacts.Elements[i].Position, ref deepestPosition, out distanceSquared);
                if (distanceSquared > furthestDistance)
                {
                    furthestDistance = distanceSquared;
                    furthestIndex    = i;
                }
            }
            for (int i = 0; i < contactCandidates.Count; i++)
            {
                Vector3Ex.DistanceSquared(ref contactCandidates.Elements[i].Position, ref deepestPosition, out distanceSquared);
                if (distanceSquared > furthestDistance)
                {
                    furthestDistance = distanceSquared;
                    furthestIndex    = contacts.Count + i;
                }
            }
            if (furthestIndex == -1)
            {
                //Either this method was called when it shouldn't have been, or all contacts and contact candidates are at the same location.
                if (contacts.Count > 0)
                {
                    for (int i = 1; i < contacts.Count; i++)
                    {
                        contactsToRemove.Add(i);
                    }
                    return;
                }
                if (contactCandidates.Count > 0)
                {
                    toAdd.Add(ref contactCandidates.Elements[0]);
                    return;
                }
                throw new ArgumentException("Cannot reduce an empty contact set.");
            }
            System.Numerics.Vector3 furthestPosition;
            if (furthestIndex < contacts.Count)
            {
                furthestPosition = contacts.Elements[furthestIndex].Position;
            }
            else
            {
                furthestPosition = contactCandidates.Elements[furthestIndex - contacts.Count].Position;
            }
            System.Numerics.Vector3 xAxis;
            Vector3Ex.Subtract(ref deepestPosition, ref furthestPosition, out xAxis);

            //Create the second axis of the 2d 'coordinate system' of the manifold.
            System.Numerics.Vector3 yAxis;
            Vector3Ex.Cross(ref xAxis, ref normal, out yAxis);

            //Determine the furthest points along the axis.
            float minYAxisDot = float.MaxValue, maxYAxisDot = -float.MaxValue;
            int   minYAxisIndex = -1, maxYAxisIndex = -1;

            for (int i = 0; i < contacts.Count; i++)
            {
                float dot;
                Vector3Ex.Dot(ref contacts.Elements[i].Position, ref yAxis, out dot);
                if (dot < minYAxisDot)
                {
                    minYAxisIndex = i;
                    minYAxisDot   = dot;
                }
                if (dot > maxYAxisDot)
                {
                    maxYAxisIndex = i;
                    maxYAxisDot   = dot;
                }
            }
            for (int i = 0; i < contactCandidates.Count; i++)
            {
                float dot;
                Vector3Ex.Dot(ref contactCandidates.Elements[i].Position, ref yAxis, out dot);
                if (dot < minYAxisDot)
                {
                    minYAxisIndex = i + contacts.Count;
                    minYAxisDot   = dot;
                }
                if (dot > maxYAxisDot)
                {
                    maxYAxisIndex = i + contacts.Count;
                    maxYAxisDot   = dot;
                }
            }

            //the deepestIndex, furthestIndex, minYAxisIndex, and maxYAxisIndex are the extremal points.
            //Cycle through the existing contacts.  If any DO NOT MATCH the existing candidates, add them to the toRemove list.
            //Cycle through the candidates.  If any match, add them to the toAdd list.

            //Repeated entries in the reduced manifold aren't a problem.
            //-Contacts list does not include repeats with itself.
            //-A contact is only removed if it doesn't match anything.

            //-Contact candidates do not repeat with themselves.
            //-Contact candidates do not repeat with contacts.
            //-Contact candidates are added if they match any of the indices.

            for (int i = 0; i < contactCandidates.Count; i++)
            {
                int totalIndex = i + contacts.Count;
                if (totalIndex == deepestIndex || totalIndex == furthestIndex || totalIndex == minYAxisIndex || totalIndex == maxYAxisIndex)
                {
                    //This contact is present in the new manifold.  Add it.
                    toAdd.Add(ref contactCandidates.Elements[i]);
                }
            }
            for (int i = 0; i < contacts.Count; i++)
            {
                if (!(i == deepestIndex || i == furthestIndex || i == minYAxisIndex || i == maxYAxisIndex))
                {
                    //This contact is not present in the new manifold.  Remove it.
                    contactsToRemove.Add(i);
                }
            }
        }
Exemplo n.º 22
0
 private void UpdateRestrictedAxes()
 {
     localConstrainedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Up, localAxisA);
     if (localConstrainedAxis1.LengthSquared() < .001f)
     {
         localConstrainedAxis1 = System.Numerics.Vector3.Cross(Vector3Ex.Right, localAxisA);
     }
     localConstrainedAxis2 = System.Numerics.Vector3.Cross(localAxisA, localConstrainedAxis1);
     localConstrainedAxis1.Normalize();
     localConstrainedAxis2.Normalize();
 }
Exemplo n.º 23
0
        ///<summary>
        /// Adds a new point to the simplex.
        ///</summary>
        ///<param name="shapeA">First shape in the pair.</param>
        ///<param name="shapeB">Second shape in the pair.</param>
        ///<param name="iterationCount">Current iteration count.</param>
        ///<param name="closestPoint">Current point on simplex closest to origin.</param>
        ///<returns>Whether or not GJK should exit due to a lack of progression.</returns>
        public bool GetNewSimplexPoint(ConvexShape shapeA, ConvexShape shapeB, int iterationCount, ref System.Numerics.Vector3 closestPoint)
        {
            System.Numerics.Vector3 negativeDirection;
            Vector3Ex.Negate(ref closestPoint, out negativeDirection);
            System.Numerics.Vector3 sa, sb;
            shapeA.GetLocalExtremePointWithoutMargin(ref negativeDirection, out sa);
            shapeB.GetExtremePointWithoutMargin(closestPoint, ref LocalTransformB, out sb);
            System.Numerics.Vector3 S;
            Vector3Ex.Subtract(ref sa, ref sb, out S);
            //If S is not further towards the origin along negativeDirection than closestPoint, then we're done.
            float dotS;

            Vector3Ex.Dot(ref S, ref negativeDirection, out dotS); //-P * S
            float distanceToClosest = closestPoint.LengthSquared();

            float progression = dotS + distanceToClosest;

            //It's likely that the system is oscillating between two or more states, usually because of a degenerate simplex.
            //Rather than detect specific problem cases, this approach just lets it run and catches whatever falls through.
            //During oscillation, one of the states is usually just BARELY outside of the numerical tolerance.
            //After a bunch of iterations, the system lets it pick the 'better' one.
            if (iterationCount > GJKToolbox.HighGJKIterations && distanceToClosest - previousDistanceToClosest < DistanceConvergenceEpsilon * errorTolerance)
            {
                return(true);
            }
            if (distanceToClosest < previousDistanceToClosest)
            {
                previousDistanceToClosest = distanceToClosest;
            }

            //If "A" is the new point always, then the switch statement can be removed
            //in favor of just pushing three points up.
            switch (State)
            {
            case SimplexState.Point:
                if (progression <= (errorTolerance = MathHelper.Max(A.LengthSquared(), S.LengthSquared())) * ProgressionEpsilon)
                {
                    return(true);
                }

                State      = SimplexState.Segment;
                B          = S;
                SimplexA.B = sa;
                SimplexB.B = sb;
                return(false);

            case SimplexState.Segment:
                if (progression <= (errorTolerance = MathHelper.Max(MathHelper.Max(A.LengthSquared(), B.LengthSquared()), S.LengthSquared())) * ProgressionEpsilon)
                {
                    return(true);
                }

                State      = SimplexState.Triangle;
                C          = S;
                SimplexA.C = sa;
                SimplexB.C = sb;
                return(false);

            case SimplexState.Triangle:
                if (progression <= (errorTolerance = MathHelper.Max(MathHelper.Max(A.LengthSquared(), B.LengthSquared()), MathHelper.Max(C.LengthSquared(), S.LengthSquared()))) * ProgressionEpsilon)
                {
                    return(true);
                }

                State      = SimplexState.Tetrahedron;
                D          = S;
                SimplexA.D = sa;
                SimplexB.D = sb;
                return(false);
            }
            return(false);
        }
Exemplo n.º 24
0
        ///<summary>
        /// Casts a fat (sphere expanded) ray against the shape.
        ///</summary>
        ///<param name="ray">Ray to test against the shape.</param>
        ///<param name="radius">Radius of the ray.</param>
        ///<param name="shape">Shape to test against.</param>
        ///<param name="shapeTransform">Transform to apply to the shape for the test.</param>
        ///<param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        ///<param name="hit">Hit data of the sphere cast, if any.</param>
        ///<returns>Whether or not the sphere cast hit the shape.</returns>
        public static bool SphereCast(Ray ray, float radius, ConvexShape shape, ref RigidTransform shapeTransform, float maximumLength,
                                      out RayHit hit)
        {
            //Transform the ray into the object's local space.
            Vector3Ex.Subtract(ref ray.Position, ref shapeTransform.Position, out ray.Position);
            System.Numerics.Quaternion conjugate;
            QuaternionEx.Conjugate(ref shapeTransform.Orientation, out conjugate);
            QuaternionEx.Transform(ref ray.Position, ref conjugate, out ray.Position);
            QuaternionEx.Transform(ref ray.Direction, ref conjugate, out ray.Direction);

            System.Numerics.Vector3 w, p;
            hit.T        = 0;
            hit.Location = ray.Position;
            hit.Normal   = Toolbox.ZeroVector;
            System.Numerics.Vector3 v = hit.Location;

            RaySimplex simplex = new RaySimplex();

            float vw, vdir;
            int   count = 0;

            //This epsilon has a significant impact on performance and accuracy.  Changing it to use BigEpsilon instead increases speed by around 30-40% usually, but jigging is more evident.
            while (v.LengthSquared() >= Toolbox.Epsilon * simplex.GetErrorTolerance(ref ray.Position))
            {
                if (++count > MaximumGJKIterations)
                {
                    //It's taken too long to find a hit.  Numerical problems are probable; quit.
                    hit = new RayHit();
                    return(false);
                }

                shape.GetLocalExtremePointWithoutMargin(ref v, out p);
                System.Numerics.Vector3 contribution;
                MinkowskiToolbox.ExpandMinkowskiSum(shape.collisionMargin, radius, ref v, out contribution);
                Vector3Ex.Add(ref p, ref contribution, out p);

                Vector3Ex.Subtract(ref hit.Location, ref p, out w);
                Vector3Ex.Dot(ref v, ref w, out vw);
                if (vw > 0)
                {
                    Vector3Ex.Dot(ref v, ref ray.Direction, out vdir);
                    hit.T = hit.T - vw / vdir;
                    if (vdir >= 0)
                    {
                        //We would have to back up!
                        return(false);
                    }
                    if (hit.T > maximumLength)
                    {
                        //If we've gone beyond where the ray can reach, there's obviously no hit.
                        return(false);
                    }
                    //Shift the ray up.
                    Vector3Ex.Multiply(ref ray.Direction, hit.T, out hit.Location);
                    Vector3Ex.Add(ref hit.Location, ref ray.Position, out hit.Location);
                    hit.Normal = v;
                }

                RaySimplex shiftedSimplex;
                simplex.AddNewSimplexPoint(ref p, ref hit.Location, out shiftedSimplex);

                shiftedSimplex.GetPointClosestToOrigin(ref simplex, out v);
            }
            //Transform the hit data into world space.
            QuaternionEx.Transform(ref hit.Normal, ref shapeTransform.Orientation, out hit.Normal);
            QuaternionEx.Transform(ref hit.Location, ref shapeTransform.Orientation, out hit.Location);
            Vector3Ex.Add(ref hit.Location, ref shapeTransform.Position, out hit.Location);

            return(true);
        }
Exemplo n.º 25
0
        ///<summary>
        /// Sweeps two shapes against another.
        ///</summary>
        ///<param name="shapeA">First shape being swept.</param>
        ///<param name="shapeB">Second shape being swept.</param>
        ///<param name="sweepA">Sweep vector for the first shape.</param>
        ///<param name="sweepB">Sweep vector for the second shape.</param>
        ///<param name="transformA">Transform to apply to the first shape.</param>
        ///<param name="transformB">Transform to apply to the second shape.</param>
        ///<param name="hit">Hit data of the sweep test, if any.</param>
        ///<returns>Whether or not the swept shapes hit each other..</returns>
        public static bool ConvexCast(ConvexShape shapeA, ConvexShape shapeB, ref System.Numerics.Vector3 sweepA, ref System.Numerics.Vector3 sweepB, ref RigidTransform transformA, ref RigidTransform transformB,
                                      out RayHit hit)
        {
            //Put the velocity into shapeA's local space.
            System.Numerics.Vector3 velocityWorld;
            Vector3Ex.Subtract(ref sweepB, ref sweepA, out velocityWorld);
            System.Numerics.Quaternion conjugateOrientationA;
            QuaternionEx.Conjugate(ref transformA.Orientation, out conjugateOrientationA);
            System.Numerics.Vector3 rayDirection;
            QuaternionEx.Transform(ref velocityWorld, ref conjugateOrientationA, out rayDirection);
            //Transform b into a's local space.
            RigidTransform localTransformB;

            QuaternionEx.Concatenate(ref transformB.Orientation, ref conjugateOrientationA, out localTransformB.Orientation);
            Vector3Ex.Subtract(ref transformB.Position, ref transformA.Position, out localTransformB.Position);
            QuaternionEx.Transform(ref localTransformB.Position, ref conjugateOrientationA, out localTransformB.Position);


            System.Numerics.Vector3 w, p;
            hit.T        = 0;
            hit.Location = System.Numerics.Vector3.Zero; //The ray starts at the origin.
            hit.Normal   = Toolbox.ZeroVector;
            System.Numerics.Vector3 v = hit.Location;

            RaySimplex simplex = new RaySimplex();


            float vw, vdir;
            int   count = 0;

            do
            {
                if (++count > MaximumGJKIterations)
                {
                    //It's taken too long to find a hit.  Numerical problems are probable; quit.
                    hit = new RayHit();
                    return(false);
                }

                MinkowskiToolbox.GetLocalMinkowskiExtremePoint(shapeA, shapeB, ref v, ref localTransformB, out p);

                Vector3Ex.Subtract(ref hit.Location, ref p, out w);
                Vector3Ex.Dot(ref v, ref w, out vw);
                if (vw > 0)
                {
                    Vector3Ex.Dot(ref v, ref rayDirection, out vdir);
                    if (vdir >= 0)
                    {
                        hit = new RayHit();
                        return(false);
                    }
                    hit.T = hit.T - vw / vdir;
                    if (hit.T > 1)
                    {
                        //If we've gone beyond where the ray can reach, there's obviously no hit.
                        hit = new RayHit();
                        return(false);
                    }
                    //Shift the ray up.
                    Vector3Ex.Multiply(ref rayDirection, hit.T, out hit.Location);
                    //The ray origin is the origin!  Don't need to add any ray position.
                    hit.Normal = v;
                }

                RaySimplex shiftedSimplex;
                simplex.AddNewSimplexPoint(ref p, ref hit.Location, out shiftedSimplex);

                shiftedSimplex.GetPointClosestToOrigin(ref simplex, out v);

                //Could measure the progress of the ray.  If it's too little, could early out.
                //Not used by default since it's biased towards precision over performance.
            } while (v.LengthSquared() >= Toolbox.Epsilon * simplex.GetErrorTolerance(ref Toolbox.ZeroVector));
            //This epsilon has a significant impact on performance and accuracy.  Changing it to use BigEpsilon instead increases speed by around 30-40% usually, but jigging is more evident.
            //Transform the hit data into world space.
            QuaternionEx.Transform(ref hit.Normal, ref transformA.Orientation, out hit.Normal);
            Vector3Ex.Multiply(ref velocityWorld, hit.T, out hit.Location);
            Vector3Ex.Add(ref hit.Location, ref transformA.Position, out hit.Location);
            return(true);
        }
Exemplo n.º 26
0
        //TODO: Consider changing the termination epsilons on these casts.  Epsilon * Modifier is okay, but there might be better options.

        ///<summary>
        /// Tests a ray against a convex shape.
        ///</summary>
        ///<param name="ray">Ray to test against the shape.</param>
        ///<param name="shape">Shape to test.</param>
        ///<param name="shapeTransform">Transform to apply to the shape for the test.</param>
        ///<param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        ///<param name="hit">Hit data of the ray cast, if any.</param>
        ///<returns>Whether or not the ray hit the shape.</returns>
        public static bool RayCast(Ray ray, ConvexShape shape, ref RigidTransform shapeTransform, float maximumLength,
                                   out RayHit hit)
        {
            //Transform the ray into the object's local space.
            Vector3Ex.Subtract(ref ray.Position, ref shapeTransform.Position, out ray.Position);
            System.Numerics.Quaternion conjugate;
            QuaternionEx.Conjugate(ref shapeTransform.Orientation, out conjugate);
            QuaternionEx.Transform(ref ray.Position, ref conjugate, out ray.Position);
            QuaternionEx.Transform(ref ray.Direction, ref conjugate, out ray.Direction);

            System.Numerics.Vector3 extremePointToRayOrigin, extremePoint;
            hit.T        = 0;
            hit.Location = ray.Position;
            hit.Normal   = Toolbox.ZeroVector;
            System.Numerics.Vector3 closestOffset = hit.Location;

            RaySimplex simplex = new RaySimplex();

            float vw, closestPointDotDirection;
            int   count = 0;

            //This epsilon has a significant impact on performance and accuracy.  Changing it to use BigEpsilon instead increases speed by around 30-40% usually, but jigging is more evident.
            while (closestOffset.LengthSquared() >= Toolbox.Epsilon * simplex.GetErrorTolerance(ref ray.Position))
            {
                if (++count > MaximumGJKIterations)
                {
                    //It's taken too long to find a hit.  Numerical problems are probable; quit.
                    hit = new RayHit();
                    return(false);
                }

                shape.GetLocalExtremePoint(closestOffset, out extremePoint);

                Vector3Ex.Subtract(ref hit.Location, ref extremePoint, out extremePointToRayOrigin);
                Vector3Ex.Dot(ref closestOffset, ref extremePointToRayOrigin, out vw);
                //If the closest offset and the extreme point->ray origin direction point the same way,
                //then we might be able to conservatively advance the point towards the surface.
                if (vw > 0)
                {
                    Vector3Ex.Dot(ref closestOffset, ref ray.Direction, out closestPointDotDirection);
                    if (closestPointDotDirection >= 0)
                    {
                        hit = new RayHit();
                        return(false);
                    }
                    hit.T = hit.T - vw / closestPointDotDirection;
                    if (hit.T > maximumLength)
                    {
                        //If we've gone beyond where the ray can reach, there's obviously no hit.
                        hit = new RayHit();
                        return(false);
                    }
                    //Shift the ray up.
                    Vector3Ex.Multiply(ref ray.Direction, hit.T, out hit.Location);
                    Vector3Ex.Add(ref hit.Location, ref ray.Position, out hit.Location);
                    hit.Normal = closestOffset;
                }

                RaySimplex shiftedSimplex;
                simplex.AddNewSimplexPoint(ref extremePoint, ref hit.Location, out shiftedSimplex);

                //Compute the offset from the simplex surface to the origin.
                shiftedSimplex.GetPointClosestToOrigin(ref simplex, out closestOffset);
            }
            //Transform the hit data into world space.
            QuaternionEx.Transform(ref hit.Normal, ref shapeTransform.Orientation, out hit.Normal);
            QuaternionEx.Transform(ref hit.Location, ref shapeTransform.Orientation, out hit.Location);
            Vector3Ex.Add(ref hit.Location, ref shapeTransform.Position, out hit.Location);

            return(true);
        }
Exemplo n.º 27
0
        ///<summary>
        /// Adds a new point to the simplex.
        ///</summary>
        ///<param name="shapeA">First shape in the pair.</param>
        ///<param name="shapeB">Second shape in the pair.</param>
        ///<param name="iterationCount">Current iteration count.</param>
        ///<param name="closestPoint">Current point on simplex closest to origin.</param>
        ///<returns>Whether or not GJK should exit due to a lack of progression.</returns>
        public bool GetNewSimplexPoint(ConvexShape shapeA, ConvexShape shapeB, int iterationCount, ref System.Numerics.Vector3 closestPoint)
        {
            System.Numerics.Vector3 negativeDirection;
            Vector3Ex.Negate(ref closestPoint, out negativeDirection);
            System.Numerics.Vector3 sa, sb;
            shapeA.GetLocalExtremePointWithoutMargin(ref negativeDirection, out sa);
            shapeB.GetExtremePointWithoutMargin(closestPoint, ref LocalTransformB, out sb);
            System.Numerics.Vector3 S;
            Vector3Ex.Subtract(ref sa, ref sb, out S);
            //If S is not further towards the origin along negativeDirection than closestPoint, then we're done.
            float dotS;
            Vector3Ex.Dot(ref S, ref negativeDirection, out dotS); //-P * S
            float distanceToClosest = closestPoint.LengthSquared();

            float progression = dotS + distanceToClosest;
            //It's likely that the system is oscillating between two or more states, usually because of a degenerate simplex.
            //Rather than detect specific problem cases, this approach just lets it run and catches whatever falls through.
            //During oscillation, one of the states is usually just BARELY outside of the numerical tolerance.
            //After a bunch of iterations, the system lets it pick the 'better' one.
            if (iterationCount > GJKToolbox.HighGJKIterations && distanceToClosest - previousDistanceToClosest < DistanceConvergenceEpsilon * errorTolerance)
                return true;
            if (distanceToClosest < previousDistanceToClosest)
                previousDistanceToClosest = distanceToClosest;

            //If "A" is the new point always, then the switch statement can be removed
            //in favor of just pushing three points up.
            switch (State)
            {
                case SimplexState.Point:
                    if (progression <= (errorTolerance = MathHelper.Max(A.LengthSquared(), S.LengthSquared())) * ProgressionEpsilon)
                        return true;

                    State = SimplexState.Segment;
                    B = S;
                    SimplexA.B = sa;
                    SimplexB.B = sb;
                    return false;
                case SimplexState.Segment:
                    if (progression <= (errorTolerance = MathHelper.Max(MathHelper.Max(A.LengthSquared(), B.LengthSquared()), S.LengthSquared())) * ProgressionEpsilon)
                        return true;

                    State = SimplexState.Triangle;
                    C = S;
                    SimplexA.C = sa;
                    SimplexB.C = sb;
                    return false;
                case SimplexState.Triangle:
                    if (progression <= (errorTolerance = MathHelper.Max(MathHelper.Max(A.LengthSquared(), B.LengthSquared()), MathHelper.Max(C.LengthSquared(), S.LengthSquared()))) * ProgressionEpsilon)
                        return true;

                    State = SimplexState.Tetrahedron;
                    D = S;
                    SimplexA.D = sa;
                    SimplexB.D = sb;
                    return false;
            }
            return false;
        }
Exemplo n.º 28
0
        internal void PreStep(float dt)
        {
            vehicleEntity    = wheel.Vehicle.Body;
            supportEntity    = wheel.SupportingEntity;
            supportIsDynamic = supportEntity != null && supportEntity.isDynamic;
            Vector3Ex.Cross(ref wheel.worldForwardDirection, ref wheel.normal, out slidingFrictionAxis);
            float axisLength = slidingFrictionAxis.LengthSquared();

            //Safety against bad cross product
            if (axisLength < Toolbox.BigEpsilon)
            {
                Vector3Ex.Cross(ref wheel.worldForwardDirection, ref Toolbox.UpVector, out slidingFrictionAxis);
                axisLength = slidingFrictionAxis.LengthSquared();
                if (axisLength < Toolbox.BigEpsilon)
                {
                    Vector3Ex.Cross(ref wheel.worldForwardDirection, ref Toolbox.RightVector, out slidingFrictionAxis);
                }
            }
            slidingFrictionAxis.Normalize();

            linearAX = slidingFrictionAxis.X;
            linearAY = slidingFrictionAxis.Y;
            linearAZ = slidingFrictionAxis.Z;

            //angular A = Ra x N
            angularAX = (wheel.ra.Y * linearAZ) - (wheel.ra.Z * linearAY);
            angularAY = (wheel.ra.Z * linearAX) - (wheel.ra.X * linearAZ);
            angularAZ = (wheel.ra.X * linearAY) - (wheel.ra.Y * linearAX);

            //Angular B = N x Rb
            angularBX = (linearAY * wheel.rb.Z) - (linearAZ * wheel.rb.Y);
            angularBY = (linearAZ * wheel.rb.X) - (linearAX * wheel.rb.Z);
            angularBZ = (linearAX * wheel.rb.Y) - (linearAY * wheel.rb.X);

            //Compute inverse effective mass matrix
            float entryA, entryB;

            //these are the transformed coordinates
            float tX, tY, tZ;

            if (vehicleEntity.isDynamic)
            {
                tX     = angularAX * vehicleEntity.inertiaTensorInverse.M11 + angularAY * vehicleEntity.inertiaTensorInverse.M21 + angularAZ * vehicleEntity.inertiaTensorInverse.M31;
                tY     = angularAX * vehicleEntity.inertiaTensorInverse.M12 + angularAY * vehicleEntity.inertiaTensorInverse.M22 + angularAZ * vehicleEntity.inertiaTensorInverse.M32;
                tZ     = angularAX * vehicleEntity.inertiaTensorInverse.M13 + angularAY * vehicleEntity.inertiaTensorInverse.M23 + angularAZ * vehicleEntity.inertiaTensorInverse.M33;
                entryA = tX * angularAX + tY * angularAY + tZ * angularAZ + vehicleEntity.inverseMass;
            }
            else
            {
                entryA = 0;
            }

            if (supportIsDynamic)
            {
                tX     = angularBX * supportEntity.inertiaTensorInverse.M11 + angularBY * supportEntity.inertiaTensorInverse.M21 + angularBZ * supportEntity.inertiaTensorInverse.M31;
                tY     = angularBX * supportEntity.inertiaTensorInverse.M12 + angularBY * supportEntity.inertiaTensorInverse.M22 + angularBZ * supportEntity.inertiaTensorInverse.M32;
                tZ     = angularBX * supportEntity.inertiaTensorInverse.M13 + angularBY * supportEntity.inertiaTensorInverse.M23 + angularBZ * supportEntity.inertiaTensorInverse.M33;
                entryB = tX * angularBX + tY * angularBY + tZ * angularBZ + supportEntity.inverseMass;
            }
            else
            {
                entryB = 0;
            }

            velocityToImpulse = -1 / (entryA + entryB); //Softness?

            //Compute friction.
            //Which coefficient? Check velocity.
            if (Math.Abs(RelativeVelocity) < staticFrictionVelocityThreshold)
            {
                blendedCoefficient = frictionBlender(staticCoefficient, wheel.supportMaterial.staticFriction, false, wheel);
            }
            else
            {
                blendedCoefficient = frictionBlender(kineticCoefficient, wheel.supportMaterial.kineticFriction, true, wheel);
            }
        }
Exemplo n.º 29
0
        /// <summary>
        /// Updates the collection of supporting contacts.
        /// </summary>
        public void UpdateSupports(ref System.Numerics.Vector3 movementDirection)
        {
            bool hadTraction = HasTraction;

            //Reset traction/support.
            HasTraction = false;
            HasSupport  = false;

            System.Numerics.Vector3 downDirection = characterBody.orientationMatrix.Down;
            System.Numerics.Vector3 bodyPosition  = characterBody.position;

            //Compute the character's radius, minus a little margin. We want the rays to originate safely within the character's body.
            //Assume vertical rotational invariance. Spheres, cylinders, and capsules don't have varying horizontal radii.
            System.Numerics.Vector3 extremePoint;
            var convexShape = characterBody.CollisionInformation.Shape as ConvexShape;

            Debug.Assert(convexShape != null, "Character bodies must be convex.");

            //Find the lowest point on the collision shape.
            convexShape.GetLocalExtremePointWithoutMargin(ref Toolbox.DownVector, out extremePoint);
            BottomDistance = -extremePoint.Y + convexShape.collisionMargin;

            convexShape.GetLocalExtremePointWithoutMargin(ref Toolbox.RightVector, out extremePoint);
            float rayCastInnerRadius = Math.Max((extremePoint.X + convexShape.collisionMargin) * 0.8f, extremePoint.X);

            //Vertically, the rays will start at the same height as the character's center.
            //While they could be started lower on a cylinder, that wouldn't always work for a sphere or capsule: the origin might end up outside of the shape!

            tractionContacts.Clear();
            supportContacts.Clear();
            sideContacts.Clear();
            headContacts.Clear();

            foreach (var pair in characterBody.CollisionInformation.Pairs)
            {
                //Don't stand on things that aren't really colliding fully.
                if (pair.CollisionRule != CollisionRule.Normal)
                {
                    continue;
                }
                ContactCategorizer.CategorizeContacts(pair, characterBody.CollisionInformation, ref downDirection, ref tractionContacts, ref supportContacts, ref sideContacts, ref headContacts);
            }

            HasSupport  = supportContacts.Count > 0;
            HasTraction = tractionContacts.Count > 0;

            //Only perform ray casts if the character has fully left the surface, and only if the previous frame had traction.
            //(If ray casts are allowed when support contacts still exist, the door is opened for climbing surfaces which should not be climbable.
            //Consider a steep slope. If the character runs at it, the character will likely be wedged off of the ground, making it lose traction while still having a support contact with the slope.
            //If ray tests are allowed when support contacts exist, the character will maintain traction despite climbing the wall.
            //The VerticalMotionConstraint can stop the character from climbing in many cases, but it's nice not to have to rely on it.
            //Disallowing ray tests when supports exist does have a cost, though. For example, consider rounded steps.
            //If the character walks off a step such that it is still in contact with the step but is far enough down that the slope is too steep for traction,
            //the ray test won't recover traction. This situation just isn't very common.)
            if (!HasSupport && hadTraction)
            {
                float supportRayLength = maximumAssistedDownStepHeight + BottomDistance;
                SupportRayData = null;
                //If the contacts aren't available to support the character, raycast down to find the ground.
                if (!HasTraction)
                {
                    //TODO: could also require that the character has a nonzero movement direction in order to use a ray cast.  Questionable- would complicate the behavior on edges.
                    Ray ray = new Ray(bodyPosition, downDirection);

                    bool           hasTraction;
                    SupportRayData data;
                    if (TryDownCast(ref ray, supportRayLength, out hasTraction, out data))
                    {
                        SupportRayData = data;
                        HasTraction    = data.HasTraction;
                        HasSupport     = true;
                    }
                }

                //If contacts and the center ray cast failed, try a ray offset in the movement direction.
                bool tryingToMove = movementDirection.LengthSquared() > 0;
                if (!HasTraction && tryingToMove)
                {
                    Ray ray = new Ray(
                        characterBody.Position +
                        movementDirection * rayCastInnerRadius, downDirection);

                    //Have to test to make sure the ray doesn't get obstructed.  This could happen if the character is deeply embedded in a wall; we wouldn't want it detecting things inside the wall as a support!
                    Ray obstructionRay;
                    obstructionRay.Position  = characterBody.Position;
                    obstructionRay.Direction = ray.Position - obstructionRay.Position;
                    if (!QueryManager.RayCastHitAnything(obstructionRay, 1))
                    {
                        //The origin isn't obstructed, so now ray cast down.
                        bool           hasTraction;
                        SupportRayData data;
                        if (TryDownCast(ref ray, supportRayLength, out hasTraction, out data))
                        {
                            if (SupportRayData == null || data.HitData.T < SupportRayData.Value.HitData.T)
                            {
                                //Only replace the previous support ray if we now have traction or we didn't have a support ray at all before,
                                //or this hit is a better (sooner) hit.
                                if (hasTraction)
                                {
                                    SupportRayData = data;
                                    HasTraction    = true;
                                }
                                else if (SupportRayData == null)
                                {
                                    SupportRayData = data;
                                }
                                HasSupport = true;
                            }
                        }
                    }
                }

                //If contacts, center ray, AND forward ray failed to find traction, try a side ray created from down x forward.
                if (!HasTraction && tryingToMove)
                {
                    //Compute the horizontal offset direction.  Down direction and the movement direction are normalized and perpendicular, so the result is too.
                    System.Numerics.Vector3 horizontalOffset;
                    Vector3Ex.Cross(ref movementDirection, ref downDirection, out horizontalOffset);
                    Vector3Ex.Multiply(ref horizontalOffset, rayCastInnerRadius, out horizontalOffset);
                    Ray ray = new Ray(bodyPosition + horizontalOffset, downDirection);

                    //Have to test to make sure the ray doesn't get obstructed.  This could happen if the character is deeply embedded in a wall; we wouldn't want it detecting things inside the wall as a support!
                    Ray obstructionRay;
                    obstructionRay.Position  = bodyPosition;
                    obstructionRay.Direction = ray.Position - obstructionRay.Position;
                    if (!QueryManager.RayCastHitAnything(obstructionRay, 1))
                    {
                        //The origin isn't obstructed, so now ray cast down.
                        bool           hasTraction;
                        SupportRayData data;
                        if (TryDownCast(ref ray, supportRayLength, out hasTraction, out data))
                        {
                            if (SupportRayData == null || data.HitData.T < SupportRayData.Value.HitData.T)
                            {
                                //Only replace the previous support ray if we now have traction or we didn't have a support ray at all before,
                                //or this hit is a better (sooner) hit.
                                if (hasTraction)
                                {
                                    SupportRayData = data;
                                    HasTraction    = true;
                                }
                                else if (SupportRayData == null)
                                {
                                    SupportRayData = data;
                                }
                                HasSupport = true;
                            }
                        }
                    }
                }

                //If contacts, center ray, forward ray, AND the first side ray failed to find traction, try a side ray created from forward x down.
                if (!HasTraction && tryingToMove)
                {
                    //Compute the horizontal offset direction.  Down direction and the movement direction are normalized and perpendicular, so the result is too.
                    System.Numerics.Vector3 horizontalOffset;
                    Vector3Ex.Cross(ref downDirection, ref movementDirection, out horizontalOffset);
                    Vector3Ex.Multiply(ref horizontalOffset, rayCastInnerRadius, out horizontalOffset);
                    Ray ray = new Ray(bodyPosition + horizontalOffset, downDirection);

                    //Have to test to make sure the ray doesn't get obstructed.  This could happen if the character is deeply embedded in a wall; we wouldn't want it detecting things inside the wall as a support!
                    Ray obstructionRay;
                    obstructionRay.Position  = bodyPosition;
                    obstructionRay.Direction = ray.Position - obstructionRay.Position;
                    if (!QueryManager.RayCastHitAnything(obstructionRay, 1))
                    {
                        //The origin isn't obstructed, so now ray cast down.
                        bool           hasTraction;
                        SupportRayData data;
                        if (TryDownCast(ref ray, supportRayLength, out hasTraction, out data))
                        {
                            if (SupportRayData == null || data.HitData.T < SupportRayData.Value.HitData.T)
                            {
                                //Only replace the previous support ray if we now have traction or we didn't have a support ray at all before,
                                //or this hit is a better (sooner) hit.
                                if (hasTraction)
                                {
                                    SupportRayData = data;
                                    HasTraction    = true;
                                }
                                else if (SupportRayData == null)
                                {
                                    SupportRayData = data;
                                }
                                HasSupport = true;
                            }
                        }
                    }
                }
            }

            UpdateSupportData(ref downDirection);
            UpdateVerticalSupportData(ref downDirection, ref movementDirection);
        }
Exemplo n.º 30
0
        /// <summary>
        /// Calculates necessary information for velocity solving.
        /// Called by preStep(float dt)
        /// </summary>
        /// <param name="dt">Time in seconds since the last update.</param>
        public override void Update(float dt)
        {
            Matrix3x3.Transform(ref localAnchorA, ref connectionA.orientationMatrix, out worldOffsetA);
            Matrix3x3.Transform(ref localAnchorB, ref connectionB.orientationMatrix, out worldOffsetB);


            float errorReductionParameter;

            springSettings.ComputeErrorReductionAndSoftness(dt, 1 / dt, out errorReductionParameter, out softness);

            //Mass System.Numerics.Matrix4x4
            Matrix3x3 k;
            Matrix3x3 linearComponent;

            Matrix3x3.CreateCrossProduct(ref worldOffsetA, out rACrossProduct);
            Matrix3x3.CreateCrossProduct(ref worldOffsetB, out rBCrossProduct);
            if (connectionA.isDynamic && connectionB.isDynamic)
            {
                Matrix3x3.CreateScale(connectionA.inverseMass + connectionB.inverseMass, out linearComponent);
                Matrix3x3 angularComponentA, angularComponentB;
                Matrix3x3.Multiply(ref rACrossProduct, ref connectionA.inertiaTensorInverse, out angularComponentA);
                Matrix3x3.Multiply(ref rBCrossProduct, ref connectionB.inertiaTensorInverse, out angularComponentB);
                Matrix3x3.Multiply(ref angularComponentA, ref rACrossProduct, out angularComponentA);
                Matrix3x3.Multiply(ref angularComponentB, ref rBCrossProduct, out angularComponentB);
                Matrix3x3.Subtract(ref linearComponent, ref angularComponentA, out k);
                Matrix3x3.Subtract(ref k, ref angularComponentB, out k);
            }
            else if (connectionA.isDynamic && !connectionB.isDynamic)
            {
                Matrix3x3.CreateScale(connectionA.inverseMass, out linearComponent);
                Matrix3x3 angularComponentA;
                Matrix3x3.Multiply(ref rACrossProduct, ref connectionA.inertiaTensorInverse, out angularComponentA);
                Matrix3x3.Multiply(ref angularComponentA, ref rACrossProduct, out angularComponentA);
                Matrix3x3.Subtract(ref linearComponent, ref angularComponentA, out k);
            }
            else if (!connectionA.isDynamic && connectionB.isDynamic)
            {
                Matrix3x3.CreateScale(connectionB.inverseMass, out linearComponent);
                Matrix3x3 angularComponentB;
                Matrix3x3.Multiply(ref rBCrossProduct, ref connectionB.inertiaTensorInverse, out angularComponentB);
                Matrix3x3.Multiply(ref angularComponentB, ref rBCrossProduct, out angularComponentB);
                Matrix3x3.Subtract(ref linearComponent, ref angularComponentB, out k);
            }
            else
            {
                throw new InvalidOperationException("Cannot constrain two kinematic bodies.");
            }
            k.M11 += softness;
            k.M22 += softness;
            k.M33 += softness;
            Matrix3x3.Invert(ref k, out massMatrix);

            Vector3Ex.Add(ref connectionB.position, ref worldOffsetB, out error);
            Vector3Ex.Subtract(ref error, ref connectionA.position, out error);
            Vector3Ex.Subtract(ref error, ref worldOffsetA, out error);


            Vector3Ex.Multiply(ref error, -errorReductionParameter, out biasVelocity);

            //Ensure that the corrective velocity doesn't exceed the max.
            float length = biasVelocity.LengthSquared();

            if (length > maxCorrectiveVelocitySquared)
            {
                float multiplier = maxCorrectiveVelocity / (float)Math.Sqrt(length);
                biasVelocity.X *= multiplier;
                biasVelocity.Y *= multiplier;
                biasVelocity.Z *= multiplier;
            }
        }
Exemplo n.º 31
0
        /// <summary>
        /// Initializes the constraint for the current frame.
        /// </summary>
        /// <param name="dt">Time between frames.</param>
        public override void Update(float dt)
        {
            basis.rotationMatrix = connectionA.orientationMatrix;
            basis.ComputeWorldSpaceAxes();

            if (settings.mode == MotorMode.Servomechanism) //Only need to do the bulk of this work if it's a servo.
            {
                //The error is computed using this equation:
                //GoalRelativeOrientation * ConnectionA.Orientation * Error = ConnectionB.Orientation
                //GoalRelativeOrientation is the original rotation from A to B in A's local space.
                //Multiplying by A's orientation gives us where B *should* be.
                //Of course, B won't be exactly where it should be after initialization.
                //The Error component holds the difference between what is and what should be.
                //Error = (GoalRelativeOrientation * ConnectionA.Orientation)^-1 * ConnectionB.Orientation

                //ConnectionA.Orientation is replaced in the above by the world space basis orientation.
                System.Numerics.Quaternion worldBasis = QuaternionEx.CreateFromRotationMatrix(basis.WorldTransform);

                System.Numerics.Quaternion bTarget;
                QuaternionEx.Concatenate(ref settings.servo.goal, ref worldBasis, out bTarget);
                System.Numerics.Quaternion bTargetConjugate;
                QuaternionEx.Conjugate(ref bTarget, out bTargetConjugate);

                System.Numerics.Quaternion error;
                QuaternionEx.Concatenate(ref bTargetConjugate, ref connectionB.orientation, out error);


                float errorReduction;
                settings.servo.springSettings.ComputeErrorReductionAndSoftness(dt, 1 / dt, out errorReduction, out usedSoftness);

                //Turn this into an axis-angle representation.
                QuaternionEx.GetAxisAngleFromQuaternion(ref error, out axis, out angle);

                //Scale the axis by the desired velocity if the angle is sufficiently large (epsilon).
                if (angle > Toolbox.BigEpsilon)
                {
                    float velocity = -(MathHelper.Min(settings.servo.baseCorrectiveSpeed, angle / dt) + angle * errorReduction);

                    biasVelocity.X = axis.X * velocity;
                    biasVelocity.Y = axis.Y * velocity;
                    biasVelocity.Z = axis.Z * velocity;


                    //Ensure that the corrective velocity doesn't exceed the max.
                    float length = biasVelocity.LengthSquared();
                    if (length > settings.servo.maxCorrectiveVelocitySquared)
                    {
                        float multiplier = settings.servo.maxCorrectiveVelocity / (float)Math.Sqrt(length);
                        biasVelocity.X *= multiplier;
                        biasVelocity.Y *= multiplier;
                        biasVelocity.Z *= multiplier;
                    }
                }
                else
                {
                    biasVelocity.X = 0;
                    biasVelocity.Y = 0;
                    biasVelocity.Z = 0;
                }
            }
            else
            {
                usedSoftness = settings.velocityMotor.softness / dt;
                angle        = 0; //Zero out the error;
                Matrix3x3 transform = basis.WorldTransform;
                Matrix3x3.Transform(ref settings.velocityMotor.goalVelocity, ref transform, out biasVelocity);
            }

            //Compute effective mass
            Matrix3x3.Add(ref connectionA.inertiaTensorInverse, ref connectionB.inertiaTensorInverse, out effectiveMassMatrix);
            effectiveMassMatrix.M11 += usedSoftness;
            effectiveMassMatrix.M22 += usedSoftness;
            effectiveMassMatrix.M33 += usedSoftness;
            Matrix3x3.Invert(ref effectiveMassMatrix, out effectiveMassMatrix);

            //Update the maximum force
            ComputeMaxForces(settings.maximumForce, dt);
        }
Exemplo n.º 32
0
        /// <summary>
        /// 相交点辐射光
        /// </summary>
        /// <param name="point"></param>
        /// <param name="normal"></param>
        /// <param name="deep"></param>
        /// <returns></returns>
        public virtual Light IntersectLight(Vector3 point, Vector3 dir, Vector3 normal, int deep)
        {
#if RayDebugger
            SceneDebug Debugger = Scene.debugger;
            if (Debugger != null)
            {
                Debugger.BeginBranch(point);
            }
#endif

            Light returnlight = default;
            // 发光体返回发光颜色
            if (Material.LightAble)
            {
                returnlight = Material.LightColor;
                goto returnPoint;
            }
            // 递归深度极限
            if (deep <= 1)
            {
                returnlight = Material.BaseColor * 0.3f;
                goto returnPoint;
            }

            dir = Vector3.Normalize(dir);
            bool IsBackFace = false;
            if (Vector3.Dot(dir, normal) > 0)               // 背面
            {
                IsBackFace = true;
                normal     = -normal;
            }

            #region 计算追踪光线总数
            int traceRayNum = RenderConfiguration.Configurations.ReflectSmapingLevel - RenderConfiguration.Configurations.RayTraceDeep + deep;
            {
                traceRayNum = (int)(traceRayNum * Material.AMetalDegree);
                if (traceRayNum < 1)
                {
                    traceRayNum = 1;
                }
                traceRayNum = traceRayNum * 3 - 2;
            }
            #endregion

            #region 计算折射光
            Light refractl     = default;         // 折射光
            float refractPower = 0.0f;            // 折射光强度
            if (Material.IsTransparent)
            {
                float riindex = Material.RefractiveIndices;
                if (!IsBackFace)
                {
                    riindex = 1.0f / riindex;
                }
                // 计算折射光线
                (float pow, Vector3 rdir) = Tools.Refract(dir, normal, riindex);
                if (pow < 0)
                {
                    goto endRefract;
                }

                refractPower = pow * Material.TransparentIndex;

                int raycount = (int)(traceRayNum * refractPower);
                traceRayNum -= raycount;

                float randomScale = Material.AMetalDegree * Material.AMetalDegree * 0.5f;
                //raycount = (int)(traceRayNum * randomScale);
                //raycount = (int)(traceRayNum * Material.AMetalDegree);
                if (raycount < 1 && refractPower > 0.00001)
                {
                    raycount = 1;
                }

                if (raycount == 0)
                {
                    refractl = Material.BaseColor;
                    goto endRefract;
                }

                rdir = normal * randomScale + rdir * (1.0f - randomScale);

                for (int nsmap = 0; nsmap < raycount; nsmap++)
                {
                    Vector3 raydir = Tools.RandomPointInSphere() * randomScale + rdir;

                    Ray r = new Ray(point, raydir);
                    //Console.WriteLine('\t' + this.Name + " [refract] : " + r);
                    (Light c, float distance) = Scene.Light(r, deep - 1, this);

                    if (IsBackFace)                       //内部光线,进行吸收计算
                    {
                        float xsl = Math.Log(distance + 1.0f) + 1.0f;
                        refractl *= Material.BaseColor / xsl;
                    }
                    refractl += c;
                }
                refractl /= raycount;
            }

endRefract:
            #endregion

            #region 计算反射光
            Light reflectl = default;             // 反射光
            {
                int raycount = traceRayNum;
                if (raycount < 1)
                {
                    if (refractPower < 0.99f)
                    {
                        raycount = 1;
                    }
                    else
                    {
                        reflectl = Material.BaseColor;
                        goto endReflact;
                    }
                }

                Vector3 spO;
                {
                    //Vector3 spRO = Tools.Reflect(dir, normal);
                    Vector3 spRO = Vector3.Reflect(dir, normal);
                    //spO = normal * (1.0f - Material.MetalDegree) + spRO * Material.MetalDegree;
                    spO = Vector3.Lerp(normal, spRO, Material.MetalDegree);
                }
                for (int nsmap = 0; nsmap < raycount; nsmap++)
                {
                    Vector3 tp     = Tools.RandomPointInSphere() * Material.AMetalDegree + spO;
                    Vector3 raydir = tp;
                    while (raydir.LengthSquared() < 0.1)
                    {
                        tp     = Tools.RandomPointInSphere() + spO;
                        raydir = tp;
                    }

                    Ray r = new Ray(point, raydir);
                    //Console.WriteLine('\t' + this.Name + " [reflact] : " + r);
                    (Light c, float _) = Scene.Light(r, deep - 1, this);                     //, this);
                    reflectl          += c;
                }
                reflectl /= raycount;
                reflectl *= (0.06f * Material.MetalDegree + 0.93f) * Material.BaseColor;
            }
            #endregion

            returnlight = refractl * refractPower + reflectl * (1.0f - refractPower);
endReflact:
returnPoint:
#if RayDebugger
            if (Debugger != null)
            {
                Debugger.EndBranch();
            }
#endif
            return(returnlight);
        }
Exemplo n.º 33
0
        /// <summary>
        /// Computes one iteration of the constraint to meet the solver updateable's goal.
        /// </summary>
        /// <returns>The rough applied impulse magnitude.</returns>
        public override float SolveIteration()
        {
            //Compute relative velocity
            System.Numerics.Vector3 lambda;
            Vector3Ex.Cross(ref r, ref entity.angularVelocity, out lambda);
            Vector3Ex.Subtract(ref lambda, ref entity.linearVelocity, out lambda);

            //Add in bias velocity
            Vector3Ex.Add(ref biasVelocity, ref lambda, out lambda);

            //Add in softness
            System.Numerics.Vector3 softnessVelocity;
            Vector3Ex.Multiply(ref accumulatedImpulse, usedSoftness, out softnessVelocity);
            Vector3Ex.Subtract(ref lambda, ref softnessVelocity, out lambda);

            //In terms of an impulse (an instantaneous change in momentum), what is it?
            Matrix3x3.Transform(ref lambda, ref effectiveMassMatrix, out lambda);

            //Sum the impulse.
            System.Numerics.Vector3 previousAccumulatedImpulse = accumulatedImpulse;
            accumulatedImpulse += lambda;

            //If the impulse it takes to get to the goal is too high for the motor to handle, scale it back.
            float sumImpulseLengthSquared = accumulatedImpulse.LengthSquared();
            if (sumImpulseLengthSquared > maxForceDtSquared)
            {
                //max / impulse gives some value 0 < x < 1.  Basically, normalize the vector (divide by the length) and scale by the maximum.
                accumulatedImpulse *= maxForceDt / (float)Math.Sqrt(sumImpulseLengthSquared);

                //Since the limit was exceeded by this corrective impulse, limit it so that the accumulated impulse remains constrained.
                lambda = accumulatedImpulse - previousAccumulatedImpulse;
            }

            entity.ApplyLinearImpulse(ref lambda);
            System.Numerics.Vector3 taImpulse;
            Vector3Ex.Cross(ref r, ref lambda, out taImpulse);
            entity.ApplyAngularImpulse(ref taImpulse);

            return (Math.Abs(lambda.X) + Math.Abs(lambda.Y) + Math.Abs(lambda.Z));
        }