- A function to generate the world for the first time :
void generate(int seed);
- And a function you can run everytime you need to render or interact with terrain :
double getAltitude(double longitude);
- a RN Generator. I’m using the standard C++ library one :
std::default_random_engine _rNGenerator;
- an array of floats/doubles to store some random “noise” :
QVector<double> _outline;
(I’m using a custom class that comes with QtCreator but any standard C++ array will do the job).
- an integer to store the number of hills :
int _nbHills;
- a float or double to store the maximum altitude :
double _mountainMaxHeight;
- an integer to adjust the general smoothness of the terrain :
int _smoothness;
- a couple more double values that will store the coordinates of your “mountains” :
double _mountainOffset1;
double _mountainOffset2;
Step 1.
void World::generate(int seed){
_outline.clear(); //Or the standard C++ variant
_rNGenerator.seed(seed);
//Terrain features
std::uniform_real_distribution<double> terrainOutlineDistribution(0.0, 1.0);
std::uniform_int_distribution<> nbHillDistribution(0, 12);
std::uniform_real_distribution<double> mountainMaxHeightDistribution(MOUNTAIN_MIN_HEIGHT, MOUNTAIN_MAX_HEIGHT); //Set those as #defines
std::uniform_int_distribution<> smoothnessDistribution(MIN_SMOOTHNESS, MAX_SMOOTHNESS);
std::uniform_real_distribution<double> mountainOffsetDistribution(0.0, 2*PI);
Step 2.
int i;
for(i=0;i<360;i++)
{
_outline.push_back(terrainOutlineDistribution(_rNGenerator)); //Or standard C++ variant
}
_nbHills = nbHillDistribution(_rNGenerator);
_mountainMaxHeight = mountainMaxHeightDistribution(_rNGenerator);
_smoothness = smoothnessDistribution(_rNGenerator);
_mountainOffset1 = mountainOffsetDistribution(_rNGenerator);
_mountainOffset2 = mountainOffsetDistribution(_rNGenerator);
}
Step 3.
double standardCoordinate(double longitude)
double World::standardCoordinate(double coordinate)
{
//Converts coordinate in radians to an equivalent value within [0;2*PI[
double a = coordinate * 1000000;
double b = (2*PI) * 1000000;
a = int(a)%int(b);
b = a/1000000;
if(b<0.0)
b = (2*PI)+b;
return b;
}
double World::getAltitude(double longitude)
{
//longitude is converted to degrees
double location = (standardCoordinate(longitude)/(2*PI))*360.0;
double progressOnCurve = location - floor(location);
double minHeightFactor;
double maxHeightFactor;
double heightFactor;
if(floor(location) == 359) //This is for the last meridian of your world, that needs to be connected to the first
{
if(_outline[359]<=_outline[0]) //going uphill
{
if(progressOnCurve <= 0.5) //first half
{
maxHeightFactor = (_outline[359]+_outline[0])/2;
minHeightFactor = _outline[359];
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
* (1 - (sqrt((1-(2*progressOnCurve))) + (1 - sqrt((1-(2*progressOnCurve)))) * (1-(2*progressOnCurve))));
}
else //second half
{
maxHeightFactor = _outline[0];
minHeightFactor = (_outline[359]+_outline[0])/2;
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
*(sqrt(2*(progressOnCurve-0.5)) + (1 - sqrt(2*(progressOnCurve-0.5))) * (2*(progressOnCurve-0.5)));
}
}
else //going downhill
{
if(progressOnCurve <= 0.5) //first half
{
maxHeightFactor = _outline[359];
minHeightFactor = (_outline[359]+_outline[0])/2;
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
* (sqrt(1-(2*progressOnCurve)) + (1 - sqrt(1-(2*progressOnCurve))) * (1-(2*progressOnCurve)));
}
else //second half
{
maxHeightFactor = (_outline[359]+_outline[0])/2;
minHeightFactor = _outline[0];
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
* (1 - (sqrt(2*(progressOnCurve-0.5)) + (1 - sqrt(2*(progressOnCurve-0.5))) * (2*(progressOnCurve-0.5))));
}
}
}
else
{
if(_outline[floor(location)]<=_outline[floor(location)+1]) //going uphill
{
if(progressOnCurve <= 0.5) //first half
{
maxHeightFactor = (_outline[floor(location)]+_outline[floor(location)+1])/2;
minHeightFactor = _outline[floor(location)];
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
* (1 - (sqrt((1-(2*progressOnCurve))) + (1 - sqrt((1-(2*progressOnCurve)))) * (1-(2*progressOnCurve))));
}
else //second half
{
maxHeightFactor = _outline[floor(location)+1];
minHeightFactor = (_outline[floor(location)]+_outline[floor(location)+1])/2;
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
*(sqrt(2*(progressOnCurve-0.5)) + (1 - sqrt(2*(progressOnCurve-0.5))) * (2*(progressOnCurve-0.5)));
}
}
else //going downhill
{
if(progressOnCurve <= 0.5) //first half
{
maxHeightFactor = _outline[floor(location)];
minHeightFactor = (_outline[floor(location)]+_outline[floor(location)+1])/2;
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
* (sqrt(1-(2*progressOnCurve)) + (1 - sqrt(1-(2*progressOnCurve))) * (1-(2*progressOnCurve)));
}
else //second half
{
maxHeightFactor = (_outline[floor(location)]+_outline[floor(location)+1])/2;
minHeightFactor = _outline[floor(location)+1];
heightFactor = minHeightFactor + (maxHeightFactor-minHeightFactor)
* (1 - (sqrt(2*(progressOnCurve-0.5)) + (1 - sqrt(2*(progressOnCurve-0.5))) * (2*(progressOnCurve-0.5))));
}
}
}
Step 4.
double smoothness = _smoothness;
return (heightFactor/smoothness+((smoothness-1)/smoothness))
*((cos(longitude*_nbHills)+1)/8+0.75)
*((cos(longitude-_mountainOffset1)+1)/4+0.5)
*((cos(longitude-_mountainOffset2)+1)/4+0.5)
*_mountainMaxHeight;
}