Terrain heightdata woes

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Terrain heightdata woes

Post by jacmoe »

I am trying to grab the heightdata from the Terrain SceneManager, by deriving my application from TerrainPageSourceListener and using the pageConstructed function to build a list of vertices which I send to OgreOpcode.

It seems to work OK.
OgreOpcode is accepting the data, and even acknowledges that my ray collides with the terrain.

As you can see, the dimensions of the terrain, and the terrain heights are all correct:
Image

However, the rendering of the triangles look funny.
Seems like something is missing:
Image

This is the code for my pageConstructed function:

Code: Select all

void OgreOpcodeTerrainExample::pageConstructed(TerrainSceneManager* pSceneMgr, size_t pagex, size_t pagez, Real* heightData) 
{
	LogManager::getSingleton().logMessage("PageConstructed called.");
	Vector3 Offset(0,0,0); // Set to something if required
	// Get parameters of the terrain
	Vector3 iScale(0,0,0);
	int iPageSize(0);
	int iTileSize(0);
	iScale = static_cast<TerrainSceneManager*>(pSceneMgr)->getScale();
	iPageSize = static_cast<TerrainSceneManager*>(pSceneMgr)->getPageSize();
	iTileSize = static_cast<TerrainSceneManager*>(pSceneMgr)->getTileSize();
	//pSceneMgr->getOption("Scale", &Scale);
	//pSceneMgr->getOption("PageSize", &iPageSize);

	// # of heightData should be iPageSize *iPageSize 
	int heightDataNum = iPageSize * iPageSize;
	float* OOData;
	OOData = new float[heightDataNum*3];
	int OODataCounter = 0;
	for(int j=0;j<iPageSize;j++)
	{
		for(int i=0;i<iPageSize;i++)
		{
			Real height = heightData[j * iPageSize + i];
			height = height * iScale.y; // scale height 

			// set proper scale
			Vector3 Pt = Vector3(( float ) i * iScale.x, height, ( float ) j * iScale.z);
			// copy it over
			OOData[OODataCounter + 0] = Pt.x;
			OOData[OODataCounter + 1] = Pt.y;
			OOData[OODataCounter + 2] = Pt.z;
			OODataCounter += 3;
		}
	}

	// pass OOData to OgreOpcode..
	terrainShape->load(heightDataNum, OOData, iPageSize, iPageSize);
	terrainCollObj->setShape(terrainShape);
	mCollideContext->addObject(terrainCollObj);
}
If you need to know how I render the triangles, I will post the visualization code, but I am using a ManualObject with OT_LINE_LIST.

I noticed that the Terrain SceneManager does not support the generalised getOption functionality.

If any of you know how to correctly build these terrain vertices, let me know. :)

I tried using TileSize, like the TerrainSceneRenderable uses, but that didn't work..
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 66

Post by sinbad »

Remember that the data you get is just strips of height data. To turn that into triangles you need to reference the height points you've been given multiple times each, since each height point is used in many triangles. You basically need to construct triangles stripifying each set of 2 lines of height data. See TerrainRenderable::generateTriListIndexes for an example, just ignore the stitching.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

sinbad wrote:Remember that the data you get is just strips of height data.
<snip>
See TerrainRenderable::generateTriListIndexes for an example, just ignore the stitching.
Ah, that figures!
Thanks!
I will post back the results! :)
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
Clay
OGRE Community Helper
OGRE Community Helper
Posts: 518
Joined: Wed Mar 17, 2004 4:14 am
x 1

Post by Clay »

Out of curiosity, what are you using to generate your terrain Jacmoe?
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

I am not generating terrain - I am grabbing the height data from the Terrain SceneManager via the pageConstructed function, and trying to generate valid geometry data for OgreOpcode, or any other geometry consumer for that matter. :)

I am aware that nxOgre chooses to generate this directly from the heightmap image, and that OgreODE chooses to use raycasting against the elevation points.

But I thought it would be nice to just get the data from Ogre, compute the vertices and the indices and pass it on. (Without digging deep into the bowels of the Terrain SM).
An added side-benefit is that I can simply ask my visualization ManualObject to save it as a mesh..
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

I am almost there! :)

I am able to feed OgreOpcode vertices and indices, and it happily detects collisions!

Proof:
Image

Each terrain tile is being fed to OgreOpcode as separate models.

