[Git] Object jittering while moving

Druha

28-09-2009 15:27:54

I am using this commit: http://github.com/betajaen/nxogre/commi ... e27bf00d4d

I noticed a jittering problem similar to discribed in this thread - viewtopic.php?f=6&t=8311

I've already tried to change Time Stepping method - still same result.
Here is my Scene creation code:

NxOgre::SceneDescription sceneDesc;
sceneDesc.mGravity = NxOgre::Vec3(0, -9.8f, 0);
sceneDesc.mMaxTimeStep = 0.001f;
sceneDesc.mTimeStepMethod = NxOgre::Enums::TimeStepMethod_Variable;
mScene = mWorld->createScene(sceneDesc);

I move my object by adding a force to it. I also noticed that if fps drops below 60 jittering becomes less noticeable, but still exists

And here is the video showing how it looks like at fps around 100 (Hope the server will be alive :wink: )
ftp://eternal-glory.com/jittering.avi
Any chance to remove this at all?

betajaen

28-09-2009 15:38:04

Urgh, that is quite bad.

I haven't ported the interpolation code from Bleeding over yet. It's easy enough to do, it's just other things have gotten in my way. I could put it in - but I'm working on the detritus code at the moment which is more pressing. If you want to put it in, I can right some steps down teaching you how to put it in.

[Edit]

Actually. I'm more or less working on the OGRE3DBody right now in the Detritus branch (NxOgre 1.6.0). I could put the interpolation code in there, and you could port the code over for BloodyMess for me.

Druha

28-09-2009 15:46:49

Hmm... I am not sure if I can port it correctly, but i can try :wink: At least it will be better than sit and wait for solution. And it may be a good programming practice for me

betajaen

28-09-2009 15:48:21

Great!

Once I've done the commit, I'll alert you in this thread, and point out the lines. Literally, it's 10 lines of code. But I don't have a "cake" to test it - All my Cakes are using the Detritus branch.

betajaen

28-09-2009 23:44:24

Okay here we go. This is the Detritus code, so you can't use it yet. So you'll have to implement it yourself.

http://github.com/betajaen/nxogre/commi ... 1c711de31d

Before your jaw drops. It's a lot easier than it looks.

In NxOgreMath.h add these two functions:

inline Quat nlerp(float alpha, Quat& a, Quat& b, bool shortest)
{
Quat out, t;
float cosine = a.dot(b);
out = a;

if (cosine < 0.0f && shortest)
t = -b;
else
t = b;

t -= a;
t *= alpha;
out += t;

out.normalise();

return out;
}

inline void interpolate(Matrix44& a, Matrix44& b, Matrix44& out, float alpha)
{
out.scaleIdentity();

// Interpolate translation.
Vec3 trans_a = a;
Vec3 trans_b = b;
out.set( alpha * (trans_b - trans_a) + trans_a);

// Interpolate orientation.

Quat orient_a = a;
Quat orient_b = b;
out.set( nlerp(alpha, orient_a, orient_b, true));

}


In OGRE3DBody.cpp

Change the advance function to this
bool OGRE3DBody::advance(float step, const NxOgre::Enums::Priority&)
{
#if 1

NxOgre::TimeStep& ts = mScene->getTimeStep();
NxOgre::Matrix44 current_pose(getGlobalPose());

if (mScene->getTimeStep().mSubSteps) // Did simulate this frame?
mAlphaPose = current_pose;

NxOgre::Matrix44 render_pose;
NxOgre::Math::interpolate(mAlphaPose, current_pose, render_pose, ts.mAlpha);

mNode->setPosition(NxOgre::Vec3(render_pose).as<Ogre::Vector3>());
mNode->setOrientation(NxOgre::Quat(render_pose).as<Ogre::Quaternion>());

#else
mNode->setPosition(NxOgre::Vec3(getGlobalPose()).as<Ogre::Vector3>());
mNode->setOrientation(NxOgre::Quat(getGlobalPose()).as<Ogre::Quaternion>());
#endif
return true;
}



Then finally add to the member variable list in OGRE3DBody this;

NxOgre::Matrix44 mAlphaPose;

