project-navigation
Personal tools

Author Topic: (Technical) Terrain Generator for Air Combat and possibly other stuff...  (Read 8982 times)

Offline Destructavator

  • Combination Multiple Specialty Developer
  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 1908
  • Creater of Scorchcrafter, knows the zarakites...
    • View Profile
OK, if anyone wants to try my code for rapid terrain generation for use in realtime 3D animation or gameplay, I have an updated version of my demo that includes it on my website:

http://www.destructavator.com/media/terrain_test_6.7z
or the very latest with a different rendering technique (read the thread) at: http://www.destructavator.com/ufoai/TigersEye3.7z


The basic, non-technical topic which also talks about implementation is at : http://ufoai.org/forum/index.php/topic,6771.msg53947.html#msg53947

I'm designing it not just for UFO AI but also for other GPL projects as well, including some of my own.  I'll try to make it so that the code can be easily brought into nearly any C++ project.

NOTE that although the demo uses Irrlicht to display the created map, my code itself doesn't require Irrlicht, it just relies on a few basic libraries that are standard for C++.  At some point I plan to add a check for #define statements that indicate the presence of libPNG and zlib, and if it finds that they are present it will enable a few additional functions to write the created map and object textures to a file, so that the programmer doesn't have to write a loop using GetTexturePixelData() for the color values of each pixel one-by-one.

The created map can be thought of as a grid of 250x250 "spots" - each spot having several pixels of texture and other information that is retained by the instance of the terrain class after it makes the map.  The reason it isn't 256x256 is that the number of verts for the mesh that the map is made on MUST be 1 unit greater (in each dimension) than the number of spots, because each spot on the map has four corners, with most of the spot and texture data pertaining to the center or the whole square.

So the default size map is 250x250 spots, with 251x251 verts, and each spot by default (unless specified otherwise in map creation parameters) has 4x4 pixels of texture, which means the texture is 1000x1000 pixels.

If you ask "Why don't you just make it 256x256 spots anyways, with 257x257 verts and a 1024x1024 pixel texture?" the answer is limitations of the OBJ model format.  The info I've found on the format says that 257x257 verts would mean too many index entries in the model file, which some 3D engines wouldn't be happy about.

The data retained for each spot currently is in several structs for each spot, aside from the texture pixel data, there's also a struct that one can get() information from:

Code: [Select]
struct TerrainPointData
{
float cornerHeight[4];
int cornerIndex[4];
float lowestPoint;
float highestPoint;
float centerLocation[3];
float steepness;
terrainLevels terrainAtPoint;
int isCoveredInSnow;
int isAboveWater;
};

I'll eventually expand this to include population information that can be used to decide what map tiles to load for the battlescape when a UFO lands or crashes at a certain spot on the map.  The reason for the isCoverdInSnow and isAboveWater being int and not bool is that each spot could have part of it under water or snow, and other part of it not, so the int value is compared with how many sub-parts of each spot there are, to make a ratio.  (By default the demo makes the map with 16 sub-spots as 4x4 for each spot, so a value of "8" for isCoveredInSnow means half the spot has snow on it.)  The "terrainAtPoint" is an enum that holds data on the type of terrain at that spot.  The "climate type" refers to what basic type of terrain the whole map is.

Code: [Select]
enum climateTypes
{
cm_TestDemo,
cm_Temperate,
cm_Tropical,
cm_Desert,
cm_Barren,
cm_Arctic,
cm_Ocean,
cm_Mountain,
cm_Volcanic,
cm_Lunar,
cm_Alien,
cm_MAX_COUNT
};

enum terrainLevels
{
trn_Deep_Water,
trn_Mid_Water,
trn_Shallow_Water,
trn_Sand,
trn_Sand_White,
trn_Sand_Grey,
trn_Sand_Dark,
trn_Grass,
trn_Grass_Dark,
trn_Hills,
trn_High_Hills,
trn_Low_Mountains,
trn_Mid_Mountains,
trn_High_Mountains,
trn_Barren,
trn_Barren_Dark,
trn_Barren_Bright,
trn_Volcanic_Dark,
trn_Volcanic_Mid,
trn_Volcanic_Bright,
trn_Alien_Purple_Dark,
trn_Alien_Purple_Mid,
trn_Alien_Purple_Bright,
trn_MAX_COUNT
};
« Last Edit: September 12, 2012, 04:13:55 pm by Destructavator »

