Object falls a third of its height through the ground

Post Reply
justjayel
Posts: 9
Joined: Sat Aug 28, 2021 4:08 pm

Object falls a third of its height through the ground

Post by justjayel »

Hiya,

I have set up a very simple demo using Bullet where an object moves along the ground using linear velocity which is constantly set to the same value over 200 frames and then the object falls through the ground but only up to a third of its height. E.g.: If I have a RidgidBody with a box collision of size x: 2, y: 8, z: 2 (half extents x: 1, y: 4, z: 1) after 200 or so frames the object falls from position 4 to 1 (ground's top is at zero).


I have set it up with the following:
  • Time step and fixed time step set to 0.0167f (so 60hz)
  • Added a ground with box collision, zero mass.
  • The dynamic object has a box collision.
I also get a similar issue when a dynamic object is falling with the collision half extents of x: 0.2, y: 1, z: 0.2

Any ideas why this is happening?


My code is all wrapped up with custom code, so I have pulled out all the Bullet Physics specific code and placed it below:

Code: Select all

float timeStep = 0.0167f;
btDiscreteDynamicsWorld* dynamicsWorld = // Standard set up of btDiscreteDynamicsWorld
dynamicsWorld->setGravity(btVector3(0, -5, 0));
dynamicsWorld->getSimulationIslandManager()->setSplitIslands(false);
{
	btCollisionShape* groundShape = new btBoxShape(btVector3(40, 5, 40));
	btVector3 localInertia(0, 0, 0);
	
	btTransform initialTransform;
	initialTransform.setIdentity();
	initialTransform.setOrigin(btVector3(0, -5, 0));
	
	btDefaultMotionState* myMotionState = new btDefaultMotionState(initialTransform);
	btRigidBody::btRigidBodyConstructionInfo rbInfo(0, myMotionState, groundShape, localInertia);
	
	btRigidBody* rb = new btRigidBody(rbInfo);
	rb->setActivationState(DISABLE_DEACTIVATION);
	rb->setSleepingThresholds(0,0);
	dynamicsWorld->addRigidBody(rb, 1, 1);
}

{
	float mass = 1;
	btCollisionShape* playerShape = new btBoxShape(btVector3(1, 4, 1));
	btVector3 localInertia(0, 0, 0);
	playerShape ->calculateLocalInertia(mass, localInertia);
	
	btTransform initialTransform;
	initialTransform.setIdentity();
	initialTransform.setOrigin(btVector3(0, 4, 0));
	
	btDefaultMotionState* myMotionState = new btDefaultMotionState(initialTransform);
	btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, myMotionState, playerShape, localInertia);
	
	btRigidBody* rb = new btRigidBody(rbInfo);
	rb->setActivationState(DISABLE_DEACTIVATION);
	rb->setSleepingThresholds(0,0);
	dynamicsWorld->addRigidBody(rb, 1, 1);
}

for (int i = 0; i < 1000; i++)
{
	dynamicsWorld->stepSimulation(timeStep , 10, timeStep);
	
	btRigidBody* playerRb = //get the player object. 
	
	// Simulate a constant velocity
	if(i > 50 && i < 200)
	{
		btVector3 newVelocity = btVector3(2.0f, 0, 0);
		playerRb->setLinearVelocity(newVelocity);
	}
}
ed_welch
Posts: 33
Joined: Wed Mar 04, 2015 6:16 pm

Re: Object falls a third of its height through the ground

Post by ed_welch »

Try setting a small amount of margin on both objects:
pCollisionShape->setMargin(0.05f);
justjayel
Posts: 9
Joined: Sat Aug 28, 2021 4:08 pm

Re: Object falls a third of its height through the ground

Post by justjayel »

I've tried setting the margin (at 0.05f and higher) after creating the collision shape and before adding it into the btDiscreteDynamicsWorld and it doesn't work.

Code: Select all

btCollisionShape* colliderShape = new btBoxShape(btVector3(inputs[0], inputs[1], inputs[2]));
colliderShape->setMargin(0.5f);

btRigidBody* rb = // create the rigid body
dynamicsWorld->addRigidBody(rb, 1, 1);

For the main issue it doesn't occur if I am setting the linear velocity constantly for less than 60 frames. But I don't think that a viable workaround.

Code: Select all

if(i > 50 && i < 110)
{
	btVector3 newVelocity = btVector3(2.0f, 0, 0);
	playerRb->setLinearVelocity(newVelocity);
}
User avatar
drleviathan
Posts: 756
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Object falls a third of its height through the ground

Post by drleviathan »

Slamming the linear velocity of a dynamic object to an arbitrary value is a Bad Idea in general because it could override velocities from collisions that would otherwise help resolve interpenetration.

One way to avoid breaking the simulation is to use applyForce() instead of setLinearVelocity() because the force will be accumulated until the substep and then introduced during the constraint solver step, which allows collisions to negate forces that would violate their constraint parameters. However forces are difficult to tune and compute for obtaining the effect you want which is why I usually do it a different way.

Blend toward the target_velocity every substep like so:

Code: Select all

