terra+bullet

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Nickak2003
Goblin
Posts: 272
Joined: Thu Jun 10, 2004 4:19 am
x 26

terra+bullet

Post by Nickak2003 »

I am working to get terra aligned with a bullet heightmap. I am having some problems. Does terra do anything special with it's triangles in the shaders? Seems like triangle edges aren't correct and iIve tried the various bullet options to no avail, which makes me think it could be terra specific?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5505
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1372

Re: terra+bullet

Post by dark_sylinc »

I use this code which matches 99.99% Bullet Terrain with Terra:

Code: Select all

m_physicsSystem.setHeightMap(
	terra->getHeightMap(), terra->getWidth(), terra->getDepth(),
	terra->getXZRelativeSize(), terra->getHeight(), terra->getTerrainXZCenter(),
	terra->getTerrainOrigin().y );

btHeightfieldTerrainShape *m_terrainShape;
btRigidBody *              m_terrainBody;

/** See https://pybullet.org/Bullet/phpBB3/viewtopic.php?f=9&t=10224
	"btHeightfieldTerrainShape : item collides but falls through"

	This callback aims at helping prevent objects from falling through the terrain.
	However it is not enough as veeeeeery fast objects (or if the heightmap moves/rotates
	which we do) this callback is not enough
*/
static bool CustomMaterialCombinerCallbackForHeightmapFix(
	btManifoldPoint &cp,                          //
	const btCollisionObjectWrapper *colObj0Wrap,  //
	int partId0,                                  //
	int index0,                                   //
	const btCollisionObjectWrapper *colObj1Wrap,  //
	int partId1,                                  //
	int index1 )
{
	// one-sided triangles
	if( colObj1Wrap->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE )
	{
		const btTriangleShape *triShape =
			static_cast<const btTriangleShape *>( colObj1Wrap->getCollisionShape() );
		const btVector3 *v = triShape->m_vertices1;
		btVector3 faceNormalLs = btCross( v[1] - v[0], v[2] - v[0] );
		faceNormalLs.normalize();
		btVector3 faceNormalWs = colObj1Wrap->getWorldTransform().getBasis() * faceNormalLs;
		float nDotF = btDot( faceNormalWs, cp.m_normalWorldOnB );
		if( nDotF <= 0.0f )
		{
			// flip the contact normal to be aligned with the face normal
			cp.m_normalWorldOnB += -2.0f * nDotF * faceNormalWs;
		}
	}

	// this return value is currently ignored, but to be on the safe side: return false if you don't
	// calculate friction
	return false;
}

void PhysicsSystem::setHeightMap( const std::vector<float> &heightMap, uint32_t width,
								  uint32_t depth, const Ogre::Vector2 &xzRelativeSize,
								  float maxHeight, const Ogre::Vector2 &terrainCenterXZ,
								  float yOrigin )
{
	destroyTerrain();

	m_terrainShape = new btHeightfieldTerrainShape( (int)width, (int)depth, &heightMap[0], 1.0f,
													0.0f, maxHeight, 1, PHY_FLOAT, true );
	btVector3 localScaling( xzRelativeSize.x, 1.0f, xzRelativeSize.y );
	m_terrainShape->setLocalScaling( localScaling );

	btVector3 localInertia( 0, 0, 0 );
	btRigidBody::btRigidBodyConstructionInfo rbInfo( 0, 0, m_terrainShape, localInertia );
	m_terrainBody = new btRigidBody( rbInfo );

	btTransform transf;
	transf.setIdentity();
	transf.setOrigin( ogreToBullet( Ogre::Vector3( terrainCenterXZ.x - xzRelativeSize.x * 0.5f,
												   maxHeight / 2.0f + yOrigin,
												   terrainCenterXZ.y - xzRelativeSize.y * 0.5f ) ) );
	m_terrainBody->setWorldTransform( transf );

	m_terrainBody->setFriction( 1.0f );
	m_terrainBody->setRollingFriction( 1.0f );

	m_terrainBody->setCollisionFlags( btCollisionObject::CF_STATIC_OBJECT |
									  btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK );

	const CollisionFilterPair colFilter = CollisionFilterSystem::getCollisionFilterPair( CFTerrain );
	m_world->addRigidBody( m_terrainBody, static_cast<int>( colFilter.group ),
						   static_cast<int>( colFilter.mask ) );

#if 0
	// Test code for checking physics matches graphics
	{
		Ogre::Vector3 vRayOrigin( 5, maxHeight + yOrigin + 100.0f, 5 );
		Ogre::Vector3 vRayDst( vRayOrigin );
		vRayDst.y = -vRayOrigin.y;
		btCollisionWorld::ClosestRayResultCallback closestResults( ogreToBullet( vRayOrigin ),
																   ogreToBullet( vRayDst ) );
		m_world->rayTest( closestResults.m_rayFromWorld, closestResults.m_rayToWorld,
						  closestResults );

		float posY = logicSystem.getHeightAtSimple( vRayOrigin );
		if( closestResults.hasHit() )
		{
			float bulletY = closestResults.m_hitPointWorld.getY();
			assert( fabs( posY - bulletY ) < 1e-4f );
		}
	}
#endif
}
Change the "#if 1" to "#if 1" to perform the test. You can also change the location of vRayOrigin to test another or multiple points.

