[SOLVED] stepSimulation() and jerky movement problem

petrocket
Posts: 4
Joined: Thu Oct 07, 2010 3:16 pm

[SOLVED] stepSimulation() and jerky movement problem

Post by petrocket »

The short version is that I have an object that is moving forward in a jerky motion instead of a smooth - frame independent way. I only have the single object in the simulation and only applyCenterImpluse(btVector3(0,0,1)) to it right after creating it and then I just step the simulation. To exaggerate the problem I made the framerate even more unpredictable so that I could see if it was just rounding errors or something more serious.

I've read and re-read the wiki information about DynamicsWorld->stepSimulation() and I'm still having problems with jerky object movement and really need some help.

I init my rigidBody like this:

Code: Select all

// create the dynamic body
mBody = new btRigidBody(btScalar(mass), motionState, mShape, localInertia);

// disable deactivation for this player object
mBody->setActivationState(DISABLE_DEACTIVATION);
I then set the body in motion using:

Code: Select all

mBody->applyCentralImpulse(btVector3(0, 0, 1));
Then when I step the simulation I do this:

Code: Select all

// deltaTime is elapsed time between ticks
mDynamicsWorld->stepSimulation((btScalar)(deltaTime * 0.001f), 10);

// sleep a random amount so our framerate is NEVER constant
// I'm doing this to exaggerate the issue as much as possible
// bullet should be able to cope with any framerate within reason
double rand = Math::Rand() / RAND_MAX;

// this sleep value max is 100 ms so the minimum fps should be 10 fps which
// our substeps of 10 should handle OK
Sleep(rand * 100.0);
When bullet steps the simulation it updates the motion state for my rigidBody whose setWorldTransform() looks like this:

Code: Select all

virtual void setWorldTransform(const btTransform& centerOfMassWorldTrans)
{
	m_graphicsWorldTrans = centerOfMassWorldTrans * m_centerOfMassOffset;
	btQuaternion rot = m_graphicsWorldTrans.getRotation();
	btVector3 pos = m_graphicsWorldTrans.getOrigin();
				
	Vector3 currentPosition(pos.x(), pos.y(), pos.z());
        
        // this time is set RIGHT before we call stepSimulation()
	double currentTime = PhysicsManagerBullet::getSingletonPtr()->getLastTickTime();
				
	if(currentTime - mLastDebugTime > 1000.0) {
		double distance = currentPosition.distance(mLastPosition);
		double elapsedTime = currentTime - mLastDebugTime;
		double speed = distance / elapsedTime;

		mLastDebugTime = currentTime;
		mLastPosition = currentPosition;

		std::cout << "Speed " << speed << " Distance " << distance << " Elapsed Time " << elapsedTime << std::endl;
	}
}
The results for speed should be constant but they are erratic/jerky. I get these type of results

Code: Select all

Speed 0.00966792 Distance 9.72162 Elapsed Time 1005.55
Speed 0.0103209 Distance 10.7008 Elapsed Time 1036.81
Speed 0.00967697 Distance 10.0042 Elapsed Time 1033.82
Speed 0.0101613 Distance 10.4589 Elapsed Time 1029.29
Speed 0.0103201 Distance 10.726 Elapsed Time 1039.33
Speed 0.00967133 Distance 9.82541 Elapsed Time 1015.93
Speed 0.00983295 Distance 9.84683 Elapsed Time 1001.41
Speed 0.0103265 Distance 10.5231 Elapsed Time 1019.04
Speed 0.0101602 Distance 10.5283 Elapsed Time 1036.22
Speed 0.00968169 Distance 10.1577 Elapsed Time 1049.16
Speed 0.0101633 Distance 10.3325 Elapsed Time 1016.65
Speed 0.0104901 Distance 10.6877 Elapsed Time 1018.84
I'm using 2.76, but will upgrade to 2.77 soon. What can I do? Is there something I'm missing?

Should I query the position of the rigidBody manually instead of using a motion state object?

Any help is MUCH appreciated!
Last edited by petrocket on Wed Oct 20, 2010 3:55 pm, edited 1 time in total.
Kanttori
Posts: 38
Joined: Thu Jul 08, 2010 12:52 am

Re: stepSimulation() and jerky movement problem

Post by Kanttori »

Are you 100% sure that the interval between currentTime's is the same as deltatime? Could be it If you run something like 10 steps to get to the 1000ms debug cout and at each step you incur an error for any reason (rounding error, the clock is set at a different time etc).
petrocket
Posts: 4
Joined: Thu Oct 07, 2010 3:16 pm

Re: stepSimulation() and jerky movement problem

Post by petrocket »

OK, I think I know what is going on. I see that Bullet is using the fixed timestep 1/60 and basis all the calculations on that. I thought that Bullet would interpolate values outside the of that fixed timestep for me. I added debug code around the stepSimulation function like this:

