| 
 | 1 | +.. _doc_drawable_textures:  | 
 | 2 | + | 
 | 3 | +DrawableTextures  | 
 | 4 | +====================  | 
 | 5 | + | 
 | 6 | +DrawableTextures are a type of Texture2D with additional functions for modifying the texture via the GPU.  | 
 | 7 | + | 
 | 8 | +Basic Blit_Rect  | 
 | 9 | +---------------  | 
 | 10 | + | 
 | 11 | +The most basic operation on a drawable texture is blit_rect()  -   | 
 | 12 | +Blitting (copying) the whole of one texture into the given rectangle on the ``DrawableTexture``.   | 
 | 13 | + | 
 | 14 | +.. code-block:: gdscript  | 
 | 15 | +
  | 
 | 16 | +    texture.blit_rect(Rect2(20, 50, 60, 60), load("res://circle.svg"))  | 
 | 17 | +    texture.blit_rect(Rect2(20, 50, 60, 60), load("res://circle.svg"), Color.WHITE, 0)  | 
 | 18 | +
  | 
 | 19 | +The code above blits the circle texture into the rectangle (20, 50, 60, 60) on the ``DrawableTexture``.   | 
 | 20 | +(20,50) is the top left corner of the rectangle being drawn to, and (60, 60) is its size.   | 
 | 21 | +If you wanted to draw over the whole texture, simply match the Rect parameter to the ``DrawableTexture``'s size.  | 
 | 22 | + | 
 | 23 | +The next parameter in blit_rect() is an optional parameter, Modulate.   | 
 | 24 | +This is a color that the output is multiplied by (in the default behavior).   | 
 | 25 | +This can be used to recolor, or even mask the drawn output of blit_rect().   | 
 | 26 | +Using a modulate of Color(0, 0, 1, 0) for example, will only draw to and update the Blue values of each pixel on the DrawableTexture.  | 
 | 27 | + | 
 | 28 | +blit_rect()'s 4th parameter, Mipmap, can specify a mipmap level to draw to.   | 
 | 29 | +You only need to use this if you want very fine control over each mipmap layer.   | 
 | 30 | +Keep in mind, you do not need to adjust the rectangle size - it is converted to a portion of the textures whole size.   | 
 | 31 | +If you just want to update all the mipmap layers, you should draw to the texture, and then call generate_mipmaps() on it  | 
 | 32 | + | 
 | 33 | +Blend Modes & TextureBlit Shaders  | 
 | 34 | +---------------------------------  | 
 | 35 | + | 
 | 36 | +The drawing process for blit_rect() and DrawableTextures is governed by a TextureBlit GDShader.   | 
 | 37 | +Even when the user does not supply one, the engine has a default TextureBlit shader it uses.  | 
 | 38 | + | 
 | 39 | +.. code-block:: glsl  | 
 | 40 | +
  | 
 | 41 | +    // Default Texture Blit shader.  | 
 | 42 | +
  | 
 | 43 | +    shader_type texture_blit;  | 
 | 44 | +    render_mode blend_mix;  | 
 | 45 | +
  | 
 | 46 | +    uniform sampler2D source_texture0 : hint_blit_source0;  | 
 | 47 | +    uniform sampler2D source_texture1 : hint_blit_source1;  | 
 | 48 | +    uniform sampler2D source_texture2 : hint_blit_source2;  | 
 | 49 | +    uniform sampler2D source_texture3 : hint_blit_source3;  | 
 | 50 | +
  | 
 | 51 | +    void blit() {  | 
 | 52 | +        // Copies from each whole source texture to a rect on each output texture.  | 
 | 53 | +        COLOR0 = texture(source_texture0, UV) * MODULATE;  | 
 | 54 | +        COLOR1 = texture(source_texture1, UV) * MODULATE;  | 
 | 55 | +        COLOR2 = texture(source_texture2, UV) * MODULATE;  | 
 | 56 | +        COLOR3 = texture(source_texture3, UV) * MODULATE;  | 
 | 57 | +    }  | 
 | 58 | +
  | 
 | 59 | +The Blend_mode defines the how the output color value   | 
 | 60 | +is blended with the current color of the pixel on the ``DrawableTexture``.  | 
 | 61 | +While the engine defaults to Blend_Mix, these shaders also support   | 
 | 62 | +blend_disabled, blend_sub, blend_add, and blend_mul.  | 
 | 63 | + | 
 | 64 | +If you just want to use a different blend_mode than the default,   | 
 | 65 | +you can instantiate and pass in a new ``BlitMaterial`` in the GDScript code.  | 
 | 66 | + | 
 | 67 | +.. code-block:: gdscript  | 
 | 68 | +
  | 
 | 69 | +    var myMat = BlitMaterial.new()  | 
 | 70 | +    myMat.blend_mode = BlitMaterial.BLEND_MODE_DISABLED  | 
 | 71 | +    texture.blit_rect(Rect2(0, 0, 200, 200), load("res://icon.svg"), Color.WHITE, 0, myMat)  | 
 | 72 | +
  | 
 | 73 | +If you want more complicated behavior, then you can write your own TextureBlit Shader!  | 
 | 74 | +Create a new TextureBlit GDShader, write your Shader code, and load it into a material  | 
 | 75 | +to pass to the function. The Material is passed as a function parameter rather than bound  | 
 | 76 | +to the resource to make it easier to perform multiple different types of Draws to the same texture.  | 
 | 77 | + | 
 | 78 | +Blit_Rect Multi  | 
 | 79 | +---------------  | 
 | 80 | + | 
 | 81 | +DrawableTextures also have a Blit_Rect multi function,   | 
 | 82 | +to allow for up to 4 inputs and outputs in the same step.  | 
 | 83 | + | 
 | 84 | +.. code-block:: gdscript  | 
 | 85 | +
  | 
 | 86 | +	texture.blit_rect_multi(Rect2(0, 0, 200, 200), [load("res://icon.svg"), load("res://circle.svg")], [otherDrawTex])  | 
 | 87 | +
  | 
 | 88 | +The default behavior of this is to just match each input and output in order  | 
 | 89 | +which can be useful for, say, drawing to an Albedo, Normal, and Depth texture simultaneously.  | 
 | 90 | + | 
 | 91 | +Of course, this behavior too can be customized via the TextureBlit shader.  | 
 | 92 | +In the GDShader, the extra outputs are written to via COLOR1, COLOR2, and COLOR3  | 
 | 93 | +And the extra inputs can be read as uniforms with hint_blit_source1, hint_blit_source2, and hint_blit_source3  | 
 | 94 | + | 
 | 95 | +.. _doc_drawable_textures_example_1:  | 
 | 96 | + | 
 | 97 | +Example 1: Simple Painting  | 
 | 98 | +--------------------------  | 
 | 99 | +One of the most intuitive uses for DrawableTextures is for, well, drawing!   | 
 | 100 | +Its easier than ever to set up a canvas the user can paint.   | 
 | 101 | +For this example, were going to start a new project, and create   | 
 | 102 | +a new UI Scene with a Control Node at its root.   | 
 | 103 | +Next, you'll want to create a TextureRect Node which is going to be our user's canvas.   | 
 | 104 | +Size it appropriately for your screen, and then attach a new GDScript to it.  | 
 | 105 | +The start of this script should initialize the TextureRect's texture to a new DrawableTexture.  | 
 | 106 | + | 
 | 107 | +.. code-block:: gdscript  | 
 | 108 | +
  | 
 | 109 | +    extends TextureRect  | 
 | 110 | +
  | 
 | 111 | +    func _ready():  | 
 | 112 | +        texture = DrawableTexture2D.new()  | 
 | 113 | +        # Be Careful - if the dimensions of the Node != the setup size here  | 
 | 114 | +        # our draw call later will seem to happen at the wrong spot  | 
 | 115 | +        texture.setup(500, 500, DrawableTexture2D.DRAWABLE_FORMAT_RGBA8, false)  | 
 | 116 | +
  | 
 | 117 | +Next, we just need the TextureRect to respond to the player clicking and dragging as if they are painting!   | 
 | 118 | +To do this, we can connect the _on_gui_input() Signal from the TextureRect to our script,   | 
 | 119 | +and parse InputMouseButton and InputMouseMotion events  | 
 | 120 | + | 
 | 121 | +.. code-block:: gdscript  | 
 | 122 | +
  | 
 | 123 | +    var drawing: bool = false  | 
 | 124 | +
  | 
 | 125 | +    func _on_gui_input(event: InputEvent) -> void:  | 
 | 126 | +        if event is InputEventMouseButton:  | 
 | 127 | +            # Mouse click/unclick - start/stop drawing  | 
 | 128 | +            drawing = !drawing  | 
 | 129 | +        if event is InputEventMouseMotion and drawing:  | 
 | 130 | +            # Calculate rect to center our drawn rectangle on mouse position  | 
 | 131 | +            # instead of mouse at top left  | 
 | 132 | +            var p = event.position  | 
 | 133 | +            var rect: Rect2 = Rect2(p.x - 10, p.y - 10, 20, 20)  | 
 | 134 | +            texture.blit_rect(rect, null)  | 
 | 135 | +
  | 
 | 136 | +This should now draw black squares as you click and drag around the TextureRect.   | 
 | 137 | +For more natural drawing, we probably want to be drawing a circle shape, and actually coloring it!   | 
 | 138 | +We can adjust whats being drawn by using a Texture to copy from, and the modulate parameter.   | 
 | 139 | +I downloaded a plain white circle texture, which I load as the Texture parameter in Blit_Rect,   | 
 | 140 | +and use Red as my Modulate parameter.  | 
 | 141 | + | 
 | 142 | +.. code-block:: gdscript  | 
 | 143 | +
  | 
 | 144 | +    if event is InputEventMouseMotion and drawing:  | 
 | 145 | +        # Calculate rect to center our drawn rectangle on mouse position  | 
 | 146 | +        # instead of mouse at top left  | 
 | 147 | +        var p = event.position  | 
 | 148 | +        var rect: Rect2 = Rect2(p.x - 10, p.y - 10, 20, 20)  | 
 | 149 | +        texture.blit_rect(rect, load("res://circle.svg"), Color.RED)  | 
 | 150 | +
  | 
 | 151 | +Now the drawing looks much more natural and colorful!   | 
 | 152 | +To further customize this, you could connect a ColorPickerButton Node to the script   | 
 | 153 | +to store a users Color choice for the Modulate Parameter of Blit_Rect.   | 
 | 154 | +You could also store a Brush Size variable, give the user a way to adjust it,   | 
 | 155 | +and incorporate it into the Rectangle calculation so the user can draw bigger or smaller strokes.  | 
 | 156 | + | 
 | 157 | +.. code-block:: gdscript  | 
 | 158 | +
  | 
 | 159 | +    var drawing: bool = false  | 
 | 160 | +    var myColor: Color = Color.RED  | 
 | 161 | +    var mySize: float = 20.0  | 
 | 162 | +
  | 
 | 163 | +    func _on_gui_input(event: InputEvent) -> void:  | 
 | 164 | +        if event is InputEventMouseButton:  | 
 | 165 | +            # Mouse click/unclick - start/stop drawing  | 
 | 166 | +            drawing = !drawing  | 
 | 167 | +        if event is InputEventMouseMotion and drawing:  | 
 | 168 | +            # Calculate rect to center our drawn rectangle on mouse position  | 
 | 169 | +            # instead of mouse at top left  | 
 | 170 | +            var p = event.position  | 
 | 171 | +            var rect: Rect2 = Rect2(p.x - mySize/2, p.y - mySize/2, mySize, mySize)  | 
 | 172 | +            texture.blit_rect(rect, load("res://circle.svg"), myColor)  | 
 | 173 | +
  | 
 | 174 | +    func _on_color_picker_button_color_changed(color: Color) -> void:  | 
 | 175 | +        myColor = color  | 
 | 176 | +
  | 
 | 177 | +    func _on_h_slider_value_changed(value: float) -> void:  | 
 | 178 | +        mySize = value  | 
0 commit comments