Welcome
Welcome to xnaquickstart

You are currently viewing our boards as a guest, which gives you limited access to view most discussions and access our other features. By joining our free community, you will have access to post topics, communicate privately with other members (PM), respond to polls, upload content, and access many other special features. Registration is fast, simple, and absolutely free, so please, join our community today!

TerrainEntity - demystified

Anything that is not postable elsewhere :)

Moderators: Coordinators, Developers

TerrainEntity - demystified

Postby Riki on Fri Jan 09, 2009 10:58 pm

The TerrainEntity is rather uncompleted and has a few design bugs, but if still anyone cares here's an explanation what works and what doesn't!

InitTerrainTextures()
If you take a look inside the SampleScene.LoadTerrain() first thing is a call to the InitTerrainTextures().
This can be commented out since this function does absolutely nothing, even worse it loads content which gets wasted! In fact the whole function should be removed from the TerrainEntity. Why is that so you might ask ? Continue reading...

Material

The current QSE implementation favors materials, if a material ever gets loaded it wins and overrides whatever the users/application tries to do. The material is hardcoded inside the TerrainEntity:
Code: Select all
this.material = this.Game.Content.Load<Material>("Material/Terrain");
but unfortunately even if this would not be the case or you comment it out there is no way to pass the needed parameters (like which textures to use) to the effect in charge for rendering the terrain (same applies to any other entity/component).
Now the funny part: Although the TerrainEntity has a hardcoded dependency on this material, the material is not a part of the project. So if you use just the library to load terrains inside your application guess what - it wouldn't work. Lucky enough the very same material is included in the SampleGame_Windows (I don't care about the Xbox so didn't bother to check). This is the case not only with the material but with the shader as well, you have to lookup inside the Sample project not inside the library.

InitTerrainNormalsTextures()
As one can already guess...toxic waste, best to be surrounded with /* ... */

Initialize(string heightImagePath, string terrainTexturePath, int scaleFactor, int? smoothingPasses, IPhysicsScene physicsScene)
The first thing really needed..well almost.

heightImagePath : this is the height map which defines the terrains shape.
I am not in the mood explaining what a height map is, if you are relay that desperate search the wiki. BTW the height map must have a size which is a power of two!

terrainTexturePath: color texture being drawn over the terrain :chillpill: WRONG! Same as any texture - not used since the material overrides it. To have a consistent behavior it gets loaded though...and wasted!

int scaleFactor: int! Wait a moment, did I read int??? OMG an int!!! Actually I am glad it's an int, could never understand that boring 3D applications forcing me to think about objects as actually having 3 dimensions. 2D is good but this is even better, why bother with a different scale for width and a height (not to be misunderstood as terrain height which is y, those are the x,z coordinates) if you can define both with an int. I have a friend who's cousin read a book about some lame 3D application defining object scales with a vector3! Oh BTW this gets clamped to 1-4.
And finally the whole truth: the scaleFactor scales actually just the terrain's height (y direction) while the terrain's x and z directions can't be scaled - those are set through the material by the SCALE_FACTOR and TERRAIN_WIDTH semantic!

int? smoothingPasses: I think this one works...something. Though didn't bother to check what exactly, I noticed that the terrain height gets somehow influenced with this parameter so I have to check what is exactly going on there.

IPhysicsScene physicsScene: You must pass here the physics scene created by a physicsmanager, usually the only one you have and can have is game.SceneManager.PhysicsScene. Cant figure out whats the point of it since the terrain already gets a pointer to the game object and could ask it for the physics scene. If you don't need physics ( I usually don't since my terrains are lightweight, or you have a client server concept where your server just renders stuff) there is nothing you could do, passing a null wouldn't work. My workaround was not to create a PhysicsComponent from the terrain, this way the physics system wouldn't take the terrain into account.

Setup a terrain

Code: Select all
Terrain newTerrain = new Terrain(this.game, LOD.High);
newTerrain.ElevationStrength = 25;
newTerrain.Initialize("./Images/Heightmaps/borderNW_hm", "./Images/Terrainmaps/borderNW_n", 4, 2, this.game.SceneManager.PhysicsScene);
newTerrain.SetupLODS();
this.game.SceneManager.AddEntity(newTerrain);