Offline Destructavator

  • Combination Multiple Specialty Developer
  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 1908
  • Creater of Scorchcrafter, knows the zarakites...
    • View Profile
This shows an example entry for a type of "climate" that refers to the whole map:

Code: [Select]
static const climateData mData[] =
{
{
cm_TestDemo, /* index */
11, /* number of levels (color zones) */
0.075f, /* vertical scale ratio */
0.25f, /* water table height factor */
100, /* min terrain passes */
120, /* max terrain passes */
0.015f, /* terrain pass min horizontal factor */
0.120f, /* terrain pass max horizontal factor */
1.00f, /* terrain pass vertical factor */
0.05f, /* terrain pass warp shaping factor */
0.75f, /* max terrain height factor */
fld_Water_Light, /* Type of fluid the water plane is made out of (texture) */
{
{trn_Deep_Water, 0.05f, 0.80f, 0.0f, 0.01f, 1.0f},
{trn_Mid_Water, 0.10f, 0.80f, 0.0f, 0.01f, 1.0f},
{trn_Shallow_Water, 0.15f, 0.80f, 0.0f, 0.01f, 1.0f},
{trn_Sand, 0.20f, 0.95f, 0.0f, 0.01f, 1.0f},
{trn_Grass, 0.30f, 0.95f, 0.0f, 0.01f, 1.0f},
{trn_Hills, 0.40f, 0.95f, 0.0f, 0.2f, 1.0f},
{trn_High_Hills, 0.45f, 0.95f, 0.0f, 0.3f, 1.0f},
{trn_Low_Mountains, 0.75f, 0.95f, 0.05f, 2.5f, 1.0f},
{trn_Mid_Mountains, 0.90f, 0.95f, 0.15f, 3.5f, 1.0f},
{trn_High_Mountains, 1.20f, 0.0f, 0.25f, 4.5f, 1.0f},
{trn_Alien_Purple_Dark, 12.00f, 0.0f, 0.40f, 5.0f, 1.0f}
}, /* Terrain type, Max Altitude, Color Blend, Snow Angle Max, Height Jitter, Height Level Weight */
},
...

Each climate type has multiple vertical levels with different characteristics.

BTW, sorry about the messed-up TAB length/spacing when I copy/paste from the source code.

The generator also supports four types of water planes, two for actual water, one for frozen water or liquid, and one for molten lava.
« Last Edit: June 14, 2012, 06:17:45 pm by Destructavator »

Offline Destructavator

  • Combination Multiple Specialty Developer
  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 1908
  • Creater of Scorchcrafter, knows the zarakites...
    • View Profile
The map code will also support dotting the landscape with tiny, simple objects depending on the type of terrain, although I haven't written all the code for it yet:

Code: [Select]
enum TerrainObjectType
{
tno_Tree_Basic,
tno_Tree_Palm,
tno_Cactus,
tno_Shrub,
tno_Boulder,
tno_House_Farm,
tno_House_Town,
tno_House_Estate,
tno_Park_Small,
tno_Park_Large,
tno_OfficeBuilding_Small,
tno_OfficeBuilding_Medium,
tno_OfficeBuilding_Large,
tno_City_Complex_Small,
tno_City_Complex_Medium,
tno_City_Complex_Large,
tno_City_Complex_Large_Tall,
tno_Military_Installation_Small,
tno_Military_Installation_Medium,
tno_Military_Installation_Large,
tno_Factory_Small,
tno_Factory_Large,
tno_Industrial,
tno_Mine,
tno_Airport,
tno_Stadium,
tno_Alien_A,
tno_Alien_B,
tno_Alien_C,
tno_MAX_COUNT
};

This list is just a start, and can be expanded.

Offline Sandro

  • Squad Leader
  • ****
  • Posts: 240
  • Maintenance guy for UFO:AI 3D engine
    • View Profile
Re: (Technical) Terrain Generator for Air Combat and possibly other stuff...
« Reply #3 on: September 04, 2012, 07:44:03 pm »
Still haven't studied the code in detail, but it appears to me that your 4x4 texels per grid cell texture is just to add some random variety, right? Indeed, if you just render non-textured vertex-colored landscape, that will be not fun. But direct-mapped texture will grow to fast with increase of resulution, so IMHO it is not the way to go.

On the other hand, even while staying strictly within OpenGL 1.3 and OpenGL ES 1.1 limitations we can do detail rendering in the very different way: define extra "overlay" texture and use some other noise (or not) texture to select or blend between vertex-defined color and the "overlay" texture color to create the fine detail.
If you can live without colored lights (which will require baking into both vertex colors and overlay texture), overlay texture can be mapped in tiled way, which, if combined with some odd scale and rotation can make for detailed and still (visually) non-repeating texture pattern.
Even more, if we allow it to be mapped in non-contiguous way (easy with UFO:AI, since we are rendering triangle soup anyway), it can be used as a sort of
"overlay texture atlas", which can contain some grass patches, scattered sand or gravel, or more realistic cutoff of snowy areas, dissolving into series of small heaps before disappearing (just use a binary, on/off noise texture for that).

Just a small idea based on knowing how OpenGL works and a bit of imagination :)

