godot icon indicating copy to clipboard operation
godot copied to clipboard

TextureRect's tiling stretch mode incompatible with atlas textures

Open SnowyStoat opened this issue 1 month ago • 4 comments

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)

texture-repeat-test.zip

SnowyStoat avatar Dec 06 '25 18:12 SnowyStoat

This is documented in AtlasTexture but should probably be mentioned in other places too like the stretch mode property description

AThousandShips avatar Dec 07 '25 10:12 AThousandShips

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 avatar Dec 07 '25 11:12 kleonc

@AThousandShips I'm more curious about why this works the way it does. Is this related to how AtlasTextures are implemented?

SnowyStoat avatar Dec 07 '25 12:12 SnowyStoat

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.

arkology avatar Dec 08 '25 09:12 arkology

@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. 😄

kleonc avatar Dec 09 '25 16:12 kleonc