[bleeding] Fixing timestep & Vehicles Jittering [SOLVED]

luis

03-03-2008 11:16:55

Reference:
http://www.ogre3d.org/phpBB2addons/view ... ccumulator
http://www.ogre3d.org/phpBB2addons/view ... light=lava
http://www.gaffer.org/game-physics/fix-your-timestep

After playing TileRacer with variable FPS and not noticing any jittering in vehicles I began to belive in Gaffer's method (which is really just the 'standard' lineal interpolation). I was several months trying to find out where was the bug in Betajaen implementation. I pulled my hairs with no results... so I take a rest and finish my terrain editor v0.1 (download ThunderWheels 0.3+ now!!!)
It i cleared my mind and i began to hunt the bug again. At first saw NxOgre's code looks correct, except for the time in wich the 'last position' is beeing saved. That is the bug.

Conclusion, to have a good interpolation you need these three things:
- Save the last position ONLY when a simulation step is done, not every frame (current implementation).
- use a timer resolution with microseconds or something with an order of magnitude less than a millisecond or the error will be to much.
- update SceneNode's children right before updating its position/orientation

The patch fixes the jittering bug for NxOgre 1.0-18 in vehicle's chassis and wheels, now the wheels's position are calculated relative to chassis scenenode.

usage instructions:
- create the scene with accumulator scene controller
- create a body (the chassis) using linear interpolation option
- create the wheels using LOD high.

Here is the patch:
http://www.g-boot.com/downloads/thunder ... ring.patch

The patch is a bit dirty but I wanted to release it quickly, i see basically two problems with it (here is when Betajaen enters in scene).
1- Some of the code depends directly on Ogre, altough Ogre::Timer is really easy to copy-paste and port it directly into nxogre. And there is more Ogre dependent code but i think it could be easly solved.
2- Inside simulation loop savelastpose is beeing called no matter if the actor needs it or has linear interpolation ON. It should not affect performance for a small amount of objects, but it isn't right. A solution could be save last state inside Body class when linear interpolation is on to a 'temp' Pose then if a simulation step is done (scenecontroller::simulated returns true) use the last frame 'temp' Pose, it will have the right Pose since using getGlobalPose() will return the NEW pose intead the previous one.

Please if onyone has time, test it. I know i'm not the only one using vehicle physics here :)

betajaen

03-03-2008 11:43:20

Alright. I'll put it in. I have some notes and concerns, indicated in this infamous point list, below:

- The OgreTimer should be replaced by my own Timer, using the TimeController as a source of the time.

- I don't understand why you have a getGlobalPose method in NodeRenderable, where getPose would do the same thing; all poses are in the global frame in NxOgre/PhysX - Unless your wheels are scenenodes of a parent scenenode (chassis), but we can use enum's for that.

- In NxOgreShapeWheel your using a reintrepret_cast into a body. Your assuming the user is using a body class. There is a better way to get the nodeRenderable - the NxUserData in the Actor, has a pointer to the renderable and it doesn't require a casting.


I'll put in some of the patch in now, some bits will have to come later - the wheels for one; my copy doesn't have a wheel shape at the moment.

luis

03-03-2008 12:11:38

I really don't know too much about the internal structure of NxOgre, specially since 1.0 version, so you'll do a better job than me, you're the master here ;)

The OgreTimer should be replaced by my own Timer, using the TimeController as a source of the time.
Yes, Ogre::Timer has to be replaced... i didnt see the class TimeController, i think it is in 1.0-19 i'm using 18 version...
The only *must* is the microsecond precision and double type, specially in the time accumulator (float has an awful error propagation).

- I don't understand why you have a getGlobalPose method in NodeRenderable, where getPose would do the same thing; all poses are in the global frame in NxOgre/PhysX - Unless your wheels are scenenodes of a parent scenenode (chassis), but we can use enum's for that.

You're right, OgreNodeRenderable::getPose will return world/global Pose if the wheel is not a child. But, since NxOgre lets you use you own scenenodes in the wheels (i belive) and you dont know what is doing the the nxogre user....
I'm fine if you want to remove it.

- In NxOgreShapeWheel your using a reintrepret_cast into a body. Your assuming the user is using a body class. There is a better way to get the nodeRenderable - the NxUserData in the Actor, has a pointer to the renderable and it doesn't require a casting.
great, much better then! I hate that reinterpret_cast....

syedhs

03-03-2008 16:47:03

Just a thought: interpolation can be bettered by having two history+current position+orientation (so that make it 3 data). That way, you can do spline interpolation (cubic or whatever) - so it *should* result is smoother interpolation. You cannot form a spline from two data.

I say *should* because I have not done it myself, just a thought. :wink:

betajaen

03-03-2008 17:01:09

I'll make the changes as the patch, and you two can squabble about the interpolation accuracy.

I would prefer it, if the end-user would have the option of choosing though.

luis

03-03-2008 20:04:47

Sure, spline interpolation would be probably the best thing....

I was so confused with the jittering and i couldn't finding the bug in a while.... I doubt about everything, the order of execution, the implementation etc... it was really frustrating. Now that we have an interpolation method working, adding more methods is trivial ;)

Anyway from my experience with linear interpolation the artifacts appears when FPS < 40 and the worst thing is the flickering when you change the orientation really fast.
Perhaps there is a better way to interpolate orientation than slerp....

Edit: continuity between curve segments seems to be CPU expensive:
http://www.theory.org/software/qfa/writeup/node12.html
perhaps i'm seeing the flickering amplified by the position interpolation (i hope!)

syedhs

04-03-2008 19:05:33

