aboutsummaryrefslogtreecommitdiff
path: root/game/src/Game/GameSession/TerrainMap.gdshader
blob: 98f9efd6ba59d69273841bc40c01cc1d61b45642 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
shader_type spatial;

render_mode unshaded;

#include "res://src/Game/GameSession/ProvinceIndexSampler.gdshaderinc"

// The samplers below do not have the source_color hint because we do not want them
// to be converted from sRGB to linear colour space, we do that manually at the end.

// Province colour texture
uniform sampler2D province_colour_tex: repeat_enable, filter_nearest;
// Index of the mouse over the map mesh
uniform uint hover_index;
// Index of the currently selected province
uniform uint selected_index;
// How much to mix between the near detailed map (0) and the far parchment map (1)
uniform float parchment_mix;
// Cosmetic terrain textures
uniform sampler2DArray terrain_tex: repeat_enable, filter_linear;
// Factor converting pixels to terrain tile size
uniform float terrain_tile_factor;
// Map stripe mask texture
uniform sampler2D stripe_tex: repeat_enable, filter_linear;
// Factor converting pixels to stripe tile size
uniform float stripe_tile_factor;
// Map parchment overlay texture
uniform sampler2D overlay_tex: repeat_enable, filter_linear;
// Factor converting pixels to overlay tile size
uniform float overlay_tile_factor;
// Land map tint
uniform sampler2D colormap_land_tex: repeat_enable, filter_linear;
// Water map tint
uniform sampler2D colormap_water_tex: repeat_enable, filter_linear;
// Overlay map tint
uniform sampler2D colormap_overlay_tex: repeat_enable, filter_linear;

struct terrain_args_t {
   vec2 uv, half_pixel_size; // Components for calculating terrain sampling UV
   vec2 terrain_uv; // UV coordinates scaled for terrain texture tiling
   vec3 land_tint_colour, water_tint_colour; // Colours for tinting the terrain
};

// Calculate terrain colour at the specified corner of the current pixel
vec3 get_terrain_colour(const terrain_args_t terrain_args, const vec2 corner) {
   // Find the terrain index at the specified corner of the current pixel
   uint terrain_index = read_uvec3(fma(corner, terrain_args.half_pixel_size, terrain_args.uv)).z;

   // Get the tinted land colour at the current position
   vec3 land_colour = texture(terrain_tex, vec3(terrain_args.terrain_uv, float(terrain_index))).rgb;
   land_colour = mix(land_colour, terrain_args.land_tint_colour, 0.3);

   // TODO - proper water texture
   vec3 water_colour = terrain_args.water_tint_colour;

   // Select land or water colour based on the terrain index (0 is water, otherwise land)
   vec3 terrain_colour = mix(land_colour, water_colour, float(terrain_index == 0u));

   return terrain_colour;
}

// Blend together terrain colours from the four corners of the current pixel
vec3 mix_terrain_colour(const terrain_args_t terrain_args, const vec2 pixel_offset) {
   return mix(
      mix(get_terrain_colour(terrain_args, vec2(-1, -1)), get_terrain_colour(terrain_args, vec2(+1, -1)), pixel_offset.x),
      mix(get_terrain_colour(terrain_args, vec2(-1, +1)), get_terrain_colour(terrain_args, vec2(+1, +1)), pixel_offset.x),
      pixel_offset.y
   );
}

// Mix overlay and base colours, used for the parchment map
vec3 mix_overlay(const vec3 overlay_colour, const vec3 base_colour) {
   return mix(
      2.0 * overlay_colour * base_colour,
      1.0 - 2.0 * (1.0 - overlay_colour) * (1.0 - base_colour),
      greaterThanEqual(overlay_colour.rrb, vec3(0.5))
   );
}

