Game Development Community

How to: Enable Vehicle->Vehicle(&Player!) Rigid Collison Solver

by Henry Todd · in Torque 3D Professional · 10/05/2010 (10:31 pm) · 12 replies

***UPDATE 1/6/2011: Now with Vehicle->Player collision and some minor improvements, as this resource:
www.torquepowered.com/community/resources/view/20727

Here's a quick video of it in action: www.youtube.com/watch?v=hpsKIL0a5h8

This info may more or less already be out there, but I don't think it's common knowledge since all of the old discussions are burried deep in the TGE forums. Feel free to skip these two paragraphs and go straight to the instructions if you don't care. ;)

Some of us may remember various attempts over the years to fix the Rigid class's collision solvers. In the end most of these attempts were abandoned when research indicated that the problems were 1) that the solvers didn't handle certain collision cases well to begin with and 2) that the formulas used in the solvers were intended to be paired with a time-reversal mechanism to solve collisions that occured between ticks.

Anyway, my understanding is that a physics abstraction layer solution for vehicles is on the table, possibly even being worked on, and that this will finally allow us to ditch Rigid in favor of PhysX or Bullet. However, since there's no concrete timeline on that, here's how you can use Rigid's vehicle vs. vehicle solver and have it be relatively stable (at least as stable as existing vehicle vs. static collisions, anyway):

(note: instructions based on T3D, however this should work with any version of Torque)
Go to T3D/vehicles/vehicle.cpp
Go to the function bool Vehicle::resolveCollision

Replace it with this:
bool Vehicle::resolveCollision(Rigid&  ns,CollisionList& cList)
{
   PROFILE_SCOPE( Vehicle_ResolveCollision );

   // Apply impulses to resolve collision
   bool collided = false;
   for (S32 i = 0; i < cList.getCount(); i++) 
   {
      Collision& c = cList[i];
      if (c.distance < mDataBlock->collisionTol) 
      {
         // [HNT] Skip converge test and contact constraints
         if (c.object->getTypeMask() & VehicleObjectType) // ADDED
         { // ADDED
            Vehicle *tmpShape = (Vehicle*)c.object; // ADDED
            ns.resolveCollision(cList[i].point, cList[i].normal, // ADDED
               &tmpShape->mRigid); // ADDED
            collided  = true; // ADDED

            // Keep track of objects we collide with // ADDED
            if (!isGhost())  // ADDED
            { // ADDED
               Point3F v,r; // ADDED
               ns.getOriginVector(c.point,&r); // ADDED
               ns.getVelocity(r,&v); // ADDED
               queueCollision(tmpShape,v - tmpShape->getVelocity()); // ADDED
            } // ADDED
         } // ADDED
         else // ADDED
         { // ADDED
            // Velocity into surface
            Point3F v,r;
            ns.getOriginVector(c.point,&r);
            ns.getVelocity(r,&v);
            F32 vn = mDot(v,c.normal);

            // Only interested in velocities greater than sContactTol,
            // velocities less than that will be dealt with as contacts
            // "constraints".
            if (vn < -mDataBlock->contactTol) 
            {
               // Apply impulses to the rigid body to keep it from
               // penetrating the surface.
               ns.resolveCollision(cList[i].point,
                  cList[i].normal);
               collided  = true;

               // Keep track of objects we collide with
               if (!isGhost() && c.object->getTypeMask() & ShapeBaseObjectType) 
               {
                  ShapeBase* col = static_cast<ShapeBase*>(c.object);
                  queueCollision(col,v - col->getVelocity());
               }
            }
         } // ADDED
         // [/HNT]
      }
   }

   return collided;
}

New lines marked with "// ADDED" so you can see what I changed.
Pretty simple, if the collided object is a Vehicle then use the version of the solver function that takes a second Rigid object as a param. I skip the converging velocity check because 1) that check only makes sense if you're colliding with a static object and 2) the solver function already does a version of this check which properly accounts for the velocity of the second Rigid body.

