0

Short version:

How can I properly add gravity to my physics update?

Detailed Version:

I am having an issue where the generic collision algorithm for a moving sphere to plane (posted below for reference) will never hit the point where a future collision will be detected. I believe this is due to how I have my physics update.

I have it setup so that only when it will be determined that there will be a future collision, does gravity get applied to the object. So before gravity is applied to an object, the collision check is first done. However, since it is always assumed that there is never any future collision, no gravity is never applied. Imagine a scenario with the values of

spherePosition = (0, 5, 0)
sphereVelocity = (2, 0, 0)
sphereRadius = 1
planeOrigin = (0, 0, 0)
planeNormal = (0, 1, 0)

This would always assume that the sphere is moving parrelel to the plane. As a result, gravity will never be applied.

My update is relatively simple, such as

mAcceleration = mTotalForces / mMass;
Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);
mVelocity += mAcceleration * fElapsedTime;

So the order of operations is roughly

int collisionResult = checkCollision(sphere, plane);
if(collisionResult == 2)
{
    sphere.applyGravity(); // Just sets "mAcceleration" to (0, -9.81, 0). Which is then removed by my physics update.
}
sphere.update(timeSlice);

With all of that being said, when should I apply gravity to my object in the physics update loop and how? If I were to apply it before the collision check, it would not matter during the collision check and if I were to do it after, for if there will be a future collision, how should my update be adjusted?

Collision check reference:

int IntersectMovingSpherePlane(float sphereRadius, const Vector3& sphereCenter, const Vector3& sphereVelocity, const Vector3& planeNormal,
    const Vector3& planeOrigin, float planeD, float &t, Vector3 &q)
{
    // Compute distance of sphere center to plane
    float dist = Vec3Dot(&planeNormal, &sphereCenter) - planeD;
    if (fabs(dist) <= sphereRadius) {
        // The sphere is already overlapping the plane. Set time of
        // intersection to zero and q to sphere center
        t = 0.0f;
        q = sphereCenter;
        return 0;
    } else {
        float denom = Vec3Dot(&planeNormal, &sphereVelocity);
        if (denom * dist >= 0.0f) {
            // No intersection as sphere moving parallel to or away from plane
            return 1;
        } else {
            // Sphere is moving towards the plane

            // Use +r in computations if sphere in front of plane, else -r
            float r = dist > 0.0f ? sphereRadius : -sphereRadius;
            t = (r - dist) / denom;
            q = sphereCenter + t * sphereVelocity - sphereRadius * planeNormal;
            return 2;
        }
    }
}
mmurphy
  • 1,327
  • 4
  • 15
  • 30
  • i'm not sure what exactly your problem is. are you sure don't to "apply gravity only if there is no collision" (in other words: apply gravity as long as the object doesn't touch). also i'd always apply gravity, then simply place restrictions on the coordinates or add a counter-force for objects underneath your plane. – kritzikratzi May 15 '12 at 00:32
  • @kritzikratzi: That is part of what I am doing. The problem is, even when I apply gravity, it doesn't ever matter due to my physics update (I posted the code example). How would you "place restrictions on the coordinates or add a counter-force for objects underneath your plane."? – mmurphy May 15 '12 at 00:37
  • one disgusting way, good to start with, could be: always do sphere.applyGravity(). then do all your physics stuff. after that do sphereCenter.y = max( 0, sphereCenter.y ); i'm still not sure what you're trying to do here. i haven't heard of a world where gravity applies in certain scenarios. – kritzikratzi May 15 '12 at 00:49
  • My way of only applying gravity in certain scenarios may be wrong, I would be willing to change it if I knew the proper was to have it applied. – mmurphy May 15 '12 at 00:51
  • okay, i just keep guessing until we find an answer :) usually how physics engines work is that you "reset" acceleration at each iteration. i.e. you start with mAcceleration = 0; then tons of things get to apply their forces; mAcceleration += (0,-9.81,0) for gravity, mAcceleration += idk...someforce... and so on. after that comes the integration part where you do mVelocity += mAcceleration*fElapsedTime; mPosition += mVelocity*fElapsedTime. – kritzikratzi May 15 '12 at 00:56
  • That is what I was thinking, however, say it is the first time through the physics loop, then how it know there is a future collision? – mmurphy May 15 '12 at 01:33
  • it depends. if (a) you check for collisions before doing the integration step: you won't know. if you do things the other way around (b) you will know. but i don't see a problem. in case (a) you get: compute forces, integrate, check collision, compute forces, integrate ...; in case (b) you get: check collision, compute forces, integrate, check collision, compute forces, etc.... you see the same things happen in the same order anyways, doesn't really if you check collisions first or last. don't worry too much about the first loop, in the long run you can't tell the difference anyways. – kritzikratzi May 15 '12 at 02:12
  • At the start of my physics loop, I should reset the acceleration (0, -9.81, 0), then check collisions, compute forces based upon the collision result, then update my physics object? – mmurphy May 15 '12 at 06:34
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/11261/discussion-between-kritzikratzi-and-mmurphy) – kritzikratzi May 15 '12 at 11:42

