I've been able to implement 9Slice for textures when the texture is just itself, that means, it does not contain any subtexture except itself. This is the code I'm using:
#version 330 core
in vec2 uv;
in vec4 color;
in vec2 size;
in vec4 nine_slice;
uniform sampler2D tex;
uniform float dt;
uniform vec2 mouse_position;
uniform vec2 texture_size;
layout(location = 0) out vec4 out_color;
vec2 uv9slice(vec2 uv, vec2 s, vec4 b) {
vec2 t = clamp((s * uv - b.xy) / (s - b.xy - b.zw), 0., 1.);
return mix(uv * s, 1. - s * (1. - uv), t);
}
vec4 draw_nine_slice() {
vec2 _s = size.xy / texture_size;
vec4 _b = nine_slice / texture_size.xxyy;
vec2 _uv = uv9slice(uv, _s, _b);
vec3 _col = vec3(texture(tex, _uv).x);
return vec4(_col, 1.0);
}
void main(void) {
out_color = draw_nine_slice();
}
Where vars are:
- size: Size of the nine patch (the imgui window shown on the second image)
- nine_slice: These are the paddings vec4(left, right, bottom, top)
- texture_size: This is the size of the texture, which will be fixed. If it is a non-atlas texture, the size is the size of the whole image, if it is a subtexture from an atlas, the size will be the size of the subtexture inside the atlas. This is the texture:
And this is how it looks with the 9-Slice:
Working example of single texture 9-Slice
So as you can see, it expands nicely. The problem comes when I use my texture atlas (change the white color of the sub textures with gray, as they could not be seen on this site white background):
I have a way in my program to get the texture coordinates, size and everything from each subtexture in the atlas, and when I render them as a non-9Slice it works great, the problem is the shader. If I try to run the engine with a subtexture from the atlas, the shader I posted before won't work, as it takes the whole texture as if it was a single one (which works on the previuos case but not this one). This is what happens when running with the shader:
Which make sense (don't mind the transparency not working). I know I have to change how the coordinates are calculated in the shader to be relative to the sub-texture coordinates, but I cannot really seem to understand how to do it, this is what I tried:
#version 330 core
in vec2 uv;
in vec4 color;
in vec2 size;
in vec4 nine_slice;
uniform sampler2D tex;
uniform float dt;
uniform vec2 mouse_position;
uniform vec2 texture_size; // If drawing a subtexture from an atlas, this is the size of the subtexture
layout(location = 0) out vec4 out_color;
vec2 uv9slice(vec2 _uv, vec2 _s, vec4 _b) {
vec2 _t = clamp((_s * _uv - _b.xz) / (_s - _b.xz - _b.yw), 0.0, 1.0);
vec2 _t_0 = _uv * _s;
vec2 _t_1 = 1.0 - _s * (1.0 - _uv);
return clamp(mix(_t_0, _t_1, _t), vec2(0, 0.75), vec2(0.25, 1.0));
}
vec4 draw_nine_slice() {
vec2 _s = size.xy / texture_size;
vec4 _b = nine_slice / texture_size.xxyy;
vec2 _uv = uv9slice(uv, _s, _b);
vec3 _col = vec3(texture(tex, _uv).x);
return vec4(_col, 1.0);
}
void main(void) {
out_color = draw_nine_slice()
}
Values are hardcoded in here, because I'm doing a test. Bottom-Left coord of the texture in the atlas is (0.0, 0.75) and Top-Right is (0.25, 1.0).
I thought clamping the values within the sub-texture range would work, but it does not. I tried also other combinations but noting seems to be working, does anyone have an idea on how to achieve the first behaviour with the single texture on the second scenario with an atlas?
Tried different ways to convert from whole image coordinates to a sub-space of the image, one of the attempts is in the description of the issue, no success with any of them.