The only thing to note in the code above is the ElevationStrength . It's usage is related with terrain height, but I had to change the terrain generation function since it had some serious issues and I didn't had a use for this parameter anymore.
Just to be clear, I did those changes locally, they are not applied to the QSE engine - so you need to set a value here!

How Terrain works
Lets start by saying that the height map used for generating the terrain gives more or less what you would expect, anything else not.
Textures passed as parameters are not applied.
The smart reader would open the material and check which textures are used, but be careful. The name of the game is WYSWYNG (what you see is NOT what you get).
Although the material defines a color texture with : <sampler semantic="TEXTURE_MAP">../Images/TerrainMaps/gcanyonTerrain</sampler>, this is not the texture drawn over the terrain.
Please open this texture inside the SmapleGame content project and you will see it contains just red, blue or green colored areas :wall:
If you opened the material you probably noticed that there are some more textures defined like grass, sand and rock. Actually those are exactly the textures that get drawn over your terrain!
So how does that work?
The shader takes care of that. First a lookup inside the texture map is made to check whats the pixel color in the texture, based on that color one of the three (grass, sand, rock) textures are sampled to get the pixel color at the searched position.

Whats wrong with the terrain heights
The current implementation has two height generation passes. The first one calculates the max and the min heights.
The second pass normalizes those heights to fit into 0-1 range. Of course having a terrain with heights only from 0 to 1 wouldn't make any sense so the ElevationStrength property was used multiply the height so you would get heights in range 0- ElevationStrength. The scaling was also applied and the actual height range was 0-ElevationStrength * scale, but since the scale is applied to all directions (not with the scaleFator) it doesn't change the terrains perception.
Design flaw: If you have just a single terrain entity you'd be living happily ever after but if you have two terrains with edges that must match perfectly the normalization process would mess it up the big way.
Say you have low land on east and mountains on west here ends your 1st terrain, next comes the 2nd terrain continuing with mountains and while moving west the mountains get higher and higher.
During the normalization of the 2nd terrain the heights would be scaled in the same range as the 1st terrain :x meaning your ultrahigh mountain on the very west gets the same high as the modest mountain on the west part in the 1st terrain while your smaller mountain on the east of the 2nd terrain suddenly gets as low as the lowest parts on the 1st terrain!

Here is my LoadHeightData() version:
Code: Select all
private void LoadHeightData(string heightImagePath)
        {
            this.heightMap = this.Game.Content.Load<Texture2D>(heightImagePath);

            //  [Riki]: Not sure if this restriction is realy needed?             
            if ((QSMath.IsPowerOfTwo(this.heightMap.Width) && QSMath.IsPowerOfTwo(this.heightMap.Height) == false))
            {
                throw new Exception("Height maps must have a width and height that is a power of two.");
            }

            //[Riki]: maxed out initial values
            float minimumHeight = float.MaxValue;             // Set a high number because this will go drop with the math below
            float maximumHeight = float.MinValue;             // Opposite of line above

            this.Size = heightMap.Width;             // Sets the map width to the same as the heightmap texture.

            // We setup the map for colors so we can use the color to determine elevations of the map
            Color[] heightMapColors = new Color[this.size * this.size];

            // XNA Built-in feature automatically copies pixel data into the heightmap.
            this.heightMap.GetData(heightMapColors);           
            this.heightData = new float[size, size];  // Create an array to hold elevations from heightMap

            // Find minimum and maximum values for the heightmap file we read in
            for (int x = 0; x < size; x++)
                for (int z = 0; z < size; z++)
                {                   
                    //[Riki]:   added scaleFactor.
                    //          The min max values are not needed but handy for debugging.
                    //          Note that we read only one color component since in greyscale images all components are set to same value!
                    this.heightData[x, z] = (heightMapColors[x + z * size].R)  * this.scaleFactor;
                    if(this.heightData[x, z] < minimumHeight) minimumHeight = this.heightData[x, z];
                    if(this.heightData[x, z] > maximumHeight) maximumHeight = this.heightData[x, z];
                }
           
           
            //// Set height by color, and then alter height by min and max amounts

            //[Riki]: I don't get it...actually I get it but this is simply wrong and therefore commented out!
            //        The author wannted to normalise all the heights to fit into a range [0-1], further the normalised height is multiplied with elevation strength to get a reasonable height and at last multiplied again with a scale.
            //        Normalizing a heightmap would be a serious design issue since:
            //              a) would make impossible to load adjacent terrains from two heightmaps whith different mins or maximum heights
            //                  e.g.   map1 | map2
            //                              |,---------_____
            //                  ___,------'/|
            //
            //                  if we normalize both maps the resulting terrain would be:
            //                  ___,------'/,---------_____
            //
            //              b) the elevationStrength factor which is a MUST in a normalized scenario would make a WYSWYG editor impossible
            //       

            //for (int x = 0; x < size; x++)
            //    for (int z = 0; z < size; z++)
            //    {
            //        //this.heightData[x, z] = (heightMapColors[x + z * size].R) + (heightMapColors[x + z * size].G) + (heightMapColors[x + z * size].B);                   
            //        this.heightData[x, z] = (this.heightData[x, z] - minimumHeight) / (maximumHeight - minimumHeight) * this.elevationStrength * this.scaleFactor;
            //    }


            this.heightMap = null;
            this.terrainEffect.Parameters["TerrainWidth"].SetValue(this.size);
        }