Now you need to go to T3D/rigid.cpp
Function is bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal, Rigid* rigid)
Should be around line 126 (note this is the first Rigid::resolveCollision function, there are 2).

Depending on your version of Torque you may or may not need to fix the second to last line to say:
"rigid->applyImpulse(r2,impulse);" instead of "applyImpulse(r2,impulse);"
This fix eventually made it in, but I'm not sure at what point.

Now, to beat the potential for two Rigid objects to "slide" into each other, I suggest slightly exaggerating the force produced by the solver. It'll make your collisions a little bumpier at low speeds, but it will keep objects from becoming stuck in each other in all but the most extreme cases (the same cases where Vehicles get stuck in static objects). To do this, go to line 137 (right above the line "if (nv > 0.0f)") and add:

nv -= 3.0f;
Yeah, I know, that's a terrible hack. But it works better than any more complex method I've tried at various times like checking for negative collision distance or changing the updateForces/updateCollisions/integrate order. Adjust the value to taste; I found 1 to be too weak (could still lodge cars into each other) and 5 to be too bouncy. You're effectively adding to the converging velocity used to calculate the impulse, so a collison of velocity 20 will be treated as 23 (little notable difference) and a collision of velocity 0.5 will be treated as 3.5 (some bouncing, but keeps the solver from totally failing).

Ugly, but it's easy to implement, stable, and far better than the stock vehicle vs. vehicle collision (each vehicle treats the other as a static object). Note that certain types of collision hulls are better than others from the solver's perspective; boxes produce too many planar collisions which can produce unsolvable scenarios. You want to have a mesh that favors point and line collisions, like the collision mesh the default Buggy comes with (I actually just keep rescaling that collision mesh onto most of my vehicles).

RigidShape
Don't try to collide RigidShapes with Vehicles using this code, it will crash. RigidShapes don't have their own typeMask (they piggyback on VehicleObjectType) so the code will try to recast the RigidShape pointer to a Vehicle pointer, destroying the universe in the process. Even if you give the RigidShape its own typeMask, you'll still need to make mRigid public in order for Vehicles to access the RigidShape's mRigid (and the other way around.

The simple workaround is to use Wheel-less WheeledVehicles instead of RigidShapes. Take your existing RigidShape datablocks and replace "RigidShapeData" with "WheeledVehicleData" and you're good to go. IE, datablock WheeledVehicleData( BouncingBoulder ) { ...everything that was in the DB originally ... };

#1
10/06/2010 (9:09 am)
Nice writeup Henry!

I've been playing these days with rigid-to-player collisions,but the stock T3D collision response is really distasteful and unstable.
I needed an accurate collision for movable platforms and what I did was to integrate a rigid body collision in PathShape to prevent from penetrations,but the rigid collision threats the player like a static environment. Even when the player is not movable,the rigid body will pass through it without applying an impulse,because the impulse works only for rigids,not for AABBs.
So in Stock T3D there is no collision solution for this,I believe I should use physics (and mostly impulses) to bypass the problem.
#2
10/06/2010 (6:14 pm)
Awesome, Henry. This has been plaguing me for a while. Works great! Thank you very much.
#3
10/07/2010 (10:36 pm)
For anyone interested in some "light reading," the Rigid solver is generally considered to be based on www.cs.cmu.edu/~baraff/sigcourse/notesd2.pdf David Baraff's rigid body constraints paper from Siggraph '97. The important bits start on page 10.

@Ivan: Your post got me interested in seeing if I could fake out a vehicle->player collision scheme. Spent the past hour and a half on it, and I think I'm starting to put something together.

Instead of attaching a full Rigid object to the Player, I went with what seemed simpler at the time: during the collision I populate a temporary Rigid object with all of the Player's data (position, mass, velocity, etc). This ended up being the hard part, as Player doesn't keep all of the same kind of data about itself that a Rigid object does.

To be honest, it probably would have been easier to just attach a Rigid object to the Player by following Vehicle's example. Except, instead of using the Rigid's position to control the Player object, do the opposite (in PhysX I believe this is called a kinematic, a dummy physics object that follows another object around to supply basic physics interactions).

Anyway, it's not finished yet (still getting some cases where vehicles pass right through the player) but it's getting there. Here's a video of a Player pushing a car (made the Player's mass 1000 so he could move the car):

