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 vec2 tilemap_size = vec2(32.0, 20.0); // Battle tilemaps are 32x20, zone tilemaps are larger (64x64?) const float INDEX_SCALE = 255.0 / 16.0; // This shader maps from tileID texels to Tiles, and then applies 4bpp (16-colour) palette. // tile_atlas hardcoded to 16x16 tiles for now const float ATLAS_SIZE = 16.0; // palette hardcoded to 16 columns of colors for now void fragment() { // GLES2 vec2 xy = UV * tilemap_size; // Texel-space coord of our texture vec2 t = texture(TEXTURE, UV).ra; int tile_idx = int(t.x * 255.0); // Luminosity channel (any RGB channel works) int palette_idx = int(t.y * 255.0); // Alpha channel // Extract flip bits from palette_idx byte int v_flip = (palette_idx / 128); palette_idx = palette_idx % 128; int h_flip = (palette_idx / 64); palette_idx = palette_idx % 64; // Convert tile_idx to a texel coordinate, then to a UV coordinate ivec2 tile_xy = ivec2(tile_idx%16, tile_idx/16); vec2 tile_uv = vec2(tile_xy)/16.0; // Get sub-tile UV vec2 sub_tile_uv = fract(xy); sub_tile_uv.x = mix(sub_tile_uv.x, 1.0 - sub_tile_uv.x, float(h_flip)); // Branchless mirroring, maybe test perf against branched version at some point sub_tile_uv.y = mix(sub_tile_uv.y, 1.0 - sub_tile_uv.y, float(v_flip)); vec2 lut_uv = tile_uv + (sub_tile_uv/ATLAS_SIZE); // TODO: move cycling palette to a sampler2DArray or sampler3D rather than rebinding float color_id = texture(tile_atlas, lut_uv).r; float color_idx16 = color_id * INDEX_SCALE; float pal_col = fract(color_idx16); float pal_row = float(palette_idx); COLOR = texture(palette, vec2(pal_col, pal_row)); // COLOR.a = step(0.000001, color_idx16); // Branchless transparency }