Offline Destructavator

  • Combination Multiple Specialty Developer
  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 1908
  • Creater of Scorchcrafter, knows the zarakites...
    • View Profile
Re: (Technical) Terrain Generator for Air Combat and possibly other stuff...
« Reply #4 on: September 12, 2012, 03:07:03 am »
Still haven't studied the code in detail, but it appears to me that your 4x4 texels per grid cell texture is just to add some random variety, right? Indeed, if you just render non-textured vertex-colored landscape, that will be not fun. But direct-mapped texture will grow to fast with increase of resulution, so IMHO it is not the way to go.

On the other hand, even while staying strictly within OpenGL 1.3 and OpenGL ES 1.1 limitations we can do detail rendering in the very different way: define extra "overlay" texture and use some other noise (or not) texture to select or blend between vertex-defined color and the "overlay" texture color to create the fine detail.
If you can live without colored lights (which will require baking into both vertex colors and overlay texture), overlay texture can be mapped in tiled way, which, if combined with some odd scale and rotation can make for detailed and still (visually) non-repeating texture pattern.
Even more, if we allow it to be mapped in non-contiguous way (easy with UFO:AI, since we are rendering triangle soup anyway), it can be used as a sort of
"overlay texture atlas", which can contain some grass patches, scattered sand or gravel, or more realistic cutoff of snowy areas, dissolving into series of small heaps before disappearing (just use a binary, on/off noise texture for that).

Just a small idea based on knowing how OpenGL works and a bit of imagination :)

Please check out http://www.destructavator.com/ufoai/TigersEye1.7z

Also a second revamped version is: http://www.destructavator.com/ufoai/TigersEye2.7z

For anyone else who didn't see the IRC discussion about this, this latest link goes to a demo that sort of uses Sandro's idea, and yes, it works much better and runs faster.
« Last Edit: September 12, 2012, 11:41:10 am by Destructavator »

Offline Destructavator

  • Combination Multiple Specialty Developer
  • Administrator
  • PHALANX Commander
  • *****
  • Posts: 1908
  • Creater of Scorchcrafter, knows the zarakites...
    • View Profile
Re: (Technical) Terrain Generator for Air Combat and possibly other stuff...
« Reply #5 on: September 12, 2012, 04:15:05 pm »
Sorry everyone, I used the wrong compiler settings for the new version, you can find a version built correctly for 32-bits at: http://www.destructavator.com/ufoai/TigersEye3.7z

Offline Sandro

  • Squad Leader
  • ****
  • Posts: 240
  • Maintenance guy for UFO:AI 3D engine
    • View Profile
Re: (Technical) Terrain Generator for Air Combat and possibly other stuff...
« Reply #6 on: September 15, 2012, 03:33:14 pm »
Another technical suggestion: since it is proved that basic design works, you should move from "proof of concept" state to "working protopype", and do some required cleanup.
For example, like keeping data in per-parameter arrays instead of arrays of structures (it saves memory bandwidth for most algos, and this is how OpenGL expects it anyway).
Also, the important thing is to define all your measures using the same unit. Personally, I would suggest using 1 meter, since scale defined this way gives easy to comprehend range of values, and will allow you to make things in realistic scale with no problems.