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