setting a scale to hkpSampledHeightFieldShape

setting a scale to hkpSampledHeightFieldShape

Hello, is there any way to set a scaling to hkpSampledHeightFieldShape , or a derived class such as hkpShape? The reason is because my heightfield is appearing much smaller than it's visual representation. Here's the code I'm using -

 Ogre::Image * heightMap = new Ogre::Image();
 heightMap->load("terrain.png","General"); 
const int xRes = heightMap->getWidth();
 const int zRes = heightMap->getHeight();  
 hkReal* mHeightData = hkAllocate<hkReal>(xRes * xRes, HK_MEMORY_CLASS_USER);
 for (int x = 0; x < xRes; x++)  {   
  for (int z = 0; z < zRes; z++)     {  
    hkReal height = heightMap->getColourAt(x, z, 0).r * mTerrainGroup->getTerrain(0,0)->getMaxHeight();         
  mHeightData[x * zRes + z] = height;   
  }
 }
 hkpSampledHeightFieldBaseCinfo cInfo;  cInfo.m_xRes = xRes;  cInfo.m_zRes = zRes;
// OgreHavokTerrainShape is derived from hkpSampledHeightFieldShape
 hkpSampledHeightFieldShape *heightFieldShape = new OgreHavokTerrainShape(cInfo, mHeightData);
    // Now that we have a shape all we need is the fixed body to represent it in the  
// the simulation. Standard rigid body setup. 
 {      hkpRigidBodyCinfo rci;  
 rci.m_motionType = hkpMotion::MOTION_FIXED;  
 rci.m_shape = heightFieldShape;  
 rci.m_friction = 0.2f;
   hkpRigidBody* body = new hkpRigidBody( rci );  
 mWorld->lock();  
 mWorld->addEntity(body);   
body->removeReference(); 
 }    
// Just need to remove the reference we hold to the shape, and that is it.  
heightFieldShape->removeReference(); 
  mWorld->unlock();
 // end terrain physics setup ----------

17 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

I found the scale member - cInfo.m_scale but now my mesh is flipped on I think the x axis, how do I flip it?

I think I also need to rotate the collision mesh, but how? It seems with everything I try, I end up crashing

Hey Andrew,

You can set the rotation of your custom HeightFieldShape Mesh through the use of the RigidBody it is attached to. Can you give me the CallStack of the Crash when you try rotating the CollisionMesh and let me know how you are currently proceeding to do so?

Thanks,
David.

David G.
Developer Support Engineer
Havok
www.havok.com

I'm currently setting a rotation as you suggested now using -


			hkpRigidBodyCinfo rci;
			rci.m_motionType = hkpMotion::MOTION_FIXED;
			rci.m_shape = heightFieldShape;

			rci.m_friction = 0.2f;
			hkpRigidBody* body = new hkpRigidBody( rci );

			// rotate mesh to align properly to the visual terrain

			hkQuaternion rotation(0,sqrt(0.5),0,sqrt(0.5));

			body->setRotation(rotation);


It looks correct as far as rotation now, but I need to flip the rigidBody across the X axis. How can I?