2 Answers2

1

May the simplest way here is to make gravity a part of the mTotalForces.

A more physics way is make gravity a separate acceleration. And add to `mTotalForces / mMass" afterward for the final acceleration value.

EDIT

Vector3 translation = (mVelocity * fElapsedTime) + 0.5 * mAcceleration * pow(fElapsedTime, 2);

It seems here your code is for uniform motion. But if you want a ball stop dropping after hit the ground, it is not uniform.

For such non-uniform motion, normally we just subdivide the time frame and do uniform calculations for a very short duration that the motion can be considered uniform, and ignore the accumulating error.

BlueWanderer
  • 2,671
  • 2
  • 21
  • 36
  • Could you elaborate on your second point? I thought the first point you mentioned would be the simplest way, however, as I demonstrated in this thread http://stackoverflow.com/questions/10393840/determining-resting-contact-between-sphere-and-plane-when-using-external-forces ... it is unfortunately not the case. In either case, I would rather do it the more "correct" way. – mmurphy May 15 '12 at 06:32
  • Could you explain "subdivide the time frame and do uniform calculations for a very short duration that the motion can be considered uniform, and ignore the accumulating error"? – mmurphy May 16 '12 at 03:05
1

The proper way of adding physics to the system is using an iterative process, known as runge-kutta method (or newton's, first order runge-kutta).

Basically, your model will have particles with mass that evolve with time. The only thing that you must 'model' is the force exerted on each particle on each tick. With that, you calculate the acceleration.

If the world has gravity, you must say that on each particle, the acceleration is -g (negative for pointing downwards and in this case independent of the mass).

For instance, first order runge-kutta is:

newVelocity = oldVelocity + tickAcceleration*dt
newPosition = oldPosition + newVelocity*dt

where dt is the time-step you are choosing for your tick.

Normally, to avoid the objects passing trough other objects, you don't actually calculate the new*. You calculate a proposed position:

proposedVelocity = oldVelocity + tickAcceleration*dt
proposedPosition = oldPosition + proposedVelocity*dt

Then calculate the collisions conditions (you test every particle against other). If particles collided, you apply the collision equations (e.g. elastic collision), and update the newPositions/velocity accordingly. If they didn't collided, you update the new* with the proposed* values,

newVelocity = proposedVelocity
newPosition = proposedPosition

EDIT:

On a 1º approximation, it doesn't matter which one you use for the collision. However a 2º approximation could be to first calculate a proposed velocity and position. Then check if the collision occurred between the old position and the new position, and calculate the time of collision, which I call dtCol. We can then divide the time step in two parts: before collision, dtCol, and after Collision dt-dtCol. First part is to set the proposedQuantities as

proposedVelocityBeforeCollision = oldVelocity + tickAcceleration*dtCol
proposedPositionBeforeCollision = oldPosition + proposedVelocityBeforeCollision*dtCol

This quantities are exactly before the collision. You can now use then to then to calculate the proposedVelocityAfterCollision with the collision equations (the proposedPositionAfterCollision = proposedPosition).

After that, you update the new* quantities:

newVelocity = proposedVelocityAfterCollision + tickAcceleration*(dt-dtCol)
newPosition = proposedPosition + newVelocity*(dt-dtCol)

Notice that this procedure does not consider more than one collision for tick.

Hope this helps.

Jorge Leitao
  • 19,085
  • 19
  • 85
  • 121
  • So if there is a collision, disregard the proposedVelocity and propostedPosition? When I am doing my collision check (which requires velocity), which velocity should I use? – mmurphy May 16 '12 at 03:04
  • Updated the text to explain what you've asked. – Jorge Leitao May 16 '12 at 05:50