Everything else is related to something else. I briefly tested it out and nothing exploded, so it should start interpolating straight away, so things may be a bit smoother. It's based upon Luis's code which I designed around the wheel system and he used it as a metric for testing rather than Bodies. So it may work with low frame rates - it may not. Only way to try is try it out and see!

Good luck!

Druha

29-09-2009 16:00:41

After I finaly compiled NxOgre with these changes I ran may app and made a face like that - :( . Seems like nothing changed.
I tried to turn vsync on, and voila, it works! It became a lot smoother, but only with vsync. And sometimes it become insane and start jumping from side to side.
What can I tweak to make it more smoth?

betajaen

29-09-2009 16:06:01

Bah!

What about if you don't alter the maxTimestep or timestepmethod?

Druha

29-09-2009 16:19:32

When I do not alter maxTimeStep and timeStepMethod I can't see something really was changed when I use vsync. Most of the time it run smooth but suddenly it start jittering and after 2-3 seconds become stable again. In that time fps keeps at max 60fps, so it looks like not the timing problem. And without vsync it looks good only with fps above ~200.
I found a property called mMaxSubSteps in SceneDescription. Is it somehow related to my problem?

betajaen

29-09-2009 16:35:10

No, I'm afraid not.

I have a few ideas; but it's a little complicated to explain. One involves rendering the previous frame, whilst PhysX calculates the next one - so it's one frame behind.

I'll have a think, and try some code out - see if I can come up with something.


[Edit]

Your using Bodies right? Not KinematicBodies?

Druha

29-09-2009 16:39:58

Yes, I use only Bodies right now, cause this box intended to be a Vehicle which must collide with surrounding reality

betajaen

29-09-2009 19:53:26

I've been experimenting with a-sync fixed timing and relying on the internal PhysX accumulator instead. The simulation is a lot more stable. A stack of 30 odd boxes don't fall over now as they did once before.

Problem is; I don't know or not if PhysX is treating every 1.0/6.0f as gospel or ignoring some frames when it's done enough in that second; i.e. time is slowing up or down based on frame rate. From what I can see the boxes are falling down quicker, but that it isn't exactly accurate measurement.

More testing is needed.

betajaen

29-09-2009 21:11:01

Alright. I think I may have a more stable solution.

- I use a accurate timer.
- It uses the fixed time stepping.
- The Accumulator has four or eight substeps
- It uses a maximum timestep of 1/60 then divided by the number of substeps.
- I perform a simulation, then allow NxOgre to do some other business, then a loop or two later, come back and fetch the results.
- I also calculate accumulator and substeps. PhysX doesn't give me a how much time it processed just then, so I have to work out the math myself.
- PhysX does the interpolation of RigidBodies; not NxOgre - There is no documentation supporting this. But I believe it's true.

It works pretty well in high framerates, there is no stuttering or jittering as before. One thing to note is if you don't divide the max timestep by the number of substeps - there is stuttering. I can't test very well in slower frame rates < 60, because my hardware is too fast, but it seems to be okay. Not great but acceptable.

All these changes are in the Detritus branch, so I'll back port my work in to the master Bloody Mess branch and give you a buzz when it's ready to download and test from github.

betajaen

30-09-2009 14:46:01

I've backported the new timing code into the master branch.

http://github.com/betajaen/nxogre/commi ... 50b4a4afb3

I would appreciate if you could download it (and overwriting the current NxOgre Git you have installed), and try it out with your application.

Druha

30-09-2009 19:54:03

Almost the same effect :? - Most of the time stable with vsync and awfull without it.
In fact my app became more "hardware hungry", and framerate stays around 150-200. So if I look straight to the ground the simulation is very good :)

betajaen

30-09-2009 19:56:29

Ack.

How are you moving the body? Can you post some code; the setup bits, and the moving bits?

Druha

30-09-2009 20:04:20

Well here is the all related code:
Creation of the body:

mBody = GameRoot::getSingleton().getPhysRender()->createBody(new NxOgre::Box(vi.physInfo.float_box), pos, vi.meshInfo.meshName);
mBody->setAngularDamping(vi.physInfo.angDamping);
mBody->setLinearDamping(vi.physInfo.linDamping);
mBody->setMass(vi.physInfo.mass);