I ended up finding a flipX function for the heightmap but my collision mesh seems to be about 10 units below the visual mesh. Also, if I try to match it up, the verts seems to be only close to each other, either too high or too low. Here's my code -


	Ogre::Image * heightMap = new Ogre::Image();

	heightMap->load("terrain.png","General");

	heightMap->flipAroundX();

	const int xRes = heightMap->getWidth();

	const int zRes = heightMap->getHeight();
	hkReal* mHeightData = hkAllocate(xRes * xRes, HK_MEMORY_CLASS_USER);

	hkpSampledHeightFieldBaseCinfo cInfo;

	cInfo.m_xRes = xRes;

	cInfo.m_zRes = zRes;

	cInfo.m_scale = hkVector4(mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize() ,mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize(), mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize());

	for (int x = 0; x < xRes; x++)

	{

	   for (int z = 0; z < zRes; z++)

	   {

		  hkReal height = heightMap->getColourAt(x, z, 0).r * mTerrainGroup->getTerrainWorldSize() / mTerrainGroup->getTerrainSize();
		  mHeightData[x * cInfo.m_xRes + z] = height;

	   }

	}
	hkpSampledHeightFieldShape *heightFieldShape = new OgreHavokTerrainShape(cInfo, mHeightData);
				// Now that we have a shape all we need is the fixed body to represent it in the

		// the simulation. Standard rigid body setup.

		{

			hkpRigidBodyCinfo rci;

			rci.m_motionType = hkpMotion::MOTION_FIXED;

			rci.m_shape = heightFieldShape;

			rci.m_friction = 0.2f;
			hkpRigidBody* body = new hkpRigidBody( rci );
			// rotate mesh to align properly to the visual terrain

			hkQuaternion rotation(0,sqrt(0.5),0,sqrt(0.5));

			body->setRotation(rotation);

			body->setPosition(hkVector4(-mTerrainGroup->getTerrainWorldSize() / 2, 0, mTerrainGroup->getTerrainWorldSize() / 2));

			mWorld->lock();

			mWorld->addEntity(body);

			body->removeReference();

		}
		// Just need to remove the reference we hold to the shape, and that is it.

		heightFieldShape->removeReference();
		mWorld->unlock();

Hey Andrew,

