Symphony Of Empires
borders.cpp
Go to the documentation of this file.
1 // Eng3D - General purpouse game engine
2 // Copyright (C) 2021, Eng3D contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // borders.cpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #include <unordered_set>
26 #include <stack>
27 #include <glm/mat4x4.hpp>
28 
29 #include "eng3d/borders.hpp"
30 #include "eng3d/texture.hpp"
31 #include "eng3d/state.hpp"
32 #include "eng3d/curve.hpp"
33 #include "eng3d/shader.hpp"
34 #include "eng3d/camera.hpp"
35 
40  : s{ _s }
41 {
42 
43  Eng3D::TextureOptions mipmap_options{};
44  mipmap_options.wrap_s = Eng3D::TextureOptions::Wrap::REPEAT;
45  mipmap_options.wrap_t = Eng3D::TextureOptions::Wrap::REPEAT;
46  mipmap_options.min_filter = Eng3D::TextureOptions::Filter::LINEAR_MIPMAP;
47  mipmap_options.mag_filter = Eng3D::TextureOptions::Filter::LINEAR;
48  mipmap_options.internal_format = Eng3D::TextureOptions::Format::SRGB;
49  water_tex = s.tex_man.load(s.package_man.get_unique("gfx/water_tex.png"), mipmap_options);
50  line_shader = std::make_unique<Eng3D::OpenGL::Program>();
51  {
52  auto vs_shader = *s.builtin_shaders["vs_3d"];
53  line_shader->attach_shader(vs_shader);
54  auto fs_shader = Eng3D::OpenGL::FragmentShader(s.package_man.get_unique("shaders/curve.fs")->read_all(), true);
55  line_shader->attach_shader(fs_shader);
56  line_shader->link();
57  }
58 
59  if(!lazy_init)
60  this->build_borders();
61 }
62 
64  std::unordered_set<int> walked_positions;
65  std::unordered_set<int> walked_paths;
66  std::stack<int> unexplored_paths;
67  std::stack<int> current_paths;
68  std::vector<std::vector<glm::vec3>>& borders;
69  const uint32_t* pixels;
70  int width;
71  int height;
72  BorderGenerator(std::vector<std::vector<glm::vec3>>& _borders, const uint32_t* _pixels, int _width, int _height)
73  : borders{ _borders },
74  pixels{ _pixels },
75  width{ _width },
76  height{ _height }
77  {
78 
79  }
80 
81  bool check_neighbor(int new_x, int new_y) {
82  if(new_x < 0 || new_y < 0 || new_x >= width - 1 || new_y >= height - 1)
83  return false;
84  int new_index = new_x + new_y * width;
85  const auto color_ul = pixels[new_index];
86  const auto color_dl = pixels[new_index + width];
87  const auto color_ur = pixels[new_index + 1];
88  const auto color_dr = pixels[new_index + width + 1];
89  // Different neighbor, ie its a border
90  if(color_ul != color_ur || color_ur != color_dr || color_dr != color_dl || color_dl != color_ul)
91  return true;
92  return false;
93  }
94 
95  void add_neighbor(int prev_x, int prev_y, int new_x, int new_y, int& connections) {
96  if(check_neighbor(new_x, new_y)) {
97  int old_index = prev_x + prev_y * width;
98  int new_index = new_x + new_y * width;
99  int index = 2 * glm::min<int>(old_index, new_index) + std::abs(prev_x - new_x);
100  if(walked_paths.count(index)) return;
101  walked_positions.insert(new_index);
102  if(connections++ > 1) {
103  unexplored_paths.push(old_index);
104  } else {
105  current_paths.push(new_index);
106  walked_paths.insert(index);
107  }
108  }
109  }
110 
111  void get_border(int current_index, int connections) {
112  int x = current_index % width;
113  int y = current_index / width;
114  auto& current_river = borders.back();
115  current_river.push_back(glm::vec3(x + 1.f, y + 1.f, -0.05));
116 
117  add_neighbor(x, y, x - 1, y + 0, connections);
118  add_neighbor(x, y, x + 1, y + 0, connections);
119  add_neighbor(x, y, x + 0, y + 1, connections);
120  add_neighbor(x, y, x + 0, y - 1, connections);
121  }
122 
123  void clear_stack() {
124  while(!current_paths.empty() || !unexplored_paths.empty()) {
125  while(!current_paths.empty()) {
126  auto current_path = current_paths.top();
127  current_paths.pop();
128  get_border(current_path, 1);
129  }
130 
131  if(!unexplored_paths.empty()) {
132  current_paths.push(unexplored_paths.top());
133  unexplored_paths.pop();
134  borders.push_back(std::vector<glm::vec3>());
135  }
136  }
137  borders.push_back(std::vector<glm::vec3>());
138  }
139 
140 
141 public:
142  static void build_borders(std::vector<std::vector<glm::vec3>>& borders, const uint32_t* pixels, int width, int height) {
143  BorderGenerator generator(borders, pixels, width, height);
144  borders.push_back(std::vector<glm::vec3>());
145  for(int y = 0; y < height; y++) {
146  for(int x = 0; x < width; x++) {
147  int curr_index = x + y * width;
148  if(generator.check_neighbor(x, y) && !generator.walked_positions.count(curr_index)) {
149  generator.get_border(curr_index, 1);
150  generator.clear_stack();
151  }
152  }
153  }
154  }
155 };
156 
157 void Eng3D::Borders::build_borders() {
158  auto border_tex = std::make_unique<Eng3D::BinaryImage>(s.package_man.get_unique("map/provinces.png")->get_abs_path());
159  int height = border_tex->height;
160  int width = border_tex->width;
161  auto pixels = border_tex->buffer.get();
162  std::vector<std::vector<glm::vec3>> borders;
163  BorderGenerator::build_borders(borders, pixels, width, height);
164 
165  // TODO FIX THIS NOT INFINITE LOOP
166  auto curve = std::make_unique<Eng3D::Curve>();
167  for(size_t i = 0; i < borders.size(); i++) {
168  std::vector<glm::vec3> river = borders[i];
169  auto length = river.size();
170  if(length < 2) continue;
171 
172  std::vector<glm::vec3> mid_points(length + 3);
173  mid_points[0] = river[0];
174  mid_points[1] = river[0];
175  for(size_t j = 0; j < length - 1; j++)
176  mid_points[j + 2] = 0.5f * (river[j] + river[j + 1]);
177  mid_points[length + 1] = river[length - 1];
178  mid_points[length + 2] = river[length - 1];
179 
180  std::vector<glm::vec3> curve_points;
181 
182  // p0 = 2.f * river[0] - river[1];
183  // p3 = 2.f * river[length - 1] - river[length - 2];
184  glm::vec3 p0, p1, p2, p3;
185  for(size_t j = 1; j < mid_points.size() - 2; j++) {
186  p0 = mid_points[j - 1];
187  p1 = mid_points[j];
188  p2 = mid_points[j + 1];
189  p3 = mid_points[j + 2];
190  float step = 1 / 1.;
191  for(float t = 1.f; t > 0.f; t -= step) {
192  float t0 = t - 2;
193  float t1 = t - 1;
194  float t2 = t + 0;
195  float t3 = t + 1;
196  glm::vec3 pt(0, 0, 0);
197  pt += p0 * (+1.f / 6.f * glm::pow(t0, 3.f) + 2.f * t0 + 4.f / 3.f + glm::pow(t0, 2.f));
198  pt += p3 * (-1.f / 6.f * glm::pow(t3, 3.f) - 2.f * t3 + 4.f / 3.f + glm::pow(t3, 2.f));
199  pt += p1 * (-1.f / 2.f * glm::pow(t1, 3.f) - glm::pow(t1, 2.f) + 2.f / 3.f);
200  pt += p2 * (+1.f / 2.f * glm::pow(t2, 3.f) - glm::pow(t2, 2.f) + 2.f / 3.f);
201  curve_points.push_back(pt);
202  }
203  }
204 
205  std::vector<glm::vec3> normals(curve_points.size() - 1, glm::vec3(0, 0, 1));
206  curve->add_line(curve_points, normals, 1.0f);
207  }
208  curve->upload();
209  this->curves.push_back(std::move(curve));
210 }
211 
212 void Eng3D::Borders::draw(const Eng3D::Camera& camera) {
213  line_shader->use();
214  glm::mat4 model(1.f);
215  line_shader->set_uniform("model", model);
216  line_shader->set_uniform("projection", camera.get_projection());
217  line_shader->set_uniform("view", camera.get_view());
218  line_shader->set_texture(0, "water_texture", *water_tex);
219  for(auto& curve : curves)
220  curve->draw();
221 }
static void build_borders(std::vector< std::vector< glm::vec3 >> &borders, const uint32_t *pixels, int width, int height)
Definition: borders.cpp:142
void draw(const Eng3D::Camera &camera)
Definition: borders.cpp:212
Borders(Eng3D::State &s, bool lazy_init=true)
Construct a new Eng 3D::Borders object.
Definition: borders.cpp:39
virtual glm::mat4 get_view() const =0
Get the view matrix.
virtual glm::mat4 get_projection() const
Get the projection matrix.
Definition: camera.hpp:91
std::shared_ptr< Eng3D::IO::Asset::Base > get_unique(const Eng3D::IO::Path &path)
Obtaining an unique asset means the "first-found" policy applies.
Definition: io.cpp:146
Eng3D::TextureManager tex_man
Definition: state.hpp:124
Eng3D::IO::PackageManager package_man
Definition: state.hpp:122
std::shared_ptr< Eng3D::Texture > load(const std::string &path, TextureOptions options=default_options)
Finds a texture in the list of a texture manager if the texture is already in the list we load the sa...
Definition: texture.cpp:432
enum Eng3D::TextureOptions::Wrap wrap_s