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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
#include "geoData.h"
#include "gfx/image.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <maths.h>
#include <random>
#include <stb/stb_image.h>
#include <stdexcept>
#include <util.h>
GeoData::GeoData(Limits l, float s) :
limit {std::move(l)}, size {(limit.second - limit.first) + 1}, scale {s}, nodes {[this]() {
return (static_cast<std::size_t>(size.x * size.y));
}()}
{
}
void
GeoData::generateRandom()
{
// We acknowledge this is terrible :)
// Add hills
std::mt19937 gen(std::random_device {}());
std::uniform_int_distribution<> rxpos(limit.first.x + 2, limit.second.x - 2),
rypos(limit.first.y + 2, limit.second.y - 2);
std::uniform_int_distribution<> rsize(10, 30);
std::uniform_real_distribution<float> rheight(1, 3);
for (int h = 0; h < 500;) {
const glm::ivec2 hpos {rxpos(gen), rypos(gen)};
const glm::ivec2 hsize {rsize(gen), rsize(gen)};
if (const auto lim1 = hpos - hsize; lim1.x > limit.first.x && lim1.y > limit.first.y) {
if (const auto lim2 = hpos + hsize; lim2.x < limit.second.x && lim2.y < limit.second.y) {
const auto height = rheight(gen);
const glm::ivec2 hsizesqrd {hsize.x * hsize.x, hsize.y * hsize.y};
for (auto y = lim1.y; y < lim2.y; y += 1) {
for (auto x = lim1.x; x < lim2.x; x += 1) {
const auto dist {hpos - glm::ivec2 {x, y}};
const glm::ivec2 distsqrd {dist.x * dist.x, dist.y * dist.y};
const auto out {rdiv(sq(x - hpos.x), sq(hsize.x)) + rdiv(sq(y - hpos.y), sq(hsize.y))};
if (out <= 1.0F) {
auto & node {nodes[at({x, y})]};
const auto m {1.F / (7.F * out - 8.F) + 1.F};
node.height += height * m;
}
}
}
h += 1;
}
}
}
}
void
GeoData::loadFromImages(const std::filesystem::path & fileName, float scale_)
{
const Image map {fileName.c_str(), STBI_grey};
size = {map.width, map.height};
limit = {{0, 0}, size - glm::uvec2 {1, 1}};
const auto points {size.x * size.y};
scale = scale_;
nodes.resize(points);
std::transform(map.data.data(), map.data.data() + points, nodes.begin(), [](auto d) {
return Node {(d * 0.1F) - 1.5F};
});
}
glm::vec3
GeoData::positionAt(const glm::vec2 wcoord) const
{
const auto coord {wcoord / scale};
const std::array<glm::vec2, 4> gridCorner {
{{std::floor(coord.x), std::floor(coord.y)}, {std::floor(coord.x), std::ceil(coord.y)},
{std::ceil(coord.x), std::floor(coord.y)}, {std::ceil(coord.x), std::ceil(coord.y)}}};
const auto point {transform_array(gridCorner, [this](const auto c) {
return nodes[at(c)].height;
})};
const glm::vec2 frac = coord - gridCorner.front();
const auto heightFloor = point[0] + ((point[2] - point[0]) * frac.x),
heightCeil = point[1] + ((point[3] - point[1]) * frac.x),
heightMid = heightFloor + ((heightCeil - heightFloor) * frac.y);
return wcoord || heightMid;
}
unsigned int
GeoData::at(glm::ivec2 coord) const
{
if (coord.x < limit.first.x || coord.x > limit.second.x || coord.y < limit.first.y || coord.y > limit.second.y) {
throw std::range_error {"Coordinates outside GeoData limits"};
}
const glm::uvec2 offset = coord - limit.first;
return offset.x + (offset.y * size.x);
}
unsigned int
GeoData::at(int x, int y) const
{
return at({x, y});
}
GeoData::Limits
GeoData::getLimit() const
{
return limit;
}
float
GeoData::getScale() const
{
return scale;
}
glm::uvec2
GeoData::getSize() const
{
return size;
}
std::span<const GeoData::Node>
GeoData::getNodes() const
{
return nodes;
}
|