godot-proposals icon indicating copy to clipboard operation
godot-proposals copied to clipboard

RandomTexture2D and RandomTexture3D

Open Shadowblitz16 opened this issue 1 year ago • 2 comments

Describe the project you are working on

I am trying to write a voxel game with procedural generation However I wanted to support block texture variants in my game.

Describe the problem or limitation you are having in your project

There is no way to randomize a texture in 2d and 3d

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Implement RandomTexture2D and RandomTexture3D

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

I tried to build it in C# but ran into issues when trying to compile the resource.

Here is an example of how I tried to implement it.

[Tool]
[GlobalClass]
public partial class RandomTexture : Texture2D
{
    [Export] public Array<Texture2D> Textures
    {
        get => _textures;
        set
        {
            if (_textures == value) return;
            
            var randomize = _textures != null;
            _textures = value;

            if (randomize) Randomize();
            EmitChanged();
        }
    }

    public RandomTexture()
    {
        if (_empty == null)
        {
            var image = Image.CreateEmpty(1, 1, false, Image.Format.Rgb8);
            _empty = ImageTexture.CreateFromImage(image);
        }
        
        Randomize();
    }

    private static ImageTexture _empty;
    private Texture2D           _texture  = null;
    private Array<Texture2D>    _textures = new();
    
    private void Randomize()
    {
        if (_textures == null || _textures.Count == 0) return;
        _texture = Textures[GD.RandRange(0, Textures.Count)];
    }

    public override int _GetWidth()
    {
        if (_texture == null) return _empty._GetWidth();
        return _texture._GetWidth();
    }
    
    public override int _GetHeight()
    {
        if (_texture == null) return _empty._GetHeight();
        return _texture._GetHeight();
    }

    public override bool _HasAlpha()
    {
        if (_texture == null) return _empty._HasAlpha();
        return _texture._HasAlpha();
    }

    public override bool _IsPixelOpaque(int x, int y)
    {
        if (_texture == null) return _empty._IsPixelOpaque(x, y);
        return _texture._IsPixelOpaque(x, y);
    }

    public override void _Draw(Rid toCanvasItem, Vector2 pos, Color modulate, bool transpose)
    {
        if (_texture == null)
        {
            _empty.Draw(toCanvasItem, pos, modulate, transpose);
            return;
        };
        _texture._Draw(toCanvasItem, pos, modulate, transpose);
    }

    public override void _DrawRect(Rid toCanvasItem, Rect2 rect, bool tile, Color modulate, bool transpose)
    {
        if (_texture == null)
        {
            _empty._DrawRect(toCanvasItem, rect, tile, modulate, transpose);
            return;
        }
        _texture._DrawRect(toCanvasItem, rect, tile, modulate, transpose);
    }

    public override void _DrawRectRegion(Rid toCanvasItem, Rect2 rect, Rect2 srcRect, Color modulate, bool transpose, bool clipUv)
    {
        if (_texture == null)
        {
            _empty._DrawRectRegion(toCanvasItem, rect, srcRect, modulate, transpose, clipUv);
            return;
        }
        _texture._DrawRectRegion(toCanvasItem, rect, srcRect, modulate, transpose, clipUv);
    }
}

If this enhancement will not be used often, can it be worked around with a few lines of script?

This may not be possible in a way that encapsulates the random chosen texture If Texture2D can't be extended then it forces script end users to use something like ImageTexture which ultimately exposes a way to mutate the random chosen image.

Also users might want to ensure textures are the same size, which I have no idea on how to enforce.

Is there a reason why this should be core and not an add-on in the asset library?

Its useful for texture variants

Shadowblitz16 avatar Sep 15 '24 00:09 Shadowblitz16

I don't think this makes much sense as a texture resource, since resources are made to be shared and cached, so your random choice would be global by default.

This is usually done by having a texture atlas (see AtlasTexture), or randomly offsetting the UV coordinates.

RedMser avatar Sep 15 '24 01:09 RedMser

Regardless of the above reasoning, I don't see a reason to have this in core, given the specific niche it fulfills. Sounds like a cool addon, though.

Mickeon avatar Sep 16 '24 11:09 Mickeon