The *only* problem now is that the faces are degenerate, and refuses to render.. :(
opcode.log wrote:OPCODE WARNING: found 1243316 degenerate faces in model! Collision might report wrong results!
<edit>
Please note that this is reported for each terrain tile (64 tiles in all) - I thought I would spare you the repetition.
</edit>

However, since the collisions are correctly reported, and since the OPCODE AABBs are OK, it is probably a small error in my code.

Here's the AABB boundingboxes from OPCODE:
Image

And here's my code:

Code: Select all

void OgreOpcodeTerrainExample::pageConstructed(TerrainSceneManager* pSceneMgr, size_t pagex, size_t pagez, Real* heightData) 
{
	LogManager::getSingleton().logMessage("PageConstructed called.");
	Vector3 Offset(0,0,0); // Set to something if required
	Vector3 iScale(0,0,0);
	int iPageSize(0);
	int iTileSize(0);
	iScale = static_cast<TerrainSceneManager*>(pSceneMgr)->getScale();
	iPageSize = static_cast<TerrainSceneManager*>(pSceneMgr)->getPageSize();
	iTileSize = static_cast<TerrainSceneManager*>(pSceneMgr)->getTileSize();
	int heightDataNum = iTileSize * iTileSize;
	int numIndexes = 0;
	int index_size = iTileSize * iTileSize * 2 * 2 * 2;
	int* indices = new int[index_size];
	int nameCounter = 0;
	// Create index data (Is this the same for each tile?)
	for ( int j = 0; j < iTileSize-1; j++)
	{
		for ( int i = 0; i < iTileSize-1; i++ )
		{
			indices[numIndexes] = i + j * iTileSize; numIndexes++;
			indices[numIndexes] = i + (j + 1) * iTileSize; numIndexes++;
			indices[numIndexes] = (i + 1) + j * iTileSize; numIndexes++;

			indices[numIndexes] = i + (j + 1) * iTileSize; numIndexes++;
			indices[numIndexes] = (i + 1) + (j + 1) * iTileSize; numIndexes++;
			indices[numIndexes] = (i + 1) + j * iTileSize; numIndexes++;
		}
	}

	// Loop through all pages
	for ( int startz = 0; startz < iPageSize - 1; startz += ( iTileSize - 1 ) )
	{
		for ( int startx = 0; startx < iPageSize - 1; startx += ( iTileSize - 1 ) )
		{
			float* OOData;
			OOData = new float[heightDataNum*3];
			int OODataCounter = 0;
			// This should be a tile
			for(int j = startz;j < (startz + iTileSize); j++)
			{
				for(int i = startx;i < (startx + iTileSize); i++)
				{
					Real height = heightData[j * iPageSize + i];

					Vector3 Pt = Vector3((float) i * iScale.x, (float)height * iScale.y, (float)j * iScale.z);
					// copy it over
					OOData[OODataCounter + 0] = Pt.x;
					OOData[OODataCounter + 1] = Pt.y;
					OOData[OODataCounter + 2] = Pt.z;
					OODataCounter += 3;
				}
			}

			// create new shapes here!
			PtrCollisionShape* tempTerrainShape;
			CollisionObject* tempCollObject;
			String shapeName = "terrainShape" + StringConverter::toString(nameCounter);
			tempCollObject = mCollideContext->createObject(shapeName);
			tempTerrainShape = CollisionManager::getSingletonPtr()->createPtrCollisionShape(shapeName);
			tempTerrainShape->load(heightDataNum, index_size, OOData, indices);
			tempCollObject->setCollClass("level");
			tempCollObject->setShape(tempTerrainShape);
			mCollideContext->addObject(tempCollObject);
			nameCounter++;
		}
	}	

	// pass OOData to OgreOpcode..
	//terrainShape->load(heightDataNum, index_size, OOData, indices);
	//terrainCollObj->setShape(terrainShape);
	//mCollideContext->addObject(terrainCollObj);
}
I try to mimick the way the Terrain SM works, but I am not sure what's supposed to go on with the step variable in TerrainRenderable::generateTriListIndexes:

Code: Select all

int step = 1 << mRenderLevel;
This is the original index generating code:

Code: Select all

        // Do the core vertices, minus stitches
        for ( int j = north; j < mOptions->tileSize - 1 - south; j += step )
        {
            for ( int i = west; i < mOptions->tileSize - 1 - east; i += step )
            {
                //triangles
                *pIdx++ = _index( i, j ); numIndexes++;
                *pIdx++ = _index( i, j + step ); numIndexes++;
                *pIdx++ = _index( i + step, j ); numIndexes++;

                *pIdx++ = _index( i, j + step ); numIndexes++;
                *pIdx++ = _index( i + step, j + step ); numIndexes++;
                *pIdx++ = _index( i + step, j ); numIndexes++;
            }
        }
In my code I just replace the 'north', 'south', etc. with '0' and 'step' with '1'.
And I assume that the index data is the same for each terrain tile.


I think I need a couple hints more! :)
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
ibrown
Gremlin
Posts: 164
Joined: Wed Aug 18, 2004 6:41 pm
Location: London

Post by ibrown »

You index generation code looks okay. I can only guess that if the scale is too small you may get some identical vertices due to running into the limits of float precision. That would need to be a pretty small scale though.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

It works perfectly, if I only do the first tile:

Image

Hmm..

Seems like 'step' is being used somehow..

So, a different set of indices for each tile, then?

I'll do the dishes, and see what I can cook up.

Right now I am brain-borked! :)
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

I would also like to add that OPCODE is happy: no degenerate vertices found! :)

