Thursday, May 28, 2009

CEGUI Ugliness And UML Diagrams

Texture Generator w/ GUI Screenshot

Yes, I looked at the Taharez and Windows skins before settling for the "Vanilla" skin. Lesser of three evils no? Integrating CEGUI (Crazy Eddies Graphical User Interface) was somewhat painless but there are some definite quirks with the code and the layout generator tool that I ran into:

  • When you deactivate a window in CEGUI your subsequent calls to getActiveChild() will still return the deactivated window.

  • The layout generator tool undo system does not seem to work and the copy and paste functionality is also finicky.

  • The use of a data directory for the layout tool is not intuitive to me. It should just use its' own program install directory and save the .layout files where you indicate via the save file dialog - isn't that the main purpose of the tool? Why confuse your average n00b by asking them where they want their data directory to be when they install? It's not like you can easily change the data directory if you select the wrong location as I found out.

  • CEGUI window skins seem to stretch by default so I will eventually have to figure out how to make them pixel perfect and resolution dependent where needed.


The reason I needed a GUI was that setting the 8 different blend properties for the 16 different layers was going to be a pain to do (and slow) by keyboard alone. I also wanted to see how easy it would be to work in what seems to be the "go to" Ogre GUI (it gets used in several demos). The GUI allows me to move the sliders that adjust the values in the look up table (or I can type the values in by using the edit boxes and pressing 'Enter'). When values are adjusted the terrain texture is automatically updated and the screen refreshes to show the new texture. I use the mouse right click to toggle between GUI mode (when the mouse cursor is revealed) and fly mode (when the cursor is hidden and the mouse controls where you look).

After integrating the GUI I realized how complicated this little tool had become and that in order to un-complicate it and make it easier to integrate into the planet engine that I would need to break out the UML tools (I used OmniGraffle Pro) in order to get a good look at the architecture.

Texture Generator UML diagram

As you can (or cannot) see, it's a tad complicated for what looks so basic by the screenshot and I'm not even including all the getter/setter functions. To integrate this with the planet engine I think I will make the render to texture a singleton helper and if I bring the GUI over then I'll probably make a GUI class that receives input from the InputHandler and then translates the GUI actions into application actions. The InputHandler will be responsible for non-GUI actions like movement, screenshots, quiting the program etc.

Sorry this isn't a very exciting post, but it was necessary. I'm going to try and get the quadtree test program running on Vista and then try to integrate the new terrain texture process. See how I said that in one sentence? It will probably take me weeks to get running right.

Oh and I should say I'm excited to see that Steve Streeting (the main dev behind Ogre 3D) appears to be working on some terrain improvements to Ogre which I plan on dissecting. You can follow his tweets at http://twitter.com/sinbad_ogre. Mine are at http://twitter.com/petrocket.

Tuesday, May 12, 2009

Dynamically Generated Heightmap Textures

Everyone has to do several of these in their life I'm sure. I mean, implementing some kind of blend scheme for making a texture for a height field is terrain 101.

Terrain Texture Image

heightmap image 2

The particular method I've been working on is based on Ysaneyva's latest method. He generates a texture for each of his terrain patches using a shader that blends up to 16 textures based on a look up table. The inputs for the look up table are slope and height - read more about it at Ysaneya's dev journal post on atmospheric scattering and terrain texturing

My test implementation here uses Ogre3d as the graphics engine. I'm using the Octree scene manager plugin to do the quick heightmap - but I highjack the textures used by the heightmap and overwrite them with my dynamically generated texture. The process is as follows:

1. create heightmap with octree terrain manager
2. load the heightmap image and create a slope map from it
3. render a full screen quad that has the the texture generation shader which combines up to 16 textures based on the terrain height,slope. The height corresponds to the y value and the slope corresponds to the x value in the look up table.
4. write the render to texture into the texture used by the heightfield

Here's what my look up table looks like:
Look up table image

I'm still experimenting with look up table values and blending between altitudes and slopes. Also, I need to add some random noise to the altitude/slope values to mix things up a bit.

Of course the actual planet textures won't be so blurry, or if I do end up using this resolution I will probably implement some kind of detail map on top of it.

Here's the fragment shader (GLSL) for the texture generation:

void main()
{

float altitude = texture2D(heightMap, gl_TexCoord[0].st).r;
float slope = texture2D(slopeMap, gl_TexCoord[0].st).r;

vec2 uvSize = vec2(uvExtents.z - uvExtents.x, uvExtents.w - uvExtents.y);

// diffuse UV should be the UV position relative to the face on the cube
// if we just used gl_TexCoord[0] we would have UV position relative to
// each quadrant and we'd get seams
vec2 diffUV = vec2(uvExtents.x + (gl_TexCoord[0].s * uvSize.x),
uvExtents.y + (gl_TexCoord[0].t * uvSize.y));

// tile the textures so they look better in this test
diffUV.x *= 10.0;
diffUV.y *= 10.0;

// get the 16 blending weights for this slope and altitude
vec4 weights0 = texture2D(lutTex, vec2(slope * 0.25 + 0.00, altitude));
vec4 weights1 = texture2D(lutTex, vec2(slope * 0.25 + 0.25, altitude));
vec4 weights2 = texture2D(lutTex, vec2(slope * 0.25 + 0.50, altitude));
vec4 weights3 = texture2D(lutTex, vec2(slope * 0.25 + 0.75, altitude));

// use w,x,y,z order because PNG uses pixel format A8R8G8B8
gl_FragColor = texture2D(diffTex0, diffUV) * weights0.w +
texture2D(diffTex1, diffUV) * weights0.x +
texture2D(diffTex2, diffUV) * weights0.y +
texture2D(diffTex3, diffUV) * weights0.z +
texture2D(diffTex4, diffUV) * weights1.w +
texture2D(diffTex5, diffUV) * weights1.x +
texture2D(diffTex6, diffUV) * weights1.y +
texture2D(diffTex7, diffUV) * weights1.z +
texture2D(diffTex8, diffUV) * weights2.w +
texture2D(diffTex9, diffUV) * weights2.x +
texture2D(diffTex10, diffUV) * weights2.y +
texture2D(diffTex11, diffUV) * weights2.z;
}


*edit* I should note that the above shader only combines 12 textures - of which I'm only using 8 for now - you should have no trouble adding the extra lines for diffTex12-diffTex15 and the weights3 vec4.