Hm, so fixing all the glitches with transparent tiles ended up needing to burn two vertices:
s_layer_groups[i] = new_group(get_default_shader());
layer_shape = new_shape(SHAPE_TRIANGLE_STRIP, get_atlas_image(tile_atlas));
for (y = 0; y < s_map->height; ++y) for (x = 0; x < s_map->width; ++x) {
tile = s_map->layers[i].tilemap[x + y * s_map->layers[i].width];
uv = get_atlas_uv(tile_atlas, tile.tile_index);
x1 = x * tile_width; x2 = x1 + tile_width;
y1 = y * tile_height; y2 = y1 + tile_height;
add_shape_vertex(layer_shape, vertex(x1, y1, uv.x1, uv.y1, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x2, y1, uv.x2, uv.y1, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x1, y2, uv.x1, uv.y2, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x2, y2, uv.x2, uv.y2, rgba(255, 255, 255, 255)));
if (x < s_map->layers[i].width - 1) {
tile = s_map->layers[i].tilemap[(x + 1) + y * s_map->layers[i].width];
uv = get_atlas_uv(tile_atlas, tile.tile_index);
add_shape_vertex(layer_shape, vertex(x2, y2, uv.x1, uv.y1, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x2, y1, uv.x1, uv.y1, rgba(255, 255, 255, 255)));
}
}
upload_shape(layer_shape);
add_group_shape(s_layer_groups[i], layer_shape);
free_shape(layer_shape);
At this point I may as well just use a list instead of a strip, I think.