Still, that only gives me one tile.. :?
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
ibrown
Gremlin
Posts: 164
Joined: Wed Aug 18, 2004 6:41 pm
Location: London

Post by ibrown »

int index_size = iTileSize * iTileSize * 2 * 2 * 2;

should be

int index_size = iTileSize * iTileSize * 6;

that means you have some uninitialised data that you are passing into tempTerrainShape->load().

Maybe that is an issue. Also, it is okay to pass the same index array into multiple load functions (is the data copied or referenced?)

Ian
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

You're right!
The '2 * 2 * 2' is taken from that TerrainRenderable index generating function, and is valid because there is a whole lotta stitching going on after the initial index generation..
I changed it to 6 in my own code, so the array should be clean now.

But the error remains. :wink:
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

Looks like I answered my own question here! :)

Anyway, it now renders under OpenGL:
Image

Still degenerate triangles, and that pisses Direct3D off ... :(

But, I am very pleased! :D

This is my new index generation code:

Code: Select all

	int index_size = (iTileSize) * (iTileSize) * 6;

	//Create indices 
	int* indices = new int[index_size]; 
	for (int x=0;x<iTileSize-1;x++)
	{ 
		for (int y=0; y<iTileSize-1;y++) 
		{ 
			indices[(x+y*(iTileSize-1))*6] = (x+1)+(y+1)*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+1] = (x+1)+y*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+2] = x+y*iTileSize; 

			indices[(x+y*(iTileSize-1))*6+3] = (x+1)+(y+1)*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+4] = x+y*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+5] = x+(y+1)*iTileSize; 
		} 
	} 
Now I have to figure out what makes Direct3D bail - and (more importantly) - what is the cause of those degenerate buggers. :wink:
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

OK. I probably need to add some stitching after all.. :wink:

Complete, working code will (definately!) follow.
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

And that's about how far I got. :|
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

OK. I promised you working code :)

Take a look at this:

Code: Select all

	Vector3 vScale(0,0,0);
	int iPageSize(0);
	int iTileSize(0);
	vScale = pSceneMgr->getScale();
	iPageSize = pSceneMgr->getPageSize();
	iTileSize = pSceneMgr->getTileSize();
	size_t heightDataNum = iPageSize * iPageSize;
	size_t numIndexes = 0;
	size_t index_size = (iTileSize-1) * (iTileSize-1) * 6;
	int nameCounter = 0;
	//Create indices 
	size_t* indices = new size_t[index_size*3]; 
	for (int x=0;x<iTileSize-1;x++)
	{ 
		for (int y=0; y<iTileSize-1;y++) 
		{ 
			indices[(x+y*(iTileSize-1))*6] = (x+1)+(y+1)*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+1] = (x+1)+y*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+2] = x+y*iTileSize; 

			indices[(x+y*(iTileSize-1))*6+3] = (x+1)+(y+1)*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+4] = x+y*iTileSize; 
			indices[(x+y*(iTileSize-1))*6+5] = x+(y+1)*iTileSize; 
		} 
	} 

	// Loop through all pages
	for ( int startz = 0; startz < iPageSize - 1; startz += ( iTileSize - 1 ) )
	{
		for ( int startx = 0; startx < iPageSize - 1; startx += ( iTileSize - 1 ) )
		{
			float* OOData;
			OOData = new float[heightDataNum*3];
			int OODataCounter = 0;
			// This should be a tile
			for(int j = startz;j < (startz + iTileSize); j++)
			{
				for(int i = startx;i < (startx + iTileSize); i++)
				{
					Real height = heightData[j * iPageSize + i];

					Vector3 Pt = Vector3((float) i * vScale.x, (float)height * vScale.y, (float)j * vScale.z);
					// copy it over
					OOData[OODataCounter + 0] = Pt.x;
					OOData[OODataCounter + 1] = Pt.y;
					OOData[OODataCounter + 2] = Pt.z;
					OODataCounter += 3;
				}
			}

			// create new shapes here!
			PtrCollisionShape* tempTerrainShape;
			CollisionObject* tempCollObject;
			String shapeName = "terrainShape" + StringConverter::toString(nameCounter);
			tempCollObject = mCollideContext->createObject(shapeName);
			tempTerrainShape = CollisionManager::getSingletonPtr()->createPtrCollisionShape(shapeName);
			tempTerrainShape->load(heightDataNum, index_size, OOData, indices);
			tempCollObject->setCollClass("level");
			tempCollObject->setShape(tempTerrainShape);
			mCollideContext->addObject(tempCollObject);
			nameCounter++;
			delete[] OOData;
			OOData = 0;
		}
	}
	delete[] indices;
	indices = 0;
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179

Post by jacmoe »

Image
:D
Last edited by jacmoe on Mon Jun 04, 2007 11:09 pm, edited 1 time in total.
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
x 16

Post by KungFooMasta »

Woohoo! Good job on this, Jacmoe! The solution should be helpful in getting ETM working with OgreOpcode as well. :D