Skip to content

Commit e76abba

Browse files
authored
GUI surface shader work (#2657)
* GUI surface shader work * Lazy update vertices + left/bottom fix * Type fix
1 parent 821712f commit e76abba

File tree

3 files changed

+75
-68
lines changed

3 files changed

+75
-68
lines changed

arcade/gui/surface.py

+63-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
from array import array
12
from contextlib import contextmanager
23
from typing import Generator
34

45
from PIL import Image
6+
from pyglet.math import Vec2, Vec4
57
from typing_extensions import Self
68

79
import arcade
810
from arcade import Texture
911
from arcade.camera import CameraData, OrthographicProjectionData, OrthographicProjector
1012
from arcade.color import TRANSPARENT_BLACK
11-
from arcade.gl import Framebuffer
13+
from arcade.gl import BufferDescription, Framebuffer
1214
from arcade.gui.nine_patch import NinePatchTexture
1315
from arcade.types import LBWH, RGBA255, Point, Rect
1416

@@ -37,6 +39,7 @@ def __init__(
3739
self._pos = position
3840
self._pixel_ratio = pixel_ratio
3941
self._pixelated = False
42+
self._area: Rect | None = None # Cached area for the last draw call
4043

4144
self.texture = self.ctx.texture(self.size_scaled, components=4)
4245
self.fbo: Framebuffer = self.ctx.framebuffer(color_attachments=[self.texture])
@@ -53,12 +56,17 @@ def __init__(
5356
*self.ctx.BLEND_DEFAULT,
5457
)
5558

56-
self._geometry = self.ctx.geometry()
59+
# 5 floats per vertex (pos 3f, tex 2f) with 4 vertices
60+
self._buffer = self.ctx.buffer(reserve=4 * 5 * 4)
61+
self._geometry = self.ctx.geometry(
62+
content=[BufferDescription(self._buffer, "3f 2f", ["in_pos", "in_uv"])],
63+
mode=self.ctx.TRIANGLE_STRIP,
64+
)
5765
self._program = self.ctx.load_program(
5866
vertex_shader=":system:shaders/gui/surface_vs.glsl",
59-
geometry_shader=":system:shaders/gui/surface_gs.glsl",
6067
fragment_shader=":system:shaders/gui/surface_fs.glsl",
6168
)
69+
self._update_geometry()
6270

6371
self._cam = OrthographicProjector(
6472
view=CameraData(),
@@ -228,6 +236,8 @@ def draw(
228236
area: Limit the area in the surface we're drawing
229237
(l, b, w, h)
230238
"""
239+
self._update_geometry(area=area)
240+
231241
# Set blend function
232242
blend_func = self.ctx.blend_func
233243
self.ctx.blend_func = self.blend_func_render
@@ -239,10 +249,7 @@ def draw(
239249
self.texture.filter = self.ctx.LINEAR, self.ctx.LINEAR
240250

241251
self.texture.use(0)
242-
self._program["pos"] = self._pos
243-
self._program["size"] = self._size
244-
self._program["area"] = (0, 0, *self._size) if not area else area.lbwh
245-
self._geometry.render(self._program, vertices=1)
252+
self._geometry.render(self._program)
246253

247254
# Restore blend function
248255
self.ctx.blend_func = blend_func
@@ -267,3 +274,52 @@ def resize(self, *, size: tuple[int, int], pixel_ratio: float) -> None:
267274
def to_image(self) -> Image.Image:
268275
"""Convert the surface to an PIL image"""
269276
return self.ctx.get_framebuffer_image(self.fbo)
277+
278+
def _update_geometry(self, area: Rect | None = None) -> None:
279+
"""
280+
Update the internal geometry of the surface mesh.
281+
282+
The geometry is a triangle strip with 4 vertices.
283+
"""
284+
if area is None:
285+
area = LBWH(0, 0, *self.size)
286+
287+
if self._area == area:
288+
return
289+
self._area = area
290+
291+
# Clamp the area inside the surface
292+
# This is the local area inside the surface
293+
_size = Vec2(*self.size)
294+
_pos = Vec2(*self.position)
295+
_area_pos = Vec2(area.left, area.bottom)
296+
_area_size = Vec2(area.width, area.height)
297+
298+
b1 = _area_pos.clamp(Vec2(0.0), _size)
299+
end_point = _area_pos + _area_size
300+
b2 = end_point.clamp(Vec2(0.0), _size)
301+
b = b2 - b1
302+
l_area = Vec4(b1.x, b1.y, b.x, b.y)
303+
304+
# Create the 4 corners of the rectangle
305+
# These are the final/global coordinates rendered
306+
p_ll = _pos + l_area.xy # type: ignore
307+
p_lr = _pos + l_area.xy + Vec2(l_area.z, 0.0) # type: ignore
308+
p_ul = _pos + l_area.xy + Vec2(0.0, l_area.w) # type: ignore
309+
p_ur = _pos + l_area.xy + l_area.zw # type: ignore
310+
311+
# Calculate the UV coordinates
312+
bottom = l_area.y / _size.y
313+
left = l_area.x / _size.x
314+
top = (l_area.y + l_area.w) / _size.y
315+
right = (l_area.x + l_area.z) / _size.x
316+
317+
# fmt: off
318+
vertices = array("f", (
319+
p_ll.x, p_ll.y, 0.0, left, bottom,
320+
p_lr.x, p_lr.y, 0.0, right, bottom,
321+
p_ul.x, p_ul.y, 0.0, left, top,
322+
p_ur.x, p_ur.y, 0.0, right, top,
323+
))
324+
# fmt: on
325+
self._buffer.write(vertices)

arcade/resources/system/shaders/gui/surface_gs.glsl

-60
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
#version 330
22

3+
uniform WindowBlock {
4+
mat4 projection;
5+
mat4 view;
6+
} window;
7+
8+
in vec3 in_pos;
9+
in vec2 in_uv;
10+
11+
out vec2 uv;
12+
313
void main() {
4-
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
14+
gl_Position = window.projection * window.view * vec4(in_pos, 1.0);
15+
uv = in_uv;
516
}

0 commit comments

Comments
 (0)