Do your mTerrainGroup->getTerrainWorldSize and mTerrainGroup->getTerrainSize() functions return integers or floats?
Also, I would recommend setting the rotation and position of your hkpRigidBody through the use of your hkpRigidBodyCinfo
as this will avoid any unnecessary computations
(eg:


		hkpRigidBodyCinfo rci;

                rci.m_rotation.setAxisAngle(hkVector4(1.0f, 0.0f, 0.0f), -HK_REAL_PI / 2.0F); //rotate by -90 degrees around hkVector4(1.0f, 0.0f, 0.0f).

		hkVector4 toCenterVector;

		toCenterVector.setRotatedDir(rci.m_rotation, heightFieldShape->m_extents);

		rci.m_position.setMul4( -0.5f, toCenterVector); // center the heightfield

You should also note that the extent of the hkpSampledHeightFieldShape is computed as follows (with hkpSampledHeightFieldBaseCinfo cInfo; your set hkpSampledHeightFieldBaseCinfo):
ExtentVector = cInfo.m_scale * (cInfo.m_xRes - 1.0f, cInfo.m_maxHeight - cInfo.m_minHeight, cInfo.m_zRes - 1.0f).
So Your mTerrainGroup->getTerrainSize() should be (cInfo.m_xRes - 1.0f, cInfo.m_maxHeight - cInfo.m_minHeight, cInfo.m_zRes - 1.0f) if you want to
set your collision mesh's extent to mTerrainGroup->getTerrainWorldSize().

Have you also browsed through our hkpSampledHieghtField demos like 'Demo\Demos\Physics\Api\Collide\Shapes\HeightField\SampledHeightField\SampledHeightFieldDemo.cpp'?

Thanks for letting me know your current status on this,

Cheers,
David.

David G.
Developer Support Engineer
Havok
www.havok.com

getTerrainWorldSize returns a float and getTerrainSize returns a uint16. Yes, I've browsed through the demos but I think I need to implement some debug drawing in order to diagnose this effectively. Any tips on how to do that?

Hey Andrew,

There are two options to draw an hkpSampledHeightFieldShape :

If you are within the Havok Demo Project, then you could use the VisualDebugger Tool that you can find in the "hk2011_3_1_r1\Tools\VisualDebugger" folder that you have downloaded from our Website. If you use that option, do not forget to add the '-d' command line argument to your project.

If you are outside the Havok Demo Project, here is some code you can use to figure out how to draw your hkpSampledHeightFieldShape:

void hkpShapeDisplayBuilder::buildShapeDisplay_SampledHeightField(const hkpSampledHeightFieldShape* heightField, const hkTransform& rigidBodyTransform, YourCustomGraphicsBuffer& geom)

{
    hkVector4 scale = heightField->m_intToFloatScale;
    const int startVertex = geom->m_vertices.getSize();

    hkVector4 *const newVertices = geom->m_vertexBuffer.expandBy( heightField->m_xRes * heightField->m_zRes );

    //TriangleIndexBuffer is a struct that allows you to insert three indices at a time.

    TriangleIndexBuffer *const newTriangles = geom->m_indexBuffer.expandBy( ( heightField->m_xRes - 1 ) * ( heightField->m_zRes - 1 ) * 2 );
    for ( int i = 0; i < heightField->m_xRes; ++i )

    {

	    for ( int j = 0; j < heightField->m_zRes; ++j )

	    {

	        hkVector4 p00;

		p00.set( (hkReal) i, heightField->getHeightAt( i, j ), (hkReal) j );

		p00.mul( scale );

		newVertices[ ( i * heightField->m_zRes ) + j ].setTransformedPos(rigidBodyTransform, p00 );

	    }

    }

    for ( int i = 0; i < heightField->m_xRes - 1; ++i )

    {

        for ( int j = 0; j < heightField->m_zRes - 1; ++j )

	{

		const int thisRowV = startVertex + i * heightField->m_zRes;

		const int nextRowV = thisRowV + heightField->m_zRes;

		const int thisRowI = i * ( heightField->m_zRes - 1 ) * 2;

		if ( heightField->getTriangleFlip())

		{

			newTriangles[ thisRowI + ( j * 2 ) ].set( thisRowV + j, thisRowV + j + 1, nextRowV + j + 1 );

			newTriangles[ thisRowI + ( j * 2 ) + 1 ].set( thisRowV + j, nextRowV + j + 1, nextRowV + j );

		}

		else

		{

			newTriangles[ thisRowI + ( j * 2 ) ].set( thisRowV + j, thisRowV + j + 1, nextRowV + j );

			newTriangles[ thisRowI + ( j * 2 ) + 1 ].set( thisRowV + j + 1, nextRowV + j + 1, nextRowV + j );

		}

	}

    }

}

Let me know if you need any other info,

Cheers,
David.

David G.
Developer Support Engineer
Havok
www.havok.com

Ok, how do I turn displayGeometries into a Ogre mesh? I see there's the heightfield function -

void buildShapeDisplay_SampledHeightField( const hkpSampledHeightFieldShape* heightField, const hkTransform& transform, hkArray& displayGeometries );

Or should I use the function you supplied? If so, what should "YourCustomGraphicsBuffer& geom" be? Also, what should TriangleIndexBuffer be? An array?

And one last thing, how do I iterate through displayGeometries, if that's what I should be using, to get my final Mesh that I can display in Ogre?

Hey Andrew,

You can use the hkpShapeDisplayBuilder::buildShapeDisplay_SampledHeightField(const hkpSampledHeightFieldShape* heightField,
const hkTransform& transform,
hkArray& displayGeometries )
method to draw your hkpSampledHeightFieldShape. The version I gave you emphasizes more what You need to implement if you are not using our Graphics Renderer: Vertex Buffers (position of the Vertex positions - represented by newVertices in the code above) / Index Buffers(represented by the Triangle Buffer where each element of the vectors set in it correspond to the vertex index in the Vertex Buffer - represented by newTriangles in the code above). In fact, I do not know much about Ogre but the knowledge of a Vertex/Index Buffer is universal enough that you should be able to find their correspondence in Ogre that will let you define a Mesh it can display (It looks like fiction has listed the actual correspondence in Ogre: ManualObjects). From what I have gathered, the following links should help you:
http://www.ogre3d.org/tikiwiki/ManualObject

http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Generating+A+Mesh

Also, the code noted by havok_joe in the 'Custom Debug Draw' Forum Thread can be useful if you want to ability to creat an hkGeometry from any hkpShape however you should not forget to apply the transform of the hkpRigidBody that this shape is attached to before drawing your Mesh in Ogre format.

Let me know if you have any other Havok related issues,

Cheers,
David.

David G.
Developer Support Engineer
Havok
www.havok.com

Can you explain further which part of my code should be adjusted when you said -

"So Your mTerrainGroup->getTerrainSize() should be (cInfo.m_xRes - 1.0f, cInfo.m_maxHeight - cInfo.m_minHeight, cInfo.m_zRes - 1.0f) if you want to
set your collision mesh's extent to mTerrainGroup->getTerrainWorldSize()."

Thank you

Hey Andrew,

The extent of the hkpSampledHeightFieldShape is computed as follows (ie.: from the hkpSampledHeightFieldShape's constructor) with ci your set hkpSampledHeightFieldBaseCinfo:


	m_extents.set( ci.m_xRes-1.0f, ci.m_maxHeight - ci.m_minHeight, ci.m_zRes-1.0f );

	m_extents.mul( ci.m_scale );

It represents the WorldSpace extent of your hkpSampledHeightFieldShape which I believe is equivalent to your mTerrainGroup->getTerrainWorldSize() . This means that you should set ci.m_scale to mTerrainGroup->getTerrainWorldSize() / (cInfo.m_xRes - 1.0f, cInfo.m_maxHeight - cInfo.m_minHeight, cInfo.m_zRes - 1.0f) in order to have your collision mesh extend to mTerrainGroup->getTerrainWorldSize and hence be the same size as your display Mesh.

Cheers,
David.

David G.
Developer Support Engineer
Havok
www.havok.com

I tried -


	cInfo.m_maxHeight = mTerrainGroup->getTerrain(0,0)->getMaxHeight();

	cInfo.m_minHeight = mTerrainGroup->getTerrain(0,0)->getMinHeight();

cInfo.m_scale = hkVector4(mTerrainGroup->getTerrainWorldSize() / (cInfo.m_xRes - 1.0f), cInfo.m_maxHeight - cInfo.m_minHeight, cInfo.m_zRes - 1.0f);


and it stretched like crazy. Did i miss something? I also tried hkpShapeDisplayBuilder::buildShapeDisplay_SampledHeightField and I notice that heightfield->getTriangleFlip() never returns true which I think should every other row...Thanks for the help

Hey Andrew,

I meant more like setting the scale as follows:


cInfo.m_maxHeight = mTerrainGroup->getTerrain(0,0)->getMaxHeight();

cInfo.m_minHeight = mTerrainGroup->getTerrain(0,0)->getMinHeight();
cInfo.m_scale = hkVector4(mTerrainGroup->getTerrainWorldSize()(0) / (cInfo.m_xRes - 1.0f), mTerrainGroup->getTerrainWorldSize()(1) / (cInfo.m_maxHeight - cInfo.m_minHeight), mTerrainGroup->getTerrainWorldSize()(2) / (cInfo.m_zRes - 1.0f));

David.

David G.
Developer Support Engineer
Havok
www.havok.com

I found where there was a bug iterating over the indices giving me a false display. The only problems I have depending on which implementation of mine I choose is : I either get a working collision mesh that somehow is all collapsed into 1 position ( all grids, quads in the same location ) , or I get a non-working collision mesh with a perfect display with triangles flipped every row ( a crash at removeReference-


hkpBvTreeShape * bvtree = HkOgre::Heightfield::processOgreTerrainGroup(mTerrainGroup);

		hkpRigidBodyCinfo rci;

		rci.m_motionType = hkpMotion::MOTION_FIXED;

		rci.m_position.set(0, 0, 0);

		rci.m_shape = bvtree;

		rci.m_friction = 0.2f;

hkpRigidBody* body = new hkpRigidBody( rci );

		mWorld->lock();

		mWorld->addEntity(body);

		body->removeReference();

I've tried changing the scale which didn't really help. I don't see where it's going wrong.
Here's my new TerrainProcess code ( MindCalamity's original work ) -
	inline hkpBvTreeShape* processOgreTerrainGroup(Ogre::TerrainGroup* terrainGroup)

		{
			// TODO We are only creating a navmesh from terrain at the moment

			// TODO look at PW's loadhouses method for entities loading
			// TODO check for no terrain

			//    if (srcMeshes.empty())

			//        return;
			// PARTS OF THE FOLLOWING CODE WERE TAKEN AND MODIFIED FROM AN OGRE3D FORUM POST

			//const int numNodes = srcMeshes.size();

			// TODO how to retrieve number of pages in terrain group? Hardcoded at the moment.

			int pagesTotal = 1;

			const int totalMeshes = pagesTotal /*+ numNodes*/;
			size_t nverts = 0;

			size_t ntris = 0;

			size_t *meshVertexCount = new size_t[totalMeshes];

			size_t *meshIndexCount = new size_t[totalMeshes];

			Ogre::Vector3 **meshVertices = new Ogre::Vector3*[totalMeshes];

			unsigned long **meshIndices = new unsigned long*[totalMeshes];
			// Calculate terrain bounds

			// TODO extract calculateTerrainExtents() ?

			Ogre::TerrainGroup::TerrainIterator ti = terrainGroup->getTerrainIterator();

			Ogre::Terrain* trn;

			// TODO we could calculate terrain count here

			size_t trnCount = 0;
			//---------------------------------------------------------------------------------

			// TERRAIN DATA BUILDING

			ti = terrainGroup->getTerrainIterator();

			trnCount = 0;

			while(ti.hasMoreElements())

			{

				trn = ti.getNext()->instance;

				// TODO do we want to set a query mask here? or does default suffice?

				//         trn->setQueryFlags(GEOMETRY_QUERY_MASK);
				// get height data, world size, map size

				float *mapptr = trn->getHeightData();

				float WorldSize = trn->getWorldSize();

				int MapSize = trn->getSize();

				// calculate where we need to move/place our vertices

				float DeltaPos = (WorldSize / 2.0f);
				float DeltaX = 0;

				float DeltaZ = 0;

				// TODO this hardcoded behaviour has to go! Supports only up to 4 terrain pages

				switch(trnCount)

				{

				case 0:

					DeltaX = -6000;

					DeltaZ = 6000;

					break;

				case 1:

					DeltaX = -6000;

					DeltaZ = -6000;

					break;

				case 2:

					DeltaX = 6000;

					DeltaZ = 6000;

					break;

				case 3:

					DeltaX = 6000;

					DeltaZ = -6000;

					break;

				default:

					DeltaX = 0;

					DeltaZ = 0;

				}
				std::cout << "Terrain: " << trnCount << std::endl;
				float Scale = WorldSize / (float)(MapSize - 1);
				//////////////////////////////

				// THIS CODE WAS TAKEN FROM

				// AN OGRE FORUMS THREAD IN THE

				// NEW TERRAIN SCREENSHOTS THREAD

				// IN THE SHOWCASE FORUMS - I ONLY MODIFIED IT

				// TO BE ABLE TO WORK FOR RECAST AND IN THE CONTEXT OF

				// THIS DEMO APPLICATION
				// build vertices

				meshVertices[trnCount] = new Ogre::Vector3[(MapSize*MapSize)];
				int i = 0;

				int u = 0;

				int max = MapSize; // i think i needed this for something before but now it's obviously redundant

				int z = 0;

				for(int x = 0;; ++x)

				{

					// if we've reached the right edge, start over on the next line

					if(x == max)

					{

						x = 0;

						++z;

					}

					// if we reached the bottom/end, we're done

					if(z == max)

						break;
					// Calculate world coordinates for terrain tile vertex. Terrain vertices are defined in tile-local coordinates.

					// add the vertex to the buffer

					meshVertices[trnCount][u] = Ogre::Vector3((Scale * x) + DeltaX, mapptr[(MapSize * z) + x], (Scale * -z) + DeltaZ);
					i += 3;

					++u;

				}
				size_t size = ((MapSize*MapSize)-(MapSize*2)) * 6;

				meshIndices[trnCount] = new unsigned long[size];

				// i will point to the 'indices' index to insert at, x points to the vertex # to use

				i = 0;

				for(int x = 0;;++x)

				{

					// skip rightmost vertices

					if((x+1)%MapSize == 0)

					{

						++x;

					}
					// make a square of 2 triangles

					/*

					if((x / (MapSize)) % 2 != 0) {

						t.indices[i] = x;

						t.indices[i+1] = x + 1;

						t.indices[i+2] = x + MapSize;
						t.indices[i+3] = x + 1;

						t.indices[i+4] = x + MapSize + 1;

						t.indices[i+5] = x + MapSize;

					}

					else {

						t.indices[i] = x;

						t.indices[i+1] = x + 1;

						t.indices[i+2] = x + MapSize + 1;
						t.indices[i+3] = x;

						t.indices[i+4] = x + MapSize + 1;

						t.indices[i+5] = x + MapSize;

					}

					*/

					if((x / (MapSize)) % 2 != 0) {

						meshIndices[trnCount][i] = x;

						meshIndices[trnCount][i+1] = x + 1;

						meshIndices[trnCount][i+2] = x + MapSize;
						meshIndices[trnCount][i+3] = x + 1;

						meshIndices[trnCount][i+4] = x + 1 + MapSize;

						meshIndices[trnCount][i+5] = x + MapSize;

					}

					else {

						meshIndices[trnCount][i] = x;

						meshIndices[trnCount][i+1] = x + 1;

						meshIndices[trnCount][i+2] = x + MapSize + 1;
						meshIndices[trnCount][i+3] = x;

						meshIndices[trnCount][i+4] = x + 1 + MapSize;

						meshIndices[trnCount][i+5] = x + MapSize;

					}

					// if we just did the final square, we're done

					if(x+1+MapSize == (MapSize*MapSize)-1)

						break;
					i += 6;

				}
				meshVertexCount[trnCount] = trn->getSize()*trn->getSize();

				meshIndexCount[trnCount] = size;
				nverts += meshVertexCount[trnCount];

				ntris += meshIndexCount[trnCount];
				if(trnCount < pagesTotal)

					++trnCount;

			}

			ntris = ntris / 3;
			hkpSimpleMeshShape* meshStorage = new hkpSimpleMeshShape(hkConvexShapeDefaultRadius);

			meshStorage->m_vertices.setSize(nverts);

			meshStorage->m_triangles.setSize(ntris);
			for(unsigned int i = 0; i < meshVertexCount[0]; i++){

				hkVector4 vertex(meshVertices[0][i].x, meshVertices[0][i].y, meshVertices[0][i].z);

				meshStorage->m_vertices[i] = vertex;

			}

			int u = 0;

			for(unsigned int i = 0; i < ntris; i++){

				meshStorage->m_triangles[i].m_a = meshIndices[0][u];

				meshStorage->m_triangles[i].m_b = meshIndices[0][u+1];

				meshStorage->m_triangles[i].m_c = meshIndices[0][u+2];

				u=+3;

			}
			hkpMoppCompilerInput mfr;

			mfr.setAbsoluteFitToleranceOfAxisAlignedTriangles( hkVector4( 0.1f, 0.1f, 0.1f ) );
			hkpMoppCode* pCode = hkpMoppUtility::buildCode( meshStorage, mfr );

			hkpBvTreeShape* shape = new hkpMoppBvTreeShape( meshStorage, pCode );

			pCode->removeReference();

			meshStorage->removeReference();
			delete [] meshVertices;

			delete [] meshVertexCount;

			delete [] meshIndices;

			delete [] meshIndexCount;
			return shape;

		}

	}

} 

Hey drwbns,

I previously gave you a function implementation of hkpShapeDisplayBuilder::buildShapeDisplay_SampledHeightField. Using this, you should be able to retrieve the World Space position of each point of your grid.

If these values look correct to you then there is something wrong with how you display your geometry Mesh and that is either an issue with how you convert those values to be understood by Ogre or an issue with using Ogre incorrectly. Unfortunately, I have no knowledge of Ogre and I suggest you post such a question to an Ogre Forum as well: "Giving the Input position values, you are getting, how could you get a working graphic Mesh".

However let me know if those values do not look correct to you. The easiest thing for me to help you debug that through would be that you submit a world snapshot of your demo once your hkpSampledHeightFieldShape's rigid body has been added to your world (see WorldSnapshotDemo) to this forum thread.

Thanks for letting me know of your progress,

David.

David G.
Developer Support Engineer
Havok
www.havok.com

Leave a Comment

Please sign in to add a comment. Not a member? Join today