Code: Select all

			btVector3 oldPos,newPos;
			double subSteps = 0;
			oldPos = comp->getRigidBody()->getInterpolationWorldTransform().getOrigin();

			subSteps = mDynamicsWorld->stepSimulation((btScalar)(deltaTime * 0.001), 10);

			newPos = comp->getRigidBody()->getInterpolationWorldTransform().getOrigin();
			double distance = oldPos.distance(newPos);
			double speed = distance / deltaTime;
			double physicsSpeed = distance / (subSteps * (1./60.));
			std::cout << "speed:" << speed << " time:" << deltaTime << " dist:" << distance << " substeps:" << subSteps << " speed2:" << physicsSpeed << std::endl;
The output of this is:

Code: Select all

speed:0.000997688 time:83.5264 dist:0.0833333 substeps:5 speed2:0.999999
speed:0.000891379 time:37.3952 dist:0.0333333 substeps:2 speed2:0.999999
speed:0.000963336 time:86.5049 dist:0.0833333 substeps:5 speed2:0.999999
speed:0.000986811 time:101.336 dist:0.0999999 substeps:6 speed2:0.999999
speed:0.00106218 time:94.1461 dist:0.0999999 substeps:6 speed2:0.999999
speed:0.00104193 time:79.9794 dist:0.0833333 substeps:5 speed2:0.999999
speed:0.000912033 time:36.5483 dist:0.0333333 substeps:2 speed2:0.999999
speed:0.00101279 time:98.7374 dist:0.0999999 substeps:6 speed2:0.999999
speed:0.00076929 time:21.665 dist:0.0166667 substeps:1 speed2:0.999999
speed:0.00138812 time:12.0067 dist:0.0166667 substeps:1 speed2:0.999999
speed:0.00100415 time:82.9885 dist:0.0833333 substeps:5 speed2:0.999999
speed2 is the speed calculation based on the fixedTimestep * numSubSteps where as speed is calculated based on deltaTime
The amount of time Bullet uses in stepSimulation() does not equal the deltaTime so that explains the speed differences.
SteveSegreto
Posts: 32
Joined: Sun Sep 12, 2010 10:25 am

Re: stepSimulation() and jerky movement problem

Post by SteveSegreto »

Great experiment. Can you share a little more about what you changed to fix the problems you were having?

Did you change the parameters to stepSimulation?
petrocket
Posts: 4
Joined: Thu Oct 07, 2010 3:16 pm

Re: stepSimulation() and jerky movement problem

Post by petrocket »

Well, it's not really fixed I guess. I think I see how the rigid body ends up with the values for its worldTransform after stepSimulation() but not how the motionState for that object ends up with its worldTransform.

From looking through the Boost code it looks like this code in btDiscreteDynamicWorld.cpp does the magic for the motionState:

Code: Select all

			btTransform interpolatedTransform;
			btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(),
				body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(),m_localTime*body->getHitFraction(),interpolatedTransform);
			body->getMotionState()->setWorldTransform(interpolatedTransform);

The m_localTime variable contains the "left over time" - that is m_localTime = totalTime % fixedTimeStep

So if my currentTime is in between fixedTimeStep times then even though the rigidBody has its worldTransform based on the fixedTimeStep, the motionState should get the exact position based on the code above because it uses m_localTime.

Unfortunately, I haven't been able to debug the motionState time accurately yet and my debug output for it still indicates that the speed of the object is not constant like it should be.
petrocket
Posts: 4
Joined: Thu Oct 07, 2010 3:16 pm

Re: stepSimulation() and jerky movement problem

Post by petrocket »

OK I've figured out why my debug statements were wrong.

setWorldTransform() can be called from stepSimulation() multiple times so what was happening is when multiple substeps were run my debug code would trigger on the first call to setWorldTransform() which was just the first of several substeps to run. I moved my debug code the place where I call stepSimulation() and now it returns me the correct time. Also, I'm using the deltaTime and not the fixedTimeStep for the debug time for the motionState.

Here's what the debug code looks like now:

Code: Select all

btVector3 oldPos,newPos;
Vector3 oldPos2,newPos2;
double subSteps = 0;
// rigid body world transform
oldPos = comp->getRigidBody()->getInterpolationWorldTransform().getOrigin();
// motionState world transform
oldPos2 = comp->getPosition();
		
btScalar fixedTimeStep = 1./60.;
			
subSteps = mDynamicsWorld->stepSimulation((btScalar)(deltaTime * 0.001), 10, fixedTimeStep);

newPos = comp->getRigidBody()->getInterpolationWorldTransform().getOrigin();
double distance = oldPos.distance(newPos);
// the rigid body delta time is based on fixTimeStep
double time = (subSteps * fixedTimeStep );
double speed = distance / time;
std::cout << "speed:" << speed << " time:" << time << " dist:" << distance << " substeps:" << subSteps << std::endl;

newPos2 = comp->getPosition();
distance = oldPos2.distance(newPos2);
// the motionState time is based on our clock
double speed2 = distance / deltaTime;
std::cout << "speed2:" << speed << " time:" << deltaTime << " dist:" << distance  << std::endl;

double rand = Math::Rand() / RAND_MAX;
DWORD sleepVal = rand * 100.0f; 
Sleep(sleepVal);