diff --git a/assets/images/cargo-ship.png b/assets/images/cargo-ship.png new file mode 100644 index 0000000000..b103cbdb01 Binary files /dev/null and b/assets/images/cargo-ship.png differ diff --git a/assets/images/train.png b/assets/images/train.png new file mode 100644 index 0000000000..9f29f4b6e7 Binary files /dev/null and b/assets/images/train.png differ diff --git a/assets/localisation/en-US/settings.csv b/assets/localisation/en-US/settings.csv index c0d5fb25ac..0237a8d751 100644 --- a/assets/localisation/en-US/settings.csv +++ b/assets/localisation/en-US/settings.csv @@ -60,4 +60,5 @@ graphics_details_label;Graphics graphics_details_0;Ugly graphics_details_1;Classic graphics_details_2;Modern +graphics_details_3;Gores disband_confirmation_label;Unit disband confirmation diff --git a/assets/shaders/glsl/map_f.glsl b/assets/shaders/glsl/map_f.glsl index 4c5802de36..2c01935ddd 100644 --- a/assets/shaders/glsl/map_f.glsl +++ b/assets/shaders/glsl/map_f.glsl @@ -189,13 +189,34 @@ vec4 get_land_terrain() { return get_terrain_mix(); } +float get_good_hatching(float frequency) { + float s = space_coords.z; + float t = space_coords.y; + float h = space_coords.x; + float angle = atan(t, s); + float vertical_angle = asin(h); + float radius = sqrt(s * s + t * t); + float equator_segment_x = fract(angle * 36.f / 2.f / PI); + float equator_segment_number = floor(angle * 36.f / 2.f / PI); + float vertical_segment = fract(vertical_angle * 2.f / PI * 8.f); + float vertical_segment_number = floor(vertical_angle * 2.f / PI * 8.f); + float horisontal_coord_before_cutting_paper = (equator_segment_x - 0.5f) * radius + 0.5f; + float hatching = texture(hatching, vec2(horisontal_coord_before_cutting_paper * (mod(vertical_segment_number, 2.f) * 2.f - 1.f), vertical_segment * (mod(equator_segment_number, 2.f) * 2.f - 1.f)) * frequency).r; + return hatching; +} + // if we display data, ignore pretty effects vec4 get_land_data_modern() { vec4 terrain = get_terrain_mix(); float is_land = terrain.a; vec4 province_sample = texture(provinces_texture_sampler, gl_FragCoord.xy / screen_size); vec2 prov_id = province_sample.xy; - return vec4(texture(province_color, vec3(prov_id, 0.)).rgb * 0.8f, is_land); + + vec4 base_color = texture(province_color, vec3(prov_id, 0.)); + vec4 secondary_color = texture(province_color, vec3(prov_id, 1.)); + vec4 result = mix(base_color, secondary_color, get_good_hatching(10.f)); + + return vec4(result.rgb * 0.8f, is_land); } vec4 get_land_political_modern() { @@ -237,38 +258,101 @@ vec4 get_land_political_modern() { return OutColor; } +vec4 get_land_political_gores() { + float s = space_coords.z; + float t = space_coords.y; + float h = space_coords.x; + float angle = tex_coord.x * PI * 2.f; + float vertical_angle = (tex_coord.y - 0.5f) * PI; //asin(h); + float radius = cos(vertical_angle); + float big_line_divisor = 3.f; + float equator_segment_x = fract(angle * 48.f / 2.f / PI * big_line_divisor); + float equator_segment_number = mod(floor(angle * 48.f / 2.f / PI * big_line_divisor + 0.5f), 36.f); + float equator_segment_number_area = mod(floor(angle * 48.f / 2.f / PI * big_line_divisor), 36.f); + float vertical_segment = fract(vertical_angle * 2.f / PI * 12.f * big_line_divisor); + float vertical_segment_number = floor(vertical_angle * 2.f / PI * 12.f * big_line_divisor + 0.5f); + float vertical_segment_number_area = floor(vertical_angle * 2.f / PI * 12.f * big_line_divisor); + float horisontal_coord_before_cutting_paper = (equator_segment_x - 0.5f) * radius + 0.5f; + float line_width = 0.015f / min(1.f, zoom / 6.f); + float line_width_v = line_width; + float line_width_h = line_width; + float softness_mult = (1.2f + abs(vertical_angle) / 2.f) / sqrt(min(1.f, zoom / 6.f)); + float softness_mod = 1.f; + float inside_v = 1.f; + if (mod(round(equator_segment_number), 4.f) == 0.f) { + line_width_v = line_width_v * 3.f / 2.f; + inside_v = 0.f; + } + float inside_h = 1.f; + if (mod(round(vertical_segment_number), 4.f) == 0.f) { + line_width_h = line_width_h * 3.f / 2.f; + inside_h = 0.f; + } + float base_unit = cos(vertical_angle); + float length_mult = 1.f / base_unit; + if (radius < 0.2f) { + length_mult = 1.f / radius; + } + float line_mult = 1.f; + float v1 = abs(fract(equator_segment_x + 0.5f) - 0.5f); + if (v1 < line_width_v * length_mult) { + line_mult = min(line_mult, 0.2f * softness_mult * (1.f + 0.5f * inside_v) + max(0.5f - zoom / 100.f, smootherstep(v1 / (line_width_v * length_mult)))); + } + float v2 = abs(fract(vertical_segment + 0.5f) - 0.5f); + if (v2 < line_width_h) { + line_mult = min(line_mult, 0.2f * softness_mult * (1.f + 0.5f * inside_h) + max(0.5f - zoom / 100.f, smootherstep(v2 / (line_width_h)))); + } -vec4 get_land_political_paper() { - vec2 uv = tex_coord * 300.f; + vec4 wc = texture(watercolor, vec2(horisontal_coord_before_cutting_paper, vertical_segment) * 10.f); + vec4 wc_global = texture(watercolor, vec2(horisontal_coord_before_cutting_paper, vertical_segment) / 2.f); + float hatching = texture(hatching, vec2(horisontal_coord_before_cutting_paper * (mod(vertical_segment_number_area, 2.f) * 2.f - 1.f), vertical_segment * (mod(equator_segment_number_area, 2.f) * 2.f - 1.f)) * 4.f).r; + + vec4 terrain = get_terrain_mix(); + float is_land = terrain.a; vec4 province_sample = texture(provinces_texture_sampler, gl_FragCoord.xy / screen_size); vec2 prov_id = province_sample.xy; float to_national_border = province_sample.z; - vec4 hatching = texture(hatching, uv / 2.f); - vec4 prov_highlight = vec4(1.f, 1.f, 1.f, 1.f); float highlight = texture(province_highlight, prov_id).r; - if (highlight > 0.f) { - prov_highlight = vec4(hatching.rgb * 0.2f * (hatching.a) + 0.5f * (1.f - hatching.a), 1.f); + prov_highlight = vec4(hatching, hatching, hatching, 1.f); } - vec4 prov_color = texture(province_color, vec3(prov_id, 0.)); + vec4 stripe_color = texture(province_color, vec3(prov_id, 1.)); + //float stripeFactor = texture(stripes_texture, stripe_coord).a; + //vec4 base_color = vec4(1.f, 247.f / 255.f, 220.f / 255.f, 1.f) * 0.9f; + vec4 base_color = vec4(0.80, 0.76, 0.63, 1.f); + float gradient = sqrt(to_national_border); + + const vec3 GREYIFY = vec3( 0.212671, 0.715160, 0.072169 ); + float grey = dot( terrain.rgb, GREYIFY ) * 2.f; - vec4 base_color = vec4(1.f, 247.f / 255.f, 240.f / 255.f, 1.f); - float wc_base = texture(watercolor, uv).b; - float gradient = 1.f; - if (int(graphics_mode) == 2) { - gradient = (0.7f + sqrt(to_national_border) * 0.3f); + float weight_base = 0.0015f; + float weight_hatching = 0.05f; + float weight_border = 3.0f * clamp(0.1f + hatching, 0.f, 1.f); + float weight_watercolor = 0.005f; + //float total_weight = weight_base + weight_hatching + weight_border + weight_watercolor; + float wc_g = dot(wc.xyz, GREYIFY); + + float final_weight = min(1, sqrt( + weight_base + + wc_g * weight_watercolor + + weight_border * (1.f - gradient) + + hatching * weight_hatching + )); + + if (stripe_color != prov_color) { + base_color = stripe_color * final_weight + base_color * (1.f - final_weight); } - float wc_texture = max(0.f, wc_base * wc_base * wc_base) * gradient; - vec4 mixed_color = prov_color * (1.f - wc_texture) + base_color * wc_texture; + vec4 mixed_color = prov_color * final_weight + base_color * (1.f - final_weight); - vec4 terrain = get_terrain_mix(); - const vec3 GREYIFY = vec3( 0.212671, 0.715160, 0.072169 ); - float grey = dot( terrain.rgb, GREYIFY ) * 2.f; - float is_land = terrain.a; - return mixed_color * prov_highlight * is_land + texture(watercolor, uv) * gradient * (1 - is_land); + //vec4 color = prov_color * wc + + vec4 result = (mixed_color - 0.f * hatching) * min(1.f, line_mult) * (0.85f + 0.05f * wc + 0.10f * dot(wc_global.xyz, GREYIFY)) * min(1.f, 0.2f + gradient); + result.a = is_land; + + return result; } vec4 get_land_political_close() { @@ -319,6 +403,7 @@ vec4 get_land_political_close() { return terrain; } + vec4 get_land_political_far() { //vec4 terrain = get_terrain(vec2(0, 0), vec2(0)); vec4 terrain = get_terrain_mix(); @@ -386,19 +471,28 @@ vec4 get_land() { return get_land_terrain(); } + if (int(graphics_mode) == 2) { if (map_mode_is_data == 0) { return get_land_political_modern(); } else { return get_land_data_modern(); } - return get_land_political_far(); + } + + if (int(graphics_mode) == 3) { + if (map_mode_is_data == 0) { + return get_land_political_gores(); + } else { + return get_land_data_modern(); + } } if (int(graphics_mode) == 0) { return get_land_political_far(); } + switch(int(subroutines_index_2)) { case 0: return get_land_terrain(); case 1: return get_land_political_close(); @@ -406,7 +500,64 @@ vec4 get_land() { default: break; } } + +vec4 get_water_gores() { + float s = space_coords.z; + float t = space_coords.y; + float h = space_coords.x; + float angle = tex_coord.x * PI * 2.f; + float vertical_angle = (tex_coord.y - 0.5f) * PI; //asin(h); + float radius = cos(vertical_angle); + float big_line_divisor = 3.f; + float equator_segment_x = fract(angle * 48.f / 2.f / PI * big_line_divisor); + float equator_segment_number = mod(floor(angle * 48.f / 2.f / PI * big_line_divisor + 0.5f), 36.f); + float equator_segment_number_area = mod(floor(angle * 48.f / 2.f / PI * big_line_divisor), 36.f); + float vertical_segment = fract(vertical_angle * 2.f / PI * 12.f * big_line_divisor); + float vertical_segment_number = floor(vertical_angle * 2.f / PI * 12.f * big_line_divisor + 0.5f); + float vertical_segment_number_area = floor(vertical_angle * 2.f / PI * 12.f * big_line_divisor); + float horisontal_coord_before_cutting_paper = (equator_segment_x - 0.5f) * radius + 0.5f; + float line_width = 0.015f / min(1.f, zoom / 6.f); + float line_width_v = line_width; + float line_width_h = line_width; + float softness_mult = (1.f + abs(vertical_angle) / 2.f) / sqrt(min(1.f, zoom / 6.f)); + float softness_mod = 1.f; + float inside_v = 1.f; + if (mod(round(equator_segment_number), 4.f) == 0.f) { + line_width_v = line_width_v * 3.f / 2.f; + inside_v = 0.f; + } + float inside_h = 1.f; + if (mod(round(vertical_segment_number), 4.f) == 0.f) { + line_width_h = line_width_h * 3.f / 2.f; + inside_h = 0.f; + } + float base_unit = cos(vertical_angle); + float length_mult = 1.f / base_unit; + if (radius < 0.2f) { + length_mult = 1.f / radius; + } + float line_mult = 1.f; + float v1 = abs(fract(equator_segment_x + 0.5f) - 0.5f); + if (v1 < line_width_v * length_mult) { + line_mult = min(line_mult, 0.2f * softness_mult * (1.f + 0.5f * inside_v) + max(0.5f - zoom / 100.f, smootherstep(v1 / (line_width_v * length_mult)))); + } + float v2 = abs(fract(vertical_segment + 0.5f) - 0.5f); + if (v2 < line_width_h) { + line_mult = min(line_mult, 0.2f * softness_mult * (1.f + 0.5f * inside_h) + max(0.5f - zoom / 100.f, smootherstep(v2 / (line_width_h)))); + } + + vec4 wc = texture(watercolor, vec2(horisontal_coord_before_cutting_paper, vertical_segment) * 10.f); + vec4 wc_global = texture(watercolor, vec2(horisontal_coord_before_cutting_paper, vertical_segment) / 2.f); + + float hatching = 0.4f * texture(hatching, vec2(horisontal_coord_before_cutting_paper * (mod(vertical_segment_number_area, 2.f) * 2.f - 1.f), vertical_segment * (mod(equator_segment_number_area, 2.f) * 2.f - 1.f)) * 4.f).r; + return vec4(0.57f - hatching, 0.65f - hatching, 0.70f, 1.f) * (0.25f + 0.5f * wc + 0.25f * wc_global) * vec4(0.2f + 0.8f * line_mult, 0.2f + 0.8f *line_mult, 1.f, 1.f); +} + vec4 get_water() { + if (int(graphics_mode) == 3) { + return get_water_gores(); + } + switch(int(subroutines_index_2)) { case 0: return get_water_terrain(); case 1: return get_water_terrain(); @@ -429,7 +580,7 @@ void main() { float light = max(0.f, dot(light_direction, space_coords)); float darkness = max(0.f, -dot(light_direction, space_coords)); - if (int(graphics_mode) == 2) { + if (int(graphics_mode) == 2 || int(graphics_mode) == 3) { vec4 province_sample = texture(provinces_texture_sampler, gl_FragCoord.xy / screen_size); float to_national_border = province_sample.z; //light += (1.f - to_national_border) * 1.5f * darkness; diff --git a/assets/shaders/glsl/map_font_f.glsl b/assets/shaders/glsl/map_font_f.glsl index 3aba9411a5..641105a606 100644 --- a/assets/shaders/glsl/map_font_f.glsl +++ b/assets/shaders/glsl/map_font_f.glsl @@ -164,8 +164,12 @@ void main() { alpha *= 0.5; } - alpha = clamp(alpha * opacity, 0.0, 1.0); - result = color * alpha; + result = color * clamp(alpha * opacity * color.a, 0.0, 1.0); + + //alpha = clamp(alpha * opacity, 0.0, 1.0); + //result = color * alpha; + + // float t = 1.0f; // float k = 1.0f; // if(glyph.count < 10) { t = 0.0f; } diff --git a/assets/shaders/glsl/map_font_v.glsl b/assets/shaders/glsl/map_font_v.glsl index 4bfd14d355..ce4d4805f5 100644 --- a/assets/shaders/glsl/map_font_v.glsl +++ b/assets/shaders/glsl/map_font_v.glsl @@ -25,7 +25,7 @@ void main() { vec4 temp_result = calc_gl_position(vertex_position); float pixels_thickness = thickness * zoom; - float min_thickness = 0.005f; + float min_thickness = 0.003f; float max_thickness = 1.f; opacity = clamp((min_thickness / pixels_thickness - 1.f) * (pixels_thickness / max_thickness - 1.f), 0.f, 1.f); diff --git a/assets/shaders/glsl/wavy_textured_line_f.glsl b/assets/shaders/glsl/wavy_textured_line_f.glsl index 4d639e29cd..0297e8539c 100644 --- a/assets/shaders/glsl/wavy_textured_line_f.glsl +++ b/assets/shaders/glsl/wavy_textured_line_f.glsl @@ -9,6 +9,7 @@ uniform vec3 light_direction; uniform float ignore_light; uniform float gamma; uniform vec2 screen_size; +uniform float railroad_level; uniform sampler2D provinces_texture_sampler; uniform sampler2D province_fow; @@ -20,10 +21,24 @@ vec4 gamma_correct(vec4 colour) { } void main() { + + float value = abs(0.5f - tex_coord); + vec2 prov_id = texture(provinces_texture_sampler, gl_FragCoord.xy / screen_size).xy; + + if (ignore_light > 0.f) { + if (texture(provinces_sea_mask, prov_id).x > 0) { + frag_color = vec4(0.0f, 0.0f, 0.0f, sin(o_dist * 2.f)); + } else { + float rail = max(0.f, sin(railroad_level * o_dist * 2.f)); + rail = rail * rail; + frag_color = vec4(0.0f, 0.0f, 0.0f, (value) * (value) * 4.f + rail); + } + return ; + } + float darkness = max(0.f, -dot(light_direction, space_coords) + 0.1f); float texture_mask = texture(line_texture, vec2(o_dist, tex_coord)).r; - vec2 prov_id = texture(provinces_texture_sampler, gl_FragCoord.xy / screen_size).xy; if (texture(provinces_sea_mask, prov_id).x > 0 || (prov_id.x == 0.f && prov_id.y == 0.f)) { discard; } @@ -33,7 +48,8 @@ void main() { float dotted_line = max(0.f, sin(o_dist * 10.f) - 0.5f); //float value = abs( (0.5f + sin(o_dist * 1.f) * 0.2) - tex_coord); - float value = abs(0.5f - tex_coord); + + vec3 inner_color = vec3(0.8f, 0.2, 0.2f); if (ignore_light == 0.f) { diff --git a/src/common_types/constants.hpp b/src/common_types/constants.hpp index 57e9416184..b6f6f8149a 100644 --- a/src/common_types/constants.hpp +++ b/src/common_types/constants.hpp @@ -578,7 +578,7 @@ enum class color_blind_mode { }; enum class graphics_mode { - ugly, classic, modern, total + ugly, classic, modern, modern_classic, total }; diff --git a/src/gamestate/serialization.cpp b/src/gamestate/serialization.cpp index 326bafc441..93d8c4ad0b 100644 --- a/src/gamestate/serialization.cpp +++ b/src/gamestate/serialization.cpp @@ -161,6 +161,9 @@ uint8_t const* read_handwritten_scenario_section(uint8_t const* ptr_in, uint8_t ptr_in = deserialize(ptr_in, state.map_state.map_data.coastal_vertices); ptr_in = deserialize(ptr_in, state.map_state.map_data.coastal_starts); ptr_in = deserialize(ptr_in, state.map_state.map_data.coastal_counts); + ptr_in = deserialize(ptr_in, state.map_state.map_data.railroad_vertices); + ptr_in = deserialize(ptr_in, state.map_state.map_data.railroad_starts); + ptr_in = deserialize(ptr_in, state.map_state.map_data.railroad_counts); ptr_in = deserialize(ptr_in, state.map_state.map_data.province_border_vertices); ptr_in = deserialize(ptr_in, state.map_state.map_data.province_border_starts); ptr_in = deserialize(ptr_in, state.map_state.map_data.province_border_counts); @@ -373,6 +376,9 @@ uint8_t* write_handwritten_scenario_section(uint8_t* ptr_in, sys::state& state, ptr_in = serialize(ptr_in, state.map_state.map_data.coastal_vertices); ptr_in = serialize(ptr_in, state.map_state.map_data.coastal_starts); ptr_in = serialize(ptr_in, state.map_state.map_data.coastal_counts); + ptr_in = serialize(ptr_in, state.map_state.map_data.railroad_vertices); + ptr_in = serialize(ptr_in, state.map_state.map_data.railroad_starts); + ptr_in = serialize(ptr_in, state.map_state.map_data.railroad_counts); ptr_in = serialize(ptr_in, state.map_state.map_data.province_border_vertices); ptr_in = serialize(ptr_in, state.map_state.map_data.province_border_starts); ptr_in = serialize(ptr_in, state.map_state.map_data.province_border_counts); @@ -585,6 +591,9 @@ size_t sizeof_handwritten_scenario_section(sys::state& state, bool exclude_local sz += serialize_size(state.map_state.map_data.coastal_vertices); sz += serialize_size(state.map_state.map_data.coastal_starts); sz += serialize_size(state.map_state.map_data.coastal_counts); + sz += serialize_size(state.map_state.map_data.railroad_vertices); + sz += serialize_size(state.map_state.map_data.railroad_starts); + sz += serialize_size(state.map_state.map_data.railroad_counts); sz += serialize_size(state.map_state.map_data.province_border_vertices); sz += serialize_size(state.map_state.map_data.province_border_starts); sz += serialize_size(state.map_state.map_data.province_border_counts); diff --git a/src/gamestate/system_state.cpp b/src/gamestate/system_state.cpp index ad8ab5652e..adc1d55ac5 100644 --- a/src/gamestate/system_state.cpp +++ b/src/gamestate/system_state.cpp @@ -37,6 +37,7 @@ #include "alice_ui.hpp" #include "commands.hpp" #include "dcon_oos_reporter_generated.hpp" +#include "math_fns.hpp" namespace sys { @@ -50,6 +51,17 @@ void state::start_state_selection(state_selection_data& data) { } } +glm::vec2 put_in_local(glm::vec2 new_point, glm::vec2 base_point, float size_x) { + auto uadjx = std::abs(new_point.x - base_point.x); + auto ladjx = std::abs(new_point.x - size_x - base_point.x); + auto radjx = std::abs(new_point.x + size_x - base_point.x); + if(uadjx < ladjx) { + return uadjx < radjx ? new_point : glm::vec2{ new_point.x + size_x, new_point.y }; + } else { + return ladjx < radjx ? glm::vec2{ new_point.x - size_x, new_point.y } : glm::vec2{ new_point.x + size_x, new_point.y }; + } +} + void state::start_national_identity_selection(national_identity_selection_data& data) { national_identity_selection = data; @@ -1863,6 +1875,22 @@ void state::open_diplomacy(dcon::nation_id target) { sys::open_diplomacy_window(*this, target); } +struct pixel_heap_member { + int x; + int y; + float dist; + float heur; + + int8_t dx; + int8_t dy; + uint8_t speed; + + auto operator<=>(const pixel_heap_member & other) const { + return (dist + heur) <=> (other.dist + other.heur); + } +}; + + void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day bookmark_date) { auto root = get_root(common_fs); auto common = open_directory(root, NATIVE("common")); @@ -3654,6 +3682,319 @@ void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day military::set_initial_leaders(*this); + province::restore_distances(*this); + + // generate a road for every adjacency + + std::vector visited; + visited.resize(map_state.map_data.size_x * map_state.map_data.size_y); + + int left_dirty = map_state.map_data.size_x * map_state.map_data.size_y; + int right_dirty = 0; + + auto encode_ternary = [&](int8_t flag, int8_t dx, int8_t dy) -> int8_t { + return flag * 9 + dx * 3 + dy; + }; + + auto set_visited = [&](int idx, int8_t movement) { + visited[idx] = movement; + left_dirty = std::min(left_dirty, idx); + right_dirty = std::max(right_dirty, idx); + }; + + auto clear_visited = [&] () { + memset(&visited[left_dirty], 0, right_dirty - left_dirty * (sizeof(uint8_t))); + }; + + std::vector pixel_queue{ }; + + + std::vector terrain_cost{}; + terrain_cost.resize(256); + terrain_cost[255] = 15.f; + + auto max_cost = 0.f; + for(int terrain = 0; terrain < 64; terrain++) { + auto modifier = context.modifier_by_terrain_index[terrain]; + if(modifier) { + auto& modifier_data = world.modifier_get_province_values(modifier); + auto move_cost_mod = 0.f; + for(uint32_t mod_index = 0; mod_index < sys::provincial_modifier_definition::modifier_definition_size; ++mod_index) { + if(!(modifier_data.offsets[mod_index])) + break; // no more modifier values + + auto fixed_offset = modifier_data.offsets[mod_index]; + auto modifier_amount = modifier_data.values[mod_index]; + if(fixed_offset == sys::provincial_mod_offsets::movement_cost) { + move_cost_mod += modifier_amount; + } + } + terrain_cost[terrain] = move_cost_mod; + if(move_cost_mod > max_cost) { + max_cost = move_cost_mod; + } + } else { + terrain_cost[terrain] = 1.f; + } + } + + // rescale so 1 remains the same while max turns into 10 + + for(int terrain = 0; terrain < 64; terrain++) { + terrain_cost[terrain] = std::max(0.05f, terrain_cost[terrain] + (terrain_cost[terrain] - 1.f) / (max_cost - 1.f) * 10.f); + } + + auto step_count_hint = 10.f; + + world.for_each_province_adjacency([&](auto adj) { + if((world.province_adjacency_get_type(adj) & province::border::impassible_bit) == province::border::impassible_bit) { + map_state.map_data.railroad_starts.push_back(GLint(map_state.map_data.railroad_vertices.size())); + map_state.map_data.railroad_counts.push_back(0); + return; + } + + auto p1 = world.province_adjacency_get_connected_provinces(adj, 0); + auto p2 = world.province_adjacency_get_connected_provinces(adj, 1); + + //if (p1.index() >= province_definitions.first_sea_province.index()) { + // map_state.map_data.railroad_starts.push_back(GLint(map_state.map_data.railroad_vertices.size())); + // map_state.map_data.railroad_counts.push_back(0); + // return; + //} + //if (p2.index() >= province_definitions.first_sea_province.index()) { + // map_state.map_data.railroad_starts.push_back(GLint(map_state.map_data.railroad_vertices.size())); + // map_state.map_data.railroad_counts.push_back(0); + // return; + //} + bool sea_route = false; + if(p2.index() >= province_definitions.first_sea_province.index() || p1.index() >= province_definitions.first_sea_province.index()) { + sea_route = true; + } + + auto mid_point_1 = world.province_get_mid_point(p1); + auto mid_point_2 = world.province_get_mid_point(p2); + + auto mid_point_1_b = world.province_get_mid_point_b(p1); + auto mid_point_2_b = world.province_get_mid_point_b(p2); + + auto dist_y = abs(mid_point_2.y - mid_point_1.y); + auto dist_x = std::min( + abs(mid_point_2.x - mid_point_1.x), + std::min( + abs(mid_point_2.x - mid_point_1.x + map_state.map_data.size_x), + abs(mid_point_2.x - mid_point_1.x - map_state.map_data.size_x) + ) + ); + + //auto base_step_x = + + //auto base_step = + + uint8_t speed_direction = 0; + uint8_t speed_value = 1; + + auto start_x = (int)mid_point_1.x; + auto start_y = (int)mid_point_1.y; + auto start_idx = start_x + start_y * map_state.map_data.size_x; + + auto end_x = (int)mid_point_2.x; + auto end_y = (int)mid_point_2.y; + auto end_idx = end_x + end_y * map_state.map_data.size_x; + + { + pixel_queue.clear(); + pixel_heap_member pixel_start { start_x, start_y, 0.f, 0.f, 0, 0, 0 }; + pixel_queue.emplace_back(pixel_start); + } + + visited[start_idx] = encode_ternary(1, 0, 0); + + + auto handle_next = [&](int x, int y, int next_x, int next_y, float base_distance, int8_t direction, int8_t dx, int8_t dy, uint8_t speed, float prev_dist) { + auto next_idx = next_x + next_y * map_state.map_data.size_x; + if(!visited[next_idx]) { + for(int i = 1; i <= (int)speed; i++) { + auto nx = x + dx * i; + auto ny = y + dy * i; + auto idx_to_set = nx + ny * map_state.map_data.size_x; + set_visited(idx_to_set, direction); + } + + set_visited(next_idx, direction); + auto local_terrain = map_state.map_data.terrain_id_map[next_idx]; + auto modifier = context.modifier_by_terrain_index[local_terrain]; + auto& modifier_data = world.modifier_get_province_values(modifier); + auto move_cost_mod = terrain_cost[local_terrain]; + if(sea_route) { + move_cost_mod = 100.f; + if (local_terrain == 255) move_cost_mod = 1.f; + } + auto next_distance = prev_dist + base_distance * std::max(0.05f, (move_cost_mod * 20.f)); + auto scaled_x = (float)next_x / (float)map_state.map_data.size_x; + auto scaled_y = (float)next_y / (float)map_state.map_data.size_y; + + glm::vec3 new_world_pos; + float angle_x = 2 * scaled_x * math::pi; + new_world_pos.x = math::cos(angle_x); + new_world_pos.y = math::sin(angle_x); + + float angle_y = scaled_y * math::pi; + new_world_pos.x *= math::sin(angle_y); + new_world_pos.y *= math::sin(angle_y); + new_world_pos.z = math::cos(angle_y); + + auto local_heur = glm::distance(mid_point_2_b, new_world_pos) * 100.f; + + pixel_heap_member to_add { next_x, next_y, next_distance, local_heur, dx, dy, uint8_t(std::min((int)speed, 20)) }; + pixel_queue.push_back(to_add); + std::push_heap(pixel_queue.begin(), pixel_queue.end(), std::greater<>{}); + } + }; + + while(true) { + // eliminate hopeless pixels + if(pixel_queue.size() >= 200) { + auto best_heuristic = pixel_queue[0].heur; + auto worst_heuristic = pixel_queue[0].heur; + for(size_t i = 0; i < pixel_queue.size(); i++) { + if(pixel_queue[i].heur > worst_heuristic) { + worst_heuristic = pixel_queue[i].heur; + } + if(pixel_queue[i].heur < best_heuristic) { + best_heuristic = pixel_queue[i].heur; + } + } + auto cutout = 0.25f * worst_heuristic + 0.75f * best_heuristic; + + for(int i = (int)pixel_queue.size() - 1; i >= 0; i--) { + if(pixel_queue[i].heur > cutout){ + pixel_queue.erase(pixel_queue.begin() + i); + } + } + std::make_heap(pixel_queue.begin(), pixel_queue.end()); + } + + auto best = pixel_queue[0]; + std::pop_heap(pixel_queue.begin(), pixel_queue.end(), std::greater<>{}); + pixel_queue.pop_back(); + + auto x = best.x; + auto y = best.y; + auto actual_distance = best.dist; + auto idx = best.x + best.y * map_state.map_data.size_x; + + for(int8_t dx = -1; dx <= 1; dx++) { + for(int8_t dy = -1; dy <= 1; dy++) { + if(dx == 0 && dy == 0) { + continue; + } + if(best.speed > 2 && dx * best.dx + dy * best.dy <= 0 && best.heur > 1.f) { + continue; + } + auto dir = encode_ternary(0, dx, dy); + // make additional "grid skipping" attempt which is supposedly faster + /* + if(best.dx == dx && best.dy == dy && best.heur > 1.f) { + uint8_t next_speed = best.speed +1; + if(next_speed > 10) { + next_speed = 10; + } + auto next_x = x + dx * next_speed; + auto next_y = y + dy * next_speed; + if(next_y < 0 || next_y >= (int)map_state.map_data.size_y) { + continue; + } + next_x = (next_x + (int)map_state.map_data.size_x) % (int)map_state.map_data.size_x; + handle_next(x, y, next_x, next_y, math::sqrt((float)(dx * dx + dy * dy)) * next_speed, dir, dx, dy, next_speed, actual_distance); + } + */ + { + auto next_x = x + dx; + auto next_y = y + dy; + if(next_y < 0 || next_y >= (int)map_state.map_data.size_y) { + continue; + } + next_x = (next_x + (int)map_state.map_data.size_x) % (int)map_state.map_data.size_x; + handle_next(x, y, next_x, next_y, math::sqrt((float)(dx * dx + dy * dy)), dir, dx, dy, 1, actual_distance); + } + } + } + + if(visited[end_idx]) { + break; + } + } + + map_state.map_data.railroad_starts.push_back(GLint(map_state.map_data.railroad_vertices.size())); + float distance = 0.f; + + int step = 0; + + glm::vec2 current_pos{ end_x, end_y }; + + + while(end_idx != start_idx && (int)(end_idx) >= 0) { + + auto y = end_idx / map_state.map_data.size_x; + auto x = end_idx - y * map_state.map_data.size_x; + + auto encoded_shift = (int)(visited[end_idx]); + + auto dy = (encoded_shift + 9) % 3; + if (dy == 2) dy = -1; + auto dx = (encoded_shift - dy) / 3; + + auto next_x = x - dx; + auto next_y = y - dy; + + auto next_idx = next_x + next_y * map_state.map_data.size_x; + + if(step % 3 != 0 && next_idx != end_idx && step != 0) { + step++; + end_idx = next_idx; + continue; + } + step++; + + auto ny = next_idx / map_state.map_data.size_x; + auto nx = next_idx - ny * map_state.map_data.size_x; + glm::vec2 next_vec { nx, ny }; + glm::vec2 next_pos = put_in_local(next_vec, current_pos, float(map_state.map_data.size_x)); + glm::vec2 prev_perpendicular = glm::normalize(next_pos - current_pos); + + auto start_normal = glm::vec2(-prev_perpendicular.y, prev_perpendicular.x); + auto norm_pos = current_pos / glm::vec2(map_state.map_data.size_x, map_state.map_data.size_y); + auto norm_next = next_pos / glm::vec2(map_state.map_data.size_x, map_state.map_data.size_y); + + auto prev_distance = distance; + distance += glm::length(next_pos - current_pos) / float(map_state.map_data.size_y); + + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_pos, +start_normal, 0.0f, prev_distance });//C + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_pos, -start_normal, 1.0f, prev_distance });//D + + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_next, +start_normal, 0.0f, distance }); + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_next, -start_normal, 1.0f, distance }); + + end_idx = next_idx; + current_pos = next_pos; + } + + glm::vec2 next_pos = put_in_local(mid_point_1, current_pos, float(map_state.map_data.size_x)); + distance += glm::length(next_pos - current_pos) / float(map_state.map_data.size_y); + glm::vec2 prev_perpendicular = glm::normalize(mid_point_1 - current_pos); + auto start_normal = glm::vec2(-prev_perpendicular.y, prev_perpendicular.x); + auto norm_pos = current_pos / glm::vec2(map_state.map_data.size_x, map_state.map_data.size_y); + auto norm_next = next_pos / glm::vec2(map_state.map_data.size_x, map_state.map_data.size_y); + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_pos, +start_normal, 0.0f, 0.f });//C + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_pos, -start_normal, 1.0f, 0.f });//D + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_next, +start_normal, 0.0f, distance }); + map_state.map_data.railroad_vertices.emplace_back(map::textured_line_vertex{ norm_next, -start_normal, 1.0f, distance }); + + map_state.map_data.railroad_counts.push_back(GLsizei(map_state.map_data.railroad_vertices.size() - map_state.map_data.railroad_starts.back())); + + clear_visited(); + }); + // run pending triggers and effects for(auto pending_decision : pending_decisions) { @@ -3775,7 +4116,6 @@ void state::preload() { m.set_pop_support(0.0f); m.set_radicalism(0.0f); } - } void state::on_scenario_load() { @@ -4227,6 +4567,7 @@ void state::fill_unsaved_data() { // reconstructs derived values that are not di nations::monthly_flashpoint_update(*this); + // // clear any pending messages from previously loaded saves // diff --git a/src/gui/main_menu.cpp b/src/gui/main_menu.cpp index 54fcbfb84a..f7a125459e 100644 --- a/src/gui/main_menu.cpp +++ b/src/gui/main_menu.cpp @@ -3962,7 +3962,7 @@ void main_menu_graphics_antialiasing_dropdown_t::on_selection(sys::state& state, quiet_on_selection(state, id); main_menu_graphics_t& graphics = *((main_menu_graphics_t*)(parent)); // BEGIN graphics::antialiasing_dropdown::on_selection - state.user_settings.antialias_level = uint8_t(sys::ui_scales[list_contents[id].index]); + state.user_settings.antialias_level = (uint8_t)list_contents[id].index; ogl::deinitialize_msaa(state); ogl::initialize_msaa(state, state.x_size, state.y_size); state.user_setting_changed = true; @@ -4111,6 +4111,7 @@ void main_menu_graphics_graphics_details_dropdown_t::on_update(sys::state& state add_item(int32_t(sys::graphics_mode::ugly), text::produce_simple_string(state, "graphics_details_0")); add_item(int32_t(sys::graphics_mode::classic), text::produce_simple_string(state, "graphics_details_1")); add_item(int32_t(sys::graphics_mode::modern), text::produce_simple_string(state, "graphics_details_2")); + add_item(int32_t(sys::graphics_mode::modern_classic), text::produce_simple_string(state, "graphics_details_3")); quiet_on_selection(state, int32_t(state.user_settings.graphics_mode)); // END } diff --git a/src/map/map.cpp b/src/map/map.cpp index 142a1eec9c..d5651bff33 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -614,6 +614,8 @@ void display_data::create_meshes() { create_unit_arrow_vbo(vbo_array[vo_other_objective_unit_arrow], other_objective_unit_arrow_vertices); glBindVertexArray(vao_array[vo_text_line]); create_text_line_vbo(vbo_array[vo_text_line]); + glBindVertexArray(vao_array[vo_bold_text_line]); + create_text_line_vbo(vbo_array[vo_bold_text_line]); glBindVertexArray(vao_array[vo_province_text_line]); create_text_line_vbo(vbo_array[vo_province_text_line]); glBindVertexArray(vao_array[vo_drag_box]); @@ -739,6 +741,7 @@ void display_data::load_shaders(simple_fs::directory& root) { shader_uniforms[i][uniform_time] = glGetUniformLocation(shaders[i], "time"); shader_uniforms[i][uniform_light_direction] = glGetUniformLocation(shaders[i], "light_direction"); shader_uniforms[i][uniform_ignore_light] = glGetUniformLocation(shaders[i], "ignore_light"); + shader_uniforms[i][uniform_railroad_level] = glGetUniformLocation(shaders[i], "railroad_level"); shader_uniforms[i][uniform_terrain_texture_sampler] = glGetUniformLocation(shaders[i], "terrain_texture_sampler"); shader_uniforms[i][uniform_terrainsheet_texture_sampler] = glGetUniformLocation(shaders[i], "terrainsheet_texture_sampler"); shader_uniforms[i][uniform_terrainsheet_texture_sampler_array] = glGetUniformLocation(shaders[i], "terrainsheet_texture_sampler_array"); @@ -1142,7 +1145,7 @@ void display_data::render( // Draw the railroads and city - if(/*zoom > map::zoom_close && */!railroad_vertices.empty() && state.user_settings.railroads_enabled) { + if(zoom > map::zoom_close && !railroad_vertices.empty() && state.user_settings.railroads_enabled) { glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); @@ -1166,7 +1169,20 @@ void display_data::render( glBindVertexArray(vao_array[vo_railroad]); glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_railroad]); - glMultiDrawArrays(GL_TRIANGLE_STRIP, railroad_starts.data(), railroad_counts.data(), GLsizei(railroad_starts.size())); + + for(uint32_t idx = 0; idx < railroad_counts.size(); idx++) { + dcon::province_adjacency_id adj { (dcon::province_adjacency_id::value_base_t)idx }; + float railroad_level = 0.f; + if(state.world.province_adjacency_is_valid(adj)) { + auto p0 = state.world.province_adjacency_get_connected_provinces(adj, 0); + auto p1 = state.world.province_adjacency_get_connected_provinces(adj, 0); + auto rail0 = state.world.province_get_building_level(p0, uint8_t(economy::province_building_type::railroad)); + auto rail1 = state.world.province_get_building_level(p1, uint8_t(economy::province_building_type::railroad)); + railroad_level = (float)std::max(rail0, rail1); + } + glUniform1f(shader_uniforms[shader_railroad_line][uniform_railroad_level], railroad_level); + glDrawArrays(GL_TRIANGLE_STRIP, railroad_starts[idx], railroad_counts[idx]); + } } if(//zoom > map::zoom_close && @@ -1530,7 +1546,7 @@ void display_data::render( glBindVertexArray(vao_array[vo_trade_flow]); glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_trade_flow]); - //glMultiDrawArrays(GL_TRIANGLE_STRIP, trade_flow_arrow_starts.data(), trade_flow_arrow_counts.data(), GLsizei(trade_flow_arrow_starts.size())); + glMultiDrawArrays(GL_TRIANGLE_STRIP, trade_flow_arrow_starts.data(), trade_flow_arrow_counts.data(), GLsizei(trade_flow_arrow_starts.size())); // trade particles if(state.user_settings.graphics_mode != sys::graphics_mode::ugly) { @@ -1542,37 +1558,61 @@ void display_data::render( state.lookup_key("gfx_storage_commodity") )->second.definition ].data.image.gfx_object; - auto& gfx_def = state.ui_defs.gfx[gfx_id]; - auto frame = state.world.commodity_get_icon(state.selected_trade_good); - auto texture_handle = ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()); + auto& gfx_def = state.ui_defs.gfx[gfx_id]; + auto frame = state.world.commodity_get_icon(state.selected_trade_good); + auto texture_handle = ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture_handle); - glUniform1i(shader_uniforms[shader_map_sprite][uniform_texture_sampler], 0); - glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_start], (float)frame / gfx_def.number_of_frames, 0.f); - glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_size], 1.f / gfx_def.number_of_frames, 1.f); - - glBindVertexArray(vao_array[vo_square]); - glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_square]); + const float speed = 0.5f; - const float speed = 0.5f; - - bool spawned_something = false; + bool spawned_something = false; + { for(size_t i = 0; i < trade_particles_positions.size(); i++) { auto& p = trade_particles_positions[i]; // update movement if(p.trade_graph_node_current != -1 && p.trade_graph_node_next != -1) { - auto direction = p.target_ - p.position_; - auto length = float(glm::length(direction)); - if(length < speed * 2) { - p.trade_graph_node_prev = p.trade_graph_node_current; - p.trade_graph_node_current = p.trade_graph_node_next; - p.trade_graph_node_next = -1; - } else { - p.position_ += direction / length * speed; + if(p.adj_index != -1) { + auto time_left = 1.f; + while(time_left > 0.f) { + auto actual_target_offset = railroad_starts[p.adj_index] + p.adj_count; + auto actual_target = railroad_vertices[actual_target_offset].position_ * glm::vec2(float(size_x), float(size_y)); + auto direction = actual_target - p.position_; + auto length = float(glm::length(direction)); + if(length < speed * 2) { + p.adj_count += p.adj_direction; + if(p.adj_count >= railroad_counts[p.adj_index] || p.adj_count < 0) { + p.trade_graph_node_prev = p.trade_graph_node_current; + p.trade_graph_node_current = p.trade_graph_node_next; + p.trade_graph_node_next = -1; + p.adj_index = -1; + break; + } + time_left -= length / (speed * 2); + } else { + for(int vagon = (int)p.vagon_positions.size() - 1; vagon > 0; vagon--) { + p.vagon_positions[vagon] = p.vagon_positions[vagon - 1]; + } + p.vagon_positions[0] = p.position_; + p.position_ += direction / length * speed; + time_left -= 1.f; + } + } + } else { + auto direction = p.target_ - p.position_; + auto length = float(glm::length(direction)); + if(length < speed * 2) { + p.trade_graph_node_prev = p.trade_graph_node_current; + p.trade_graph_node_current = p.trade_graph_node_next; + p.trade_graph_node_next = -1; + } else { + for(int vagon = (int)p.vagon_positions.size() - 1; vagon > 0; vagon--) { + p.vagon_positions[vagon] = p.vagon_positions[vagon - 1]; + } + p.vagon_positions[0] = p.position_; + p.position_ += direction / length * speed; + } } } @@ -1599,16 +1639,34 @@ void display_data::render( p.trade_graph_node_current = -1; } else { p.trade_graph_node_next = target; + if(target >= (int)state.world.province_size()) { + target -= state.world.province_size(); + } p.target_ = put_in_local(trade_node_position[target], p.position_, (float)size_x); + + auto current = p.trade_graph_node_current; + if(current >= (int)state.world.province_size()) { + current -= state.world.province_size(); + } + + auto adj = state.world.get_province_adjacency_by_province_pair( + dcon::province_id{ dcon::province_id::value_base_t(target) }, + dcon::province_id{ dcon::province_id::value_base_t(current) } + ); + if(adj && railroad_counts[adj.index()] > 0) { + p.adj_index = adj.index(); + p.adj_count = 0; + p.adj_direction = 1; + if(state.world.province_adjacency_get_connected_provinces(adj, 0).index() != target) { + p.adj_count = railroad_counts[adj.index()] - 1; + p.adj_direction = -1; + } + } } } - if(p.trade_graph_node_current != -1) { - glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_offsets], trade_particles_positions[i].position_.x / float(size_x), trade_particles_positions[i].position_.y / float(size_y)); - glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_scale], 4.f / float(size_x), 4.f / float(size_y)); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - } + // spawn "new" particles // don't spawn them too often @@ -1636,6 +1694,96 @@ void display_data::render( } } } + } + // draw + { + + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_handle); + + glUniform1i(shader_uniforms[shader_map_sprite][uniform_texture_sampler], 0); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_start], (float)frame / gfx_def.number_of_frames, 0.f); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_size], 1.f / gfx_def.number_of_frames, 1.f); + + glBindVertexArray(vao_array[vo_square]); + glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_square]); + + for(size_t i = 0; i < trade_particles_positions.size(); i++) { + auto& p = trade_particles_positions[i]; + if(p.trade_graph_node_current != -1) { + for(int vagon = (int)p.vagon_positions.size() - 1; vagon > 0; vagon--) { + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_offsets], trade_particles_positions[i].vagon_positions[vagon].x / float(size_x), trade_particles_positions[i].vagon_positions[vagon].y / float(size_y)); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_scale], 1.f / float(size_x), 1.f / float(size_y)); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + } + } + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures[texture_train]); + + glUniform1i(shader_uniforms[shader_map_sprite][uniform_texture_sampler], 0); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_start], 0.f, 0.f); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_size], 1.f, 1.f); + + glBindVertexArray(vao_array[vo_square]); + glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_square]); + + for(size_t i = 0; i < trade_particles_positions.size(); i++) { + auto& p = trade_particles_positions[i]; + + dcon::province_adjacency_id adj{ dcon::province_adjacency_id::value_base_t(p.adj_index) }; + auto p1 = state.world.province_adjacency_get_connected_provinces(adj, 0); + auto p2 = state.world.province_adjacency_get_connected_provinces(adj, 1); + auto p1_is_sea = p1.index() >= state.province_definitions.first_sea_province.index() && p1.index() < (int)state.world.province_size(); + auto p2_is_sea = p2.index() >= state.province_definitions.first_sea_province.index() && p2.index() < (int)state.world.province_size(); + + if(p1_is_sea || p2_is_sea) { + continue; + } + + if(p.trade_graph_node_current != -1) { + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_offsets], trade_particles_positions[i].position_.x / float(size_x), trade_particles_positions[i].position_.y / float(size_y)); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_scale], 1.f / float(size_x), 1.f / float(size_y)); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures[texture_ship]); + + glUniform1i(shader_uniforms[shader_map_sprite][uniform_texture_sampler], 0); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_start], 0.f, 0.f); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_texture_size], 1.f, 1.f); + + glBindVertexArray(vao_array[vo_square]); + glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_square]); + + for(size_t i = 0; i < trade_particles_positions.size(); i++) { + auto& p = trade_particles_positions[i]; + + dcon::province_adjacency_id adj{ dcon::province_adjacency_id::value_base_t(p.adj_index) }; + auto p1 = state.world.province_adjacency_get_connected_provinces(adj, 0); + auto p2 = state.world.province_adjacency_get_connected_provinces(adj, 1); + auto p1_is_sea = p1.index() >= state.province_definitions.first_sea_province.index() && p1.index() < (int)state.world.province_size(); + auto p2_is_sea = p2.index() >= state.province_definitions.first_sea_province.index() && p2.index() < (int)state.world.province_size(); + + if(!(p1_is_sea || p2_is_sea)) { + continue; + } + + if(p.trade_graph_node_current != -1) { + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_offsets], trade_particles_positions[i].position_.x / float(size_x), trade_particles_positions[i].position_.y / float(size_y)); + glUniform2f(shader_uniforms[shader_map_sprite][uniform_sprite_scale], 1.f / float(size_x), 1.f / float(size_y)); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + } + } } } @@ -1707,36 +1855,72 @@ void display_data::render( glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); //glDisable(GL_CULL_FACE); - //glDisable(GL_DEPTH_TEST); + glDisable(GL_DEPTH_TEST); //glDisable(GL_BLEND); - auto location = shader_uniforms[shader_text_line][uniform_color]; + auto color_location = shader_uniforms[shader_text_line][uniform_color]; + /* if(state.user_settings.black_map_font) glUniform4f(location, 0.0f, 0.0f, 0.0f, 1.0f); else glUniform4f(location, 1.0f, 1.0f, 1.0f, 1.0f); + */ - location = shader_uniforms[shader_text_line][uniform_glyphs]; + auto location = shader_uniforms[shader_text_line][uniform_glyphs]; glUniform1i(location, 0); location = shader_uniforms[shader_text_line][uniform_curves]; glUniform1i(location, 1); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.glyph_texture); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.curve_texture); - glActiveTexture(GL_TEXTURE0); - //if (zoom < map::zoom_very_close) { + glUniform4f(color_location, 0.0f, 0.0f, 0.0f, 1.0f); + + //if (zoom > map::zoom_very_close) { + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.glyph_texture); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.curve_texture); + + glActiveTexture(GL_TEXTURE0); + + glUniform4f(color_location, 0.0f, 0.0f, 0.0f, 1.0f); + glBindVertexArray(vao_array[vo_province_text_line]); glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_province_text_line]); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)province_text_line_vertices.size()); + //} if((!state.cheat_data.province_names || zoom < map::zoom_very_close) && !text_line_vertices.empty()) { + glUniform4f(color_location, 0.9f, 0.9f, 0.9f, 0.8f); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.bold_glyph_texture); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.bold_curve_texture); + + glActiveTexture(GL_TEXTURE0); + + glBindVertexArray(vao_array[vo_bold_text_line]); + glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_bold_text_line]); + glDrawArrays(GL_TRIANGLES, 0, last_size_of_bold_text_line_vertices); + + glUniform4f(color_location, 0.0f, 0.0f, 0.0f, 1.0f); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.glyph_texture); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_BUFFER, state.font_collection.mfont.curve_texture); + + glActiveTexture(GL_TEXTURE0); + glBindVertexArray(vao_array[vo_text_line]); glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_text_line]); glDrawArrays(GL_TRIANGLES, 0, last_size_of_text_line_vertices); @@ -3193,6 +3377,7 @@ void display_data::update_sprawl(sys::state& state) { } }; + /* railroad_vertices.clear(); railroad_starts.clear(); railroad_counts.clear(); @@ -3217,6 +3402,7 @@ void display_data::update_sprawl(sys::state& state) { assert(railroad_counts.back() > 1); } assert(railroad_counts.size() == railroad_starts.size()); + */ if(!railroad_vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, vbo_array[vo_railroad]); @@ -3255,7 +3441,10 @@ float get_max_glyph_height(sys::state& state, text_line_generator_data const& e) return max_glyph_height; } -void push_polynomial_text_to_vertex_array(sys::state& state, display_data& display_data, text_line_generator_data const& e, std::vector& out) { +void push_polynomial_text_to_vertex_array(sys::state& state, display_data& display_data, text_line_generator_data const& e, + std::vector& out, + std::vector& bold_out +) { float size_x = (float)display_data.size_x; float size_y = (float)display_data.size_y; @@ -3383,57 +3572,76 @@ void push_polynomial_text_to_vertex_array(sys::state& state, display_data& displ auto max_glyph_height = get_max_glyph_height(state, e); unsigned int glyph_count = static_cast(e.text.glyph_info.size()); - for(unsigned int i = 0; i < glyph_count; i++) { - hb_codepoint_t glyphid = e.text.glyph_info[i].codepoint; - auto& gi = state.font_collection.mfont.glyphs[glyphid]; - + auto push_glyph = [&]( + text::map_font::map_font_glyph const& gi, + text::stored_glyph const& info, + std::vector& out, + float mult + ) { float glyph_width = gi.ft_width * letter_scale; float glyph_height = gi.ft_height * letter_scale; - float x_advance = float(e.text.glyph_info[i].x_advance) * letter_scale; - float x_offset = float(e.text.glyph_info[i].x_offset) * letter_scale; - float y_offset = float(e.text.glyph_info[i].y_offset) * letter_scale; + float x_advance = float(info.x_advance) * letter_scale; + float x_offset = float(info.x_offset) * letter_scale; + float y_offset = float(info.y_offset) * letter_scale; float x_bearing = float(gi.ft_x_bearing) * letter_scale; float y_bearing = float(gi.ft_y_bearing) * letter_scale; - if(gi.curveCount > 0) { - // rectangle - equirectangular::tangent forward_rect { { { 0.f, 0.f } } , { ratio.x, ratio.y * dpoly_fn(cur_x) } }; - square::tangent forward = equirectangular::to_square(forward_rect, (float)size_x, (float)size_y); - float norm = sqrt(equirectangular::dot(forward, forward, (float)size_x, (float)size_y)); - if(norm <= 0.f) norm = 1.f; - forward.data /= norm; - square::tangent up = equirectangular::rotate_left(forward, (float)size_x, (float)size_y); + float expansion = 1.f; - equirectangular::point center_rect { glm::vec2(cur_x, poly_fn(cur_x)) * ratio + basis }; - square::point center = equirectangular::to_square(center_rect, (float)size_x, (float)size_y); + auto embold_x = mult / 2.f; + auto embold_y = mult / 2.f; - auto shift_up = (-max_glyph_height / 2.f + y_bearing - y_offset) * size / 64.f; - auto shift_down = (-max_glyph_height / 2.f - y_offset) * size / 64.f; + if(gi.curveCount == 0) return; - auto left_base = center.data; - auto right_base = left_base + forward.data * size / 64.f * glyph_width; + // rectangle + equirectangular::tangent forward_rect{ { { 0.f, 0.f } } , { ratio.x, ratio.y * dpoly_fn(cur_x) } }; + square::tangent forward = equirectangular::to_square(forward_rect, (float)size_x, (float)size_y); + float norm = sqrt(equirectangular::dot(forward, forward, (float)size_x, (float)size_y)); + if(norm <= 0.f) norm = 1.f; + forward.data /= norm; + square::tangent up = equirectangular::rotate_left(forward, (float)size_x, (float)size_y); - auto p00 = left_base + up.data * shift_down; - auto p01 = left_base + up.data * shift_up; - auto p10 = right_base + up.data * shift_down; - auto p11 = right_base + up.data * shift_up; + equirectangular::point center_rect{ glm::vec2(cur_x, poly_fn(cur_x)) * ratio + basis }; + square::point center = equirectangular::to_square(center_rect, (float)size_x, (float)size_y); - float u0 = float(gi.ft_x_bearing) / (64.0f * text::dr_size); - float v0 = float(gi.ft_y_bearing - gi.ft_height) / (64.0f * text::dr_size); - float u1 = float(gi.ft_x_bearing + gi.ft_width) / (64.0f * text::dr_size); - float v1 = float(gi.ft_y_bearing) / (64.0f * text::dr_size); + auto shift_up = (-max_glyph_height / 2.f + y_bearing - y_offset) * size / 64.f + expansion * size - embold_y * size; + auto shift_down = (-max_glyph_height / 2.f +y_bearing - y_offset - glyph_height) * size / 64.f - expansion * size - embold_y * size; - float height_scale = float(gi.ft_height) / (64.0f * text::dr_size); - float width_scale = float(gi.ft_width) / (64.0f * text::dr_size); + auto left_base = center.data - forward.data * expansion * size - forward.data * embold_x * size; + auto right_base = center.data + forward.data * size / 64.f * glyph_width + forward.data * expansion * size - forward.data * embold_x * size; - out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); - out.emplace_back(p00, glm::vec2(u0, v0), real_text_half_size, gi.bufferIndex); - out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); + auto p00 = left_base + up.data * shift_down; + auto p01 = left_base + up.data * shift_up; + auto p10 = right_base + up.data * shift_down; + auto p11 = right_base + up.data * shift_up; - out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); - out.emplace_back(p11, glm::vec2(u1, v1), real_text_half_size, gi.bufferIndex); - out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); - } + + float u0 = float(gi.ft_x_bearing) / (64.0f * text::dr_size) - expansion; + float v0 = float(gi.ft_y_bearing - gi.ft_height) / (64.0f * text::dr_size) - expansion; + float u1 = float(gi.ft_x_bearing + gi.ft_width) / (64.0f * text::dr_size) + expansion; + float v1 = float(gi.ft_y_bearing) / (64.0f * text::dr_size) + expansion; + + + float height_scale = float(gi.ft_height) / (64.0f * text::dr_size); + float width_scale = float(gi.ft_width) / (64.0f * text::dr_size); + + out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); + out.emplace_back(p00, glm::vec2(u0, v0), real_text_half_size, gi.bufferIndex); + out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); + + out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); + out.emplace_back(p11, glm::vec2(u1, v1), real_text_half_size, gi.bufferIndex); + out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); + }; + + for(unsigned int i = 0; i < glyph_count; i++) { + hb_codepoint_t glyphid = e.text.glyph_info[i].codepoint; + auto& gi = state.font_collection.mfont.glyphs[glyphid]; + auto& bold_gi = state.font_collection.mfont.bold_glyphs[glyphid]; + float x_advance = float(e.text.glyph_info[i].x_advance) * letter_scale; + + push_glyph(gi, e.text.glyph_info[i], out, 0.f); + push_glyph(bold_gi, e.text.glyph_info[i], bold_out, (float)text::map_outline_embolden / 64.f); auto current_spacing = (i == glyph_count - 1) ? 0.f : letter_spacing_map; float glyph_advance = x_advance * size / 64.f; @@ -3454,7 +3662,13 @@ void push_polynomial_text_to_vertex_array(sys::state& state, display_data& displ */ } -void push_spherical_text_to_vertex_array(sys::state& state, display_data& display_data, text_line_generator_data const& e, std::vector& out, int size_limit, int min_size_cutoff, float opacity_mult) { +void push_spherical_text_to_vertex_array( + sys::state& state, display_data& display_data, text_line_generator_data const& e, + std::vector& out, + std::vector& bold_out, + bool bold, + int size_limit, int min_size_cutoff, float opacity_mult +) { float size_x = (float)display_data.size_x; float size_y = (float)display_data.size_y; @@ -3558,58 +3772,83 @@ void push_spherical_text_to_vertex_array(sys::state& state, display_data& displa auto max_glyph_height = get_max_glyph_height(state, e); unsigned int glyph_count = static_cast(e.text.glyph_info.size()); - for(unsigned int i = 0; i < glyph_count; i++) { - hb_codepoint_t glyphid = e.text.glyph_info[i].codepoint; - auto& gi = state.font_collection.mfont.glyphs[glyphid]; - + auto push_glyph = [ + &max_glyph_height, + &sph_placement_runner, + &sphere_tangent, + &sphere_shift, + &sph_direction, + &size, + &letter_scale, + &real_text_half_size + ]( + text::map_font::map_font_glyph const& gi, + text::stored_glyph const& info, + std::vector& out, + float mult + ) { + if(gi.curveCount == 0) return; + float glyph_width = gi.ft_width * letter_scale; float glyph_height = gi.ft_height * letter_scale; - float x_advance = float(e.text.glyph_info[i].x_advance) * letter_scale; - float x_offset = float(e.text.glyph_info[i].x_offset) * letter_scale; - float y_offset = float(e.text.glyph_info[i].y_offset) * letter_scale; + float x_advance = float(info.x_advance) * letter_scale; + float x_offset = float(info.x_offset) * letter_scale; + float y_offset = float(info.y_offset) * letter_scale; float x_bearing = float(gi.ft_x_bearing) * letter_scale; float y_bearing = float(gi.ft_y_bearing) * letter_scale; - if(gi.curveCount > 0) { - auto forward_sphere = sphere_tangent(sph_placement_runner, sph_direction); - auto up_sphere = sphere_R3::rotate(forward_sphere); + auto forward_sphere = sphere_tangent(sph_placement_runner, sph_direction); + auto up_sphere = sphere_R3::rotate(forward_sphere); + + float expansion = 1.f; + + float u0 = float(gi.ft_x_bearing) / (64.0f * text::dr_size) - expansion; + float v0 = float(gi.ft_y_bearing - gi.ft_height) / (64.0f * text::dr_size) - expansion; + float u1 = float(gi.ft_x_bearing + gi.ft_width) / (64.0f * text::dr_size) + expansion; + float v1 = float(gi.ft_y_bearing) / (64.0f * text::dr_size) + expansion; - float u0 = float(gi.ft_x_bearing) / (64.0f * text::dr_size); - float v0 = float(gi.ft_y_bearing - gi.ft_height) / (64.0f * text::dr_size); - float u1 = float(gi.ft_x_bearing + gi.ft_width) / (64.0f * text::dr_size); - float v1 = float(gi.ft_y_bearing) / (64.0f * text::dr_size); + auto embold_x = mult / 2.f; + auto embold_y = mult / 2.f; - auto left_base = sph_placement_runner; - auto right_base = sphere_shift(left_base, sph_direction, size / 64.f * glyph_width); - /* - We want our great circle to be a central line -> we shift the bottom line by half of max glyph height "down". - We want to top points to be at glyph height distance from the bottom line -> we shift it by half of max height down and add max height. - Both lines have to be offset by y bearing (letter specific) and y offset (kerning specific) - */ + auto left_base = sphere_shift(sph_placement_runner, sph_direction, -expansion * size - embold_x * size); + auto right_base = sphere_shift(sph_placement_runner, sph_direction, size / 64.f * glyph_width + expansion * size - embold_x * size); - auto shift_up = (-max_glyph_height / 2.f + y_bearing - y_offset) * size / 64.f; - auto shift_down = (-max_glyph_height / 2.f - y_offset) * size / 64.f; + /* + We want our great circle to be a central line -> we shift the bottom line by half of max glyph height "down". + We want to top points to be at glyph height distance from the bottom line -> we shift it by half of max height down and add max height. + Both lines have to be offset by y bearing (letter specific) and y offset (kerning specific) + */ - sphere_R3::point left_up_point = {sphere_shift(left_base, up_sphere.data, shift_up)}; - sphere_R3::point left_bottom_point = {sphere_shift(left_base, up_sphere.data, shift_down)}; - sphere_R3::point right_up_point = {sphere_shift(right_base, up_sphere.data, shift_up)}; - sphere_R3::point right_bottom_point ={sphere_shift(right_base, up_sphere.data, shift_down)}; + auto shift_up = (-max_glyph_height / 2.f + y_bearing - y_offset + expansion * 64.f) * size / 64.f - embold_y * size; + auto shift_down = (-max_glyph_height / 2.f + y_bearing - y_offset - glyph_height - expansion * 64.f) * size / 64.f - embold_y * size; - auto p01 = sphere_R3::to_square(left_up_point).data; - auto p00 = sphere_R3::to_square(left_bottom_point).data; - auto p10 = sphere_R3::to_square(right_bottom_point).data; - auto p11 = sphere_R3::to_square(right_up_point).data; + sphere_R3::point left_up_point = { sphere_shift(left_base, up_sphere.data, shift_up) }; + sphere_R3::point left_bottom_point = { sphere_shift(left_base, up_sphere.data, shift_down) }; + sphere_R3::point right_up_point = { sphere_shift(right_base, up_sphere.data, shift_up) }; + sphere_R3::point right_bottom_point = { sphere_shift(right_base, up_sphere.data, shift_down) }; - out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); - out.emplace_back(p00, glm::vec2(u0, v0), real_text_half_size, gi.bufferIndex); - out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); + auto p01 = sphere_R3::to_square(left_up_point).data; + auto p00 = sphere_R3::to_square(left_bottom_point).data; + auto p10 = sphere_R3::to_square(right_bottom_point).data; + auto p11 = sphere_R3::to_square(right_up_point).data; - out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); - out.emplace_back(p11, glm::vec2(u1, v1), real_text_half_size, gi.bufferIndex); - out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); - } + out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); + out.emplace_back(p00, glm::vec2(u0, v0), real_text_half_size, gi.bufferIndex); + out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); + + out.emplace_back(p10, glm::vec2(u1, v0), real_text_half_size, gi.bufferIndex); + out.emplace_back(p11, glm::vec2(u1, v1), real_text_half_size, gi.bufferIndex); + out.emplace_back(p01, glm::vec2(u0, v1), real_text_half_size, gi.bufferIndex); + }; + for(unsigned int i = 0; i < glyph_count; i++) { + hb_codepoint_t glyphid = e.text.glyph_info[i].codepoint; + auto& gi = state.font_collection.mfont.glyphs[glyphid]; + auto& bold_gi = state.font_collection.mfont.bold_glyphs[glyphid]; + float x_advance = float(e.text.glyph_info[i].x_advance) * letter_scale; + push_glyph(gi, e.text.glyph_info[i], out, 0.f); + push_glyph(bold_gi, e.text.glyph_info[i], bold_out, (float)text::map_outline_embolden / 64.f); auto current_spacing = (i == glyph_count - 1) ? 0.f : letter_spacing_map; float glyph_advance = x_advance * size / 64.f; float D = glyph_advance + current_spacing; @@ -3620,6 +3859,7 @@ void push_spherical_text_to_vertex_array(sys::state& state, display_data& displa void display_data::set_text_lines(sys::state& state) { // Clear previous text line vertices text_line_vertices.clear(); + bold_text_line_vertices.clear(); for(const auto& e : text_data) { // Skip invalid coefficients if(!std::isfinite(e.coeff[0]) || !std::isfinite(e.coeff[1]) || !std::isfinite(e.coeff[2]) || !std::isfinite(e.coeff[3])) @@ -3634,9 +3874,9 @@ void display_data::set_text_lines(sys::state& state) { bool is_spherical = state.user_settings.map_label == sys::map_label_mode::spherical; if(is_spherical) { - push_spherical_text_to_vertex_array(state, *this, e, text_line_vertices, 70, -60, 1.f); + push_spherical_text_to_vertex_array(state, *this, e, text_line_vertices, bold_text_line_vertices, true, 70, -60, 1.f); } else { - push_polynomial_text_to_vertex_array(state, *this, e, text_line_vertices); + push_polynomial_text_to_vertex_array(state, *this, e, text_line_vertices, bold_text_line_vertices); } } } @@ -3654,7 +3894,7 @@ void display_data::set_province_text_lines(sys::state& state) { if(e.text.glyph_info.empty()) continue; - push_spherical_text_to_vertex_array(state, *this, e, province_text_line_vertices, -65, -90, 0.25f); + push_spherical_text_to_vertex_array(state, *this, e, province_text_line_vertices, province_text_line_vertices, false, -65, -90, 0.25f); //push_spherical_text_to_vertex_array(state, *this, e, province_text_line_vertices, -40, -90, 0.25f); } @@ -4232,6 +4472,11 @@ void display_data::load_map(sys::state& state) { textures[texture_river_body] = load_dds_texture(assets_dir, NATIVE("river.dds")); ogl::set_gltex_parameters(textures[texture_river_body], GL_TEXTURE_2D, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT); + textures[texture_train] = ogl::make_gl_texture(assets_dir, NATIVE("images/train.png")); + ogl::set_gltex_parameters(textures[texture_train], GL_TEXTURE_2D, GL_LINEAR_MIPMAP_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + textures[texture_ship] = ogl::make_gl_texture(assets_dir, NATIVE("images/cargo-ship.png")); + ogl::set_gltex_parameters(textures[texture_ship], GL_TEXTURE_2D, GL_LINEAR_MIPMAP_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + textures[texture_national_border] = load_dds_texture(assets_dir, NATIVE("nat_border.dds")); ogl::set_gltex_parameters(textures[texture_national_border], GL_TEXTURE_2D, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_CLAMP_TO_EDGE); diff --git a/src/map/map.hpp b/src/map/map.hpp index f38578f431..455e3d13cf 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -39,6 +39,10 @@ struct trade_particle { int trade_graph_node_current; int trade_graph_node_prev = -1; int trade_graph_node_next; + int adj_index = -1; + int adj_count = 0; + int adj_direction = 1; + std::array vagon_positions {} ; }; struct screen_vertex { screen_vertex(float x, float y) : position_(x, y){}; @@ -282,6 +286,8 @@ class display_data { // std::vector text_line_vertices; GLsizei last_size_of_text_line_vertices = 0; + std::vector bold_text_line_vertices; + GLsizei last_size_of_bold_text_line_vertices = 0; std::vector province_text_line_vertices; std::vector drag_box_vertices; std::vector terrain_id_map; @@ -323,7 +329,8 @@ class display_data { static constexpr uint32_t vo_cities = 17; static constexpr uint32_t vo_arbitrary_map_triangles = 18; static constexpr uint32_t vo_state_border = 19; - static constexpr uint32_t vo_count = 20; + static constexpr uint32_t vo_bold_text_line = 20; + static constexpr uint32_t vo_count = 21; GLuint vao_national_borders_array[map::national_groups_count] = {0}; GLuint vbo_national_borders_array[map::national_groups_count] = { 0 }; GLuint vao_array[vo_count] = { 0 }; @@ -360,7 +367,9 @@ class display_data { static constexpr uint32_t texture_printbrush = 28; static constexpr uint32_t texture_hatching = 29; static constexpr uint32_t texture_watercolor = 30; - static constexpr uint32_t texture_count = 31; + static constexpr uint32_t texture_train = 31; + static constexpr uint32_t texture_ship = 32; + static constexpr uint32_t texture_count = 33; GLuint textures[texture_count] = { 0 }; // Texture Array static constexpr uint32_t texture_array_terrainsheet = 0; @@ -440,7 +449,8 @@ class display_data { static constexpr uint32_t uniform_printbrush = 49; static constexpr uint32_t uniform_hatching = 50; static constexpr uint32_t uniform_watercolor = 51; - static constexpr uint32_t uniform_count = 52; + static constexpr uint32_t uniform_railroad_level = 52; + static constexpr uint32_t uniform_count = 53; GLint shader_uniforms[shader_count][uniform_count] = { }; // models: Textures for static meshes diff --git a/src/map/map_state.cpp b/src/map/map_state.cpp index 8826815129..31eb8d2e81 100644 --- a/src/map/map_state.cpp +++ b/src/map/map_state.cpp @@ -1388,7 +1388,19 @@ void commit_text_lines(sys::state& state, display_data& map_data) { &map_data.text_line_vertices[0], GL_STATIC_DRAW ); + glBindBuffer( + GL_ARRAY_BUFFER, + map_data.vbo_array[map_data.vo_bold_text_line] + ); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(text_line_vertex) + * map_data.bold_text_line_vertices.size(), + &map_data.bold_text_line_vertices[0], + GL_STATIC_DRAW + ); map_data.last_size_of_text_line_vertices = (GLsizei)map_data.text_line_vertices.size(); + map_data.last_size_of_bold_text_line_vertices = (GLsizei)map_data.bold_text_line_vertices.size(); glBindBuffer(GL_ARRAY_BUFFER, 0); } } diff --git a/src/text/fonts.cpp b/src/text/fonts.cpp index 09116f609c..1a867fd80e 100644 --- a/src/text/fonts.cpp +++ b/src/text/fonts.cpp @@ -1180,20 +1180,40 @@ uint16_t make_font_id(sys::state& state, bool as_header, float target_line_size) } } +void convert_contour( + std::vector& buffer_glyphs, + std::vector& buffer_curves, + const FT_Outline* outline, + int32_t firstIndex, + int32_t lastIndex +); + void map_font::load_font(FT_Library& ft_library, char const* file_data_in, uint32_t file_size) { buffer_glyphs.clear(); + buffer_bold_glyphs.clear(); buffer_curves.clear(); + buffer_bold_curves.clear(); glyphs.clear(); + bold_glyphs.clear(); + if(face) FT_Done_Face(face); + if(bold_face) + FT_Done_Face(bold_face); file_data = std::unique_ptr(new FT_Byte[file_size]); memcpy(file_data.get(), file_data_in, file_size); + FT_New_Memory_Face(ft_library, file_data.get(), file_size, 0, &face); FT_Select_Charmap(face, FT_ENCODING_UNICODE); - FT_Set_Pixel_Sizes(face, dr_size, dr_size); + + FT_New_Memory_Face(ft_library, file_data.get(), file_size, 0, &bold_face); + FT_Select_Charmap(bold_face, FT_ENCODING_UNICODE); + //FT_Set_Char_Size(bold_face, 1024 << 6, 1024 << 6, 1024, 1024); + FT_Set_Pixel_Sizes(bold_face, dr_size, dr_size); + hb_font_face = hb_ft_font_create(face, nullptr); hb_buf = hb_buffer_create(); @@ -1208,6 +1228,16 @@ void map_font::load_font(FT_Library& ft_library, char const* file_data_in, uint3 glDeleteBuffers(1, &glyph_buffer); if(curve_buffer) glDeleteBuffers(1, &curve_buffer); + if(bold_glyph_texture) { + glDeleteTextures(1, &bold_glyph_texture); + bold_glyph_texture = 0; + } + if(bold_curve_texture) + glDeleteTextures(1, &bold_curve_texture); + if(bold_glyph_buffer) + glDeleteBuffers(1, &bold_glyph_buffer); + if(bold_curve_buffer) + glDeleteBuffers(1, &bold_curve_buffer); } map_font::~map_font() { if(glyph_texture) @@ -1220,6 +1250,16 @@ map_font::~map_font() { if(curve_buffer) glDeleteBuffers(1, &curve_buffer); + if(bold_glyph_texture) + glDeleteTextures(1, &glyph_texture); + if(bold_curve_texture) + glDeleteTextures(1, &curve_texture); + + if(bold_glyph_buffer) + glDeleteBuffers(1, &glyph_buffer); + if(bold_curve_buffer) + glDeleteBuffers(1, &curve_buffer); + if(hb_font_face) hb_font_destroy(hb_font_face); if(hb_buf) @@ -1234,6 +1274,11 @@ void map_font::ready_textures() { glGenBuffers(1, &glyph_buffer); glGenBuffers(1, &curve_buffer); + glGenTextures(1, &bold_glyph_texture); + glGenTextures(1, &bold_curve_texture); + glGenBuffers(1, &bold_glyph_buffer); + glGenBuffers(1, &bold_curve_buffer); + glBindBuffer(GL_TEXTURE_BUFFER, glyph_buffer); glBindBuffer(GL_TEXTURE_BUFFER, curve_buffer); @@ -1244,41 +1289,92 @@ void map_font::ready_textures() { glBindTexture(GL_TEXTURE_BUFFER, curve_texture); glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, curve_buffer); glBindTexture(GL_TEXTURE_BUFFER, 0); + + glBindBuffer(GL_TEXTURE_BUFFER, bold_glyph_buffer); + glBindBuffer(GL_TEXTURE_BUFFER, bold_curve_buffer); + + glBindTexture(GL_TEXTURE_BUFFER, bold_glyph_texture); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32I, bold_glyph_buffer); + glBindTexture(GL_TEXTURE_BUFFER, 0); + + glBindTexture(GL_TEXTURE_BUFFER, bold_curve_texture); + glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, bold_curve_buffer); + glBindTexture(GL_TEXTURE_BUFFER, 0); } } void map_font::make_glyph(uint32_t glyph_id) { ready_textures(); - if(glyphs.contains(glyph_id)) - return; - map_font_buffer_glyph temp_buffer_glyph; - temp_buffer_glyph.start = static_cast(buffer_curves.size()); + if(!glyphs.contains(glyph_id)) { + map_font_buffer_glyph temp_buffer_glyph; + temp_buffer_glyph.start = static_cast(buffer_curves.size()); + + FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_BITMAP); + short start = 0; + for(int i = 0; i < face->glyph->outline.n_contours; i++) { + // Note: The end indices in face->glyph->outline.contours are inclusive. + convert_contour(buffer_glyphs, buffer_curves, &face->glyph->outline, start, face->glyph->outline.contours[i]); + start = face->glyph->outline.contours[i] + 1; + } + + temp_buffer_glyph.count = static_cast(buffer_curves.size()) - temp_buffer_glyph.start; + + int32_t bufferIndex = static_cast(buffer_glyphs.size()); + buffer_glyphs.push_back(temp_buffer_glyph); + + { + map_font_glyph glyph; - FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_BITMAP); + glyph.ft_height = face->glyph->metrics.height; + glyph.ft_width = face->glyph->metrics.width; + glyph.ft_x_bearing = face->glyph->metrics.horiBearingX; + glyph.ft_y_bearing = face->glyph->metrics.horiBearingY; - short start = 0; - for(int i = 0; i < face->glyph->outline.n_contours; i++) { - // Note: The end indices in face->glyph->outline.contours are inclusive. - convert_contour(&face->glyph->outline, start, face->glyph->outline.contours[i]); - start = face->glyph->outline.contours[i] + 1; + glyph.bufferIndex = bufferIndex; + glyph.curveCount = temp_buffer_glyph.count; + glyphs[glyph_id] = glyph; + } } - temp_buffer_glyph.count = static_cast(buffer_curves.size()) - temp_buffer_glyph.start; + if(!bold_glyphs.contains(glyph_id)) { + map_font_buffer_glyph temp_buffer_glyph; + temp_buffer_glyph.start = static_cast(buffer_bold_curves.size()); + + FT_Load_Glyph(bold_face, glyph_id, FT_LOAD_NO_BITMAP); + if(!(FT_Outline_Get_Orientation(&bold_face->glyph->outline) == FT_ORIENTATION_NONE)) { + //FT_BBox bbox; + //auto error = FT_Outline_Get_BBox(&bold_face->glyph->outline, &bbox); + //assert(error == 0); + auto error = FT_Outline_Embolden(&bold_face->glyph->outline, dr_size * map_outline_embolden); + assert(error == 0); + } + short start = 0; + for(int i = 0; i < bold_face->glyph->outline.n_contours; i++) { + // Note: The end indices in face->glyph->outline.contours are inclusive. + convert_contour(buffer_bold_glyphs, buffer_bold_curves, &bold_face->glyph->outline, start, bold_face->glyph->outline.contours[i]); + start = bold_face->glyph->outline.contours[i] + 1; + } - int32_t bufferIndex = static_cast(buffer_glyphs.size()); - buffer_glyphs.push_back(temp_buffer_glyph); + temp_buffer_glyph.count = static_cast(buffer_bold_curves.size()) - temp_buffer_glyph.start; - map_font_glyph glyph; + int32_t bufferIndex = static_cast(buffer_bold_glyphs.size()); + buffer_bold_glyphs.push_back(temp_buffer_glyph); - glyph.ft_height = face->glyph->metrics.height; - glyph.ft_width = face->glyph->metrics.width; - glyph.ft_x_bearing = face->glyph->metrics.horiBearingX; - glyph.ft_y_bearing = face->glyph->metrics.horiBearingY; + { + map_font_glyph glyph; + + glyph.ft_height = bold_face->glyph->metrics.height; + glyph.ft_width = bold_face->glyph->metrics.width; + glyph.ft_x_bearing = bold_face->glyph->metrics.horiBearingX; + glyph.ft_y_bearing = bold_face->glyph->metrics.horiBearingY; + + glyph.bufferIndex = bufferIndex; + glyph.curveCount = temp_buffer_glyph.count; + bold_glyphs[glyph_id] = glyph; + } + } - glyph.bufferIndex = bufferIndex; - glyph.curveCount = temp_buffer_glyph.count; - glyphs[glyph_id] = glyph; upload_buffers(); } @@ -1299,8 +1395,23 @@ void map_font::upload_buffers() { glBindBuffer(GL_TEXTURE_BUFFER, curve_buffer); glBufferData(GL_TEXTURE_BUFFER, sizeof(map_font_buffer_curve) * buffer_curves.size(), buffer_curves.data(), GL_STATIC_DRAW); glBindBuffer(GL_TEXTURE_BUFFER, 0); + + + glBindBuffer(GL_TEXTURE_BUFFER, bold_glyph_buffer); + glBufferData(GL_TEXTURE_BUFFER, sizeof(map_font_buffer_glyph) * buffer_bold_glyphs.size(), buffer_bold_glyphs.data(), GL_STATIC_DRAW); + glBindBuffer(GL_TEXTURE_BUFFER, 0); + + glBindBuffer(GL_TEXTURE_BUFFER, bold_curve_buffer); + glBufferData(GL_TEXTURE_BUFFER, sizeof(map_font_buffer_curve) * buffer_bold_curves.size(), buffer_bold_curves.data(), GL_STATIC_DRAW); + glBindBuffer(GL_TEXTURE_BUFFER, 0); } -void map_font::convert_contour(const FT_Outline* outline, int32_t firstIndex, int32_t lastIndex) { +void convert_contour( + std::vector& buffer_glyphs, + std::vector& buffer_curves, + const FT_Outline* outline, + int32_t firstIndex, + int32_t lastIndex +) { if(firstIndex == lastIndex) return; short dIndex = 1; @@ -1323,7 +1434,7 @@ void map_font::convert_contour(const FT_Outline* outline, int32_t firstIndex, in }; auto makeCurve = [](const glm::vec2& p0, const glm::vec2& p1, const glm::vec2& p2) { - map_font_buffer_curve result; + map_font::map_font_buffer_curve result; result.x0 = p0.x; result.y0 = p0.y; result.x1 = p1.x; diff --git a/src/text/fonts.hpp b/src/text/fonts.hpp index bd64364d41..3316911df4 100644 --- a/src/text/fonts.hpp +++ b/src/text/fonts.hpp @@ -16,6 +16,7 @@ namespace text { inline constexpr uint32_t max_texture_layers = 256; inline constexpr int magnification_factor = 4; inline constexpr int dr_size = 64 * magnification_factor; +inline constexpr int map_outline_embolden = 4; enum class font_selection { body_font, @@ -156,10 +157,14 @@ class map_font { std::vector buffer_glyphs; std::vector buffer_curves; + std::vector buffer_bold_glyphs; + std::vector buffer_bold_curves; ankerl::unordered_dense::map glyphs; + ankerl::unordered_dense::map bold_glyphs; std::unique_ptr file_data; FT_Face face = nullptr; + FT_Face bold_face = nullptr; hb_font_t* hb_font_face = nullptr; hb_buffer_t* hb_buf = nullptr; @@ -168,6 +173,11 @@ class map_font { GLuint glyph_buffer = 0; GLuint curve_buffer = 0; + GLuint bold_glyph_texture = 0; + GLuint bold_curve_texture = 0; + GLuint bold_glyph_buffer = 0; + GLuint bold_curve_buffer = 0; + // The glyph quads are expanded by this amount to enable proper // anti-aliasing. Value is relative to emSize. float dilation = 0; @@ -181,7 +191,7 @@ class map_font { ~map_font(); void make_glyph(uint32_t glyph_id); void upload_buffers(); - void convert_contour(const FT_Outline* outline, int32_t firstIndex, int32_t lastIndex); + //void convert_contour(const FT_Outline* outline, int32_t firstIndex, int32_t lastIndex); void load_font(FT_Library& ft_library, char const* file_data, uint32_t file_size); void ready_textures(); void remake_map_cache(sys::state& state, stored_glyphs& txt, std::string const& source);