// Calculate map colour at specified UV coordinates, combining terrain, province base and stripe colour,
// parchment overlay, and highlighting the result if it is a hovered over and/or selected province.
vec3 get_map_colour(vec2 uv) {
   vec2 map_size = vec2(textureSize(province_shape_tex, 0).xy) * province_shape_subdivisions;
   vec2 uv_map_pixels = fma(uv, map_size, vec2(0.5));
   // Offset of uv_map_pixels from the top left corner of the current pixel
   vec2 pixel_offset = fract(uv_map_pixels);

   terrain_args_t terrain_args;
   terrain_args.uv = uv;
   terrain_args.half_pixel_size = 0.49 / map_size;
   // Terrain texture tiling UV
   terrain_args.terrain_uv = 0.5 - uv_map_pixels * terrain_tile_factor;

   vec2 colormap_uv = vec2(uv.x, 1.0 - uv.y);
   // Terrain tinting colours
   terrain_args.land_tint_colour = texture(colormap_land_tex, colormap_uv).rgb;
   terrain_args.water_tint_colour = texture(colormap_water_tex, colormap_uv).rgb;
   // Parchment tint colour
   vec3 overlay_tint_colour = texture(colormap_overlay_tex, colormap_uv).rgb;

   // Blended terrain colour (average of four corners of current pixel)
   vec3 terrain_colour = mix_terrain_colour(terrain_args, pixel_offset);

   vec2 stripe_uv = uv_map_pixels * stripe_tile_factor;
   // Stripe mask value - between 0 (base) and 1 (stripe)
   float stripe_mask = texture(stripe_tex, stripe_uv).b;

   vec2 overlay_uv = vec2(uv_map_pixels.x, map_size.y - uv_map_pixels.y) * overlay_tile_factor;
   // Parchment overlay colour
   vec3 overlay_colour = texture(overlay_tex, overlay_uv).rgb;

   // Current province index as a pair of byte coordinates and as a combined 16 bit value
   uvec2 province_data = read_uvec3(uv).xy;
   uint province_index = uvec2_to_uint(province_data);

   // Get base and stripe colours for province at the current position
   province_data.x *= 2u; // Double "x coordinate" as colours come in (base, stripe) pairs
   vec4 province_base_colour = texelFetch(province_colour_tex, ivec2(province_data), 0);
   province_data.x += 1u; // Add 1 to "x coordinate" to move from base to strip colour
   vec4 province_stripe_colour = texelFetch(province_colour_tex, ivec2(province_data), 0);
   // Blend the base and stripe colours according to the current position's stripe mask value
   vec4 province_colour = mix(province_base_colour, province_stripe_colour, stripe_mask);
   // Darken the province colour
   province_colour.rgb -= 0.7;

   // Rec.709 luma coefficients
   const vec3 luma_weights = vec3(0.2126, 0.7152, 0.0722);
   float terrain_luma = dot(terrain_colour, luma_weights);

   // Near colour is either the terrain's luma component tinted with the province colour and brightened,
   // or the normal terrain colour
   vec3 near_province_colour = mix(vec3(terrain_luma), province_colour.rgb, 0.3) * 1.5;
   vec3 near_colour = mix(near_province_colour, terrain_colour, float(province_colour.a == 0.0));

   // Far colour is either the province colour mixed with the overlay texture, tinted with the overlay colormap and brightened,
   // or the normal terrain colour mixed with the overlay texture (primarily for water)
   vec3 far_province_colour = mix_overlay(overlay_colour, province_colour.rgb);
   far_province_colour = mix(overlay_tint_colour, far_province_colour, 0.3) * 1.5;
   vec3 far_terrain_colour = mix_overlay(overlay_colour, terrain_colour);
   vec3 far_colour = mix(far_province_colour, far_terrain_colour, float(province_colour.a == 0.0));

   // Blend the near (detailed terrain) and far (parchment) colours according to the parchment mix factor (0 for near, 1 for far)
   vec3 final_colour = mix(near_colour, far_colour, parchment_mix);

   // Significantly brighted the colour if it is hovered over and/or selected, but not if it has province index 0 (all invalid pixels)
   const vec3 highlight_colour = vec3(1.0);
   float highlight_mix_val = 0.4 * (float(province_index == hover_index) + float(province_index == selected_index)) * float(province_index != 0u);
   vec3 highlighted_colour = mix(final_colour, highlight_colour, highlight_mix_val);

   return highlighted_colour;
}

// Convert from standard RGB to linear colour space (Godot requires the output to be linear)
vec3 srgb_to_linear(vec3 srgb) {
   return mix(
      srgb * (1.0 / 12.92),
      pow((srgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)),
      greaterThan(srgb, vec3(0.04045))
   );
}

void fragment() {

   // TODO - add borders, coastlines, rivers, fog of war

   ALBEDO = srgb_to_linear(get_map_colour(UV));
}