2013-07-29 07:13:03 -04:00
// Nosie3DGenerator.cpp
// Generates terrain using 3D noise, rather than composing. Is a test.
# include "Globals.h"
# include "Noise3DGenerator.h"
# include "../OSSupport/File.h"
2014-10-23 09:15:10 -04:00
# include "../IniFile.h"
2013-07-29 07:13:03 -04:00
# include "../LinearInterpolation.h"
# include "../LinearUpscale.h"
/*
// Perform an automatic test of upscaling upon program start (use breakpoints to debug):
class Test
{
public :
Test ( void )
{
DoTest1 ( ) ;
DoTest2 ( ) ;
}
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
void DoTest1 ( void )
{
float In [ 3 * 3 * 3 ] ;
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( In ) ; i + + )
2013-07-29 07:13:03 -04:00
{
In [ i ] = ( float ) ( i % 5 ) ;
}
Debug3DNoise ( In , 3 , 3 , 3 , " Upscale3D in " ) ;
float Out [ 17 * 33 * 35 ] ;
LinearUpscale3DArray ( In , 3 , 3 , 3 , Out , 8 , 16 , 17 ) ;
Debug3DNoise ( Out , 17 , 33 , 35 , " Upscale3D test " ) ;
}
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
void DoTest2 ( void )
{
float In [ 3 * 3 ] ;
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( In ) ; i + + )
2013-07-29 07:13:03 -04:00
{
In [ i ] = ( float ) ( i % 5 ) ;
}
Debug2DNoise ( In , 3 , 3 , " Upscale2D in " ) ;
float Out [ 17 * 33 ] ;
LinearUpscale2DArray ( In , 3 , 3 , Out , 8 , 16 ) ;
Debug2DNoise ( Out , 17 , 33 , " Upscale2D test " ) ;
}
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
} gTest ;
//*/
2014-11-09 08:33:37 -05:00
/** Linearly interpolates between two values.
Assumes that a_Ratio is in range [ 0 , 1 ] . */
inline static NOISE_DATATYPE Lerp ( NOISE_DATATYPE a_Val1 , NOISE_DATATYPE a_Val2 , NOISE_DATATYPE a_Ratio )
{
return a_Val1 + ( a_Val2 - a_Val1 ) * a_Ratio ;
}
/** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */
inline static NOISE_DATATYPE ClampedLerp ( NOISE_DATATYPE a_Val1 , NOISE_DATATYPE a_Val2 , NOISE_DATATYPE a_Ratio )
{
if ( a_Ratio < 0 )
{
return a_Val1 ;
}
if ( a_Ratio > 1 )
{
return a_Val2 ;
}
2014-11-09 09:33:01 -05:00
return Lerp ( a_Val1 , a_Val2 , a_Ratio ) ;
2014-11-09 08:33:37 -05:00
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 07:13:03 -04:00
// cNoise3DGenerator:
cNoise3DGenerator : : cNoise3DGenerator ( cChunkGenerator & a_ChunkGenerator ) :
super ( a_ChunkGenerator ) ,
m_Perlin ( 1000 ) ,
m_Cubic ( 1000 )
{
m_Perlin . AddOctave ( 1 , ( NOISE_DATATYPE ) 0.5 ) ;
m_Perlin . AddOctave ( ( NOISE_DATATYPE ) 0.5 , 1 ) ;
m_Perlin . AddOctave ( ( NOISE_DATATYPE ) 0.5 , 2 ) ;
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
#if 0
// DEBUG: Test the noise generation:
// NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
// In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
m_SeaLevel = 62 ;
m_HeightAmplification = 0 ;
m_MidPoint = 75 ;
m_FrequencyX = 4 ;
m_FrequencyY = 4 ;
m_FrequencyZ = 4 ;
m_AirThreshold = 0.5 ;
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
const int NumChunks = 4 ;
NOISE_DATATYPE Noise [ NumChunks ] [ cChunkDef : : Width * cChunkDef : : Width * cChunkDef : : Height ] ;
for ( int x = 0 ; x < NumChunks ; x + + )
{
GenerateNoiseArray ( x , 5 , Noise [ x ] ) ;
}
// Save in XY cuts:
cFile f1 ;
if ( f1 . Open ( " Test_XY.grab " , cFile : : fmWrite ) )
{
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
{
for ( int y = 0 ; y < cChunkDef : : Height ; y + + )
{
for ( int i = 0 ; i < NumChunks ; i + + )
{
int idx = y * cChunkDef : : Width + z * cChunkDef : : Width * cChunkDef : : Height ;
unsigned char buf [ cChunkDef : : Width ] ;
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
{
buf [ x ] = ( unsigned char ) ( std : : min ( 256 , std : : max ( 0 , ( int ) ( 128 + 32 * Noise [ i ] [ idx + + ] ) ) ) ) ;
}
f1 . Write ( buf , cChunkDef : : Width ) ;
}
} // for y
} // for z
} // if (XY file open)
cFile f2 ;
if ( f2 . Open ( " Test_XZ.grab " , cFile : : fmWrite ) )
{
for ( int y = 0 ; y < cChunkDef : : Height ; y + + )
{
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
{
for ( int i = 0 ; i < NumChunks ; i + + )
{
int idx = y * cChunkDef : : Width + z * cChunkDef : : Width * cChunkDef : : Height ;
unsigned char buf [ cChunkDef : : Width ] ;
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
{
buf [ x ] = ( unsigned char ) ( std : : min ( 256 , std : : max ( 0 , ( int ) ( 128 + 32 * Noise [ i ] [ idx + + ] ) ) ) ) ;
}
f2 . Write ( buf , cChunkDef : : Width ) ;
}
} // for z
} // for y
} // if (XZ file open)
# endif // 0
}
cNoise3DGenerator : : ~ cNoise3DGenerator ( )
{
// Nothing needed yet
}
2014-01-10 16:22:54 -05:00
void cNoise3DGenerator : : Initialize ( cIniFile & a_IniFile )
2013-07-29 07:13:03 -04:00
{
// Params:
m_SeaLevel = a_IniFile . GetValueSetI ( " Generator " , " Noise3DSeaLevel " , 62 ) ;
m_HeightAmplification = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DHeightAmplification " , 0 ) ;
m_MidPoint = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DMidPoint " , 75 ) ;
m_FrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DFrequencyX " , 8 ) ;
m_FrequencyY = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DFrequencyY " , 8 ) ;
m_FrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DFrequencyZ " , 8 ) ;
m_AirThreshold = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DAirThreshold " , 0.5 ) ;
}
void cNoise3DGenerator : : GenerateBiomes ( int a_ChunkX , int a_ChunkZ , cChunkDef : : BiomeMap & a_BiomeMap )
{
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( a_BiomeMap ) ; i + + )
2013-07-29 07:13:03 -04:00
{
a_BiomeMap [ i ] = biExtremeHills ;
}
}
void cNoise3DGenerator : : DoGenerate ( int a_ChunkX , int a_ChunkZ , cChunkDesc & a_ChunkDesc )
{
NOISE_DATATYPE Noise [ 17 * 257 * 17 ] ;
GenerateNoiseArray ( a_ChunkX , a_ChunkZ , Noise ) ;
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
// Output noise into chunk:
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
{
for ( int y = 0 ; y < cChunkDef : : Height ; y + + )
{
int idx = z * 17 * 257 + y * 17 ;
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
{
NOISE_DATATYPE n = Noise [ idx + + ] ;
BLOCKTYPE BlockType ;
if ( n > m_AirThreshold )
{
BlockType = ( y > m_SeaLevel ) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER ;
}
else
{
BlockType = E_BLOCK_STONE ;
}
a_ChunkDesc . SetBlockType ( x , y , z , BlockType ) ;
}
}
}
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
UpdateHeightmap ( a_ChunkDesc ) ;
ComposeTerrain ( a_ChunkDesc ) ;
}
void cNoise3DGenerator : : GenerateNoiseArray ( int a_ChunkX , int a_ChunkZ , NOISE_DATATYPE * a_OutNoise )
{
NOISE_DATATYPE NoiseO [ DIM_X * DIM_Y * DIM_Z ] ; // Output for the Perlin noise
NOISE_DATATYPE NoiseW [ DIM_X * DIM_Y * DIM_Z ] ; // Workspace that the noise calculation can use and trash
// Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
NOISE_DATATYPE StartX = ( ( NOISE_DATATYPE ) ( a_ChunkX * cChunkDef : : Width ) ) / m_FrequencyX ;
NOISE_DATATYPE EndX = ( ( NOISE_DATATYPE ) ( ( a_ChunkX + 1 ) * cChunkDef : : Width ) - 1 ) / m_FrequencyX ;
NOISE_DATATYPE StartZ = ( ( NOISE_DATATYPE ) ( a_ChunkZ * cChunkDef : : Width ) ) / m_FrequencyZ ;
NOISE_DATATYPE EndZ = ( ( NOISE_DATATYPE ) ( ( a_ChunkZ + 1 ) * cChunkDef : : Width ) - 1 ) / m_FrequencyZ ;
NOISE_DATATYPE StartY = 0 ;
NOISE_DATATYPE EndY = ( ( NOISE_DATATYPE ) 256 ) / m_FrequencyY ;
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
m_Perlin . Generate3D ( NoiseO , DIM_X , DIM_Y , DIM_Z , StartX , EndX , StartY , EndY , StartZ , EndZ , NoiseW ) ;
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
// DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ));
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
// Precalculate a "height" array:
NOISE_DATATYPE Height [ DIM_X * DIM_Z ] ; // Output for the cubic noise heightmap ("source")
m_Cubic . Generate2D ( Height , DIM_X , DIM_Z , StartX / 25 , EndX / 25 , StartZ / 25 , EndZ / 25 ) ;
2013-12-20 10:01:34 -05:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( Height ) ; i + + )
2013-07-29 07:13:03 -04:00
{
2014-09-22 03:22:36 -04:00
Height [ i ] = std : : abs ( Height [ i ] ) * m_HeightAmplification + 1 ;
2013-07-29 07:13:03 -04:00
}
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
// Modify the noise by height data:
for ( int y = 0 ; y < DIM_Y ; y + + )
{
NOISE_DATATYPE AddHeight = ( y * UPSCALE_Y - m_MidPoint ) / 20 ;
AddHeight * = AddHeight * AddHeight ;
for ( int z = 0 ; z < DIM_Z ; z + + )
{
NOISE_DATATYPE * CurRow = & ( NoiseO [ y * DIM_X + z * DIM_X * DIM_Y ] ) ;
for ( int x = 0 ; x < DIM_X ; x + + )
{
CurRow [ x ] + = AddHeight / Height [ x + DIM_X * z ] ;
}
}
}
// DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ));
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
// Upscale the Perlin noise into full-blown chunk dimensions:
LinearUpscale3DArray (
NoiseO , DIM_X , DIM_Y , DIM_Z ,
a_OutNoise , UPSCALE_X , UPSCALE_Y , UPSCALE_Z
) ;
2013-11-16 13:43:40 -05:00
2013-07-29 07:13:03 -04:00
// DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ));
}
void cNoise3DGenerator : : UpdateHeightmap ( cChunkDesc & a_ChunkDesc )
{
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
{
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
{
for ( int y = cChunkDef : : Height - 1 ; y > 0 ; y - - )
{
if ( a_ChunkDesc . GetBlockType ( x , y , z ) ! = E_BLOCK_AIR )
{
a_ChunkDesc . SetHeight ( x , z , y ) ;
break ;
}
} // for y
} // for x
} // for z
}
void cNoise3DGenerator : : ComposeTerrain ( cChunkDesc & a_ChunkDesc )
{
// Make basic terrain composition:
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
{
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
{
int LastAir = a_ChunkDesc . GetHeight ( x , z ) + 1 ;
bool HasHadWater = false ;
for ( int y = LastAir - 1 ; y > 0 ; y - - )
{
switch ( a_ChunkDesc . GetBlockType ( x , y , z ) )
{
case E_BLOCK_AIR :
{
LastAir = y ;
break ;
}
case E_BLOCK_STONE :
{
if ( LastAir - y > 3 )
{
break ;
}
if ( HasHadWater )
{
a_ChunkDesc . SetBlockType ( x , y , z , E_BLOCK_SAND ) ;
}
else
{
a_ChunkDesc . SetBlockType ( x , y , z , ( LastAir = = y + 1 ) ? E_BLOCK_GRASS : E_BLOCK_DIRT ) ;
}
break ;
}
case E_BLOCK_STATIONARY_WATER :
{
LastAir = y ;
HasHadWater = true ;
break ;
}
} // switch (GetBlockType())
} // for y
a_ChunkDesc . SetBlockType ( x , 0 , z , E_BLOCK_BEDROCK ) ;
} // for x
} // for z
}
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-07-29 07:13:03 -04:00
// cNoise3DComposable:
cNoise3DComposable : : cNoise3DComposable ( int a_Seed ) :
2014-11-09 08:33:37 -05:00
m_ChoiceNoise ( a_Seed ) ,
m_DensityNoiseA ( a_Seed + 1 ) ,
m_DensityNoiseB ( a_Seed + 2 ) ,
m_BaseNoise ( a_Seed + 3 )
2013-07-29 07:13:03 -04:00
{
}
void cNoise3DComposable : : Initialize ( cIniFile & a_IniFile )
{
// Params:
2014-11-09 08:33:37 -05:00
// The defaults generate extreme hills terrain
2013-07-29 07:13:03 -04:00
m_SeaLevel = a_IniFile . GetValueSetI ( " Generator " , " Noise3DSeaLevel " , 62 ) ;
2014-11-09 08:33:37 -05:00
m_HeightAmplification = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DHeightAmplification " , 0.045 ) ;
2013-07-29 07:13:03 -04:00
m_MidPoint = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DMidPoint " , 75 ) ;
2014-11-09 08:33:37 -05:00
m_FrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DFrequencyX " , 40 ) ;
m_FrequencyY = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DFrequencyY " , 40 ) ;
m_FrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DFrequencyZ " , 40 ) ;
m_BaseFrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DBaseFrequencyX " , 40 ) ;
m_BaseFrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DBaseFrequencyZ " , 40 ) ;
m_ChoiceFrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DChoiceFrequencyX " , 40 ) ;
m_ChoiceFrequencyY = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DChoiceFrequencyY " , 80 ) ;
m_ChoiceFrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DChoiceFrequencyZ " , 40 ) ;
m_AirThreshold = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DAirThreshold " , 0 ) ;
int NumChoiceOctaves = a_IniFile . GetValueSetI ( " Generator " , " Noise3DNumChoiceOctaves " , 4 ) ;
int NumDensityOctaves = a_IniFile . GetValueSetI ( " Generator " , " Noise3DNumDensityOctaves " , 6 ) ;
int NumBaseOctaves = a_IniFile . GetValueSetI ( " Generator " , " Noise3DNumBaseOctaves " , 6 ) ;
NOISE_DATATYPE BaseNoiseAmplitude = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " Noise3DBaseAmplitude " , 1 ) ;
// Add octaves for the choice noise:
NOISE_DATATYPE wavlen = 1 , ampl = 0.5 ;
for ( int i = 0 ; i < NumChoiceOctaves ; i + + )
{
m_ChoiceNoise . AddOctave ( wavlen , ampl ) ;
wavlen = wavlen * 2 ;
ampl = ampl / 2 ;
}
// Add octaves for the density noises:
wavlen = 1 , ampl = 1 ;
for ( int i = 0 ; i < NumDensityOctaves ; i + + )
{
m_DensityNoiseA . AddOctave ( wavlen , ampl ) ;
m_DensityNoiseB . AddOctave ( wavlen , ampl ) ;
wavlen = wavlen * 2 ;
ampl = ampl / 2 ;
}
// Add octaves for the base noise:
wavlen = 1 , ampl = BaseNoiseAmplitude ;
for ( int i = 0 ; i < NumBaseOctaves ; i + + )
{
m_BaseNoise . AddOctave ( wavlen , ampl ) ;
wavlen = wavlen * 2 ;
ampl = ampl / 2 ;
}
2013-07-29 07:13:03 -04:00
}
void cNoise3DComposable : : GenerateNoiseArrayIfNeeded ( int a_ChunkX , int a_ChunkZ )
{
if ( ( a_ChunkX = = m_LastChunkX ) & & ( a_ChunkZ = = m_LastChunkZ ) )
{
2014-11-10 11:00:14 -05:00
// The noise for this chunk is already generated in m_NoiseArray
2013-07-29 07:13:03 -04:00
return ;
}
m_LastChunkX = a_ChunkX ;
m_LastChunkZ = a_ChunkZ ;
2013-11-16 13:43:40 -05:00
2014-11-09 08:33:37 -05:00
// Generate all the noises:
NOISE_DATATYPE ChoiceNoise [ 5 * 5 * 33 ] ;
NOISE_DATATYPE Workspace [ 5 * 5 * 33 ] ;
NOISE_DATATYPE DensityNoiseA [ 5 * 5 * 33 ] ;
NOISE_DATATYPE DensityNoiseB [ 5 * 5 * 33 ] ;
NOISE_DATATYPE BaseNoise [ 5 * 5 ] ;
NOISE_DATATYPE BlockX = static_cast < NOISE_DATATYPE > ( a_ChunkX * cChunkDef : : Width ) ;
NOISE_DATATYPE BlockZ = static_cast < NOISE_DATATYPE > ( a_ChunkZ * cChunkDef : : Width ) ;
2014-11-12 15:24:26 -05:00
// Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
m_ChoiceNoise . Generate3D ( ChoiceNoise , 33 , 5 , 5 , 0 , 257 / m_ChoiceFrequencyY , BlockX / m_ChoiceFrequencyX , ( BlockX + 17 ) / m_ChoiceFrequencyX , BlockZ / m_ChoiceFrequencyZ , ( BlockZ + 17 ) / m_ChoiceFrequencyZ , Workspace ) ;
m_DensityNoiseA . Generate3D ( DensityNoiseA , 33 , 5 , 5 , 0 , 257 / m_FrequencyY , BlockX / m_FrequencyX , ( BlockX + 17 ) / m_FrequencyX , BlockZ / m_FrequencyZ , ( BlockZ + 17 ) / m_FrequencyZ , Workspace ) ;
m_DensityNoiseB . Generate3D ( DensityNoiseB , 33 , 5 , 5 , 0 , 257 / m_FrequencyY , BlockX / m_FrequencyX , ( BlockX + 17 ) / m_FrequencyX , BlockZ / m_FrequencyZ , ( BlockZ + 17 ) / m_FrequencyZ , Workspace ) ;
2014-11-09 08:33:37 -05:00
m_BaseNoise . Generate2D ( BaseNoise , 5 , 5 , BlockX / m_BaseFrequencyX , ( BlockX + 17 ) / m_BaseFrequencyX , BlockZ / m_FrequencyZ , ( BlockZ + 17 ) / m_FrequencyZ , Workspace ) ;
// Calculate the final noise based on the partial noises:
2014-11-12 15:24:26 -05:00
for ( int z = 0 ; z < 5 ; z + + )
2013-07-29 07:13:03 -04:00
{
2014-11-12 15:24:26 -05:00
for ( int x = 0 ; x < 5 ; x + + )
2013-07-29 07:13:03 -04:00
{
2014-11-12 15:24:26 -05:00
NOISE_DATATYPE curBaseNoise = BaseNoise [ x + 5 * z ] ;
for ( int y = 0 ; y < 33 ; y + + )
2013-07-29 07:13:03 -04:00
{
2014-11-12 15:24:26 -05:00
NOISE_DATATYPE AddHeight = ( static_cast < NOISE_DATATYPE > ( y * 8 ) - m_MidPoint ) * m_HeightAmplification ;
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
if ( AddHeight < 0 )
{
AddHeight * = 4 ;
}
int idx = 33 * x + 33 * 5 * z + y ;
Workspace [ idx ] = ClampedLerp ( DensityNoiseA [ idx ] , DensityNoiseB [ idx ] , 8 * ( ChoiceNoise [ idx ] + 0.5f ) ) + AddHeight + curBaseNoise ;
2013-07-29 07:13:03 -04:00
}
}
}
2014-11-12 15:24:26 -05:00
LinearUpscale3DArray < NOISE_DATATYPE > ( Workspace , 33 , 5 , 5 , m_NoiseArray , 8 , 4 , 4 ) ;
2014-11-10 11:00:14 -05:00
}
2013-11-16 13:43:40 -05:00
2014-11-10 11:00:14 -05:00
2014-11-12 15:24:26 -05:00
void cNoise3DComposable : : GenShape ( int a_ChunkX , int a_ChunkZ , cChunkDesc : : Shape & a_Shape )
2014-11-10 11:00:14 -05:00
{
GenerateNoiseArrayIfNeeded ( a_ChunkX , a_ChunkZ ) ;
2014-11-12 15:24:26 -05:00
// Translate the noise array into Shape:
2014-11-10 11:00:14 -05:00
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
2013-07-29 07:13:03 -04:00
{
2014-11-10 11:00:14 -05:00
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
2013-07-29 07:13:03 -04:00
{
2014-11-12 15:24:26 -05:00
for ( int y = 0 ; y < cChunkDef : : Height ; y + + )
2013-07-29 07:13:03 -04:00
{
2014-11-12 15:24:26 -05:00
a_Shape [ y + x * 256 + z * 256 * 16 ] = ( m_NoiseArray [ y + 257 * x + 257 * 17 * z ] > m_AirThreshold ) ? 0 : 1 ;
2014-11-10 11:00:14 -05:00
}
} // for x
} // for z
2013-07-29 07:13:03 -04:00
}
2014-11-10 11:00:14 -05:00
////////////////////////////////////////////////////////////////////////////////
// cBiomalNoise3DComposable:
cBiomalNoise3DComposable : : cBiomalNoise3DComposable ( int a_Seed , cBiomeGenPtr a_BiomeGen ) :
m_ChoiceNoise ( a_Seed ) ,
m_DensityNoiseA ( a_Seed + 1 ) ,
m_DensityNoiseB ( a_Seed + 2 ) ,
m_BaseNoise ( a_Seed + 3 ) ,
m_BiomeGen ( a_BiomeGen )
{
// Generate the weight distribution for summing up neighboring biomes:
m_WeightSum = 0 ;
for ( int z = 0 ; z < = AVERAGING_SIZE * 2 ; z + + )
{
for ( int x = 0 ; x < = AVERAGING_SIZE * 2 ; x + + )
{
m_Weight [ z ] [ x ] = static_cast < NOISE_DATATYPE > ( ( 5 - std : : abs ( 5 - x ) ) + ( 5 - std : : abs ( 5 - z ) ) ) ;
m_WeightSum + = m_Weight [ z ] [ x ] ;
}
}
}
void cBiomalNoise3DComposable : : Initialize ( cIniFile & a_IniFile )
{
// Params:
// The defaults generate extreme hills terrain
m_SeaLevel = a_IniFile . GetValueSetI ( " Generator " , " BiomalNoise3DSeaLevel " , 62 ) ;
m_FrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DFrequencyX " , 40 ) ;
m_FrequencyY = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DFrequencyY " , 40 ) ;
m_FrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DFrequencyZ " , 40 ) ;
m_BaseFrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DBaseFrequencyX " , 40 ) ;
m_BaseFrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DBaseFrequencyZ " , 40 ) ;
m_ChoiceFrequencyX = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DChoiceFrequencyX " , 40 ) ;
m_ChoiceFrequencyY = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DChoiceFrequencyY " , 80 ) ;
m_ChoiceFrequencyZ = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DChoiceFrequencyZ " , 40 ) ;
m_AirThreshold = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DAirThreshold " , 0 ) ;
int NumChoiceOctaves = a_IniFile . GetValueSetI ( " Generator " , " BiomalNoise3DNumChoiceOctaves " , 4 ) ;
int NumDensityOctaves = a_IniFile . GetValueSetI ( " Generator " , " BiomalNoise3DNumDensityOctaves " , 6 ) ;
int NumBaseOctaves = a_IniFile . GetValueSetI ( " Generator " , " BiomalNoise3DNumBaseOctaves " , 6 ) ;
NOISE_DATATYPE BaseNoiseAmplitude = ( NOISE_DATATYPE ) a_IniFile . GetValueSetF ( " Generator " , " BiomalNoise3DBaseAmplitude " , 1 ) ;
// Add octaves for the choice noise:
NOISE_DATATYPE wavlen = 1 , ampl = 0.5 ;
for ( int i = 0 ; i < NumChoiceOctaves ; i + + )
{
m_ChoiceNoise . AddOctave ( wavlen , ampl ) ;
wavlen = wavlen * 2 ;
ampl = ampl / 2 ;
}
// Add octaves for the density noises:
wavlen = 1 , ampl = 1 ;
for ( int i = 0 ; i < NumDensityOctaves ; i + + )
{
m_DensityNoiseA . AddOctave ( wavlen , ampl ) ;
m_DensityNoiseB . AddOctave ( wavlen , ampl ) ;
wavlen = wavlen * 2 ;
ampl = ampl / 2 ;
}
// Add octaves for the base noise:
wavlen = 1 , ampl = BaseNoiseAmplitude ;
for ( int i = 0 ; i < NumBaseOctaves ; i + + )
{
m_BaseNoise . AddOctave ( wavlen , ampl ) ;
wavlen = wavlen * 2 ;
ampl = ampl / 2 ;
}
}
void cBiomalNoise3DComposable : : GenerateNoiseArrayIfNeeded ( int a_ChunkX , int a_ChunkZ )
{
if ( ( a_ChunkX = = m_LastChunkX ) & & ( a_ChunkZ = = m_LastChunkZ ) )
{
// The noise for this chunk is already generated in m_NoiseArray
return ;
}
m_LastChunkX = a_ChunkX ;
m_LastChunkZ = a_ChunkZ ;
// Calculate the parameters for the biomes:
ChunkParam MidPoint ;
ChunkParam HeightAmp ;
CalcBiomeParamArrays ( a_ChunkX , a_ChunkZ , HeightAmp , MidPoint ) ;
// Generate all the noises:
NOISE_DATATYPE ChoiceNoise [ 5 * 5 * 33 ] ;
NOISE_DATATYPE Workspace [ 5 * 5 * 33 ] ;
NOISE_DATATYPE DensityNoiseA [ 5 * 5 * 33 ] ;
NOISE_DATATYPE DensityNoiseB [ 5 * 5 * 33 ] ;
NOISE_DATATYPE BaseNoise [ 5 * 5 ] ;
NOISE_DATATYPE BlockX = static_cast < NOISE_DATATYPE > ( a_ChunkX * cChunkDef : : Width ) ;
NOISE_DATATYPE BlockZ = static_cast < NOISE_DATATYPE > ( a_ChunkZ * cChunkDef : : Width ) ;
2014-11-12 15:24:26 -05:00
// Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
m_ChoiceNoise . Generate3D ( ChoiceNoise , 33 , 5 , 5 , 0 , 257 / m_ChoiceFrequencyY , BlockX / m_ChoiceFrequencyX , ( BlockX + 17 ) / m_ChoiceFrequencyX , BlockZ / m_ChoiceFrequencyZ , ( BlockZ + 17 ) / m_ChoiceFrequencyZ , Workspace ) ;
m_DensityNoiseA . Generate3D ( DensityNoiseA , 33 , 5 , 5 , 0 , 257 / m_FrequencyY , BlockX / m_FrequencyX , ( BlockX + 17 ) / m_FrequencyX , BlockZ / m_FrequencyZ , ( BlockZ + 17 ) / m_FrequencyZ , Workspace ) ;
m_DensityNoiseB . Generate3D ( DensityNoiseB , 33 , 5 , 5 , 0 , 257 / m_FrequencyY , BlockX / m_FrequencyX , ( BlockX + 17 ) / m_FrequencyX , BlockZ / m_FrequencyZ , ( BlockZ + 17 ) / m_FrequencyZ , Workspace ) ;
2014-11-10 11:00:14 -05:00
m_BaseNoise . Generate2D ( BaseNoise , 5 , 5 , BlockX / m_BaseFrequencyX , ( BlockX + 17 ) / m_BaseFrequencyX , BlockZ / m_FrequencyZ , ( BlockZ + 17 ) / m_FrequencyZ , Workspace ) ;
// Calculate the final noise based on the partial noises:
2014-11-12 15:24:26 -05:00
for ( int z = 0 ; z < 5 ; z + + )
2014-11-10 11:00:14 -05:00
{
2014-11-12 15:24:26 -05:00
for ( int x = 0 ; x < 5 ; x + + )
2014-11-10 11:00:14 -05:00
{
2014-11-12 15:24:26 -05:00
NOISE_DATATYPE curMidPoint = MidPoint [ x + 5 * z ] ;
NOISE_DATATYPE curHeightAmp = HeightAmp [ x + 5 * z ] ;
NOISE_DATATYPE curBaseNoise = BaseNoise [ x + 5 * z ] ;
for ( int y = 0 ; y < 33 ; y + + )
2014-11-10 11:00:14 -05:00
{
2014-11-12 15:24:26 -05:00
NOISE_DATATYPE AddHeight = ( static_cast < NOISE_DATATYPE > ( y * 8 ) - curMidPoint ) * curHeightAmp ;
2014-11-10 11:00:14 -05:00
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
if ( AddHeight < 0 )
{
AddHeight * = 4 ;
}
2014-11-12 15:24:26 -05:00
int idx = 33 * x + y + 33 * 5 * z ;
Workspace [ idx ] = ClampedLerp ( DensityNoiseA [ idx ] , DensityNoiseB [ idx ] , 8 * ( ChoiceNoise [ idx ] + 0.5f ) ) + AddHeight + curBaseNoise ;
2014-11-10 11:00:14 -05:00
}
}
}
2014-11-12 15:24:26 -05:00
LinearUpscale3DArray < NOISE_DATATYPE > ( Workspace , 33 , 5 , 5 , m_NoiseArray , 8 , 4 , 4 ) ;
2014-11-10 11:00:14 -05:00
}
void cBiomalNoise3DComposable : : CalcBiomeParamArrays ( int a_ChunkX , int a_ChunkZ , ChunkParam & a_HeightAmp , ChunkParam & a_MidPoint )
{
// Generate the 3*3 chunks of biomes around this chunk:
cChunkDef : : BiomeMap neighborBiomes [ 3 * 3 ] ;
for ( int z = 0 ; z < 3 ; z + + )
{
for ( int x = 0 ; x < 3 ; x + + )
{
m_BiomeGen - > GenBiomes ( a_ChunkX + x - 1 , a_ChunkZ + z - 1 , neighborBiomes [ x + 3 * z ] ) ;
}
}
// Sum up the biome values:
for ( int z = 0 ; z < 5 ; z + + )
{
for ( int x = 0 ; x < 5 ; x + + )
{
NOISE_DATATYPE totalHeightAmp = 0 ;
NOISE_DATATYPE totalMidPoint = 0 ;
// Add up the biomes around this point:
for ( int relz = 0 ; relz < = AVERAGING_SIZE * 2 ; + + relz )
{
int colz = 16 + z * 4 + relz - AVERAGING_SIZE ; // Biome Z coord relative to the neighborBiomes start
int neicellz = colz / 16 ; // Chunk Z coord relative to the neighborBiomes start
int neirelz = colz % 16 ; // Biome Z coord relative to cz in neighborBiomes
for ( int relx = 0 ; relx < = AVERAGING_SIZE * 2 ; + + relx )
{
int colx = 16 + x * 4 + relx - AVERAGING_SIZE ; // Biome X coord relative to the neighborBiomes start
int neicellx = colx / 16 ; // Chunk X coord relative to the neighborBiomes start
int neirelx = colx % 16 ; // Biome X coord relative to cz in neighborBiomes
EMCSBiome biome = cChunkDef : : GetBiome ( neighborBiomes [ neicellx + neicellz * 3 ] , neirelx , neirelz ) ;
NOISE_DATATYPE heightAmp , midPoint ;
GetBiomeParams ( biome , heightAmp , midPoint ) ;
totalHeightAmp + = heightAmp * m_Weight [ relz ] [ relx ] ;
totalMidPoint + = midPoint * m_Weight [ relz ] [ relx ] ;
} // for relx
} // for relz
a_HeightAmp [ x + 5 * z ] = totalHeightAmp / m_WeightSum ;
a_MidPoint [ x + 5 * z ] = totalMidPoint / m_WeightSum ;
} // for x
} // for z
}
void cBiomalNoise3DComposable : : GetBiomeParams ( EMCSBiome a_Biome , NOISE_DATATYPE & a_HeightAmp , NOISE_DATATYPE & a_MidPoint )
{
switch ( a_Biome )
{
2014-11-10 16:08:07 -05:00
case biDesert : a_HeightAmp = 0.29f ; a_MidPoint = 62 ; break ; // Needs verification
case biExtremeHills : a_HeightAmp = 0.045f ; a_MidPoint = 75 ; break ;
case biExtremeHillsPlus : a_HeightAmp = 0.04f ; a_MidPoint = 80 ; break ;
case biFrozenRiver : a_HeightAmp = 0.4f ; a_MidPoint = 53 ; break ;
case biPlains : a_HeightAmp = 0.3f ; a_MidPoint = 62 ; break ; // Needs verification
case biRiver : a_HeightAmp = 0.4f ; a_MidPoint = 53 ; break ;
case biSwampland : a_HeightAmp = 0.25f ; a_MidPoint = 59 ; break ;
case biSwamplandM : a_HeightAmp = 0.11f ; a_MidPoint = 59 ; break ;
2014-11-10 11:00:14 -05:00
default :
{
// Make a crazy terrain so that it stands out
a_HeightAmp = 0.001f ;
a_MidPoint = 90 ;
break ;
}
}
}
2014-11-12 15:24:26 -05:00
void cBiomalNoise3DComposable : : GenShape ( int a_ChunkX , int a_ChunkZ , cChunkDesc : : Shape & a_Shape )
2013-07-29 07:13:03 -04:00
{
GenerateNoiseArrayIfNeeded ( a_ChunkX , a_ChunkZ ) ;
2013-11-16 13:43:40 -05:00
2014-11-12 15:24:26 -05:00
// Translate the noise array into Shape:
2013-07-29 07:13:03 -04:00
for ( int z = 0 ; z < cChunkDef : : Width ; z + + )
{
for ( int x = 0 ; x < cChunkDef : : Width ; x + + )
{
2014-11-12 15:24:26 -05:00
for ( int y = 0 ; y < cChunkDef : : Height ; y + + )
2013-07-29 07:13:03 -04:00
{
2014-11-12 15:24:26 -05:00
a_Shape [ y + x * 256 + z * 256 * 16 ] = ( m_NoiseArray [ y + 257 * x + 257 * 17 * z ] > m_AirThreshold ) ? 0 : 1 ;
2013-07-29 07:13:03 -04:00
}
} // for x
} // for z
}
2014-11-12 15:24:26 -05:00