Cuberite
A lightweight, fast and extensible game server for Minecraft
HeiGen.cpp
Go to the documentation of this file.
1 
2 // HeiGen.cpp
3 
4 // Implements the various terrain height generators
5 
6 #include "Globals.h"
7 #include "HeiGen.h"
8 #include "../LinearUpscale.h"
9 #include "../IniFile.h"
10 #include "DistortedHeightmap.h"
11 #include "EndGen.h"
12 #include "Noise3DGenerator.h"
13 #include "ProtIntGen.h"
14 
15 
16 
17 
18 
20 // cHeiGenSteppy:
21 
23  public cTerrainHeightGen
24 {
25 public:
26  cHeiGenSteppy(int a_Seed) :
27  m_Seed(a_Seed)
28  {
29  m_Gen =
30  std::make_shared<cProtIntGenWeightAvg<16, 1, 0>>(
31  std::make_shared<cProtIntGenSmooth> (a_Seed + 1,
32  std::make_shared<cProtIntGenZoom> (a_Seed + 2,
33  std::make_shared<cProtIntGenSmooth> (a_Seed + 3,
34  std::make_shared<cProtIntGenZoom> (a_Seed + 4,
35  std::make_shared<cProtIntGenAddRnd> (a_Seed + 5, 1,
36  std::make_shared<cProtIntGenSmooth> (a_Seed + 6,
37  std::make_shared<cProtIntGenZoom> (a_Seed + 7,
38  std::make_shared<cProtIntGenRndBetween> (a_Seed + 8, 60,
39  std::make_shared<cProtIntGenAddRnd> (a_Seed + 9, 1,
40  std::make_shared<cProtIntGenSmooth> (a_Seed + 1,
41  std::make_shared<cProtIntGenZoom> (a_Seed + 2,
42  std::make_shared<cProtIntGenRndBetween> (a_Seed + 3, 60,
43  std::make_shared<cProtIntGenSmooth> (a_Seed + 4,
44  std::make_shared<cProtIntGenZoom> (a_Seed + 5,
45  std::make_shared<cProtIntGenRndBetween> (a_Seed + 6, 60,
46  std::make_shared<cProtIntGenRndChoice> (a_Seed + 7, 10, 50, 50,
47  std::make_shared<cProtIntGenSmooth> (a_Seed + 8,
48  std::make_shared<cProtIntGenZoom> (a_Seed + 9,
49  std::make_shared<cProtIntGenRndChoice> (a_Seed + 1, 10, 50, 50,
50  std::make_shared<cProtIntGenAddRnd> (a_Seed + 2, 2,
51  std::make_shared<cProtIntGenZoom> (a_Seed + 3,
52  std::make_shared<cProtIntGenZoom> (a_Seed + 4,
53  std::make_shared<cProtIntGenChoice> (a_Seed + 5, 10)
54  )))))))))))))))))))))));
55  }
56 
57  // cTerrainHeightGen overrides:
58  virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap & a_HeightMap) override
59  {
60  int heights[cChunkDef::Width * cChunkDef::Width];
61  m_Gen->GetInts(
62  a_ChunkCoords.m_ChunkX * cChunkDef::Width, a_ChunkCoords.m_ChunkZ * cChunkDef::Width,
63  static_cast<size_t>(cChunkDef::Width), static_cast<size_t>(cChunkDef::Width), heights
64  );
65  for (size_t i = 0; i < ARRAYCOUNT(heights); i++)
66  {
67  a_HeightMap[i] = static_cast<HEIGHTTYPE>(std::max(std::min(60 + heights[i], cChunkDef::Height - 60), 40));
68  }
69  }
70 
71 protected:
72  int m_Seed;
73 
74  std::shared_ptr<cProtIntGen> m_Gen;
75 };
76 
77 
78 
79 
80 
82 // cHeiGenFlat:
83 
85 {
86  UNUSED(a_ChunkCoords);
87  for (size_t i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
88  {
89  a_HeightMap[i] = m_Height;
90  }
91 }
92 
93 
94 
95 
96 
98 {
99  m_Height = static_cast<HEIGHTTYPE>(a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height));
100 }
101 
102 
103 
104 
105 
107 // cHeiGenCache:
108 
109 cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, size_t a_CacheSize) :
110  m_HeiGenToCache(a_HeiGenToCache),
111  m_CacheSize(a_CacheSize),
112  m_NumHits(0),
113  m_NumMisses(0),
114  m_TotalChain(0)
115 {
116  m_CacheOrder.resize(a_CacheSize);
117  m_CacheData.resize(a_CacheSize);
118  for (size_t i = 0; i < m_CacheSize; i++)
119  {
120  m_CacheOrder[i] = i;
121  }
122 }
123 
124 
125 
126 
127 
129 {
130  /*
131  if (((m_NumHits + m_NumMisses) % 1024) == 10)
132  {
133  LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
134  LOGD("HeiGenCache: Avg cache chain length: %.2f", static_cast<double>(m_TotalChain) / m_NumHits);
135  }
136  //*/
137 
138  for (size_t i = 0; i < m_CacheSize; i++)
139  {
140  if (m_CacheData[m_CacheOrder[i]].m_Coords != a_ChunkCoords)
141  {
142  continue;
143  }
144  // Found it in the cache
145  auto Idx = m_CacheOrder[i];
146 
147  // Move to front:
148  for (size_t j = i; j > 0; j--)
149  {
150  m_CacheOrder[j] = m_CacheOrder[j - 1];
151  }
152  m_CacheOrder[0] = Idx;
153 
154  // Use the cached data:
155  memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap));
156 
157  m_NumHits++;
158  m_TotalChain += i;
159  return;
160  } // for i - cache
161 
162  // Not in the cache:
163  m_NumMisses++;
164  m_HeiGenToCache.GenHeightMap(a_ChunkCoords, a_HeightMap);
165 
166  // Insert it as the first item in the MRU order:
167  auto Idx = m_CacheOrder[m_CacheSize - 1];
168  for (auto i = m_CacheSize - 1; i > 0; i--)
169  {
170  m_CacheOrder[i] = m_CacheOrder[i - 1];
171  } // for i - m_CacheOrder[]
172  m_CacheOrder[0] = Idx;
173  memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap));
174  m_CacheData[Idx].m_Coords = a_ChunkCoords;
175 }
176 
177 
178 
179 
180 
181 HEIGHTTYPE cHeiGenCache::GetHeightAt(int a_BlockX, int a_BlockZ)
182 {
183  // First try if the chunk is already in the cache:
184  int chunkX, chunkZ;
185  cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, chunkX, chunkZ);
186  HEIGHTTYPE res;
187  if (GetHeightAt(chunkX, chunkZ, a_BlockX - chunkX * cChunkDef::Width, a_BlockZ - chunkZ * cChunkDef::Width, res))
188  {
189  return res;
190  }
191 
192  // Chunk not in cache, generate the chunk and ask again:
193  cChunkDef::HeightMap heightMap;
194  GenHeightMap({chunkX, chunkZ}, heightMap);
195  return cChunkDef::GetHeight(heightMap, a_BlockX - chunkX * cChunkDef::Width, a_BlockZ - chunkZ * cChunkDef::Width);
196 }
197 
198 
199 
200 
201 
202 bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
203 {
204  for (size_t i = 0; i < m_CacheSize; i++)
205  {
206  if ((m_CacheData[i].m_Coords.m_ChunkX == a_ChunkX) && (m_CacheData[i].m_Coords.m_ChunkZ == a_ChunkZ))
207  {
208  a_Height = cChunkDef::GetHeight(m_CacheData[i].m_HeightMap, a_RelX, a_RelZ);
209  return true;
210  }
211  } // for i - m_CacheData[]
212  return false;
213 }
214 
215 
216 
217 
218 
220 // cHeiGenMultiCache:
221 
222 cHeiGenMultiCache::cHeiGenMultiCache(std::unique_ptr<cTerrainHeightGen> a_HeiGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches):
223  m_NumSubCaches(a_NumSubCaches),
224  m_Underlying(std::move(a_HeiGenToCache))
225 {
226  // Create the individual sub-caches:
227  m_SubCaches.reserve(a_NumSubCaches);
228  for (size_t i = 0; i < a_NumSubCaches; i++)
229  {
230  m_SubCaches.push_back(std::make_unique<cHeiGenCache>(*m_Underlying, a_SubCacheSize));
231  }
232 }
233 
234 
235 
236 
237 
239 {
240  // Get the subcache responsible for this chunk:
241  const size_t cacheIdx = (static_cast<size_t>(a_ChunkCoords.m_ChunkX) + m_CoeffZ * static_cast<size_t>(a_ChunkCoords.m_ChunkZ)) % m_NumSubCaches;
242 
243  // Ask the subcache:
244  m_SubCaches[cacheIdx]->GenHeightMap(a_ChunkCoords, a_HeightMap);
245 }
246 
247 
248 
249 
250 
251 HEIGHTTYPE cHeiGenMultiCache::GetHeightAt(int a_BlockX, int a_BlockZ)
252 {
253  // First try if the chunk is already in the cache:
254  int chunkX, chunkZ;
255  cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, chunkX, chunkZ);
256  HEIGHTTYPE res;
257  if (GetHeightAt(chunkX, chunkZ, a_BlockX - chunkX * cChunkDef::Width, a_BlockZ - chunkZ * cChunkDef::Width, res))
258  {
259  return res;
260  }
261 
262  // Chunk not in cache, generate the chunk and ask again:
263  cChunkDef::HeightMap heightMap;
264  GenHeightMap({chunkX, chunkZ}, heightMap);
265  return cChunkDef::GetHeight(heightMap, a_BlockX - chunkX * cChunkDef::Width, a_BlockZ - chunkZ * cChunkDef::Width);
266 }
267 
268 
269 
270 
271 
272 bool cHeiGenMultiCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
273 {
274  // Get the subcache responsible for this chunk:
275  const size_t cacheIdx = (static_cast<size_t>(a_ChunkX) + m_CoeffZ * static_cast<size_t>(a_ChunkZ)) % m_NumSubCaches;
276 
277  // Ask the subcache:
278  return m_SubCaches[cacheIdx]->GetHeightAt(a_ChunkX, a_ChunkZ, a_RelX, a_RelZ, a_Height);
279 }
280 
281 
282 
283 
284 
286 // cHeiGenClassic:
287 
289  m_Seed(a_Seed),
290  m_Noise(a_Seed),
291  m_HeightFreq1(1.0f),
292  m_HeightAmp1(1.0f),
293  m_HeightFreq2(0.5f),
294  m_HeightAmp2(0.5f),
295  m_HeightFreq3(0.1f),
296  m_HeightAmp3(0.1f)
297 {
298 }
299 
300 
301 
302 
303 
304 float cHeiGenClassic::GetNoise(float x, float y)
305 {
309 
310  float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f) * 2;
311 
312  float flatness = ((m_Noise.CubicNoise2D(x * 0.5f, y * 0.5f) + 1.f) * 0.5f) * 1.1f; // 0 ... 1.5
313  flatness *= flatness * flatness;
314 
315  return (oct1 + oct2 + oct3) * flatness + height;
316 }
317 
318 
319 
320 
321 
323 {
324  for (int z = 0; z < cChunkDef::Width; z++)
325  {
326  const float zz = static_cast<float>(a_ChunkCoords.m_ChunkZ * cChunkDef::Width + z);
327  for (int x = 0; x < cChunkDef::Width; x++)
328  {
329  const float xx = static_cast<float>(a_ChunkCoords.m_ChunkX * cChunkDef::Width + x);
330 
331  HEIGHTTYPE hei = static_cast<HEIGHTTYPE>(Clamp(static_cast<int>(64 + (GetNoise(xx * 0.05f, zz * 0.05f) * 16)), 10, 250));
332  cChunkDef::SetHeight(a_HeightMap, x, z, hei);
333  } // for x
334  } // for z
335 }
336 
337 
338 
339 
340 
342 {
343  m_HeightFreq1 = static_cast<float>(a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1));
344  m_HeightFreq2 = static_cast<float>(a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0));
345  m_HeightFreq3 = static_cast<float>(a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0));
346  m_HeightAmp1 = static_cast<float>(a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0));
347  m_HeightAmp2 = static_cast<float>(a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5));
348  m_HeightAmp3 = static_cast<float>(a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5));
349 }
350 
351 
352 
353 
354 
356 // cHeiGenMountains:
357 
359  m_Seed(a_Seed),
360  m_MountainNoise(a_Seed + 100),
361  m_DitchNoise(a_Seed + 200),
362  m_Perlin(a_Seed + 300)
363 {
364 }
365 
366 
367 
368 
369 
371 {
372  NOISE_DATATYPE StartX = static_cast<NOISE_DATATYPE>(a_ChunkCoords.m_ChunkX * cChunkDef::Width);
373  NOISE_DATATYPE EndX = static_cast<NOISE_DATATYPE>(a_ChunkCoords.m_ChunkX * cChunkDef::Width + cChunkDef::Width - 1);
374  NOISE_DATATYPE StartZ = static_cast<NOISE_DATATYPE>(a_ChunkCoords.m_ChunkZ * cChunkDef::Width);
375  NOISE_DATATYPE EndZ = static_cast<NOISE_DATATYPE>(a_ChunkCoords.m_ChunkZ * cChunkDef::Width + cChunkDef::Width - 1);
376  NOISE_DATATYPE Workspace[16 * 16];
377  NOISE_DATATYPE MountainNoise[16 * 16];
378  NOISE_DATATYPE DitchNoise[16 * 16];
379  NOISE_DATATYPE PerlinNoise[16 * 16];
380  m_MountainNoise.Generate2D(MountainNoise, 16, 16, StartX, EndX, StartZ, EndZ, Workspace);
381  m_DitchNoise.Generate2D(DitchNoise, 16, 16, StartX, EndX, StartZ, EndZ, Workspace);
382  m_Perlin.Generate2D(PerlinNoise, 16, 16, StartX, EndX, StartZ, EndZ, Workspace);
383  for (int z = 0; z < cChunkDef::Width; z++)
384  {
385  int IdxZ = z * cChunkDef::Width;
386  for (int x = 0; x < cChunkDef::Width; x++)
387  {
388  int idx = IdxZ + x;
389  HEIGHTTYPE hei = static_cast<HEIGHTTYPE>(Clamp(100 - static_cast<int>((MountainNoise[idx] - DitchNoise[idx] + PerlinNoise[idx]) * 15), 10, 250));
390  cChunkDef::SetHeight(a_HeightMap, x, z, hei);
391  } // for x
392  } // for z
393 }
394 
395 
396 
397 
398 
400 {
401  // TODO: Read the params from an INI file
402  m_MountainNoise.AddOctave(0.1f, 0.2f);
403  m_MountainNoise.AddOctave(0.05f, 0.4f);
404  m_MountainNoise.AddOctave(0.02f, 1.0f);
405  m_DitchNoise.AddOctave(0.1f, 0.2f);
406  m_DitchNoise.AddOctave(0.05f, 0.4f);
407  m_DitchNoise.AddOctave(0.02f, 1.0f);
408 
409  m_Perlin.AddOctave(0.01f, 1.5f);
410 }
411 
412 
413 
414 
415 
417 // cHeiGenBiomal:
418 
420 {
421  /* Fast-changing | Middle-changing | Slow-changing | */
422  /* Biome | Freq1 | Amp1 | Freq2 | Amp2 | Freq3 | Amp3 | BaseHeight */
423  /* biOcean */ { 0.1f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 50},
424  /* biPlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
425  /* biDesert */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
426  /* biExtremeHills */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 100},
427  /* biForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
428  /* biTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
429  /* biSwampland */ { 0.1f, 1.1f, 0.05f, 1.5f, 0.02f, 2.5f, 61.5},
430  /* biRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56},
431  /* biNether */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing
432  /* biSky */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing
433  /* biFrozenOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
434  /* biFrozenRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56},
435  /* biIcePlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
436  /* biIceMountains */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80},
437  /* biMushroomIsland */ { 0.1f, 2.0f, 0.05f, 8.0f, 0.01f, 6.0f, 80},
438  /* biMushroomShore */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 64},
439  /* biBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64},
440  /* biDesertHills */ { 0.2f, 2.0f, 0.05f, 5.0f, 0.01f, 4.0f, 75},
441  /* biForestHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
442  /* biTaigaHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
443  /* biExtremeHillsEdge */ { 0.2f, 3.0f, 0.05f, 16.0f, 0.01f, 12.0f, 80},
444  /* biJungle */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70},
445  /* biJungleHills */ { 0.2f, 3.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80},
446  /* biJungleEdge */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70},
447  /* biDeepOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
448  /* biStoneBeach */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40},
449  /* biColdBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64},
450  /* biBirchForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
451  /* biBirchForestHills */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80},
452  /* biRoofedForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
453  /* biColdTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
454  /* biColdTaigaHills */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80},
455  /* biMegaTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70},
456  /* biMegaTaigaHills */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80},
457  /* biExtremeHillsPlus */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 120},
458  /* biSavanna */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68},
459  /* biSavannaPlateau */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80},
460  /* biMesa */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 70}, // 165
461  /* biMesaPlateauF */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80},
462  /* biMesaPlateau */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80},
463 
464  // biomes 40 .. 128 are unused, 89 empty placeholders here:
465  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 40 .. 49
466  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 50 .. 59
467  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 60 .. 69
468  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 70 .. 79
469  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 80 .. 89
470  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 90 .. 99
471  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 100 .. 109
472  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 110 .. 119
473  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 120 .. 128
474 
475  /* biSunflowerPlains */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 129
476  /* biDesertM */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 130
477  /* biExtremeHillsM */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 131
478  /* biFlowerForest */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 132
479  /* biTaigaM */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 133
480  /* biSwamplandM */ { 1.0f, 3.0f, 1.10f, 7.0f, 0.01f, 0.01f, 60}, // 134
481 
482  // Biomes 135 .. 139 unused, 5 empty placeholders here:
483  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 135 .. 139
484 
485  /* biIcePlainsSpikes */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, // 140
486 
487  // Biomes 141 .. 148 unused, 8 empty placeholders here:
488  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 141 .. 148
489 
490  /* biJungleM */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, // 149
491  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 150
492  /* biJungleEdgeM */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, // 151
493  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 152 .. 154
494  /* biBirchForestM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 155
495  /* biBirchForestHillsM */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, // 156
496  /* biRoofedForestM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 157
497  /* biColdTaigaM */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 158
498  {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0}, // 159
499  /* biMegaSpruceTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, // 160
500  /* biMegaSpruceTaigaHills */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, // 161
501  /* biExtremeHillsPlusM */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 120}, // 162
502  /* biSavannaM */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, // 163
503  /* biSavannaPlateauM */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80}, // 164
504  /* biMesaBryce */ { 0.2f, 2.0f, 0.1f, 30.0f, 0.01f, 8.0f, 80},
505  /* biMesaPlateauFM */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80}, // 166
506  /* biMesaPlateauM */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 80}, // 167
507 } ;
508 
509 
510 
511 
512 
514 {
515  // Generate a 3x3 chunk area of biomes around this chunk:
516  BiomeNeighbors Biomes;
517  for (int z = -1; z <= 1; z++)
518  {
519  for (int x = -1; x <= 1; x++)
520  {
521  m_BiomeGen.GenBiomes({a_ChunkCoords.m_ChunkX + x, a_ChunkCoords.m_ChunkZ + z}, Biomes[x + 1][z + 1]);
522  } // for x
523  } // for z
524 
525  // Linearly interpolate 4x4 blocks of heightmap:
526  // Must be done on a floating point datatype, otherwise the results are ugly!
527  const int STEPZ = 4; // Must be a divisor of 16
528  const int STEPX = 4; // Must be a divisor of 16
529  NOISE_DATATYPE Height[17 * 17];
530  for (int z = 0; z < 17; z += STEPZ)
531  {
532  for (int x = 0; x < 17; x += STEPX)
533  {
534  Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkCoords.m_ChunkX, a_ChunkCoords.m_ChunkZ, Biomes);
535  }
536  }
537  LinearUpscale2DArrayInPlace<17, 17, STEPX, STEPZ>(Height);
538 
539  // Copy into the heightmap
540  for (int z = 0; z < cChunkDef::Width; z++)
541  {
542  for (int x = 0; x < cChunkDef::Width; x++)
543  {
544  cChunkDef::SetHeight(a_HeightMap, x, z, static_cast<HEIGHTTYPE>(Height[x + 17 * z]));
545  }
546  }
547 }
548 
549 
550 
551 
552 
554 {
555  // No user-settable params
556 }
557 
558 
559 
560 
561 
562 NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors)
563 {
564  // Sum up how many biomes of each type there are in the neighborhood:
565  int BiomeCounts[256];
566  memset(BiomeCounts, 0, sizeof(BiomeCounts));
567  int Sum = 0;
568  for (int z = -8; z <= 8; z++)
569  {
570  int FinalZ = a_RelZ + z + cChunkDef::Width;
571  int IdxZ = FinalZ / cChunkDef::Width;
572  int ModZ = FinalZ % cChunkDef::Width;
573  int WeightZ = 9 - abs(z);
574  for (int x = -8; x <= 8; x++)
575  {
576  int FinalX = a_RelX + x + cChunkDef::Width;
577  int IdxX = FinalX / cChunkDef::Width;
578  int ModX = FinalX % cChunkDef::Width;
579  EMCSBiome Biome = cChunkDef::GetBiome(a_BiomeNeighbors[IdxX][IdxZ], ModX, ModZ);
580  int WeightX = 9 - abs(x);
581  BiomeCounts[Biome] += WeightX + WeightZ;
582  Sum += WeightX + WeightZ;
583  } // for x
584  } // for z
585 
586  // For each biome type that has a nonzero count, calc its height and add it:
587  if (Sum > 0)
588  {
589  NOISE_DATATYPE Height = 0;
590  int BlockX = a_ChunkX * cChunkDef::Width + a_RelX;
591  int BlockZ = a_ChunkZ * cChunkDef::Width + a_RelZ;
592  for (size_t i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
593  {
594  if (BiomeCounts[i] == 0)
595  {
596  continue;
597  }
598 
599  /*
600  // Sanity checks for biome parameters, enable them to check the biome param table in runtime (slow):
601  ASSERT(m_GenParam[i].m_HeightFreq1 >= 0);
602  ASSERT(m_GenParam[i].m_HeightFreq1 < 1000);
603  ASSERT(m_GenParam[i].m_HeightFreq2 >= 0);
604  ASSERT(m_GenParam[i].m_HeightFreq2 < 1000);
605  ASSERT(m_GenParam[i].m_HeightFreq3 >= 0);
606  ASSERT(m_GenParam[i].m_HeightFreq3 < 1000);
607  */
608 
609  NOISE_DATATYPE oct1 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq1, BlockZ * m_GenParam[i].m_HeightFreq1) * m_GenParam[i].m_HeightAmp1;
610  NOISE_DATATYPE oct2 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq2, BlockZ * m_GenParam[i].m_HeightFreq2) * m_GenParam[i].m_HeightAmp2;
611  NOISE_DATATYPE oct3 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq3, BlockZ * m_GenParam[i].m_HeightFreq3) * m_GenParam[i].m_HeightAmp3;
612  Height += BiomeCounts[i] * (m_GenParam[i].m_BaseHeight + oct1 + oct2 + oct3);
613  }
614  NOISE_DATATYPE res = Height / Sum;
615  return std::min(static_cast<NOISE_DATATYPE>(250), std::max(res, static_cast<NOISE_DATATYPE>(5)));
616  }
617 
618  // No known biome around? Weird. Return a bogus value:
619  ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
620  return 5;
621 }
622 
623 
624 
625 
626 
628 // cHeiGenMinMax:
629 
631  public cTerrainHeightGen
632 {
634 
636  static const int AVERAGING_SIZE = 4;
637 
638 public:
639 
640  cHeiGenMinMax(int a_Seed, cBiomeGen & a_BiomeGen):
641  m_Noise(a_Seed),
642  m_BiomeGen(a_BiomeGen),
643  m_TotalWeight(0)
644  {
645  // Initialize the weights:
646  for (int z = 0; z <= AVERAGING_SIZE * 2; z++)
647  {
648  for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
649  {
650  m_Weights[z][x] = 1 + 2 * AVERAGING_SIZE - std::abs(x - AVERAGING_SIZE) - std::abs(z - AVERAGING_SIZE);
651  m_TotalWeight += m_Weights[z][x];
652  }
653  }
654 
655  // Initialize the Perlin generator:
656  m_Perlin.AddOctave(0.04f, 0.2f);
657  m_Perlin.AddOctave(0.02f, 0.1f);
658  m_Perlin.AddOctave(0.01f, 0.05f);
659  }
660 
661 
662  virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap & a_HeightMap) override
663  {
664  // Generate the biomes for the 3 * 3 neighbors:
665  cChunkDef::BiomeMap neighborBiomes[3][3];
666  for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++)
667  {
668  m_BiomeGen.GenBiomes({a_ChunkCoords.m_ChunkX + x - 1, a_ChunkCoords.m_ChunkZ + z - 1}, neighborBiomes[z][x]);
669  }
670 
671  // Get the min and max heights based on the biomes:
672  double minHeight[cChunkDef::Width * cChunkDef::Width];
673  double maxHeight[cChunkDef::Width * cChunkDef::Width];
674  for (int z = 0; z < cChunkDef::Width; z++)
675  {
676  for (int x = 0; x < cChunkDef::Width; x++)
677  {
678  // For each column, sum the min and max values of the neighborhood around it:
679  double min = 0, max = 0;
680  for (int relz = 0; relz <= AVERAGING_SIZE * 2; relz++)
681  {
682  int bz = z + 16 + relz - AVERAGING_SIZE; // Biome Z coord relative to the neighborBiomes start
683  int cz = bz / 16; // Chunk Z coord relative to the neighborBiomes start
684  bz = bz % 16; // Biome Z coord relative to cz in neighborBiomes
685  for (int relx = 0; relx <= AVERAGING_SIZE * 2; relx++)
686  {
687  int bx = x + 16 + relx - AVERAGING_SIZE; // Biome X coord relative to the neighborBiomes start
688  int cx = bx / 16; // Chunk X coord relative to the neighborBiomes start
689  bx = bx % 16; // Biome X coord relative to cz in neighborBiomes
690 
691  // Get the biome's min and max heights:
692  double bmin, bmax;
693  getBiomeMinMax(cChunkDef::GetBiome(neighborBiomes[cz][cx], bx, bz), bmin, bmax);
694 
695  // Add them to the total, with the weight depending on their relative position to the column:
696  min += bmin * m_Weights[relz][relx];
697  max += bmax * m_Weights[relz][relx];
698  } // for relx
699  } // for relz
700  minHeight[x + z * cChunkDef::Width] = min / m_TotalWeight;
701  maxHeight[x + z * cChunkDef::Width] = max / m_TotalWeight;
702  } // for x
703  } // for z
704 
705  // Generate the base noise:
708  NOISE_DATATYPE startX = static_cast<float>(a_ChunkCoords.m_ChunkX * cChunkDef::Width);
709  NOISE_DATATYPE endX = startX + cChunkDef::Width - 1;
710  NOISE_DATATYPE startZ = static_cast<float>(a_ChunkCoords.m_ChunkZ * cChunkDef::Width);
711  NOISE_DATATYPE endZ = startZ + cChunkDef::Width - 1;
712  m_Perlin.Generate2D(noise, 16, 16, startX, endX, startZ, endZ, workspace);
713 
714  // Make the height by ranging the noise between min and max:
715  for (int z = 0; z < cChunkDef::Width; z++)
716  {
717  for (int x = 0; x < cChunkDef::Width; x++)
718  {
719  double min = minHeight[x + z * cChunkDef::Width];
720  double max = maxHeight[x + z * cChunkDef::Width];
721  double h = (max + min) / 2 + noise[x + z * cChunkDef::Width] * (max - min);
722  cChunkDef::SetHeight(a_HeightMap, x, z, static_cast<HEIGHTTYPE>(h));
723  }
724  }
725  }
726 
727 
728  virtual void InitializeHeightGen(cIniFile & a_IniFile) override
729  {
730  // No settings available
731  }
732 
733 protected:
735 
737 
740 
742  double m_Weights[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1];
743 
746 
747 
749  void getBiomeMinMax(EMCSBiome a_Biome, double & a_Min, double & a_Max)
750  {
751  switch (a_Biome)
752  {
753  case biBeach: a_Min = 61; a_Max = 64; break;
754  case biBirchForest: a_Min = 63; a_Max = 75; break;
755  case biBirchForestHills: a_Min = 63; a_Max = 90; break;
756  case biBirchForestHillsM: a_Min = 63; a_Max = 90; break;
757  case biBirchForestM: a_Min = 63; a_Max = 75; break;
758  case biColdBeach: a_Min = 61; a_Max = 64; break;
759  case biColdTaiga: a_Min = 63; a_Max = 75; break;
760  case biColdTaigaHills: a_Min = 63; a_Max = 90; break;
761  case biColdTaigaM: a_Min = 63; a_Max = 75; break;
762  case biDeepOcean: a_Min = 30; a_Max = 60; break;
763  case biDesert: a_Min = 63; a_Max = 70; break;
764  case biDesertHills: a_Min = 63; a_Max = 85; break;
765  case biDesertM: a_Min = 63; a_Max = 70; break;
766  case biEnd: a_Min = 10; a_Max = 100; break;
767  case biExtremeHills: a_Min = 60; a_Max = 120; break;
768  case biExtremeHillsEdge: a_Min = 63; a_Max = 100; break;
769  case biExtremeHillsM: a_Min = 60; a_Max = 120; break;
770  case biExtremeHillsPlus: a_Min = 60; a_Max = 140; break;
771  case biExtremeHillsPlusM: a_Min = 60; a_Max = 140; break;
772  case biFlowerForest: a_Min = 63; a_Max = 75; break;
773  case biForest: a_Min = 63; a_Max = 75; break;
774  case biForestHills: a_Min = 63; a_Max = 90; break;
775  case biFrozenOcean: a_Min = 45; a_Max = 64; break;
776  case biFrozenRiver: a_Min = 60; a_Max = 62; break;
777  case biIceMountains: a_Min = 63; a_Max = 90; break;
778  case biIcePlains: a_Min = 63; a_Max = 70; break;
779  case biIcePlainsSpikes: a_Min = 60; a_Max = 70; break;
780  case biJungle: a_Min = 60; a_Max = 80; break;
781  case biJungleEdge: a_Min = 62; a_Max = 75; break;
782  case biJungleEdgeM: a_Min = 62; a_Max = 75; break;
783  case biJungleHills: a_Min = 60; a_Max = 90; break;
784  case biJungleM: a_Min = 60; a_Max = 75; break;
785  case biMegaSpruceTaiga: a_Min = 63; a_Max = 75; break;
786  case biMegaSpruceTaigaHills: a_Min = 63; a_Max = 90; break;
787  case biMegaTaiga: a_Min = 63; a_Max = 75; break;
788  case biMegaTaigaHills: a_Min = 63; a_Max = 90; break;
789  case biMesa: a_Min = 63; a_Max = 90; break;
790  case biMesaBryce: a_Min = 60; a_Max = 67; break;
791  case biMesaPlateau: a_Min = 75; a_Max = 85; break;
792  case biMesaPlateauF: a_Min = 80; a_Max = 90; break;
793  case biMesaPlateauFM: a_Min = 80; a_Max = 90; break;
794  case biMesaPlateauM: a_Min = 75; a_Max = 85; break;
795  case biMushroomIsland: a_Min = 63; a_Max = 90; break;
796  case biMushroomShore: a_Min = 60; a_Max = 75; break;
797  case biNether: a_Min = 10; a_Max = 100; break;
798  case biOcean: a_Min = 45; a_Max = 64; break;
799  case biPlains: a_Min = 63; a_Max = 70; break;
800  case biRiver: a_Min = 60; a_Max = 62; break;
801  case biRoofedForest: a_Min = 63; a_Max = 75; break;
802  case biRoofedForestM: a_Min = 63; a_Max = 75; break;
803  case biSavanna: a_Min = 63; a_Max = 75; break;
804  case biSavannaM: a_Min = 63; a_Max = 80; break;
805  case biSavannaPlateau: a_Min = 75; a_Max = 100; break;
806  case biSavannaPlateauM: a_Min = 80; a_Max = 160; break;
807  case biStoneBeach: a_Min = 60; a_Max = 64; break;
808  case biSunflowerPlains: a_Min = 63; a_Max = 70; break;
809  case biSwampland: a_Min = 60; a_Max = 67; break;
810  case biSwamplandM: a_Min = 61; a_Max = 67; break;
811  case biTaiga: a_Min = 63; a_Max = 75; break;
812  case biTaigaHills: a_Min = 63; a_Max = 90; break;
813  case biTaigaM: a_Min = 63; a_Max = 80; break;
814  case biInvalidBiome:
815  case biNumBiomes:
816  case biVariant:
817  case biNumVariantBiomes:
818  {
819  ASSERT(!"Unknown biome");
820  a_Min = 10;
821  a_Max = 10;
822  break;
823  }
824  }
825  }
826 };
827 
828 
829 
830 
831 
833 // cTerrainHeightGen:
834 
835 std::unique_ptr<cTerrainHeightGen> cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
836 {
837  AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
838  if (HeightGenName.empty())
839  {
840  LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
841  HeightGenName = "Biomal";
842  }
843 
844  a_CacheOffByDefault = false;
845  std::unique_ptr<cTerrainHeightGen> res;
846  if (NoCaseCompare(HeightGenName, "Flat") == 0)
847  {
848  res = std::make_unique<cHeiGenFlat>();
849  a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
850  }
851  else if (NoCaseCompare(HeightGenName, "classic") == 0)
852  {
853  res = std::make_unique<cHeiGenClassic>(a_Seed);
854  }
855  else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
856  {
857  // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
858  // Return an empty pointer, the caller will create the proper generator:
859  return nullptr;
860  }
861  else if (NoCaseCompare(HeightGenName, "End") == 0)
862  {
863  // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
864  // Return an empty pointer, the caller will create the proper generator:
865  return nullptr;
866  }
867  else if (NoCaseCompare(HeightGenName, "MinMax") == 0)
868  {
869  res = std::make_unique<cHeiGenMinMax>(a_Seed, a_BiomeGen);
870  }
871  else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
872  {
873  res = std::make_unique<cHeiGenMountains>(a_Seed);
874  }
875  else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0)
876  {
877  // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
878  // Return an empty pointer, the caller will create the proper generator:
879  return nullptr;
880  }
881  else if (NoCaseCompare(HeightGenName, "Steppy") == 0)
882  {
883  res = std::make_unique<cHeiGenSteppy>(a_Seed);
884  }
885  else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
886  {
887  // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
888  // Return an empty pointer, the caller will create the proper generator:
889  return nullptr;
890  }
891  else if (NoCaseCompare(HeightGenName, "Biomal") == 0)
892  {
893  res = std::make_unique<cHeiGenBiomal>(a_Seed, a_BiomeGen);
894 
895  /*
896  // Performance-testing:
897  LOGINFO("Measuring performance of cHeiGenBiomal...");
898  clock_t BeginTick = clock();
899  for (int x = 0; x < 500; x++)
900  {
901  cChunkDef::HeightMap Heights;
902  res->GenHeightMap(x * 5, x * 5, Heights);
903  }
904  clock_t Duration = clock() - BeginTick;
905  LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
906  //*/
907  }
908  else
909  {
910  // No match found, force-set the default and retry
911  LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
912  a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
913  return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
914  }
915 
916  // Read the settings:
917  res->InitializeHeightGen(a_IniFile);
918 
919  return res;
920 }
921 
922 
923 
924 
925 
EMCSBiome
Biome IDs The first batch corresponds to the clientside biomes, used by MineCraft.
Definition: BiomeDef.h:18
@ biJungleEdge
Definition: BiomeDef.h:50
@ biIceMountains
Definition: BiomeDef.h:38
@ biMesa
Definition: BiomeDef.h:64
@ biMesaPlateau
Definition: BiomeDef.h:66
@ biExtremeHillsM
Definition: BiomeDef.h:79
@ biForestHills
Definition: BiomeDef.h:43
@ biNether
Definition: BiomeDef.h:31
@ biBirchForest
Definition: BiomeDef.h:54
@ biBirchForestM
Definition: BiomeDef.h:86
@ biDesert
Definition: BiomeDef.h:24
@ biMesaPlateauF
Definition: BiomeDef.h:65
@ biSunflowerPlains
Definition: BiomeDef.h:77
@ biExtremeHillsPlusM
Definition: BiomeDef.h:92
@ biColdTaigaHills
Definition: BiomeDef.h:58
@ biFlowerForest
Definition: BiomeDef.h:80
@ biInvalidBiome
Definition: BiomeDef.h:19
@ biMegaSpruceTaiga
Definition: BiomeDef.h:90
@ biStoneBeach
Definition: BiomeDef.h:52
@ biMushroomShore
Definition: BiomeDef.h:40
@ biSavanna
Definition: BiomeDef.h:62
@ biTaigaHills
Definition: BiomeDef.h:44
@ biExtremeHillsPlus
Definition: BiomeDef.h:61
@ biJungle
Definition: BiomeDef.h:46
@ biMegaTaiga
Definition: BiomeDef.h:59
@ biJungleHills
Definition: BiomeDef.h:47
@ biOcean
Definition: BiomeDef.h:22
@ biDesertHills
Definition: BiomeDef.h:42
@ biBeach
Definition: BiomeDef.h:41
@ biForest
Definition: BiomeDef.h:26
@ biJungleEdgeM
Definition: BiomeDef.h:85
@ biTaigaM
Definition: BiomeDef.h:81
@ biBirchForestHillsM
Definition: BiomeDef.h:87
@ biJungleM
Definition: BiomeDef.h:84
@ biExtremeHillsEdge
Definition: BiomeDef.h:45
@ biNumVariantBiomes
Definition: BiomeDef.h:99
@ biFrozenRiver
Definition: BiomeDef.h:35
@ biRiver
Definition: BiomeDef.h:29
@ biMesaPlateauFM
Definition: BiomeDef.h:96
@ biMushroomIsland
Definition: BiomeDef.h:39
@ biDeepOcean
Definition: BiomeDef.h:51
@ biSavannaPlateauM
Definition: BiomeDef.h:94
@ biMesaBryce
Definition: BiomeDef.h:95
@ biPlains
Definition: BiomeDef.h:23
@ biRoofedForestM
Definition: BiomeDef.h:88
@ biBirchForestHills
Definition: BiomeDef.h:55
@ biColdBeach
Definition: BiomeDef.h:53
@ biMegaSpruceTaigaHills
Definition: BiomeDef.h:91
@ biSavannaM
Definition: BiomeDef.h:93
@ biSwamplandM
Definition: BiomeDef.h:82
@ biVariant
Definition: BiomeDef.h:73
@ biEnd
Definition: BiomeDef.h:33
@ biIcePlainsSpikes
Definition: BiomeDef.h:83
@ biFrozenOcean
Definition: BiomeDef.h:34
@ biExtremeHills
Definition: BiomeDef.h:25
@ biColdTaiga
Definition: BiomeDef.h:57
@ biMegaTaigaHills
Definition: BiomeDef.h:60
@ biNumBiomes
Definition: BiomeDef.h:69
@ biRoofedForest
Definition: BiomeDef.h:56
@ biIcePlains
Definition: BiomeDef.h:36
@ biTaiga
Definition: BiomeDef.h:27
@ biDesertM
Definition: BiomeDef.h:78
@ biSavannaPlateau
Definition: BiomeDef.h:63
@ biMesaPlateauM
Definition: BiomeDef.h:97
@ biColdTaigaM
Definition: BiomeDef.h:89
@ biSwampland
Definition: BiomeDef.h:28
unsigned char HEIGHTTYPE
The type used by the heightmap.
Definition: ChunkDef.h:47
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:231
T Clamp(T a_Value, T a_Min, T a_Max)
Clamp X to the specified range.
Definition: Globals.h:336
#define ASSERT(x)
Definition: Globals.h:276
#define UNUSED
Definition: Globals.h:72
#define LOGWARN
Definition: LoggerSimple.h:88
float NOISE_DATATYPE
The datatype used by all the noise generators.
Definition: Noise.h:9
int NoCaseCompare(const AString &s1, const AString &s2)
Case-insensitive string comparison.
std::string AString
Definition: StringUtils.h:11
Definition: FastNBT.h:132
Wraps the chunk coords into a single structure.
Definition: ChunkDef.h:57
int m_ChunkZ
Definition: ChunkDef.h:60
int m_ChunkX
Definition: ChunkDef.h:59
static void BlockToChunk(int a_X, int a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords to chunk coords:
Definition: ChunkDef.h:210
static void SetHeight(HeightMap &a_HeightMap, int a_X, int a_Z, HEIGHTTYPE a_Height)
Definition: ChunkDef.h:311
HEIGHTTYPE HeightMap[Width *Width]
The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the hig...
Definition: ChunkDef.h:132
static const int Width
Definition: ChunkDef.h:124
static const int Height
Definition: ChunkDef.h:125
static HEIGHTTYPE GetHeight(const HeightMap &a_HeightMap, int a_X, int a_Z)
Definition: ChunkDef.h:303
static EMCSBiome GetBiome(const BiomeMap &a_BiomeMap, int a_X, int a_Z)
Definition: ChunkDef.h:319
EMCSBiome BiomeMap[Width *Width]
The type used for any biomemap operations and storage inside Cuberite, using Cuberite biomes (need no...
Definition: ChunkDef.h:137
The interface that a biome generator must implement A biome generator takes chunk coords on input and...
virtual void GenBiomes(cChunkCoords a_ChunkCoords, cChunkDef::BiomeMap &a_BiomeMap)=0
Generates biomes for the given chunk.
The interface that is used to query terrain height from the shape generator.
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap)=0
Retrieves the heightmap for the specified chunk.
static std::unique_ptr< cTerrainHeightGen > CreateHeightGen(cIniFile &a_IniFile, cBiomeGen &a_BiomeGen, int a_Seed, bool &a_CacheOffByDefault)
Creates a cTerrainHeightGen descendant based on the INI file settings.
Definition: HeiGen.cpp:835
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:58
cHeiGenSteppy(int a_Seed)
Definition: HeiGen.cpp:26
std::shared_ptr< cProtIntGen > m_Gen
Definition: HeiGen.cpp:74
double m_Weights[AVERAGING_SIZE *2+1][AVERAGING_SIZE *2+1]
Weights applied to each of the min / max values in the neighborhood of the currently evaluated column...
Definition: HeiGen.cpp:742
double m_TotalWeight
Sum of all the m_Weights items.
Definition: HeiGen.cpp:745
cNoise m_Noise
Definition: HeiGen.cpp:734
static const int AVERAGING_SIZE
Size of the averaging process, in columns (for each direction).
Definition: HeiGen.cpp:636
virtual void InitializeHeightGen(cIniFile &a_IniFile) override
Initializes the generator, reading its parameters from the INI file.
Definition: HeiGen.cpp:728
cBiomeGen & m_BiomeGen
The biome generator to query for the underlying biomes.
Definition: HeiGen.cpp:739
cPerlinNoise m_Perlin
Definition: HeiGen.cpp:736
void getBiomeMinMax(EMCSBiome a_Biome, double &a_Min, double &a_Max)
Returns the minimum and maximum heights for the given biome.
Definition: HeiGen.cpp:749
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:662
cHeiGenMinMax(int a_Seed, cBiomeGen &a_BiomeGen)
Definition: HeiGen.cpp:640
size_t m_CacheSize
Definition: HeiGen.h:56
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:128
std::vector< size_t > m_CacheOrder
Definition: HeiGen.h:57
size_t m_NumHits
Definition: HeiGen.h:61
virtual HEIGHTTYPE GetHeightAt(int a_BlockX, int a_BlockZ) override
Returns the height at the specified column.
Definition: HeiGen.cpp:181
cTerrainHeightGen & m_HeiGenToCache
The terrain height generator that is being cached.
Definition: HeiGen.h:53
size_t m_TotalChain
Definition: HeiGen.h:63
std::vector< sCacheData > m_CacheData
Definition: HeiGen.h:58
cHeiGenCache(cTerrainHeightGen &a_HeiGenToCache, size_t a_CacheSize)
Definition: HeiGen.cpp:109
size_t m_NumMisses
Definition: HeiGen.h:62
virtual HEIGHTTYPE GetHeightAt(int a_BlockX, int a_BlockZ) override
Returns the height at the specified column.
Definition: HeiGen.cpp:251
size_t m_NumSubCaches
Number of sub-caches, pulled out of m_SubCaches.size() for performance reasons.
Definition: HeiGen.h:90
static const size_t m_CoeffZ
The coefficient used to turn Z coords into index (x + Coeff * z).
Definition: HeiGen.h:87
cHeiGenMultiCache(std::unique_ptr< cTerrainHeightGen > a_HeightGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches)
Definition: HeiGen.cpp:222
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:238
std::vector< std::unique_ptr< cHeiGenCache > > m_SubCaches
The individual sub-caches.
Definition: HeiGen.h:93
std::unique_ptr< cTerrainHeightGen > m_Underlying
The underlying height generator.
Definition: HeiGen.h:96
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:84
HEIGHTTYPE m_Height
Definition: HeiGen.h:111
virtual void InitializeHeightGen(cIniFile &a_IniFile) override
Initializes the generator, reading its parameters from the INI file.
Definition: HeiGen.cpp:97
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:322
float m_HeightAmp1
Definition: HeiGen.h:132
cNoise m_Noise
Definition: HeiGen.h:131
cHeiGenClassic(int a_Seed)
Definition: HeiGen.cpp:288
float m_HeightFreq1
Definition: HeiGen.h:132
virtual void InitializeHeightGen(cIniFile &a_IniFile) override
Initializes the generator, reading its parameters from the INI file.
Definition: HeiGen.cpp:341
float m_HeightAmp3
Definition: HeiGen.h:134
float m_HeightFreq2
Definition: HeiGen.h:133
float GetNoise(float x, float y)
Definition: HeiGen.cpp:304
float m_HeightFreq3
Definition: HeiGen.h:134
float m_HeightAmp2
Definition: HeiGen.h:133
cPerlinNoise m_Perlin
Definition: HeiGen.h:158
cHeiGenMountains(int a_Seed)
Definition: HeiGen.cpp:358
virtual void InitializeHeightGen(cIniFile &a_IniFile) override
Initializes the generator, reading its parameters from the INI file.
Definition: HeiGen.cpp:399
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:370
cRidgedMultiNoise m_DitchNoise
Definition: HeiGen.h:157
cRidgedMultiNoise m_MountainNoise
Definition: HeiGen.h:156
virtual HEIGHTTYPE GetHeightAt(int a_BlockX, int a_BlockZ) override
Returns the height at the specified column.
Definition: HeiGen.h:184
virtual void InitializeHeightGen(cIniFile &a_IniFile) override
Initializes the generator, reading its parameters from the INI file.
Definition: HeiGen.cpp:553
virtual void GenHeightMap(cChunkCoords a_ChunkCoords, cChunkDef::HeightMap &a_HeightMap) override
Retrieves the heightmap for the specified chunk.
Definition: HeiGen.cpp:513
static const sGenParam m_GenParam[256]
Definition: HeiGen.h:205
cChunkDef::BiomeMap BiomeNeighbors[3][3]
Definition: HeiGen.h:192
cNoise m_Noise
Definition: HeiGen.h:194
cBiomeGen & m_BiomeGen
Definition: HeiGen.h:195
int GetValueSetI(const AString &keyname, const AString &valuename, const int defValue=0) override
Definition: IniFile.cpp:559
bool SetValue(const int keyID, const int valueID, const AString &value)
Definition: IniFile.cpp:397
AString GetValueSet(const AString &keyname, const AString &valuename, const AString &defValue="") override
Gets the value; if not found, write the default to the repository.
Definition: IniFile.cpp:526
double GetValueSetF(const AString &keyname, const AString &valuename, const double defValue=0.0)
Definition: IniFile.cpp:549
Definition: Noise.h:20
NOISE_DATATYPE CubicNoise2D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const
Definition: Noise.cpp:593
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude)
Adds a new octave to the list of octaves that compose this noise.
Definition: OctavedNoise.h:38
void Generate2D(NOISE_DATATYPE *a_Array, int a_SizeX, int a_SizeY, NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, NOISE_DATATYPE *a_Workspace=nullptr) const
Fills a 2D array with the values of the noise.
Definition: OctavedNoise.h:45