www.youtube.com/watch?v=1gttSY29EZk
And here's one with the mass back to 100 where a Buggy is pushing the Player:
www.youtube.com/watch?v=7q01SYzyfZ4
Here's a better example. I had the runForce set way too high, which also produces resistance to being impulsed. Turned it down to Gideon levels.
www.youtube.com/watch?v=OKehcpMaYms

I'm taking a break for now, but I'll get back to this later this evening and see if I can't solve the remaining issues. If I feel especially adventurous I'll try attaching a full Rigid object to the Player -- can't be any worse than creating a Rigid object for every collision detected.

@Konrad:
I kind of feel bad for not posting this as a resource sooner, but it actually wasn't until doing a port from B2 to B3 that I decided to redo this code from the really horrible mess I'd made of it during the TGE/A days. Looking at it now, I can't believe the stuff I was trying when a simple -3 did the trick (I think back then I was still trying to get the solver to work for, say, stacking boxes, which this solution doesn't do).
#4
10/07/2010 (11:49 pm)
@Henry: Well, I'm glad you did post it now. :) But I know how it goes. My code is full of stuff I'd really like to share, but it's very hard to find the time to prepare changes and separate code from other stuff.

Once I'm able to, I'd really like to publish a resource / blog about path-finding based strategic procedural level generation in Torque 3D. And share the frame that makes it work. Then there's another simpler thing that lets users download missions from the internet on demand, which I think is nice when packed up and ready for use. Our game just entered beta (more like alpha), so who knows when I can get there. But sooner or later I will. :)

Thanks again.
#5
10/08/2010 (12:40 am)
Whoops! I forgot about that call to queueCollision and the return value. Updated the OP to correctly call queueCollision on the Vehicle case. Without that, vehicle collisions will fail to trigger script callbacks (which you might, say, be using for collision damage).

collided  = true;
            // Keep track of objects we collide with
            if (!isGhost()) 
            {
               Point3F v,r;
               ns.getOriginVector(c.point,&r);
               ns.getVelocity(r,&v);
               queueCollision(tmpShape,v - tmpShape->getVelocity());
            }

Add that right after the call to ns.resolveCollision (or just paste in the new version of the function above).
#6
10/08/2010 (3:43 pm)
wow, nice to see someone advancing and expanding the rigid class a bit,
good on you Henry, do more
#7
10/09/2010 (8:25 am)
Great work Henry!
Faking a rigid to rigid colision is not a bad idea.This can be tricky because the player does not have an anglular velocity but only linear velocity.
#8
10/10/2010 (9:37 am)
((Long-form discussion of Rigid solvers moved to Blog, see post below))
#9
10/10/2010 (9:38 am)
((Long-form discussion of Rigid solvers moved to blog, see link below))
#10
10/11/2010 (6:16 am)
***EDIT: I just posted another huge thing as a triple-post (which I was about to upgrade to a quad-post because of the reply limits) when I realized I really should move this to a blog post instead of spamming the forums.

www.torquepowered.com/community/blogs/view/20395

Can't delete this one but if you're interested in this Rigid stuff click over to my blog page for updates (turns out I was totally missing the cause of the bad collisions). I won't bump this thread again until I finalize the changes into a mini-resource including the Rigid/Player thing.
#11
12/18/2010 (4:38 pm)
never mind, sillyness.
#12
01/06/2011 (10:29 pm)
As is my MO I started talking about something and then moved on once I got it working to my satisfaction. I had intended to clean up the code a bit and maybe tweak it more, but apparently if I wait for that to be done it could be next year (again) before I post anything.

Vehicle/Vehicle and Vehicle/Player collision resource:
www.torquepowered.com/community/resources/view/20727