104 lines
5.2 KiB
Plaintext
104 lines
5.2 KiB
Plaintext
shader_type canvas_item;
|
|
uniform sampler2D tile_atlas : hint_normal;
|
|
uniform sampler2D palette : hint_normal;
|
|
uniform float palette_rows = 8.0; // 128 colours
|
|
// uniform float tile_width = 8.0;
|
|
uniform float tilemap_width = 512.0; // Require square tilemap for now
|
|
uniform float uv_scale = 512.0; // Require square tilemap for now
|
|
uniform bool enable_sea_scroll = true; // Disable on underwater maps (tileset 3)
|
|
uniform bool enable_waterfall_scroll = true; // Only enable on tileset 1
|
|
uniform bool enable_tile_globbing = false; // hack for minimap
|
|
uniform float timescale_desert = 7.5;
|
|
uniform float timescale_sea = 0.234375; // 60.0/256.0
|
|
uniform float timescale_waterfall = 1.3;
|
|
// uniform vec2 subtile_offset = vec2(0.0);
|
|
const float index_scale = 255.0 / 16.0;
|
|
const vec2 sea_tile_uv = vec2(2.0, 6.0)/16.0;
|
|
const vec2 sea_tile_uv_end = vec2(4.0, 8.0)/16.0;
|
|
const vec2 sea_tile_size = sea_tile_uv_end - sea_tile_uv;
|
|
const vec2 sea_tile_size_inv = 1.0/sea_tile_size;
|
|
const vec2 waterfall_tile_uv = vec2(7.0, 8.0)/16.0;
|
|
const vec2 waterfall_tile_uv_end = vec2(9.0, 9.0)/16.0;
|
|
const vec2 waterfall_tile_uv_end_hack = vec2(9.0, 10.0)/16.0; // we hack a second row in the atlas
|
|
const vec2 waterfall_tile_size = waterfall_tile_uv_end - waterfall_tile_uv;
|
|
const vec2 waterfall_tile_size_inv = 1.0/waterfall_tile_size;
|
|
const float desert_pal_row_int = 5.0;
|
|
const float desert_pal_col_start = 1.0/16.0;
|
|
const float desert_pal_col_end = 5.0/16.0;
|
|
const float desert_pal_col_range = desert_pal_col_end - desert_pal_col_start + 1.0/16.0;
|
|
|
|
// This shader maps from tileID texels to Tiles, and then applies palette.
|
|
// tiles hardcoded to 16x16 tiles for now
|
|
// palette hardcoded to 16x16 colors for now
|
|
|
|
vec2 get_tile_atlas_uv(float tile_id, vec2 uv) {
|
|
float tile_idx16 = tile_id * index_scale; // Rescale from [0.0, 1.0] to [0, 255] to [0, 15.9375 (15+15/16)]
|
|
float tile_row = trunc(tile_idx16) / 16.0;
|
|
float tile_col = fract(tile_idx16);
|
|
vec2 tile_uv = vec2(tile_col, tile_row); // Convert 15.9375 to vec2(0.9375==15/16, (15)/16), this should result in integer coordinates in [0,15] scaled to [0,15/16] for UV
|
|
// vec2 sub_tile_uv = fract((uv * uv_scale) + subtile_offset) / 16.0;
|
|
vec2 sub_tile_uv = fract(uv * uv_scale) / 16.0;
|
|
|
|
vec2 out_uv = tile_uv + sub_tile_uv;
|
|
|
|
// Sea HScroll tile UV modulation
|
|
// Make this branchless later
|
|
if (enable_sea_scroll && all(greaterThanEqual(out_uv, sea_tile_uv)) && all(lessThan(out_uv, sea_tile_uv_end))) {
|
|
vec2 sea_tile_uv_diff = out_uv - sea_tile_uv;
|
|
vec2 pos = sea_tile_uv_diff * sea_tile_size_inv;
|
|
// Silly warpy effects
|
|
// pos.x = fract(pos.x + fract(TIME*0.4) + cos(pos.y*0.5) + 20.0*sin(uv.y*20.0));
|
|
// pos.x = fract(pos.x + fract(TIME*0.4) + 20.0*sin(uv.y*20.0));
|
|
// pos.y = fract(pos.y + 0.5*sin(sin(TIME*0.5 + 32.0*uv.x)));
|
|
|
|
// Real animation seems to scroll in a cycle of shift row 1px, next frame, increment by 9 rows, shift that one, repeat
|
|
// i.e. every 16 frames it will have scrolled all rows 1px
|
|
// every 256 frames it will have performed a full scroll
|
|
// At 60fps this is one full scroll every 4.266... seconds
|
|
pos.x = fract(pos.x - TIME*timescale_sea + (pos.y * (9.0/16.0)));
|
|
out_uv = mix(sea_tile_uv, sea_tile_uv_end, pos);
|
|
}
|
|
// Waterfall VScroll UV modulation
|
|
// This has a super annoying property of being a 2tall tile scroll laid out horizontally
|
|
// In sprite_loader we hack the atlas such that we can just vscroll each one
|
|
if (enable_waterfall_scroll && all(greaterThanEqual(out_uv, waterfall_tile_uv)) && all(lessThan(out_uv, waterfall_tile_uv_end))) {
|
|
vec2 waterfall_tile_uv_diff = out_uv - waterfall_tile_uv;
|
|
vec2 pos = waterfall_tile_uv_diff * waterfall_tile_size_inv;
|
|
// Real animation seems to scroll in a cycle of shift col 1px, next frame, shift col 1px, next frame, increment by 4 rows twice, shift that one, repeat
|
|
// 1, 1, 5, 5, 3, 3, 7, 7, 2, 2, 6, 6, 4, 4, 8, 8
|
|
// For now, don't bother about the double increment
|
|
pos.y = fract((pos.y - TIME*timescale_waterfall + (pos.x * (4.5/32.0))) * 0.5);
|
|
out_uv = mix(waterfall_tile_uv, waterfall_tile_uv_end_hack, pos);
|
|
}
|
|
|
|
return out_uv;
|
|
}
|
|
|
|
void fragment() {
|
|
// GLES2
|
|
// vec2 uv_tile = trunc(UV * tilemap_width) / tilemap_width;
|
|
vec2 uv_tile = UV;
|
|
if (enable_tile_globbing) {
|
|
uv_tile += step(0.5, fract(UV*0.5*uv_scale))/tilemap_width;
|
|
}
|
|
float tile_id = texture(TEXTURE, uv_tile).r;
|
|
|
|
// TODO: move cycling palette to a sampler2DArray or sampler3D rather than rebinding
|
|
|
|
vec2 lut_uv = get_tile_atlas_uv(tile_id, UV);
|
|
float color_id = texture(tile_atlas, lut_uv).r;
|
|
float color_idx16 = color_id * index_scale;
|
|
float pal_row_int = trunc(color_idx16);
|
|
float pal_row = pal_row_int / palette_rows;
|
|
float pal_col = fract(color_idx16);
|
|
// Scroll #1~#5 for moving desert every 8 frames at 60fps (7.5Hz)
|
|
if (enable_waterfall_scroll && pal_row_int == desert_pal_row_int && pal_col >= desert_pal_col_start && pal_col <= desert_pal_col_end) {
|
|
pal_col = mod(pal_col + trunc(TIME*timescale_desert), desert_pal_col_range) + desert_pal_col_start;
|
|
}
|
|
// World 1 also has row #6 cols #12 and #13 swap every 5 frames at 60fps (12Hz) (world 2 portal)
|
|
vec2 palette_uv = vec2(pal_col, pal_row);
|
|
COLOR.rgb = texture(palette, palette_uv).rgb;
|
|
|
|
COLOR.a *= step(0.000001, color_idx16); // Branchless transparency
|
|
}
|