Moving code:

if(mJetEnabled){
for(unsigned int i=0;i<4;++i){
NxOgre::Vec3 globalPose = pose * mJets[i];

NxOgre::RaycastHit hit;
hit = GameRoot::getSingleton().getScene()->raycastClosestShape(NxOgre::Ray(globalPose,NxOgre::Vec3(0,-1,0)),NxOgre::Enums::ShapesType_Static,1,40);
if(hit.mShape){
NxOgre::Real force = -9.8 * mPhysInfo.mass * time * mPhysInfo.jetPower / ((hit.mDistance > 0.01f) ? hit.mDistance : 0.1f) ;
mBody->addForceAtLocalPos(force*NxOgre::Vec3(0,-1,0),mJets[i]);
}
}
NxOgre::Real tiltAngle = NxOgre::Math::arccos(v_down.dotProduct(Vector3::NEGATIVE_UNIT_Y));
NxOgre::Real sign = v_right.dotProduct(Vector3::NEGATIVE_UNIT_Y);
if (sign > 0.0f)
tiltAngle = -tiltAngle;
mBody->addLocalTorque(NxOgre::Vec3(time * tiltAngle * 600.0f, 0.0f, 0.0f));
}
if(flags & VM_FORWARD)
mBody->addLocalForce(NxOgre::Vec3(0,0,-mPhysInfo.boost_front*time*mPhysInfo.mass));
if(flags & VM_BACKWARD)
mBody->addLocalForce(NxOgre::Vec3(0,0,mPhysInfo.boost_rear*time*mPhysInfo.mass));
if(flags & VM_STRAFELEFT)
mBody->addLocalForce(NxOgre::Vec3(-mPhysInfo.boost_side*time*mPhysInfo.mass,0,0));
if(flags & VM_STRAFERIGHT)
mBody->addLocalForce(NxOgre::Vec3(mPhysInfo.boost_side*time*mPhysInfo.mass,0,0));
if(flags & VM_ROTLEFT)
mBody->addLocalTorque(NxOgre::Vec3(0,mPhysInfo.rot_speed*time,0));
if(flags & VM_ROTRIGHT)
mBody->addLocalTorque(NxOgre::Vec3(0,-mPhysInfo.rot_speed*time,0));

betajaen

30-09-2009 22:11:47

This is my code, based off yours (as much as I could understand and do):

#include "Cake.h"
#include "CakeOGRE.h"

using namespace NxOgre;

OGRE3DBody* mBody = 0;
float height = 10;
bool jets_on;
float boost_front = 100000;
float boost_rear = 100000;
float boost_side = 100000;
float timec = 1.0/60.0f;
float rot_speed = 1E6;


void CakeOGRE::createScene()
{

Material* default_material = mScene->getMaterial(0);
default_material->setStaticFriction(0.5f);
default_material->setDynamicFriction(0.5f);

OGRE3DRigidBodyDescription jet_desc;
jet_desc.mLinearDamping = 0.5f;
jet_desc.mAngularDamping = 0.5f;
jet_desc.mMass = 100.0f;

mBody = mRenderSystem->createBody(new Box(2,1,2), Vec3(0, 2, 0), "cube.1m.mesh", jet_desc);
mBody->getSceneNode()->setScale(2,1,2);
jets_on = false;


Ogre::SceneNode* fake_node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(0,0,0), Ogre::Quaternion(Ogre::Degree(20), Ogre::Vector3(1, 0, 0)));
fake_node->attachObject(mSceneMgr->createEntity("ground1", "cube.1m.mesh"));
fake_node->setScale(20, 2, 20);

mScene->createSceneGeometry(new Box(20,2,20), Matrix44(Vec3(0,0,0), Quat(fake_node->getOrientation())));

}

void CakeOGRE::destroyScene()
{
}

