34 #include <glm/gtc/matrix_transform.hpp>
35 #include <tbb/blocked_range.h>
36 #include <tbb/concurrent_vector.h>
37 #include <tbb/parallel_for.h>
38 #include <tbb/combinable.h>
40 #ifdef E3D_BACKEND_OPENGL
43 #elif defined E3D_BACKEND_GLES
44 # include <GLES3/gl3.h>
47 #include "eng3d/texture.hpp"
48 #include "eng3d/primitive.hpp"
49 #include "eng3d/shader.hpp"
50 #include "eng3d/framebuffer.hpp"
51 #include "eng3d/state.hpp"
52 #include "eng3d/utils.hpp"
53 #include "eng3d/log.hpp"
54 #include "eng3d/orbit_camera.hpp"
55 #include "eng3d/flat_camera.hpp"
56 #include "eng3d/camera.hpp"
66 :
Eng3D::BaseMap(_gs, glm::ivec2{ _gs.world->width, _gs.world->height }),
70 if(this->gs.
world->
width != this->terrain_map->width || this->gs.world->height != this->terrain_map->height)
71 CXX_THROW(std::runtime_error,
"Color map differs from province map!");
78 assert((this->terrain_map->width* this->terrain_map->height) % 5400 == 0);
80 tbb::parallel_for(
static_cast<size_t>(0), (this->terrain_map->width* this->terrain_map->height) / 5400, [
this](
const auto y) {
81 const auto i = y * 5400;
82 for(size_t x = 0; x < 5400; x++)
83 this->terrain_map->buffer.get()[i + x] |= static_cast<size_t>(this->gs.world->tiles[i + x]) & 0xffff;
87 terrain_map_options.
internal_format = Eng3D::TextureOptions::Format::RGBA;
88 terrain_map_options.editable =
true;
89 terrain_map_options.compressed =
false;
90 this->terrain_map->upload(terrain_map_options);
96 tile_sheet = std::make_unique<Eng3D::Texture>(256, 256);
97 std::fill_n(tile_sheet->buffer.get(), 256 * 256, 0xffdddddd);
98 tile_sheet_nation = std::make_unique<Eng3D::Texture>(256, 256);
99 std::fill_n(tile_sheet_nation->buffer.get(), 256 * 256, 0xffdddddd);
106 #ifdef E3D_BACKEND_OPENGL
107 no_drop_options.internal_format = Eng3D::TextureOptions::Format::SRGB_ALPHA;
108 #elif defined E3D_BACKEND_GLES
109 no_drop_options.internal_format = Eng3D::TextureOptions::Format::RGBA;
111 no_drop_options.compressed =
false;
112 tile_sheet->upload(no_drop_options);
116 province_opt = std::make_unique<Eng3D::Texture>(256, 256);
117 for(
size_t i = 0; i < 256 * 256; i++)
118 province_opt->buffer.get()[i] = 0x000000ff;
122 province_opt->upload(no_drop_options);
125 std::vector<ProvinceId> province_ids;
126 province_ids.reserve(this->gs.
world->provinces.size());
127 for(
auto const& province : this->gs.
world->provinces)
128 province_ids.push_back(province);
133 border_gen_shader = std::make_unique<Eng3D::OpenGL::Program>();
136 border_gen_shader->attach_shader(vs_shader);
138 border_gen_shader->attach_shader(fs_shader);
139 border_gen_shader->attach_shader(*gs.builtin_shaders[
"fs_lib"].get());
140 border_gen_shader->link();
143 sdf_shader = std::make_unique<Eng3D::OpenGL::Program>();
146 sdf_shader->attach_shader(vs_shader);
148 sdf_shader->attach_shader(fs_shader);
149 sdf_shader->attach_shader(*gs.builtin_shaders[
"fs_lib"].get());
153 output_shader = std::make_unique<Eng3D::OpenGL::Program>();
155 output_shader->attach_shader(*gs.builtin_shaders[
"vs_2d"].get());
157 output_shader->attach_shader(fs_shader);
158 output_shader->attach_shader(*gs.builtin_shaders[
"fs_lib"].get());
159 output_shader->link();
165 map_shader = std::make_unique<Eng3D::OpenGL::Program>();
167 std::vector<Eng3D::GLSL::Define> defined_options;
171 defined_option.
name = option.get_option();
172 defined_options.push_back(defined_option);
176 map_shader->attach_shader(vs_shader);
178 map_shader->attach_shader(fs_shader);
179 map_shader->attach_shader(*gs.builtin_shaders[
"fs_lib"]);
185 mipmap_options.
wrap_s = Eng3D::TextureOptions::Wrap::REPEAT;
186 mipmap_options.wrap_t = Eng3D::TextureOptions::Wrap::REPEAT;
187 mipmap_options.min_filter = Eng3D::TextureOptions::Filter::LINEAR_MIPMAP;
188 mipmap_options.mag_filter = Eng3D::TextureOptions::Filter::LINEAR;
190 mipmap_options.internal_format = Eng3D::TextureOptions::Format::RED;
191 if(this->bathymethry.get() ==
nullptr)
193 if(this->river_tex.get() ==
nullptr)
203 if(this->normal_topo.get() ==
nullptr) {
204 auto topo_map = std::make_unique<Eng3D::Texture>(gs.
package_man.
get_unique(
"map/topo.png")->get_abs_path());
205 this->normal_topo = std::make_unique<Eng3D::Texture>(gs.
package_man.
get_unique(
"map/normal.png")->get_abs_path());
206 size_t map_size = topo_map->width * topo_map->height;
207 for(
size_t i = 0; i < map_size; i++) {
208 this->normal_topo->buffer.get()[i] &= (0x00FFFFFF);
209 this->normal_topo->buffer.get()[i] |= (topo_map->buffer.get()[i] & 0xFF) << 24;
213 mipmap_options.min_filter = Eng3D::TextureOptions::Filter::LINEAR_MIPMAP;
214 mipmap_options.mag_filter = Eng3D::TextureOptions::Filter::LINEAR;
216 this->normal_topo->upload(mipmap_options);
223 if(this->border_sdf.get() ==
nullptr) {
225 std::unique_ptr<FILE, int(*)(FILE*)> fp(::fopen(
"sdf_cache.png",
"rb"), ::fclose);
226 if(fp.get() ==
nullptr) {
228 this->border_sdf->to_file(
"sdf_cache.png");
231 sdf_options.
wrap_s = Eng3D::TextureOptions::Wrap::REPEAT;
232 sdf_options.wrap_t = Eng3D::TextureOptions::Wrap::REPEAT;
233 sdf_options.internal_format = Eng3D::TextureOptions::Format::RGB32F;
234 sdf_options.min_filter = Eng3D::TextureOptions::Filter::LINEAR_MIPMAP;
235 sdf_options.mag_filter = Eng3D::TextureOptions::Filter::LINEAR;
237 this->border_sdf = std::make_unique<Eng3D::Texture>(
"sdf_cache.png");
238 this->border_sdf->upload(sdf_options);
246 #include "eng3d/framebuffer.hpp"
252 glEnable(GL_SCISSOR_TEST);
253 glViewport(update_area.
left, update_area.
top, update_area.
width(), update_area.
height());
254 glScissor(update_area.
left, update_area.
top, update_area.
width(), update_area.
height());
260 border_tex_options.
internal_format = Eng3D::TextureOptions::Format::RGB32F;
261 border_tex_options.min_filter = Eng3D::TextureOptions::Filter::LINEAR_MIPMAP;
262 border_tex_options.mag_filter = Eng3D::TextureOptions::Filter::LINEAR;
263 border_tex_options.editable =
true;
265 border_tex.
upload(border_tex_options);
268 auto tex_coord_scale = update_area;
269 tex_coord_scale.
scale(1.f / glm::vec2(width, height));
273 border_fbuffer.
use();
274 border_fbuffer.set_texture(0, border_tex);
277 border_gen_shader->use();
278 border_gen_shader->set_uniform(
"map_size", width, height);
279 border_gen_shader->set_uniform(
"tex_coord_scale", tex_coord_scale.left, tex_coord_scale.top, tex_coord_scale.right, tex_coord_scale.bottom);
280 border_gen_shader->set_texture(0,
"terrain_map", *terrain_map);
281 border_gen_shader->set_texture(1,
"tile_sheet_nation", *tile_sheet_nation);
284 glBindFramebuffer(GL_FRAMEBUFFER, 0);
288 sdf_shader->set_uniform(
"map_size", width, height);
289 sdf_shader->set_uniform(
"tex_coord_scale", tex_coord_scale.left, tex_coord_scale.top, tex_coord_scale.right, tex_coord_scale.bottom);
292 fbo_mipmap_options.
internal_format = Eng3D::TextureOptions::Format::RGB32F;
293 fbo_mipmap_options.min_filter = Eng3D::TextureOptions::Filter::LINEAR_MIPMAP;
294 fbo_mipmap_options.mag_filter = Eng3D::TextureOptions::Filter::LINEAR;
295 fbo_mipmap_options.editable =
true;
298 if(border_sdf.get() ==
nullptr) {
299 border_sdf = std::make_unique<Eng3D::Texture>(border_tex.
width, border_tex.
height);
300 border_sdf->upload(fbo_mipmap_options);
303 auto swap_tex = std::make_unique<Eng3D::Texture>(width, height);
304 swap_tex->upload(fbo_mipmap_options);
310 const float max_steps = 7.f;
311 const float max_dist = std::pow(2, max_steps);
312 sdf_shader->set_uniform(
"max_dist", max_dist);
314 bool draw_on_tex0 =
true;
315 for(
int step = max_dist; step >= 1; step /= 2) {
317 draw_on_tex0 = !draw_on_tex0;
318 border_sdf->gen_mipmaps();
319 swap_tex->gen_mipmaps();
320 sdf_shader->set_uniform(
"jump", (
float)step);
322 fbo.set_texture(0, draw_on_tex0 ? *border_sdf : *swap_tex);
323 if(step == max_dist) sdf_shader->set_texture(0,
"tex", border_tex);
324 else sdf_shader->set_texture(0,
"tex", draw_on_tex0 ? *swap_tex : *border_sdf);
334 border_sdf = std::move(swap_tex);
336 border_sdf->gen_mipmaps();
337 glDisable(GL_SCISSOR_TEST);
338 glViewport(0, 0, window_size.x, window_size.y);
344 for(
size_t i = 0; i < this->gs.
world->provinces.size(); i++)
345 if(this->gs.
world->terrain_types[this->gs.world->provinces[i].terrain_type_id].is_water_body)
348 for(
const auto province_color : province_colors)
349 tile_sheet->buffer.get()[
static_cast<size_t>(province_color.id)] = province_color.color.get_value();
352 no_drop_options.internal_format = Eng3D::TextureOptions::Format::SRGB;
353 this->tile_sheet->upload(no_drop_options);
358 std::vector<NationId> nation_ids;
359 for(
const auto province_id : province_ids) {
360 const auto& province = this->gs.
world->provinces[province_id];
361 this->tile_sheet_nation->buffer.get()[province] =
static_cast<size_t>(province.controller_id);
362 nation_ids.push_back(province.controller_id);
364 std::sort(nation_ids.begin(), nation_ids.end());
365 auto last = std::unique(nation_ids.begin(), nation_ids.end());
366 nation_ids.erase(last, nation_ids.end());
370 this->tile_sheet_nation->upload(no_drop_options);
373 for(
const auto nation_id : nation_ids)
377 void MapRender::update_city_lights() {
382 this->req_update_vision =
true;
385 void MapRender::update_visibility() {
388 std::fill_n(province_opt->buffer.get(), gs.
world->provinces.size(), 0x000000ff);
391 std::fill_n(province_opt->buffer.get(), gs.
world->provinces.size(), 0x00000080);
394 for(
const auto& province : gs.
world->provinces) {
395 const auto total = glm::max(province.total_pops(), 0.1f);
396 const auto density = glm::clamp(total / 100'000.f, 0.f, 1.f) * 255.f;
397 this->province_opt->buffer[province] |= (
static_cast<uint8_t
>(density) & 0xff) << 8;
400 for(
const auto& nation : gs.
world->nations) {
401 const auto nation_id = nation;
404 for(
const auto province_id : gs.
world->nations[nation_id].controlled_provinces) {
405 const auto& province = gs.
world->provinces[province_id];
406 this->province_opt->buffer[province_id] |= 0x000000ff;
407 for(
const auto neighbour_id : province.neighbour_ids)
408 this->province_opt->buffer[neighbour_id] |= 0x000000ff;
413 auto &_gs = this->gs;
415 if(unit.
owner_id != _gs.curr_nation->get_id() && !_gs.world->get_relation(unit.
owner_id, _gs.curr_nation->get_id()).is_allied())
418 this->province_opt->buffer[prov_id] |= 0x000000ff;
419 for(
const auto neighbour_id : _gs.world->provinces[prov_id].neighbour_ids)
420 this->province_opt->buffer[neighbour_id] |= 0x000000ff;
422 if(gs.
map->province_selected)
423 this->province_opt->buffer[gs.
map->selected_province_id] |= 0x400000ff;
428 this->req_update_vision =
true;
432 std::vector<ProvinceId> update_provinces;
434 update_provinces.insert(update_provinces.end(), changed_owner_provinces.cbegin(), changed_owner_provinces.cend());
437 update_provinces.insert(update_provinces.end(), changed_control_provinces.cbegin(), changed_control_provinces.cend());
439 std::sort(update_provinces.begin(), update_provinces.end());
440 auto last = std::unique(update_provinces.begin(), update_provinces.end());
441 update_provinces.erase(last, update_provinces.end());
443 if(!update_provinces.empty()) {
444 for(
const auto province_id : update_provinces) {
445 auto& province = gs.
world->provinces[province_id];
446 update_area = update_area.join(province.box_area);
453 if(this->req_update_vision) {
454 this->update_visibility();
455 this->req_update_vision =
false;
457 this->update_city_lights();
460 this->province_opt->upload(no_drop_options);
465 const auto view = camera.
get_view();
466 map_shader->set_uniform(
"view", view);
468 map_shader->set_uniform(
"view_pos", cam_pos.x, cam_pos.y, cam_pos.z);
470 map_shader->set_uniform(
"projection", projection);
472 float distance_to_map = map_pos.z / this->gs.
world->
width;
473 map_shader->set_uniform(
"dist_to_map", distance_to_map);
474 map_shader->set_uniform(
"map_size",
static_cast<float>(this->gs.
world->
width),
static_cast<float>(this->gs.world->height));
476 const auto now = std::chrono::system_clock::now().time_since_epoch();
477 const auto millisec_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
478 const auto time =
static_cast<float>(millisec_since_epoch % 1000000) / 1000.f;
479 map_shader->set_uniform(
"time", time);
480 map_shader->set_uniform(
"ticks", this->gs.
world->
time);
482 map_shader->set_texture(0,
"tile_sheet", *tile_sheet);
483 map_shader->set_texture(1,
"tile_sheet_nation", *tile_sheet_nation);
485 map_shader->set_texture(2,
"water_texture", *water_tex);
487 map_shader->set_texture(3,
"noise_texture", *noise_tex);
488 map_shader->set_texture(4,
"terrain_map", *terrain_map);
490 map_shader->set_texture(5,
"border_sdf", *(border_sdf.get()));
492 map_shader->set_texture(6,
"river_texture", *river_tex);
494 map_shader->set_texture(7,
"wave1", *wave1);
495 map_shader->set_texture(8,
"wave2", *wave2);
496 map_shader->set_texture(9,
"normal", *normal_topo);
498 map_shader->set_texture(10,
"bathymethry", *bathymethry);
499 map_shader->set_texture(11,
"paper_tex", *paper_tex);
501 map_shader->set_texture(13,
"province_opt", *province_opt);
502 map_shader->set_texture(14,
"terrain_sheet", *terrain_sheet);
505 for(
const auto& quad : this->map_quads)
virtual glm::mat4 get_view() const =0
Get the view matrix.
virtual glm::vec3 get_map_pos() const =0
Get the map position of the camera.
virtual glm::mat4 get_projection() const
Get the projection matrix.
glm::vec3 get_world_pos() const
Get the world positions of the camera.
std::shared_ptr< Eng3D::IO::Asset::Base > get_unique(const Eng3D::IO::Path &path)
Obtaining an unique asset means the "first-found" policy applies.
Eng3D::TextureManager tex_man
Eng3D::IO::PackageManager package_man
void upload(TextureOptions options=default_options)
Frontend for uploading (schedules or instantly uploads)
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...
enum Eng3D::TextureOptions::Wrap wrap_s
std::unique_ptr< Map > map
void update_nation_label(const Nation &nation)
void request_update_visibility()
void update_nations(std::vector< ProvinceId > &nations)
void update_options(MapOptions options)
void update_border_sdf(Eng3D::Rect update_area, glm::ivec2 window_size)
Creates the "waving" border around the continent to give it a 19th century map feel Generate a distan...
void draw(const Eng3D::Camera &camera, MapView view_mode)
void update_mapmode(std::vector< ProvinceColor > &province_colors)
const std::vector< ProvinceId > & get_changed_control_provinces() const
const std::vector< ProvinceId > & get_changed_owner_provinces() const
bool is_provinces_changed() const
Roughly a batallion, consisting of approximately 500 soldiers each.
Eng3D::Freelist< Unit > units
ProvinceId get_unit_current_province(UnitId unit_id) const
Nation::Relation & get_relation(NationId a, NationId b)
ProvinceManager province_manager
std::unique_ptr< ProvinceId[]> tiles
void debug(const std::string_view category, const std::string_view msg)
void parallel_for(T range, F &&func)
Primitive color type used through the engine.
void for_each(const F &lambda) const
constexpr float height() const
Obtains the height.
constexpr void scale(glm::vec2 factor)
Scales the rectangle by factor.
constexpr float width() const
Obtains the width.
constexpr Id get_id() const
Id cached_id
Id used to speed up Id lookups on any context.
std::vector< Option > get_options()
#define CXX_THROW(class,...)