Conclusion
Q: Is it working?
A: Yes!

Q: Can I use it with my terrain (heightmap+texturemap)?
A: Not in this life!

Q: That sucks I have so nice texturemaps, and don't wanna use that RGB crap. Is there no other way?
A: No, not untill you rewrite the shader! Actually I wasn't totally honest here. You could add your texturemap to the content, change the material so that say rock points to your map, figure out what color is used to define rocktexture and fill a new map with only that color, finally replace the texturemap in the material to point to your single color map. Guess what - changing the shader is much easier!

Q: Ok I'll use your workaround, but my texture maps are larger then my height maps (which is the usual way of doing terrains), will that work?
A: Not in this life! Changing the shader is the only option.

Q: Can I have different x and z sizes of my terrain?
A: Not in this life!

Q: Can I at least apply different scales to width and height?
A: Not in this life!

Q: I have multiple terrains in my app, can I have different heightmaps for each one of them?
A: Not in this life!

Q: OK, so what can I do with such an implementation
A: You have some decent textures right out of the box beeing painted over your terrain and you can of course exchange the default textures (sand rock grass) with your own textures.

Q: OK I will use just one TerrainEntity with my heightmap, but I need to generate the RGB 'texturemap' so it would fit my terrain shape! How can I do that?
A. Yes that's true you have to...but I cant figure out an easy way to do this except drawing it by hand.

Q: Why is it implemented that way?
A: Don't ask me, my guess is that's the way Xna tutorials demonstrate terrain rendering. If my memory don't plays silly games with me I could swear I remember some terrain demos where a given heightmap defines the terrain shape but the colors are generated in a procedural way (very high white for snow, mid heights brown, low terrain parts green), I also remember seeing such an approach where the terrain colors are not generated height based but through a texture lookup which is a pointer to other textures.

Q: Will you change this?
A: Probably, if enough people here show interest.

Q: Will you at least write a shader that could handle my texturemap instead that RGP loppkup indirection?
A: Yes!
User avatar
Riki
Beginner
 
Posts: 21
Joined: Tue Dec 23, 2008 12:28 pm
Location: Croatia

Re: TerrainEntity - demystified

Postby lordikon on Tue Jan 13, 2009 10:03 pm

Riki wrote:The TerrainEntity is rather uncompleted and has a few design bugs, but if still anyone cares here's an explanation what works and what doesn't!

InitTerrainTextures()
InitTerrainNormalsTextures()


These used to do something, somehow the rendering system has taken over and loads them itself. Unfortunately I can't even get ahold of the guy who worked on it, so I'd have to take some time to figure out how it is being loaded.

Riki wrote:Initialize(string heightImagePath, string terrainTexturePath, int scaleFactor, int? smoothingPasses, IPhysicsScene physicsScene)
The first thing really needed..well almost.

heightImagePath : this is the height map which defines the terrains shape.
BTW the height map must have a size which is a power of two!


So far, yes. Although I don't see a problem with rectangular heightmaps later on.

Riki wrote:terrainTexturePath: color texture being drawn over the terrain :chillpill: WRONG! Same as any texture - not used since the material overrides it. To have a consistent behavior it gets loaded though...and wasted!

Yes, also being overriden by the rendering system.

