1 #include "Generator.hpp"
3 #include "BlockType.hpp"
4 #include "BlockTypeRegistry.hpp"
6 #include "../rand/OctaveNoise.hpp"
16 const BlockType *type;
18 Candidate(const BlockType *type, float threshold)
19 : type(type), threshold(threshold) { }
22 std::vector<Candidate> candidates;
26 Generator::Generator(const Config &config) noexcept
30 , solidity_noise(config.seed ^ config.solidity.seed_mask)
31 , humidity_noise(config.seed ^ config.humidity.seed_mask)
32 , temperature_noise(config.seed ^ config.temperature.seed_mask)
33 , richness_noise(config.seed ^ config.richness.seed_mask)
34 , random_noise(config.seed ^ config.randomness.seed_mask) {
38 void Generator::LoadTypes(const BlockTypeRegistry ®) {
41 for (const BlockType &type : reg) {
43 types.push_back(&type);
44 if (type.min_solidity < min_solidity) {
45 min_solidity = type.min_solidity;
49 candidates.reserve(types.size());
54 struct Interpolation {
55 /// sample points for interpolation
56 /// given coordinates should be the absoloute position of the chunk's (0,0,0) block
58 const SimplexNoise &noise,
59 const glm::vec3 &base,
60 const Generator::Config::NoiseParam &conf
62 for (int z = 0; z < 5; ++z) {
63 for (int y = 0; y < 5; ++y) {
64 for (int x = 0; x < 5; ++x) {
65 samples[z][y][x] = OctaveNoise(
67 base + (glm::vec3(x, y, z) * 4.0f),
78 float samples[5][5][5];
96 struct Generator::ValueField {
98 Interpolation solidity;
99 Interpolation humidity;
100 Interpolation temperature;
101 Interpolation richness;
102 Interpolation randomness;
104 static Parameters GetParams(const glm::ivec3 &pos) noexcept {
112 static float Interpolate(const Interpolation &i, const Parameters &p) noexcept {
113 constexpr float A[4] = { 1.0f, 0.75f, 0.5f, 0.25f };
114 constexpr float B[4] = { 0.0f, 0.25f, 0.5f, 0.75f };
115 const float l1[4] = {
116 i.samples[p.a.z][p.a.y][p.a.x] * A[p.d.x] + i.samples[p.a.z][p.a.y][p.b.x] * B[p.d.x],
117 i.samples[p.a.z][p.b.y][p.a.x] * A[p.d.x] + i.samples[p.a.z][p.b.y][p.b.x] * B[p.d.x],
118 i.samples[p.b.z][p.a.y][p.a.x] * A[p.d.x] + i.samples[p.b.z][p.a.y][p.b.x] * B[p.d.x],
119 i.samples[p.b.z][p.b.y][p.a.x] * A[p.d.x] + i.samples[p.b.z][p.b.y][p.b.x] * B[p.d.x],
121 const float l2[2] = {
122 l1[0] * A[p.d.y] + l1[1] * B[p.d.y],
123 l1[2] * A[p.d.y] + l1[3] * B[p.d.y],
125 return l2[0] * A[p.d.z] + l2[1] * B[p.d.z];
130 void Generator::operator ()(Chunk &chunk) const noexcept {
131 ExactLocation::Fine coords(chunk.Position() * ExactLocation::Extent());
134 { solidity_noise, coords, config.solidity },
135 { humidity_noise, coords, config.humidity },
136 { temperature_noise, coords, config.temperature },
137 { richness_noise, coords, config.richness },
138 { random_noise, coords, config.randomness },
140 for (int z = 0; z < Chunk::side; ++z) {
141 for (int y = 0; y < Chunk::side; ++y) {
142 for (int x = 0; x < Chunk::side; ++x) {
143 chunk.SetBlock(RoughLocation::Fine(x, y, z), Generate(field, RoughLocation::Fine(x, y, z)));
147 chunk.SetGenerated();
150 Block Generator::Generate(const ValueField &field, const glm::ivec3 &pos) const noexcept {
151 Parameters params(ValueField::GetParams(pos));
152 float solidity = ValueField::Interpolate(field.solidity, params);
153 if (solidity < min_solidity) {
156 float humidity = ValueField::Interpolate(field.humidity, params);
157 float temperature = ValueField::Interpolate(field.temperature, params);
158 float richness = ValueField::Interpolate(field.richness, params);
162 for (const BlockType *type : types) {
163 if (solidity < type->min_solidity || solidity > type->max_solidity) continue;
164 if (humidity < type->min_humidity || humidity > type->max_humidity) continue;
165 if (temperature < type->min_temperature || temperature > type->max_temperature) continue;
166 if (richness < type->min_richness || richness > type->max_richness) continue;
167 float solidity_match = 4.0f - ((solidity - type->mid_solidity) * (solidity - type->mid_solidity));
168 float humidity_match = 4.0f - ((humidity - type->mid_humidity) * (humidity - type->mid_humidity));
169 float temperature_match = 4.0f - ((temperature - type->mid_temperature) * (temperature - type->mid_temperature));
170 float richness_match = 4.0f - ((richness - type->mid_richness) * (richness - type->mid_richness));
171 float chance = (solidity_match + humidity_match + temperature_match + richness_match) * type->commonness;
173 candidates.emplace_back(type, total);
175 if (candidates.empty()) {
178 float random = ValueField::Interpolate(field.randomness, params);
179 // as weird as it sounds, but this is faster tham glm::fract and generates a
180 // better distribution than (transformed variants of) erf, erfc, atan, and smoothstep
181 if (random < 0.0f) random += 1.0f;
182 float value = random * total;
183 // TODO: change to binary search
184 for (const Candidate &cand : candidates) {
185 if (value < cand.threshold) {
186 return Block(cand.type->id);
189 // theoretically, this should never happen
190 return Block(candidates.back().type->id);