Editable Terrain Manager [v2.2 released]

A place to show off your latest screenshots and for people to comment on them. Only start a new thread here if you have some nice images to show off!
CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Thu Jun 07, 2007 9:14 am

Not to slow your enthusiasm down, but to use ETM you will at some point have to link against it ;) Though moving that point away from the library to the application is definitely a good idea.

As for vertices / indices: I'm not an expert, either, but it's really not that much of a mystery. You can imagine a vertex as just a point in space. It is basically a set of 3D coordinates stating its position. A vertex buffer is a collection or list of vertices.
Index buffers, on the other hand, state which vertices are to be used and how. An index is really just that - the index of a vertex in a vertex buffer. The index buffer doesn't care about the actual vertices, it doesn't even care about a concrete vertex buffer. They really only determine how a set of vertices is to be rendered. You can shape them into triangles. You can even omit certain vertices (that's what is done with terrain LOD - every second vertex from the previous level is skipped in the index buffer, creating coarser triangles, but still using the very same vertex buffers).
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Fri Jun 08, 2007 6:54 am

I'm hoping you can spot my nub mistake with tiling. I have made a function called setupTerrainCollisionObject which is meant to imitate the TerrainCollisionShape constructor. It allows for user defined number of x and z tiles used for collision parts. (must be power of 2)

I am using a Manual Object to verify that I am getting the vertices correctly. Setting z tiling seems to work fine (1/2/4/8), but when I specify x tiling for more than 1, I get line artifacts. Oddly enough, I am only plotting points, so lines should not be possible! :?

Here is xTiling = 1, zTiling = 2 (Only top half of terrain is represented):

Image

Here is xTiling = 2, zTiling = 1 (Only left half of terrain is represented):

Image

Here is the beginning of the function:

Code: Select all

void setupTerrainCollisionObject(const std::vector<float>& HeightmapData, size_t Width, size_t Depth, Ogre::Vector3 Offset, Ogre::Vector3 Scale, unsigned int numTilesX = 2, unsigned int numTilesZ = 1)
	{
		// enforcing number of x and z tiles to be greater than zero, and also a power of 2: 2, 4, 8, 16, etc.
		if( !powerOf2(numTilesX) || (numTilesX == 0) ) return;
		if( !powerOf2(numTilesZ) || (numTilesZ == 0) ) return;

		// TerrainShape constructor would init these values
		mHeightmapData = &HeightmapData;
		mWidth = Width;
		mDepth = Depth;
		mOffset = Offset;
		mScale = Scale;

		// calculate the number of vertices for each tile, 
		// for both x direction (in 3d space) verts, and z direction (in 3d space) verts
		int xTileSize = ((mWidth - 1) / numTilesX) + 1;
		int zTileSize = ((mDepth - 1) / numTilesZ) + 1;
And here is the Tiling code:

Code: Select all

// Loop through all tiles 
		for ( unsigned int startz = 0; startz < numTilesZ; startz += ( zTileSize - 1 ) ) 
		{ 
			for ( unsigned int startx = 0; startx < numTilesX; startx += ( xTileSize - 1 ) ) 
			{ 
				float* OOData; 
				int numVertsPerTile = xTileSize * zTileSize;
				OOData = new float[numVertsPerTile * 3];
				int OODataCounter = 0; 

				Ogre::ManualObject* mo = new Ogre::ManualObject("tile" + Ogre::StringConverter::toString(tileCounter++));
				mo->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST);

				// This should be a tile 
				for( unsigned int  j = startz; j < (startz + zTileSize); j++) 
				{ 
					for( unsigned int  i = startx; i < (startx + xTileSize); i++) 
					{ 
						Ogre::Vector3 v3(
							mOffset.x + (i * mScale.x),
							mOffset.y + (at(i,j) * mScale.y),
							mOffset.z + (j * mScale.z)
							);
						OOData[OODataCounter + 0] = v3.x; 
						OOData[OODataCounter + 1] = v3.y; 
						OOData[OODataCounter + 2] = v3.z; 
						OODataCounter += 3; 

						mo->position(v3);
					} 
				} 

				mo->end();
				mSceneManager->getRootSceneNode()->attachObject(mo);

				delete[] OOData; 
				OOData = 0; 
			} 
			break;
		} 
The break I added in is just to test one Tile. If more than one tile was loaded at 0,0,0 I wouldn't be able to tell tiles apart.

I am hoping I made some dumb obvious mistake. I don't see why lines should be appearing at all. And it only appears with xTiling > 1. :?

Thanks for any help,

KungFooMasta
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Fri Jun 08, 2007 9:51 am

I am not sure if it's related to the line error, but I think you do have an error in the outer for loops:

Code: Select all

for ( unsigned int startz = 0; startz < numTilesZ; startz += ( zTileSize - 1 ) )
{
   for ( unsigned int startx = 0; startx < numTilesX; startx += ( xTileSize - 1 ) )
The break condition makes no sense (to me at least). Unless you changed the meaning of numTiles during the start of the function and the loop (which you should NOT do ;) ), then both loops will only do one iteration each. You probably meant this:

Code: Select all

startz < mDepth
startx < mWidth
Other than that, I'm afraid I have no idea about the error.


I do have a few suggestions for improving your code, if you don't mind them.
  • Unify your variable names. Some of them begin with a capital, others don't.
  • Code: Select all

          if( !powerOf2(numTilesX) || (numTilesX == 0) ) return;
          if( !powerOf2(numTilesZ) || (numTilesZ == 0) ) return;
    
    Do NOT return on this. This is definitely an error from the calling code and should be answered with an exception. Some time someone (possibly you ;) ) will call the function with invalid arguments and wonder why nothing happens.
  • Code: Select all

                float* OOData;
                int numVertsPerTile = xTileSize * zTileSize;
                OOData = new float[numVertsPerTile * 3];
     
    Define variables as late as possible and initialise them in the process, if you can. If in this code you ever accidentally insert a line before OOData = ... which references OOData, you may or may not get a warning from your compiler, but you'll definitely get into trouble on runtime. Better change it to this:

    Code: Select all

    int numVertsPerTile = xTileSize * zTileSize;
    float* ooData = new float[numVertsPerTile * 3];
    
  • Code: Select all

                delete[] OOData;
                OOData = 0;
    
    Though generally a good idea, in this case setting OOData to zero is quite pointless because the variable goes out of scope immediately after that - there's no way anyone can access it after this line :) Unless you insert something there, but that will get you into trouble, either way.
