TextureRect's tiling stretch mode incompatible with atlas textures
Tested versions
Reproducible in: v4.5.1.stable.official [f62fdbde1], v4.2.1.stable.official [b09f793f5], v4.0.3.stable.official [5222a99f5]
System information
Godot v4.5.1.stable - Windows 10 (build 19045) - Multi-window, 1 monitor - OpenGL 3 (Compatibility) - NVIDIA GeForce GTX 1660 Ti (NVIDIA; 31.0.15.3667) - AMD Ryzen 7 3750H with Radeon Vega Mobile Gfx (8 threads) - 15.81 GiB memory
Issue description
The TextureRect node's stretch_mode property is supposed to control how the texture behaves when changing the rect's size, however, this doesn't quite apply when said texture is a part of an atlas. If stretch_mode is set to "Tile", the texture doesn't tile as would be expected and as it normally does when it's not an atlas texture; instead, it behaves the exact same way as with the Scale mode.
Steps to reproduce
Create a TextureRect, slap any atlas texture on it, set stretch mode to Tile, stretch the rect.
Minimal reproduction project (MRP)
This is documented in AtlasTexture but should probably be mentioned in other places too like the stretch mode property description
Note you could tile an AtlasTexture with a custom shader using REGION_RECT (see #90436), something like:
shader_type canvas_item;
instance uniform vec2 rect_size;
void fragment() {
vec2 region_position = REGION_RECT.xy;
vec2 region_size = REGION_RECT.zw;
vec2 region_uv = (UV - region_position) / region_size;
vec2 tiled_uv = region_position + mod(region_uv * rect_size * TEXTURE_PIXEL_SIZE, region_size);
COLOR = texture(TEXTURE, tiled_uv);
}
For TextureRect using it:
texture_rect.set_instance_shader_parameter("rect_size", texture_rect.size)
@AThousandShips I'm more curious about why this works the way it does. Is this related to how AtlasTextures are implemented?
Note you could tile an AtlasTexture with a custom shader using
REGION_RECT(see #90436), something like:shader_type canvas_item;
instance uniform vec2 rect_size;
void fragment() { vec2 region_position = REGION_RECT.xy; vec2 region_size = REGION_RECT.zw; vec2 region_uv = (UV - region_position) / region_size;
vec2 tiled_uv = region_position + mod(region_uv * rect_size * TEXTURE_PIXEL_SIZE, region_size);
COLOR = texture(TEXTURE, tiled_uv); } For TextureRect using it:
texture_rect.set_instance_shader_parameter("rect_size", texture_rect.size)
@kleonc BTW, is it allowed to integrate custom shaders into node source code (I mean, directly inside official Godot source)? So there will be no need for user to search and write solutions by themself, everything will just work out of the box.
@kleonc BTW, is it allowed to integrate custom shaders into node source code (I mean, directly inside official Godot source)? So there will be no need for user to search and write solutions by themself, everything will just work out of the box.
@arkology Good question but I'd rather ask whether it's viable/feasible, not if it's allowed (if it's "disallowed" then what's important is "why").
If you're asking literally "into node source code" (so e.g. directly into TextureRect.cpp) then I'd say no, as such added shader would basically still be a custom shader just like the one added by the user, meaning if the user would actually add their own custom shader then it would replace the "built-in custom one".
The built-in support should rather work with custom shaders, which means what would need to be modified is the canvas shader to which the custom shaders are injected into (see the #CODE tags).
How feasible is this I'm not sure, would need to ask the rendering team.
And now I've realized that nine-patch is already supported by the mentioned built-in canvas shader, it's exposed to the RenderingServer, and... it already supports tiling a region. Meaning we can add support for tiling an AtlasTexture in TextureRect by simply drawing it as a nine-patch behind the scenes, without any modifications to the rendering server / shaders. I've opened a PR: #113808. Sometimes just asking a question is enough to lead into a direction. 😄