void CakeOGRE::onFrame(float deltaTime)
{

//for (unsigned int i=0;i < 1E7;i++);

if (jets_on)
{

Vec3 globalPose = mBody->getGlobalPosition();

NxOgre::RaycastHit hit;

hit = mScene->raycastClosestShape(NxOgre::Ray(globalPose,NxOgre::Vec3(0,-1,0)), NxOgre::Enums::ShapesType_Static,1,40);

if(hit.mShape)
{

float m = mBody->getMass();

float force = (-mScene->getGravity().y * m) // -g(m)
+ -mBody->getLinearVelocity().y * m // -Vx(m)
+ m * ((hit.mWorldImpact.y + 5) - mBody->getGlobalPosition().y); // m(Ty-Py)

mBody->addForce(NxOgre::Vec3(0, force, 0)); //, NxOgre::Enums::ForceMode_Impulse);

}
}
}

void CakeOGRE::onKeyEvent(OIS::KeyCode key)
{

if (key == OIS::KC_SPACE)
{
jets_on = !jets_on;
}

if (key == OIS::KC_P)
mBody->addForce(Vec3(0, 1000, 0));

if(key == OIS::KC_I)
mBody->addLocalForce(NxOgre::Vec3(0,0,-boost_front*timec*mBody->getMass()));

if(key == OIS::KC_K)
mBody->addLocalForce(NxOgre::Vec3(0,0,boost_rear*timec*mBody->getMass()));

if(key == OIS::KC_J)
mBody->addLocalForce(NxOgre::Vec3(-boost_side*timec*mBody->getMass(),0,0));

if(key == OIS::KC_L)
mBody->addLocalForce(NxOgre::Vec3(boost_side*timec*mBody->getMass(),0,0));

if(key == OIS::KC_U)
mBody->addLocalTorque(NxOgre::Vec3(0,rot_speed*timec,0));

if(key == OIS::KC_O)
mBody->addLocalTorque(NxOgre::Vec3(0,-rot_speed*timec,0));

}

void CakeOGRE::doConfig()
{
mKeyConfig.CameraForward = OIS::KC_W;
mKeyConfig.CameraBackward = OIS::KC_S;
mKeyConfig.CameraLeft = OIS::KC_A;
mKeyConfig.CameraRight = OIS::KC_D;
mKeyConfig.CameraUp = OIS::KC_Q;
mKeyConfig.CameraDown = OIS::KC_Z;
}



I did a hack (the commented for loop in onFrame) to limit the FPS, and it seemed fine. The FPS was at 12, and it was like I was watching a slide show. I really don't think anything more can be done. When you have such a low FPS there is only a limited amount that PhysX can do in that little time; the SDK gets more unstable and wild things can happen.

You can try out my code in your app, but other than that I'm at a lost what to do.

Druha

01-10-2009 11:37:11

In fact my next problem will be adding network sync to physics between multiple clients, so i'll try to find solution there...
Any way, thank you for help :)

Edit
Wait... Is it right that there is no interpolation code in OGRE3DBody::advance() any more in last commit?

al2950

01-10-2009 18:31:52

This update has made a massive differnce to my app. (In a good way!)

I have been trying to recreate an edited version of the rope example in the physX SDK using NxOgre. Before this update the rope did not behave as expected, it had a lot of jittering, the joints seemed to pull away from each other and the rope did move around as expected, now it seems to work alsmost perfectly......

I still have some jittering at the top of the rope but i am pretty certain this is something to do with the kinematic actor it is attached to. Do you know why this might be, the kinematic actors seems to jitter alot, at any frame rate (30-80fps).

I have almost exactly the same setup as i do in the PhysX rope example and that is very steady....

Cheers

Gus

betajaen

01-10-2009 18:38:10

I'm glad to hear it works so well for you. I noticed the stability as well, with no more micro-stuttering in cake, and stacked cubes didn't fall over automatically (which they wouldn't in RL).

How are you moving the KinematicActor; moveGlobalPosition or setPosition?. moveGlobalPosition is the preferred way of doing it.

al2950

01-10-2009 19:17:20

I have tried both 'move' and 'set' it makes no difference :(

I have been stepping through your code and i have found a couple of potential issues..... actually i would not call them issues, just things i dont understand!! I will start a new thread as not really relevant to this thread

betajaen

01-10-2009 19:20:52

Go for it. :)