// blend_timescale is the exponential timescale time it takes for the object to change its velocity
// toward the target.  After one period the object will decrease the delta to 1/e of what it was.
// The shorter the timescale the faster it adjusts, for very long timescales the
// object takes forever to respond.  It cannot be shorter than the substep_period (else instability).
// For best results it should be not less than two or three substep_periods, but typically for human
// interaction timescales you can allow the period to be something closer to half a second.
// This is your easy one-knob tuning adjustment to make the object's responsiveness just right.
btScalar blend_timescale = 2.0; // seconds

// blend
btScalar del = substep_period / blend_timescale;
btVector3 new_velocity = (1.0 - del) * current_velocity + del * target_velocity;

// slam
body->setLinearVelocity(new_velocity);
If you set the blend_timescale to a reasonable value then you give the collision constraints time to successfully push the object out of penetration.

Note, this method is mass agnostic.
justjayel
Posts: 9
Joined: Sat Aug 28, 2021 4:08 pm

Re: Object falls a third of its height through the ground

Post by justjayel »

Hi drleviathan,
Can substep_period and blend_timescale be any static values within your description? I'll try this a bit later when I have more time.


I quickly checked my issue again on the HelloWorld demo and it seems like the issue is somewhat related to the ratio of the collision shape; seems like tall slim objects will fall through the ground and stay there. Ratios less than a 0.3 between X and Y seems to run into this issue. E.g.:
  • X: 1, Y: 4 - fails
  • X: 1.2, Y: 4 - works
User avatar
drleviathan
Posts: 756
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Object falls a third of its height through the ground

Post by drleviathan »

For stability your substep_period should be fixed. I believe the default behavior in Bullet is fixed step_period = 1/60 sec with 1 substep per step.

Yes, for most applications the blend_timescale would be a constant. You would tweak it to get the responsiveness you want and then nail it down as a compile time constant.

I wonder if the X/Y ratio threshold is actually caused by the tall box starting to tip over because of friction at the contacts. For curiosity's sake maybe try setting all frictions (floor and dynamic object) to be zero to see if the the transition between behaviors goes away.
justjayel
Posts: 9
Joined: Sat Aug 28, 2021 4:08 pm

Re: Object falls a third of its height through the ground

Post by justjayel »

Hi drleviathan,

Seems you were right, the setFriction() method worked! Stops my character object from sinking. Thanks!!

The gradual incremental velocity I don't think helped, but its great for simulating a bit of realism, I will definitely look into using it!

Thanks again!
User avatar
drleviathan
Posts: 756
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Object falls a third of its height through the ground

Post by drleviathan »

If you're trying to make a tall think character always stand up maybe you should use the "zero inverse inertia tensor diagonal" trick. This would allow you to use whatever friction you want. In other words, if your character should only ever rotate about its local vertical Y-axis, then you can set the X- and Z-components of its local inverse inertia tensor diagonal to zero like so:

Code: Select all

btVector3 inv_inertia = body->getInvInertiaDiagLocal();
inv_inertia.setX(0.0f);
inv_inertia.setZ(0.0f);
body->setInvInertiaDiagLocal(inv_inertia);
Effectively this is like setting its local angular mass to infinity on those two axes --> torques will never be able to budge it except around its one pivot axis.
justjayel
Posts: 9
Joined: Sat Aug 28, 2021 4:08 pm

Re: Object falls a third of its height through the ground

Post by justjayel »

drleviathan wrote: Wed Sep 01, 2021 3:06 pm If you're trying to make a tall think character always stand up maybe you should use the "zero inverse inertia tensor diagonal" trick. This would allow you to use whatever friction you want. In other words, if your character should only ever rotate about its local vertical Y-axis, then you can set the X- and Z-components of its local inverse inertia tensor diagonal to zero like so:

Code: Select all

btVector3 inv_inertia = body->getInvInertiaDiagLocal();
inv_inertia.setX(0.0f);
inv_inertia.setZ(0.0f);
body->setInvInertiaDiagLocal(inv_inertia);
Effectively this is like setting its local angular mass to infinity on those two axes --> torques will never be able to budge it except around its one pivot axis.
Your solution worked.

Now if I set the friction on both objects to non-zero and apply a little velocity to the player in either X or Z direction it spins slowly in a circle around the Y axis. Would anyone know why?

On a different note, is there anywhere with a bit more in depth tutorials or documentation? Is it just the examples?


EDIT:
I fixed the spinning by setting the inverse inertia on the Y axis to zero as well. Not sure if that is the correct way. But I guess that is fine for a character rather than an actual object?

Code: Select all

inv_inertia.setY(0.0f);
User avatar
drleviathan
Posts: 756
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Object falls a third of its height through the ground

Post by drleviathan »

Setting the inverse inertia diagonal to zeros is fine: it produces an object that will never ever rotate from external forces, which is often what you want for a character. With dynamic characters I typically push them around with velocity adjustments, allow them to rotate about the vertical axis, but slam their rotation and zero their angular velocity every step, which is effectively the same thing. Another way to put it is: my characters are dynamic in the linear dimensions but kinematic in the angular.
Post Reply