Riki wrote:int scaleFactor: int! Wait a moment, did I read int??? OMG an int!!! Actually I am glad it's an int, could never understand that boring 3D applications forcing me to think about objects as actually having 3 dimensions. 2D is good but this is even better, why bother with a different scale for width and a height (not to be misunderstood as terrain height which is y, those are the x,z coordinates) if you can define both with an int. I have a friend who's cousin read a book about some lame 3D application defining object scales with a vector3! Oh BTW this gets clamped to 1-4.
And finally the whole truth: the scaleFactor scales actually just the terrain's height (y direction) while the terrain's x and z directions can't be scaled - those are set through the material by the SCALE_FACTOR and TERRAIN_WIDTH semantic!

Scale should be working properly, you'll notice the 1024x1024 terrain loads all the way out to 4096x4096, this is because the scale is set to 4 I believe. It is an int because having terrain vertices (at least they X, Z coords) seems like an annoyance when debugging. This could easily be changed to a float though. I'll take a look into this if something is wrong, it shouldn't be altering the Y values, only X and Z. Elevation strength is where Y is set.

Riki wrote:int? smoothingPasses: I think this one works...something. Though didn't bother to check what exactly, I noticed that the terrain height gets somehow influenced with this parameter so I have to check what is exactly going on there.

This works wonderfully, although slowly, and would be integrated into the terrain content processor when one was made. Set this to value to 1 and see how your terrain looks......a little rough.

Riki wrote:IPhysicsScene physicsScene: You must pass here the physics scene created by a physicsmanager, usually the only one you have and can have is game.SceneManager.PhysicsScene. Cant figure out whats the point of it since the terrain already gets a pointer to the game object and could ask it for the physics scene. If you don't need physics ( I usually don't since my terrains are lightweight, or you have a client server concept where your server just renders stuff) there is nothing you could do, passing a null wouldn't work. My workaround was not to create a PhysicsComponent from the terrain, this way the physics system wouldn't take the terrain into account.

It has been awhile, but if I remember right you pass the terrain shape to the physics scene as a heightmap-shape and it will create a heightmap for the physics system to use.

Riki wrote:Design flaw: If you have just a single terrain entity you'd be living happily ever after but if you have two terrains with edges that must match perfectly the normalization process would mess it up the big way.
Say you have low land on east and mountains on west here ends your 1st terrain, next comes the 2nd terrain continuing with mountains and while moving west the mountains get higher and higher.
During the normalization of the 2nd terrain the heights would be scaled in the same range as the 1st terrain :x meaning your ultrahigh mountain on the very west gets the same high as the modest mountain on the west part in the 1st terrain while your smaller mountain on the east of the 2nd terrain suddenly gets as low as the lowest parts on the 1st terrain!

Agreed. Using greyscale only gives you 255 elevations however. Using color heightmaps you can get quite a bit of detail, but they're nearly impossible to edit without an editor. Using a elevation modifier is a stopgap to let players affect elevations more than simply 0-255. Normalizing the value would have to be removed later as well.

I understand your frustrations, believe me. When I started the terrain system it was much easier to use. The rendering system has crippled the ability to modify it easily. In fact, feel free to take a look at v0.182b, which is the previous version of the engine before the rewrite and rendering system. Sure, v0.182b is much much worse than v0.19+, but it actually allows you to change the terrain textures in the code, so you can at least see what I had intended.

The level editor is truly the missing link to the terrain system, it would prevent the user from having to create their own heightmap and texture splatting map.
User avatar
lordikon
Administrator
 
Posts: 342
Joined: Thu Apr 03, 2008 11:26 pm
Location: Colorado

Postby Knight_Christian on Sat Apr 04, 2009 9:15 am

@ Riki: Your thoughts sound good, so why don't you join the project? Since we have almost no active developers (including me, so far - but trying to continue working on the engine), we could need each possibly interested person to help us out. And since you got ideas how to improve stuff etc. I think you could help us pushing the new engine :)
- Musician (Bass guitar, Keyboard)
- Developer
- C#/Basic/.NET Coder
- Happy with all of it
Knight_Christian
Beginner
 
Posts: 44
Joined: Thu Apr 03, 2008 12:15 pm
Location: Germany


Return to General discussions

Who is online

Users browsing this forum: No registered users and 0 guests