Well, just a few suggestions, hope you don't mind. :)
0 x

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

Post by jacmoe » Fri Jun 08, 2007 4:23 pm

You can blame me for those errors - I'll mend my ways - thanks! :)
0 x
/* 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
Contact:

Post by KungFooMasta » Fri Jun 08, 2007 6:23 pm

Thanks for all the tips, they are good points. :)

For the loop, the break is intentional. Basically I am asking the user how to partition the terrain (how much x tiling and how much z tiling, which will tell me how many tiles to use to represent the terrain). The break is added only to stop after 1 iteration of a tile, so I can see the results. As you can see, I found a an issue creating a tile when more than 1 tiling in the x direction is requested.

Using mDepth and mWidth would iterate over the entire terrain, but I really want to iterate tile by tile.

x tiling = 1, z tiling = 1 (entire terrain is 1 tile)

|////|
|////|

x tiling = 1, z tiling =2 (terrain broken into 2 vertical tiles)

|////|

|\\\\|

x tiling = 2, z tiling = 1 (terrain broken into 2 horizontal tiles)

|// \\|
|// \\|

So I just get the vertices of the first tile and stop. The break would not normally be in the loop. Manual Objects are really a PITA for me. :(

KungFooMasta
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Fri Jun 08, 2007 6:32 pm

I did not mean the break, and neither do I mind you to iterate over the tiles, but I still say the loop conditions are wrong ;)

The way you are using startx and startz they need to point at the heightmap data position of the initial vertex in the current tile. That's why you are updating them with += (xTileSize-1), which is correct.

But, your tilesize may be 33 or whatever, wheres your numTilesX will only be 8 or something. Do you notice what happens when the loop runs? It will terminate after the first iteration, regardless of the break. You really need to use mWidth, mHeight, the step size of xTileSize ensures that you are only looping as often as you have tiles.
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Fri Jun 08, 2007 6:52 pm

Oh you're right! I'm manipulating the counter within the for loops.. I think this is a very bad idea. (In my defense I didn't write this code. :P )

I'll probably rewrite the code to something like:

Code: Select all

// Loop through all tiles 
for ( unsigned int zIndex = 0; zIndex < numTilesZ; ++zIndex ) 
{ 
	for ( unsigned int xIndex = 0; xIndex < numTilesX; ++xIndex ) 
	{ 
		int numVertsPerTile = xTileSize * zTileSize; 
		float* OOData = new float[numVertsPerTile * 3]; 
		int OODataCounter = 0; 

		Ogre::ManualObject* mo = new Ogre::ManualObject("tile" + Ogre::StringConverter::toString(tileCounter++)); 
		mo->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST); 

		unsigned int zStart = zIndex * (zTileSize - 1);
		unsigned int xStart = xIndex * (xTileSize - 1);
		for( unsigned int col = zStart; col < (zStart + zTileSize); ++col) 
		{ 
		   for( unsigned int row = xStart; row < (xStart + xTileSize); ++row) 
		   { 
			   Ogre::Vector3 v3( 
				   mOffset.x + (row * mScale.x), 
				   mOffset.y + (at(row,col) * mScale.y), 
				   mOffset.z + (col * mScale.z) 
				   ); 
			   OOData[OODataCounter + 0] = v3.x; 
			   OOData[OODataCounter + 1] = v3.y; 
			   OOData[OODataCounter + 2] = v3.z; 
			   OODataCounter += 3; 

			   mo->position(v3); 
	   } 
	} 

	mo->end(); 
	mSceneManager->getRootSceneNode()->attachObject(mo); 

	delete[] OOData; 
	} 
	break; // removed when finished testing
} 
I haven't tested this out..
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Fri Jun 08, 2007 7:07 pm

That's possible, but changing the loop condition as I said would have sufficed ;) But if this approach is more readable to you, then do change it that way. Writing code is all about readability ;)
0 x

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

Post by jacmoe » Fri Jun 08, 2007 7:20 pm

KungFooMasta wrote:(In my defense I didn't write this code. :P )
I nicked most of it from the Ogre TSM. :P

If you jump through the vertex array in tile-sized steps, and for each step run through all the vertices in the tile and add them to a renderable, you are doing what your previous code did (and what I do for the TSM).
The two outer loops jumps the width*height array in tile-sized steps.
The two innner loops runs through all vertices in a tile.
0 x
/* 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
Contact:

Post by KungFooMasta » Fri Jun 08, 2007 7:27 pm

Yah, the functionality got lost between converting from TSM to ETM tiling, especially when I wanted to have a user specified tiling in x and z. :lol:

But I really don't like having embedded loops which alter variables used as the conditional for an outter loop. Not really sure if this will fix the line artifacts I was seeing, but the code looks cleaner in my eyes!
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Fri Jun 08, 2007 8:07 pm

D'oh, I wasn't even seeing that - that's a double bug, then, and yeah, then your resolution is the only possible. Scratch my comments about changing the loop condition to mWidth/mHeight ;)
0 x

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

Post by jacmoe » Fri Jun 08, 2007 9:04 pm

I fail to see where the 'embedded loops are altering the outer loop conditions'.

Enough of this already: we are seriously hi-jacking your topic, CABAListic! :D
0 x
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Fri Jun 08, 2007 9:06 pm

Code: Select all

// Loop through all tiles
      for ( unsigned int startz = 0; startz < numTilesZ; startz += ( zTileSize - 1 ) )
      {
         for ( unsigned int startx = 0; startx < numTilesX; startx += ( xTileSize - 1 ) )
         {
            // [...]

            for( unsigned int  j = startz; j < (startz + zTileSize); j++)
            {
               for( unsigned int  i = startx; i < (startx + xTileSize); i++)
               {
The loop variables were identical before KungFooMaster's last post which is definitely an error. Of course, you could omit the update on the outer loops, change the loop condition according to what I said earlier, and it should work as well, but that's borderline readable ;)
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Sat Jun 09, 2007 2:56 am

I ran into the getHeightAt function, this is only useful when you have Ogre 3d world space coordinates right? We want to be using the at function?

Also, I updated the code, but I'm still seeing the lines.

This is a very strange issue. The lines are only visible when you specify x Tiling at set intervals:

1 (no lines)
2 (lines)
4 (no lines)
8 (lines)
16 (no lines)
32 (lines)

See the trend? What does this mean?? so strange.. :?

Also, the zTiling can be any order of 2 and never causes lines, nor does it affect xTiling causing lines.
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Sat Jun 09, 2007 10:16 am

I ran into the getHeightAt function, this is only useful when you have Ogre 3d world space coordinates right? We want to be using the at function?
getHeightAt works in the coordinate scaling that you've chosen for the TerrainInfo object via setExtents. You can scale that to whatever you want and therefore scale getHeightAt to whatever you want. But I'd agree, to calculate discrete vertex positions, using the actual heightmap data is probably more elegant.

As for the ManualObject, so far as I see, you are still only filling in vertices, no index buffers. I have no idea if that mode of operation is even legal since from my understanding, the index buffers determine how a set of vertices is supposed to be rendered. If it is not legal, you shouldn't worry about it. If it is legal, then it's obviously rendering garbage (you aren't using the OpenGL renderer, by chance?), but I'm still not certain whether it's your fault. In any case, if I were you, I'd remove the ManualObject now and see if it's working with Opcode.
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Sat Jun 09, 2007 10:32 pm

Image

It looks like it's working! (2 x 2 tiling, meaning 4 tiles, and 4 bounding boxes seen in the screenshot) I won't know until the next part, where I actual make the ninja into a collision object and try to walk along the terrain. :wink:

My use of Manual Object must not be correct. Next time I have to do this type of work I won't put so much importance on Manual Object, especially if I'm not using it correctly. :lol:

I usually work in Direct X.
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Sun Jun 10, 2007 1:31 am

It seems indices are a must for Manual Objects. :wink:

Image

I hope this is the end of our index/vertex journey. :D (Unless Overhanging terrain manager integrates with ETM :twisted: )

Thanks for all your help up to this point!

I'd also like to note that I was seeing 250-300 FPS on release mode. ^_^

[Edit] Staring at the image I can see some bolded straight lines... Collision seems to be working for the ninja on the terrain, so I'm not going to invest any more thought into these lines. :lol:

Image

[/Edit]
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Tue Jun 12, 2007 11:31 pm

CABAListic, just thought I'd pop in and see if you've done anything recently with ETM? Have you found anything that you think needs work, or any core features that you want to add? (Is it likely we'll see v3?)

On another note, how goes progress on your own personal work? (revival of a strategy game or something similar)
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Wed Jun 13, 2007 2:58 am

I'm currently not missing any major features, and none have been suggested. I suppose further documentation and examples about material usage (especially dynamically created) may be helpful, but that'll have to wait. So the next release I make will be a maintenance release, including your getHeightmapData call, a Linux makefile and any bugfixes that I stumble upon (found one in the SplattingManager, for example).

As for our personal project, it's not going anywhere near as fast as I'd like it to, but that's due to University. I have the basics of the map editor and will hopefully be able to work on the game more in autumn :)
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Sun Jun 24, 2007 2:22 am

Ok, I've released a first maintenance update, v2.1. Changes in detail:
  • Fixed Linux compile errors and added a very basic Makefile. The CodeBlocks project file also has build targets for Linux now.
  • Corrected the ETPrerequisites.h header.
  • Fixed bug: SplattingManager::setNumTextures(0) (integer overflow)
  • Fixed bug: Loading brushes from images failed for some image formats
  • Added feature: You can now pass the SplattingManager a list of texture colours, which are then used to generate a colour map.
  • Added feature: createMinimap function which takes a colour map and a lightmap and modulates the two to produce an image that is suitable for a terrain minimap.
  • Added feature: TerrainInfo::getHeightmapData() call

The added minimap feature I felt necessary for my map editor, and since I think that someone else might also need it, I included it into the ETM library. Here's a quick example of what ETM is currently capable of doing (if you put enough effort to it):

Image

(Although, to be fair, the terrain in this screenshot is automatically generated by libnoise and auto-textured based on height :) )
0 x

User avatar
eugen
OGRE Expert User
OGRE Expert User
Posts: 1422
Joined: Sat May 22, 2004 5:28 am
Location: Bucharest
Contact:

Post by eugen » Sun Jun 24, 2007 2:26 am

Is getting better and better, thank u for such releases!
0 x

User avatar
KungFooMasta
OGRE Contributor
OGRE Contributor
Posts: 2087
Joined: Thu Mar 03, 2005 7:11 am
Location: WA, USA
Contact:

Post by KungFooMasta » Sun Jun 24, 2007 2:54 am

Awesome, thanks for the update!

How did you do the Decals? I haven't tackled it, just wondering if you used the wiki decal implementation or rolled your own. :wink:

I like the green Square effect. Lots of nice ideas for a terrain editting GUI. Good stuff!
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Sun Jun 24, 2007 2:25 pm

It's a projective decal as described in the wiki, yes. Seemed the easiest to accomplish with the best result, since I only need one of them.
0 x

User avatar
ahmedali
Gnome
Posts: 302
Joined: Fri Feb 20, 2004 8:52 pm
Location: Lahore, Pakistan

Post by ahmedali » Tue Jun 26, 2007 10:33 pm

I expect to do hundreds of ray collisions with the terrain. With 1000 rayIntersects calls perframe, even my DualCore3.4 couldnt stand it.

@KungFooMasta
Does OgreOpcode give boost with ray checks? Is it possible to batch lines/rays to optimise ray checks?
0 x

CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
Contact:

Post by CABAListic » Tue Jun 26, 2007 10:48 pm

1000 per frame? Heh, hm, that'll be trouble anyway, I'd guess. ETM's rayIntersects is by no means optimised (although it should be a definite improvement over TSM), but I doubt you could get it up to that speed. Of course, it depends on how your ray queries actually look.
Opcode is a dedicated collision library, so it may be faster. But then, it does collision tests on triangle level, so it probably has its limits, too.
0 x

Post Reply