Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/terrain/StarKarstCave.cpp
blob: aaa982360e541941443e7b3c1ec49b6a6e9f3efa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include "StarKarstCave.hpp"
#include "StarJsonExtra.hpp"
#include "StarRandom.hpp"
#include "StarInterpolation.hpp"
#include "StarGameTypes.hpp"
#include "StarLogging.hpp"

namespace Star {

char const* const KarstCaveSelector::Name = "karstcave";

KarstCaveSelector::KarstCaveSelector(Json const& config, TerrainSelectorParameters const& parameters)
  : TerrainSelector(Name, config, parameters) {
  m_sectorSize = config.getUInt("sectorSize", 64);
  m_layerResolution = config.getInt("layerResolution");
  m_layerDensity = config.getFloat("layerDensity");
  m_bufferHeight = config.getInt("bufferHeight");
  m_caveTaperPoint = config.getFloat("caveTaperPoint");

  m_caveDecisionPerlinConfig = config.get("caveDecision");
  m_layerHeightVariationPerlinConfig = config.get("layerHeightVariation");
  m_caveHeightVariationPerlinConfig = config.get("caveHeightVariation");
  m_caveFloorVariationPerlinConfig = config.get("caveFloorVariation");

  m_worldWidth = parameters.worldWidth;
  m_seed = parameters.seed;

  m_layerPerlinsCache.setMaxSize(config.getUInt("layerPerlinsCacheSize", 16));
  m_sectorCache.setMaxSize(config.getUInt("sectorCacheSize", 16));
}

float KarstCaveSelector::get(int x, int y) const {
  Vec2I key = Vec2I(x - pmod(x, m_sectorSize), y - pmod(y, m_sectorSize));
  return m_sectorCache.get(key, [=](Vec2I const& key) {
      return Sector(this, key);
    }).get(x, y);
}

KarstCaveSelector::Sector::Sector(KarstCaveSelector const* parent, Vec2I sector)
  : parent(parent), sector(sector), values(square(parent->m_sectorSize)) {

  m_maxValue = 0;

  for (int y = sector[1] - parent->m_bufferHeight; y < sector[1] + parent->m_sectorSize + parent->m_bufferHeight; y++) {
    float layerChance = parent->m_layerDensity * parent->m_layerResolution;
    // determine whether this layer has caves
    if (y % parent->m_layerResolution == 0 && staticRandomFloat(parent->m_seed, y) <= layerChance) {
      LayerPerlins const& layerPerlins = parent->layerPerlins(y);

      // carve out cave layer
      for (int x = sector[0]; x < sector[0] + parent->m_sectorSize; x++) {
        // use wrapping noise
        float noiseAngle = 2 * Constants::pi * x / parent->m_worldWidth;
        float noiseX = (std::cos(noiseAngle) * parent->m_worldWidth) / (2 * Constants::pi);
        float noiseY = (std::sin(noiseAngle) * parent->m_worldWidth) / (2 * Constants::pi);

        // determine where caves be at
        float isThereACaveHere = layerPerlins.caveDecision.get(noiseX, noiseY);
        if (isThereACaveHere > 0) {
          float taperFactor = isThereACaveHere < parent->m_caveTaperPoint
              ? std::sin((0.5 * Constants::pi * isThereACaveHere) / parent->m_caveTaperPoint)
              : 1.0f;

          int baseY = y + layerPerlins.layerHeightVariation.get(noiseX, noiseY);
          int ceilingY = baseY + layerPerlins.caveHeightVariation.get(noiseX, noiseY) * taperFactor;
          int floorY = baseY + layerPerlins.caveFloorVariation.get(noiseX, noiseY) * taperFactor;
          float halfHeight = abs(ceilingY - floorY + 1) / 2.0f;
          float midpointY = (floorY + ceilingY) / 2.0f;

          m_maxValue = max(m_maxValue, halfHeight);

          for (int pointY = floorY; pointY < ceilingY; pointY++)
            if (inside(x, pointY))
              set(x, pointY, max(get(x, pointY), halfHeight - abs(midpointY - pointY)));
        }
      }
    }
  }
}

float KarstCaveSelector::Sector::get(int x, int y) {
  auto val = values[(x - sector[0]) + parent->m_sectorSize * (y - sector[1])];
  if (val > 0)
    return val;
  else
    return -m_maxValue;
}

bool KarstCaveSelector::Sector::inside(int x, int y) {
  int x_ = x - sector[0];
  if (x_ < 0 || x_ >= parent->m_sectorSize)
    return false;
  int y_ = y - sector[1];
  if (y_ < 0 || y_ >= parent->m_sectorSize)
    return false;
  return true;
}

void KarstCaveSelector::Sector::set(int x, int y, float value) {
  values[(x - sector[0]) + parent->m_sectorSize * (y - sector[1])] = value;
}

KarstCaveSelector::LayerPerlins const& KarstCaveSelector::layerPerlins(int y) const {
  return m_layerPerlinsCache.get(y, [this](int y) {
      return LayerPerlins{
        PerlinF(m_caveDecisionPerlinConfig, staticRandomU64(y, m_seed, "CaveDecision")),
        PerlinF(m_layerHeightVariationPerlinConfig, staticRandomU64(y, m_seed, "LayerHeightVariation")),
        PerlinF(m_caveHeightVariationPerlinConfig, staticRandomU64(y, m_seed, "CaveHeightVariation")),
        PerlinF(m_caveFloorVariationPerlinConfig, staticRandomU64(y, m_seed, "CaveFloorVariation"))
      };
    });
}

}