Cuberite
A lightweight, fast and extensible game server for Minecraft
Caves.cpp
Go to the documentation of this file.
1 
2 // Caves.cpp
3 
4 // Implements the various cave structure generators:
5 // - cStructGenWormNestCaves
6 // - cStructGenDualRidgeCaves
7 // - cStructGenMarbleCaves
8 // - cStructGenNetherCaves
9 
10 /*
11 WormNestCave generator:
12 Caves are generated in "nests" - groups of tunnels generated from a single point.
13 For each chunk, all the nests that could intersect it are generated.
14 For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...)
15 Then each tunnel is randomized by inserting points in between its ends.
16 Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other.
17 When the tunnels are ready, they are simply carved into the chunk, one by one.
18 To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it.
19 
20 MarbleCaves generator:
21 For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept.
22 Problem with this is the amount of CPU work needed for each chunk.
23 Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground.
24 
25 DualRidgeCaves generator:
26 Instead of evaluating a single noise function, two different noise functions are multiplied. This produces
27 regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be
28 reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best.
29 */
30 
31 #include "Globals.h"
32 #include "Caves.h"
33 #include "../BlockInfo.h"
34 
35 
36 
37 
38 
39 const int MIN_RADIUS = 3;
40 const int MAX_RADIUS = 8;
41 
42 
43 
44 
45 
47 {
48  int m_BlockX;
49  int m_BlockY;
50  int m_BlockZ;
51  int m_Radius;
52 
53  cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) :
54  m_BlockX(a_BlockX),
55  m_BlockY(a_BlockY),
56  m_BlockZ(a_BlockZ),
57  m_Radius(a_Radius)
58  {
59  }
60 } ;
61 
62 typedef std::vector<cCaveDefPoint> cCaveDefPoints;
63 
64 
65 
66 
67 
70 {
71  // The bounding box, including the radii around defpoints:
75 
77  void Randomize(cNoise & a_Noise);
78 
80  bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst);
81 
83  void Smooth(void);
84 
86  void FinishLinear(void);
87 
89  void CalcBoundingBox(void);
90 
91 public:
93 
95  int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
96  int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
97  cNoise & a_Noise
98  );
99 
101  void ProcessChunk(
102  int a_ChunkX, int a_ChunkZ,
103  cChunkDef::BlockTypes & a_BlockTypes,
104  cChunkDesc::BlockNibbleBytes & a_BlockMetas,
105  cChunkDef::HeightMap & a_HeightMap
106  );
107 
108  #ifndef NDEBUG
109  AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
110  #endif // !NDEBUG
111 } ;
112 
113 typedef std::vector<cCaveTunnel *> cCaveTunnels;
114 
115 
116 
117 
118 
122 {
124 
125 public:
126 
127  // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
128  int m_BlockX;
129  int m_BlockZ;
130 
131  cCaveSystem(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
132  virtual ~cCaveSystem() override;
133 
134 protected:
135  int m_Size;
137 
138  void Clear(void);
139 
142  int a_OriginX, int a_OriginY, int a_OriginZ,
143  cNoise & a_Noise, int a_Segments
144  );
145 
147  int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ);
148 
149  // cGridStructGen::cStructure overrides:
150  virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override;
151 } ;
152 
153 
154 
155 
156 
158 // cCaveTunnel:
159 
161  int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
162  int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
163  cNoise & a_Noise
164 )
165 {
166  m_Points.emplace_back(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius);
167  m_Points.emplace_back(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius);
168 
169  if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
170  {
171  // Don't bother detailing this cave, it's under the world anyway
172  m_MinBlockX = m_MaxBlockX = 0;
173  m_MinBlockY = m_MaxBlockY = -1;
174  m_MinBlockZ = m_MaxBlockZ = 0;
175  return;
176  }
177 
178  Randomize(a_Noise);
179  Smooth();
180 
181  // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data:
182  CalcBoundingBox();
183 
184  FinishLinear();
185 }
186 
187 
188 
189 
190 
192 {
193  // Repeat 4 times:
194  for (int i = 0; i < 4; i++)
195  {
196  // For each already present point, insert a point in between it and its predecessor, shifted randomly.
197  cCaveDefPoint & Point = m_Points.front();
198  int PrevX = Point.m_BlockX;
199  int PrevY = Point.m_BlockY;
200  int PrevZ = Point.m_BlockZ;
201  int PrevR = Point.m_Radius;
202  cCaveDefPoints Pts;
203  Pts.reserve(m_Points.size() * 2 + 1);
204  Pts.push_back(Point);
205  for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
206  {
207  int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11;
208  int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX);
209  len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY);
210  len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ);
211  len = 3 * static_cast<int>(sqrt(static_cast<double>(len))) / 4;
212  int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1));
213  Random /= 4;
214  int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2);
215  Random /= 256;
216  int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4);
217  Random /= 256;
218  int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2);
219  Pts.emplace_back(x, y, z, Rad);
220  Pts.push_back(*itr);
221  PrevX = itr->m_BlockX;
222  PrevY = itr->m_BlockY;
223  PrevZ = itr->m_BlockZ;
224  PrevR = itr->m_Radius;
225  }
226  std::swap(Pts, m_Points);
227  }
228 }
229 
230 
231 
232 
233 
235 {
236  if (a_Src.size() < 2)
237  {
238  // There are no midpoints, nothing to smooth
239  return true;
240  }
241 
242  // Smoothing: for each line segment, add points on its 1 / 4 lengths
243  bool res = false;
244  size_t Num = a_Src.size() - 2; // this many intermediary points
245  a_Dst.clear();
246  a_Dst.reserve(Num * 2 + 2);
247  cCaveDefPoints::const_iterator itr = a_Src.begin() + 1;
248  const cCaveDefPoint & Source = a_Src.front();
249  a_Dst.push_back(Source);
250  int PrevX = Source.m_BlockX;
251  int PrevY = Source.m_BlockY;
252  int PrevZ = Source.m_BlockZ;
253  int PrevR = Source.m_Radius;
254  for (size_t i = 0; i <= Num; ++i, ++itr)
255  {
256  int dx = itr->m_BlockX - PrevX;
257  int dy = itr->m_BlockY - PrevY;
258  int dz = itr->m_BlockZ - PrevZ;
259  if (abs(dx) + abs(dz) + abs(dy) < 6)
260  {
261  // Too short a segment to smooth-subdivide into quarters
262  PrevX = itr->m_BlockX;
263  PrevY = itr->m_BlockY;
264  PrevZ = itr->m_BlockZ;
265  PrevR = itr->m_Radius;
266  continue;
267  }
268  int dr = itr->m_Radius - PrevR;
269  int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
270  int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
271  a_Dst.emplace_back(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1);
272  a_Dst.emplace_back(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2);
273  PrevX = itr->m_BlockX;
274  PrevY = itr->m_BlockY;
275  PrevZ = itr->m_BlockZ;
276  PrevR = itr->m_Radius;
277  res = true;
278  }
279  a_Dst.push_back(a_Src.back());
280  return res && (a_Src.size() < a_Dst.size());
281 }
282 
283 
284 
285 
286 
288 {
289  cCaveDefPoints Pts;
290  for (;;)
291  {
292  if (!RefineDefPoints(m_Points, Pts))
293  {
294  std::swap(Pts, m_Points);
295  return;
296  }
297  if (!RefineDefPoints(Pts, m_Points))
298  {
299  return;
300  }
301  }
302 }
303 
304 
305 
306 
307 
309 {
310  // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints
311  cCaveDefPoints Pts;
312  std::swap(Pts, m_Points);
313 
314  m_Points.reserve(Pts.size() * 3);
315  cCaveDefPoint & PrevPoint = Pts.front();
316  int PrevX = PrevPoint.m_BlockX;
317  int PrevY = PrevPoint.m_BlockY;
318  int PrevZ = PrevPoint.m_BlockZ;
319  for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
320  {
321  int x1 = itr->m_BlockX;
322  int y1 = itr->m_BlockY;
323  int z1 = itr->m_BlockZ;
324  int dx = abs(x1 - PrevX);
325  int dy = abs(y1 - PrevY);
326  int dz = abs(z1 - PrevZ);
327  int sx = (PrevX < x1) ? 1 : -1;
328  int sy = (PrevY < y1) ? 1 : -1;
329  int sz = (PrevZ < z1) ? 1 : -1;
330  int R = itr->m_Radius;
331 
332  if (dx >= std::max(dy, dz)) // x dominant
333  {
334  int yd = dy - dx / 2;
335  int zd = dz - dx / 2;
336 
337  for (;;)
338  {
339  m_Points.emplace_back(PrevX, PrevY, PrevZ, R);
340 
341  if (PrevX == x1)
342  {
343  break;
344  }
345 
346  if (yd >= 0) // move along y
347  {
348  PrevY += sy;
349  yd -= dx;
350  }
351 
352  if (zd >= 0) // move along z
353  {
354  PrevZ += sz;
355  zd -= dx;
356  }
357 
358  // move along x
359  PrevX += sx;
360  yd += dy;
361  zd += dz;
362  }
363  }
364  else if (dy >= std::max(dx, dz)) // y dominant
365  {
366  int xd = dx - dy / 2;
367  int zd = dz - dy / 2;
368 
369  for (;;)
370  {
371  m_Points.emplace_back(PrevX, PrevY, PrevZ, R);
372 
373  if (PrevY == y1)
374  {
375  break;
376  }
377 
378  if (xd >= 0) // move along x
379  {
380  PrevX += sx;
381  xd -= dy;
382  }
383 
384  if (zd >= 0) // move along z
385  {
386  PrevZ += sz;
387  zd -= dy;
388  }
389 
390  // move along y
391  PrevY += sy;
392  xd += dx;
393  zd += dz;
394  }
395  }
396  else
397  {
398  // z dominant
399  ASSERT(dz >= std::max(dx, dy));
400  int xd = dx - dz / 2;
401  int yd = dy - dz / 2;
402 
403  for (;;)
404  {
405  m_Points.emplace_back(PrevX, PrevY, PrevZ, R);
406 
407  if (PrevZ == z1)
408  {
409  break;
410  }
411 
412  if (xd >= 0) // move along x
413  {
414  PrevX += sx;
415  xd -= dz;
416  }
417 
418  if (yd >= 0) // move along y
419  {
420  PrevY += sy;
421  yd -= dz;
422  }
423 
424  // move along z
425  PrevZ += sz;
426  xd += dx;
427  yd += dy;
428  }
429  } // if (which dimension is dominant)
430  } // for itr
431 }
432 
433 
434 
435 
436 
438 {
439  cCaveDefPoint & Point = m_Points.front();
440  m_MinBlockX = m_MaxBlockX = Point.m_BlockX;
441  m_MinBlockY = m_MaxBlockY = Point.m_BlockY;
442  m_MinBlockZ = m_MaxBlockZ = Point.m_BlockZ;
443  for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
444  {
445  m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius);
446  m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius);
447  m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius);
448  m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius);
449  m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius);
450  m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius);
451  } // for itr - m_Points[]
452 }
453 
454 
455 
456 
457 
459  int a_ChunkX, int a_ChunkZ,
460  cChunkDef::BlockTypes & a_BlockTypes,
461  cChunkDesc::BlockNibbleBytes & a_BlockMetas,
462  cChunkDef::HeightMap & a_HeightMap
463 )
464 {
465  int BaseX = a_ChunkX * cChunkDef::Width;
466  int BaseZ = a_ChunkZ * cChunkDef::Width;
467  if (
468  (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) ||
469  (BaseZ > m_MaxBlockZ) || (BaseZ + cChunkDef::Width < m_MinBlockZ)
470  )
471  {
472  // Tunnel does not intersect the chunk at all, bail out
473  return;
474  }
475 
476  int BlockStartX = a_ChunkX * cChunkDef::Width;
477  int BlockStartZ = a_ChunkZ * cChunkDef::Width;
478  int BlockEndX = BlockStartX + cChunkDef::Width;
479  int BlockEndZ = BlockStartZ + cChunkDef::Width;
480  for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
481  {
482  if (
483  (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
484  (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
485  (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
486  (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
487  )
488  {
489  // Cannot intersect, bail out early
490  continue;
491  }
492 
493  // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3 / 7 off the top and bottom:
494  int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
495  int DifY = itr->m_BlockY;
496  int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
497  int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1);
498  int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, static_cast<int>(cChunkDef::Height));
499  int SqRad = itr->m_Radius * itr->m_Radius;
500  for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
501  {
502  for (int y = Bottom; y <= Top; y++)
503  {
504  int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z);
505  if (4 * SqDist <= SqRad)
506  {
507  if (cBlockInfo::CanBeTerraformed(cChunkDef::GetBlock(a_BlockTypes, x, y, z)))
508  {
509  cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
510  }
511  }
512  else if (SqDist <= SqRad * 2)
513  {
514  if (cChunkDef::GetBlock(a_BlockTypes, x, y, z) == E_BLOCK_SAND)
515  {
516  const auto Index = cChunkDef::MakeIndex(x, y, z);
517  if (a_BlockMetas[Index] == 1)
518  {
519  a_BlockMetas[Index] = 0;
520  cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_RED_SANDSTONE);
521  }
522  else
523  {
524  cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_SANDSTONE);
525  }
526  }
527  }
528  } // for y
529  } // for x, z
530  } // for itr - m_Points[]
531 
532  /*
533  #ifndef NDEBUG
534  // For debugging purposes, outline the shape of the cave using glowstone, after carving the entire cave:
535  for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
536  {
537  int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
538  int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
539  if (
540  (DifX >= 0) && (DifX < cChunkDef::Width) &&
541  (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) &&
542  (DifZ >= 0) && (DifZ < cChunkDef::Width)
543  )
544  {
545  cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE);
546  }
547  } // for itr - m_Points[]
548  #endif // !NDEBUG
549  //*/
550 }
551 
552 
553 
554 
555 
556 #ifndef NDEBUG
557 AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
558 {
559  AString SVG;
560  SVG.reserve(m_Points.size() * 20 + 200);
561  SVG.append(fmt::format(FMT_STRING("<path style=\"fill:none;stroke:#{:06x};stroke-width:1px;\"\nd=\""), a_Color));
562  char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
563  for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
564  {
565  SVG.append(fmt::format(FMT_STRING("{} {}, {} "), Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ));
566  Prefix = 'L';
567  }
568  SVG.append("\"/>\n");
569  return SVG;
570 }
571 #endif // !NDEBUG
572 
573 
574 
575 
576 
578 // cStructGenWormNestCaves::cCaveSystem:
579 
580 cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
581  Super(a_GridX, a_GridZ, a_OriginX, a_OriginZ),
582  m_Size(a_Size)
583 {
584  int Num = 1 + a_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) % 3;
585  for (int i = 0; i < Num; i++)
586  {
587  int OriginX = a_OriginX + (a_Noise.IntNoise3DInt(13 * a_OriginX, 17 * a_OriginZ, 11 * i) / 19) % a_MaxOffset;
588  int OriginZ = a_OriginZ + (a_Noise.IntNoise3DInt(17 * a_OriginX, 13 * a_OriginZ, 11 * i) / 23) % a_MaxOffset;
589  int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_OriginX, 13 * a_OriginZ, 11 * i) / 17) % 20;
590 
591  // Generate three branches from the origin point:
592  // The tunnels generated depend on X, Y, Z and Branches,
593  // for the same set of numbers it generates the same offsets!
594  // That's why we add a +1 to X in the third line
595  GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3);
596  GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2);
597  GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3);
598  }
599 }
600 
601 
602 
603 
604 
606 {
607  Clear();
608 }
609 
610 
611 
612 
613 
615 {
616  int ChunkX = a_ChunkDesc.GetChunkX();
617  int ChunkZ = a_ChunkDesc.GetChunkZ();
618  cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
619  cChunkDef::HeightMap & HeightMap = a_ChunkDesc.GetHeightMap();
620  cChunkDesc::BlockNibbleBytes & BlockMetas = a_ChunkDesc.GetBlockMetasUncompressed();
621  for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
622  {
623  (*itr)->ProcessChunk(ChunkX, ChunkZ, BlockTypes, BlockMetas, HeightMap);
624  } // for itr - m_Tunnels[]
625 }
626 
627 
628 
629 
630 
632 {
633  for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
634  {
635  delete *itr;
636  }
637  m_Tunnels.clear();
638 }
639 
640 
641 
642 
643 
645  int a_OriginX, int a_OriginY, int a_OriginZ,
646  cNoise & a_Noise, int a_NumSegments
647 )
648 {
649  int DoubleSize = m_Size * 2;
650  int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX);
651  for (int i = a_NumSegments - 1; i >= 0; --i)
652  {
653  int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2;
654  int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4;
655  int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2;
656  int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX);
657  m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise));
658  GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i);
659  a_OriginX = EndX;
660  a_OriginY = EndY;
661  a_OriginZ = EndZ;
662  Radius = EndR;
663  } // for i - a_NumSegments
664 }
665 
666 
667 
668 
669 
670 int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ)
671 {
672  // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large
673  int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11;
674  /*
675  // Not good enough:
676  // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value.
677  // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise
678  int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff);
679  // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384.
680  // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center
681  int res = 3 + abs((sum / 24) - 16);
682  */
683 
684  // Algorithm of choice: random value in the range of zero to random value - heavily towards zero
685  int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1);
686  return res;
687 }
688 
689 
690 
691 
692 
694 // cStructGenWormNestCaves:
695 
696 cGridStructGen::cStructurePtr cStructGenWormNestCaves::CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ)
697 {
698  return cStructurePtr(new cCaveSystem(a_GridX, a_GridZ, a_OriginX, a_OriginZ, m_MaxOffset, m_Size, m_Noise));
699 }
700 
701 
702 
703 
704 
706 // cStructGenMarbleCaves:
707 
708 static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise)
709 {
710  static const float PI_2 = 1.57079633f;
711  float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4;
712 
713  oct1 = oct1 * oct1 * oct1;
714  if (oct1 < 0.f)
715  {
716  oct1 = PI_2;
717  }
718  if (oct1 > PI_2)
719  {
720  oct1 = PI_2;
721  }
722 
723  return oct1;
724 }
725 
726 
727 
728 
729 
731 {
732  cNoise Noise(m_Seed);
733  for (int z = 0; z < cChunkDef::Width; z++)
734  {
735  const float zz = static_cast<float>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z);
736  for (int x = 0; x < cChunkDef::Width; x++)
737  {
738  const float xx = static_cast<float>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x);
739 
740  int Top = a_ChunkDesc.GetHeight(x, z);
741  for (int y = 1; y < Top; ++y)
742  {
743  if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE)
744  {
745  continue;
746  }
747 
748  const float yy = static_cast<float>(y);
749  const float WaveNoise = 1;
750  if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
751  {
752  a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
753  }
754  } // for y
755  } // for x
756  } // for z
757 }
758 
759 
760 
761 
762 
764 // cStructGenDualRidgeCaves:
765 
767 {
768  for (int z = 0; z < cChunkDef::Width; z++)
769  {
770  const float zz = static_cast<float>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10;
771  for (int x = 0; x < cChunkDef::Width; x++)
772  {
773  const float xx = static_cast<float>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10;
774 
775  int Top = a_ChunkDesc.GetHeight(x, z);
776  for (int y = 1; y <= Top; ++y)
777  {
778  const float yy = static_cast<float>(y / 10);
779  float n1 = m_Noise1.CubicNoise3D(xx, yy, zz);
780  float n2 = m_Noise2.CubicNoise3D(xx, yy, zz);
781  float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
782  float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
783  if ((std::abs(n1 + n3) * std::abs(n2 + n4)) > m_Threshold)
784  {
785  a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
786  }
787  } // for y
788  } // for x
789  } // for z
790 }
791 
792 
793 
794 
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_RED_SANDSTONE
Definition: BlockType.h:198
@ E_BLOCK_SANDSTONE
Definition: BlockType.h:34
@ E_BLOCK_STONE
Definition: BlockType.h:11
@ E_BLOCK_SAND
Definition: BlockType.h:22
std::vector< cCaveTunnel * > cCaveTunnels
Definition: Caves.cpp:113
static float GetMarbleNoise(float x, float y, float z, cNoise &a_Noise)
Definition: Caves.cpp:708
const int MAX_RADIUS
Definition: Caves.cpp:40
std::vector< cCaveDefPoint > cCaveDefPoints
Definition: Caves.cpp:62
const int MIN_RADIUS
Definition: Caves.cpp:39
#define ASSERT(x)
Definition: Globals.h:276
std::string AString
Definition: StringUtils.h:11
bool Bottom(const BlockState Block)
static bool CanBeTerraformed(BLOCKTYPE Block)
Can a finisher change it?
Definition: BlockInfo.cpp:575
BLOCKTYPE BlockTypes[NumBlocks]
The type used for block type operations and storage, AXIS_ORDER ordering.
Definition: ChunkDef.h:140
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 BLOCKTYPE GetBlock(const BLOCKTYPE *a_BlockTypes, Vector3i a_RelPos)
Definition: ChunkDef.h:280
static size_t MakeIndex(int x, int y, int z)
Definition: ChunkDef.h:227
static const int Width
Definition: ChunkDef.h:124
static const int Height
Definition: ChunkDef.h:125
static void SetBlock(BLOCKTYPE *a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type)
Definition: ChunkDef.h:264
int m_BlockY
Definition: Caves.cpp:49
int m_Radius
Definition: Caves.cpp:51
int m_BlockX
Definition: Caves.cpp:48
cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius)
Definition: Caves.cpp:53
int m_BlockZ
Definition: Caves.cpp:50
A single non-branching tunnel of a WormNestCave.
Definition: Caves.cpp:70
int m_MaxBlockX
Definition: Caves.cpp:72
void FinishLinear(void)
Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block.
Definition: Caves.cpp:308
int m_MinBlockX
Definition: Caves.cpp:72
void Randomize(cNoise &a_Noise)
Generates the shaping defpoints for the cave, based on the cave block coords and noise.
Definition: Caves.cpp:191
int m_MinBlockZ
Definition: Caves.cpp:74
void Smooth(void)
Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true.
Definition: Caves.cpp:287
void ProcessChunk(int a_ChunkX, int a_ChunkZ, cChunkDef::BlockTypes &a_BlockTypes, cChunkDesc::BlockNibbleBytes &a_BlockMetas, cChunkDef::HeightMap &a_HeightMap)
Carves the tunnel into the chunk specified.
Definition: Caves.cpp:458
bool RefineDefPoints(const cCaveDefPoints &a_Src, cCaveDefPoints &a_Dst)
Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (...
Definition: Caves.cpp:234
int m_MaxBlockY
Definition: Caves.cpp:73
void CalcBoundingBox(void)
Calculates the bounding box of the points present.
Definition: Caves.cpp:437
int m_MaxBlockZ
Definition: Caves.cpp:74
cCaveDefPoints m_Points
Definition: Caves.cpp:92
int m_MinBlockY
Definition: Caves.cpp:73
AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
Definition: Caves.cpp:557
cCaveTunnel(int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, cNoise &a_Noise)
Definition: Caves.cpp:160
A collection of connected tunnels, possibly branching.
Definition: Caves.cpp:122
virtual ~cCaveSystem() override
Definition: Caves.cpp:605
void GenerateTunnelsFromPoint(int a_OriginX, int a_OriginY, int a_OriginZ, cNoise &a_Noise, int a_Segments)
Generates a_Segment successive tunnels, with possible branches.
Definition: Caves.cpp:644
cCaveSystem(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ, int a_MaxOffset, int a_Size, cNoise &a_Noise)
Definition: Caves.cpp:580
int GetRadius(cNoise &a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ)
Returns a radius based on the location provided.
Definition: Caves.cpp:670
virtual void DrawIntoChunk(cChunkDesc &a_ChunkDesc) override
Draws self into the specified chunk.
Definition: Caves.cpp:614
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: Caves.cpp:730
virtual void GenFinish(cChunkDesc &a_ChunkDesc) override
Definition: Caves.cpp:766
virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override
Create a new structure at the specified gridpoint.
Definition: Caves.cpp:696
HEIGHTTYPE GetHeight(int a_RelX, int a_RelZ) const
Definition: ChunkDesc.cpp:144
void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
Definition: ChunkDesc.cpp:81
int GetChunkX() const
Definition: ChunkDesc.h:49
cChunkDef::BlockTypes & GetBlockTypes(void)
Definition: ChunkDesc.h:235
cChunkDef::HeightMap & GetHeightMap(void)
Definition: ChunkDesc.h:239
BlockNibbleBytes & GetBlockMetasUncompressed(void)
Definition: ChunkDesc.h:238
BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ) const
Definition: ChunkDesc.cpp:90
NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks]
Uncompressed block metas, 1 meta per byte.
Definition: ChunkDesc.h:39
int GetChunkZ() const
Definition: ChunkDesc.h:50
std::shared_ptr< cStructure > cStructurePtr
Definition: GridStructGen.h:77
int m_Seed
Seed for generating grid offsets and also available for descendants.
cNoise m_Noise
The noise used for generating grid offsets.
Represents a single structure that occupies the grid point.
Definition: GridStructGen.h:50
Definition: Noise.h:20
NOISE_DATATYPE CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const
Definition: Noise.cpp:621
int IntNoise3DInt(int a_X, int a_Y, int a_Z) const
Definition: Noise.h:254
int IntNoise2DInt(int a_X, int a_Y) const
Definition: Noise.h:243