Let me know if you have trouble integrating it.

Cheers
Matias
Nickak2003
Goblin
Posts: 272
Joined: Thu Jun 10, 2004 4:19 am
x 26

Re: terra+bullet

Post by Nickak2003 »

I have implemented your code, however, I am having difficulty getting the origin correct, I think. If i just want origin to be center of the terrain, what do I send to terra and the physics?
Nickak2003
Goblin
Posts: 272
Joined: Thu Jun 10, 2004 4:19 am
x 26

Re: terra+bullet

Post by Nickak2003 »

also, what is your, terra->getTerrainXZCenter(), function?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5505
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1372

Re: terra+bullet

Post by dark_sylinc »

Code: Select all

//-----------------------------------------------------------------------------------
Vector2 Terra::getTerrainXZCenter(void) const
{
	return Vector2( m_terrainOrigin.x + m_xzDimensions.x * 0.5f,
					m_terrainOrigin.z + m_xzDimensions.y * 0.5f );
}
Nickak2003
Goblin
Posts: 272
Joined: Thu Jun 10, 2004 4:19 am
x 26

Re: terra+bullet

Post by Nickak2003 »

OK, I don't know how on earth you got that code to work with bullet,or if it still works for you or not, but it is not at all how it turned out for me. I got bullet to align however, and here's the code folks.

Code: Select all

   terra->load(objectTemplate->mHeightMap,Ogre::Vector3(0,0,0), dimensions);
   
   	mWidth = terra->getHeightMapTex()->getWidth();
	mDepth = terra->getHeightMapTex()->getHeight();
	mHeight = terra->getMaxHeight();



	btVector3 xzRelativeSize = MathUtilities::ogre2Bullet(terra->getXZDimensions());


    auto createShape = [&]() {

		mShape = new btHeightfieldTerrainShape((int)mWidth, (int)mDepth, terra->getHeightMap().data(), 1.0f,
			0.0f, mHeight  , 1, PHY_FLOAT, false);
		btVector3 localScaling(xzRelativeSize.x()/ mWidth, 1.0f, xzRelativeSize.y()/mDepth);
		mShape->setLocalScaling(localScaling);
	
        // create ground object
        physics.addShape(mShapeName, mShape);
    };
    createShape();
    
    
   auto alignPhysicsToTerra = [&]() {

		btScalar yOrigin = -dimensions.y / 2.0f;

		btScalar maxHeight = terra->getMaxHeight();

		btTransform transf;
		transf.setIdentity();
		transf.setOrigin(btVector3(-dimensions.x / mWidth / 2.0,
			maxHeight / 2.0f + yOrigin,
			-dimensions.z / mDepth / 2.0));
		rigidBody->setWorldTransform(transf);


	};
	alignPhysicsToTerra();
    

This works with centering everything about origin

Thanks for the help though, your idea to move the physics to match terra is a good one. I also implemented the triangle callback.

edit: flipquads should be false