Okay maybe it is late, but I think one of the reason of jittering vehicle is because the physics calculation is done in one frame listener, but rendering is in another frame listener. Because given any time, there is no guarantee that physic listener is always executed prior to rendering listener (or vice versa). Maybe before, phsyics listener comes first followed by rendering listener, but the next is no guarantee of execution order. (Well I didn't perform any test to validate my thought anyway).

betajaen

04-03-2008 19:18:51

But the framelisteners are stored in a stl::map or vector, the iterator goes through them in the order added first, it's not random is it?

luis

04-03-2008 19:22:22

I though that also.... but it isn't the case.

I checked everything printing the Ogre's frame number and printing positions in the poor's man debugger (the DOS console). And it is right.

The problem is that we are not really doing interpolation, we are doing extrapolation so there is a frame latency because of this.

Edit: also i'm manually calling in my own framelistener the update methods in nxogre... and also updating the scenenode's children (read Lava post).

syedhs

04-03-2008 19:31:25

But the framelisteners are stored in a stl::map or vector, the iterator goes through them in the order added first, it's not random is it?

Framelisteners are stored in std::set.

luis

04-03-2008 20:16:36

I've been playing with cubic interpolation:

inline Pose NxInterpolate(Pose First, Pose Second, NxReal c) {
Pose r;
//Cubic interpolation
//k(u) = k(0)×(2u3-3u2+1) + k(1)×(3u2-2u3)
const NxReal u2 = c*c, u3 = c*c*c;
r.v = First.v * ( 2 * u3 - 3 * u2 + 1 ) + Second.v * ( 3 * u2 - 2 * u3 );
..
..



But i dont see any difference at low fps... <30.
If i change the speed very fast over a plain surface (to avoid changing orientation) linear interpolation seems to be.... just a bit worse but i'm not sure at all.
But when you drive over hills the worst artifact is the flickering in the orientation.... :(

Any way it is more than acceptable, with just 33fps i can play well, and it is the minimum frame rate you should have in any modern game if you want a good 'experience'...

Toby

01-05-2008 21:38:56

Hello,

I use now bleeding and my wheel car jitter. I do not use NxOgre::Body but my own class inherit of Actor and RenderableSource.

I set my Scene like this

mWorld = new NxOgre::World("time-controller: ogre , log : yes");

mScene = mWorld->createScene("Main", "renderer: ogre, controller: accumulator, gravity: yes, floor: yes");



My Vehicle manager create like this one vehicle:

mVehicles.push_back(new Vehicle( mVehicleName+".chassis."+StringConverter::toString(0),
mScene,
new NxOgre::Cube(cubeshapeX,cubeshapeY,cubeshapeZ),
Ogre::Vector3(-160,4,160),
"model: "+mVehicleName+".chassis.mesh, mode: interpolate", "mass:"+mass+", density:"+density+"",
mVehicleName,
vehiclesGroup,
Vehicle::CONTROLANDFOLLOW,
0));
mVehicles.back()->createVehicle( );


and in createVehicle I set up wheelset with four wheel.

mWheelSet = WheelSet::createFourWheelSet(
NxReal(atof((setupfile.getSetting("radius")).c_str())),
NxOgre::float3(atof((setupfile.getSetting("LeftFrontWheelPosition.X")).c_str()),
atof((setupfile.getSetting("LeftFrontWheelPosition.Y")).c_str()),
atof((setupfile.getSetting("LeftFrontWheelPosition.Z")).c_str())),
NxOgre::float3(atof((setupfile.getSetting("RightBackWheelPosition.X")).c_str()),
atof((setupfile.getSetting("RightBackWheelPosition.Y")).c_str()),
atof((setupfile.getSetting("RightBackWheelPosition.Z")).c_str())),
this,
wp,
"model: "+mName+".wheel.l.mesh, mode: interpolate",
"model: "+mName+".wheel.r.mesh, mode: interpolate");

// Set the wheels as front wheel drive.
mWheelSet->setAs(WheelSet::TL_Front);




In order to simulate motor like in 0.9 I implement my own Motor class but I can not derived from CombustionEngine because it is not well implemented yet.

How to solve jittering ?

I do not set LOD high for wheel but how to do it?

ps: I noticed a bug in createFourWheelSet
I corrected backward_right.i = forward_left.i; ----> backward_right.i = -forward_left.i;

betajaen

01-05-2008 22:09:44

Apparently there is no "mode" param, I must of forgot to implement it. I've always used the render mode function directly to set interpolation.

Anyway, for some stupid reason I made mWheels protected in WheelSet. So make it a public variable, recompile NxOgre. Then insert this piece of code after your vehicle setup and apply RM_Interpolate in the body to - just to make sure.

wheel_set->get(0)->mWheel->setRenderMode(RenderableSource::RM_Interpolate);
wheel_set->get(1)->mWheel->setRenderMode(RenderableSource::RM_Interpolate);
wheel_set->get(2)->mWheel->setRenderMode(RenderableSource::RM_Interpolate);
wheel_set->get(3)->mWheel->setRenderMode(RenderableSource::RM_Interpolate);


I'll come up with something better than that in the future.

Toby

02-05-2008 09:46:28

mWheels not needed to be public. We can acces Wheel class by:

mWheelSet->get(j)->setRenderMode(RenderableSource::RM_Interpolate);


But jittering is always present.

luis

04-05-2008 15:39:17

Hi Toby,

But jittering is always present.

I'm using 1.0-18 plus my patch and i think you're using 1.0-21, see my posts here:
http://www.ogre3d.org/phpBB2addons/viewtopic.php?t=6891

Toby

05-05-2008 10:00:59

Ok now i understand why jitter appear. Thanks.