btHeightfieldTerrainShape going nuts

Post Reply
thegeneralsolution
Posts: 16
Joined: Sun Jul 05, 2015 12:55 am

btHeightfieldTerrainShape going nuts

Post by thegeneralsolution »

I had my code to initialize a btHeightfieldTerrainShape working just fine some time ago. I think I may have upgraded my version of Bullet since then (now 2.83.7), and that may have broken things.

Since my objects did not appear to be colliding with the terrain properly, I initialize all the values in the heightfield to a constant, to just produce a flat terrain. I made sure the constant value is well within the min and max height values, and the width and height of the array of values passed in is 2^n + 1 (65 in my case).

I have a set of spheres that fall onto the flat terrain. They stop at the appropriate height, but they roll around in random directions, as if something is generating a lateral force on them. They don't settle to a stop, and usually end up rolling off the edge of the terrain.

I wondered if I had somehow turned off the angular damping, or friction on the spheres, but replacing the terrain with a large flat cube, the spheres slow to a stop correctly.

Here is my heightfield initialization code:

Code: Select all

	void vTypeStaticCollisionShape::initializeAsHeightfield(vNode * node, btTransform&offset)
	{
		this->m_transform = node->currentTransform;

		btHeightfieldTerrainShape * heightfield= 0;

		int width=0,height=0;
		float * pixels = getHeightmapData(node,width, height);

		float minX=0,maxX=0,minY=0,maxY=0,minZ=0,maxZ=0;

		::getHeightfieldMinMaxValues(node,minX,maxX,minY,maxY,minZ,maxZ);

		offset.setIdentity();
		offset.setOrigin(btVector3(0.5*(minX+maxX), 0.5*(minY+maxY),0.5*(minZ+maxZ)));

		int pixelCount = width*height;

                //for debugging, we ignore the image data and just use a constant value to produce a flat terrain
		for (int i = 0; i < pixelCount; i++)
			pixels[i] = (btScalar)0.1;// (btScalar)interp(0, minY + 0.001, 1, maxY - 0.001, pixels[i]);

		heightfield = new 	 btHeightfieldTerrainShape(width,height,
					  pixels,
					  1,
					  minY, maxY,
					  1, PHY_FLOAT,false);

		heightfield->setLocalScaling( btVector3((maxX-minX)/width,1, (maxZ-minZ)/height));
		
		this->m_collisionShape = heightfield;
	}
Any ideas for what might be going on would be very much appreciated!
thegeneralsolution
Posts: 16
Joined: Sun Jul 05, 2015 12:55 am

Re: btHeightfieldTerrainShape going nuts

Post by thegeneralsolution »

I wonder if this is related to this issue...

https://github.com/bulletphysics/bullet3/issues/57

Its a little frustrating because the link above cites issues with the terrain shape in Bullet3, but I cannot find the terrain demo in any of the bullet3 releases, for me to confirm which version is bug free, or have some working example code to compare to.
DannyChapman
Posts: 84
Joined: Sun Jan 07, 2007 4:29 pm
Location: Oxford, England
Contact:

Re: btHeightfieldTerrainShape going nuts

Post by DannyChapman »

Could they be hitting the internal edges? This can happen even if the terrain is completely flat, due to the way that the collision checks are done against the individual triangles.

This started causing big problems for me, after putting up with it for a long time. You can see the problem here: https://youtu.be/UfJQSoq3xqo - the glider (convex mesh) "bumps" as it gets pulled along the ground (heightfield). It turned into a big problem for other smaller gliders, as the bump would cause the tow rope to break (when a max force is exceeded).

In the end I put in a huge hack to just force the contact normal to be that of the triangle associated with the heightfield. Perhaps this causes problems in some situations... but I haven't noticed any for my uses, and it completely solves this internal edge problem. I didn't have time/inclination to fix it properly I'm afraid! I can post the code later, if you think it might be relevant.

If you search for something like "bullet internal mesh edge" you'll find info and solutions for avoiding this with conventional meshes, but it wasn't immediately obvious to me how to translate this to a heightfield. I find it a bit surprising that the heightfield collision goes through this rather generic triangle-triangle/GJK collision route (surely the knowledge that it's a heightfield can be used to provide a big speedup).

Of course, this may be irrelevant :) Also I see with the benefit of hackery-hindsight that this could be done better using something like http://www.bulletphysics.org/Bullet/php ... 052#p12060
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland
Contact:

Re: btHeightfieldTerrainShape going nuts

Post by benelot »

The old terrain demo is here, in the old_demos branch:
https://github.com/bulletphysics/bullet ... errainDemo

It is still in the old format, so you might have to port it to the bullet example browser to test it quickly. If you port it, could you post the code here so that we can contribute it to the bullet example browser? Of course you can also add the pull request by yourself.
gjaegy
Posts: 178
Joined: Fri Apr 18, 2008 2:20 pm

Re: btHeightfieldTerrainShape going nuts

Post by gjaegy »

Hi Danny,

some time later :) May I ask you, how have you managed to modify the contact point normal manually ? I couldn't find how to achieve that...
DannyChapman
Posts: 84
Joined: Sun Jan 07, 2007 4:29 pm
Location: Oxford, England
Contact:

Re: btHeightfieldTerrainShape going nuts

Post by DannyChapman »

It's odd looking at this old code again!

You can set the contact added callback from btManifoldResult.cpp:

Code: Select all

ContactAddedCallback		gContactAddedCallback=0;
I set mine to this:

Code: Select all

//----------------------------------------------------------------------------------------------------------------------
bool ContactAdded(
  btManifoldPoint& cp, 
  const btCollisionObjectWrapper* colObj0, int partId0, int index0, 
  const btCollisionObjectWrapper* colObj1, int partId1, int index1)
{
  if (colObj0->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE)
  {
    btVector3 tempNorm;
    ((btTriangleShape*)colObj0->getCollisionShape())->calcNormal(tempNorm);
    float dot = tempNorm.dot(cp.m_normalWorldOnB);
    cp.m_normalWorldOnB = dot > 0.0f ? tempNorm : -tempNorm;
  }
  else if (colObj1->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE)
  {
    btVector3 tempNorm;
    ((btTriangleShape*)colObj1->getCollisionShape())->calcNormal(tempNorm);
    float dot = tempNorm.dot(cp.m_normalWorldOnB);
    cp.m_normalWorldOnB = dot > 0.0f ? tempNorm : -tempNorm;
  }
  return true;
}
I have honestly no idea if this is a good thing or not - but it worked for me!
gjaegy
Posts: 178
Joined: Fri Apr 18, 2008 2:20 pm

Re: btHeightfieldTerrainShape going nuts

Post by gjaegy »

I'll have a try, thanks !
gjaegy
Posts: 178
Joined: Fri Apr 18, 2008 2:20 pm

Re: btHeightfieldTerrainShape going nuts

Post by gjaegy »

Modifying the normal indeed improves the situation significantly. I still have some rare jittering cases, no idea why really. But it's acceptable.

Looking at the implementation of btAdjustInternalEdgeContacts(), it seems there is a little bit of additional work being done there :/
PcChip
Posts: 33
Joined: Sun May 20, 2018 3:09 pm

Re: btHeightfieldTerrainShape going nuts

Post by PcChip »

last time I had crazy jittering and wonky behavior on a heightfield, it turned out I was calling my GenerateTerrain() function twice and it was overwriting memory already allocated to bullet
Post Reply