Skip to content

Commit 72599fc

Browse files
authored
Circle / ellipse no geo shader (#2715)
* Fix rect rotation * unbuffered ellipse shader: remove geometry shader * Remove unused in attribute * ellipse outline no geometry shader
1 parent cb5799f commit 72599fc

7 files changed

Lines changed: 105 additions & 177 deletions

File tree

arcade/context.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,10 @@ def __init__(
159159
self.shape_ellipse_filled_unbuffered_program: Program = self.load_program(
160160
vertex_shader=":system:shaders/shapes/ellipse/filled_unbuffered_vs.glsl",
161161
fragment_shader=":system:shaders/shapes/ellipse/filled_unbuffered_fs.glsl",
162-
geometry_shader=":system:shaders/shapes/ellipse/filled_unbuffered_geo.glsl",
163162
)
164163
self.shape_ellipse_outline_unbuffered_program: Program = self.load_program(
165164
vertex_shader=":system:shaders/shapes/ellipse/outline_unbuffered_vs.glsl",
166165
fragment_shader=":system:shaders/shapes/ellipse/outline_unbuffered_fs.glsl",
167-
geometry_shader=":system:shaders/shapes/ellipse/outline_unbuffered_geo.glsl",
168166
)
169167
self.shape_rectangle_filled_unbuffered_program = self.load_program(
170168
vertex_shader=":system:shaders/shapes/rectangle/filled_unbuffered_vs.glsl",
@@ -258,16 +256,10 @@ def __init__(
258256
],
259257
mode=self.TRIANGLE_STRIP,
260258
)
261-
# ellipse/circle filled
262-
self.shape_ellipse_unbuffered_buffer = self.buffer(reserve=8)
263-
self.shape_ellipse_unbuffered_geometry: Geometry = self.geometry(
264-
[BufferDescription(self.shape_ellipse_unbuffered_buffer, "2f", ["in_vert"])]
265-
)
266-
# ellipse/circle outline
267-
self.shape_ellipse_outline_unbuffered_buffer = self.buffer(reserve=8)
268-
self.shape_ellipse_outline_unbuffered_geometry: Geometry = self.geometry(
269-
[BufferDescription(self.shape_ellipse_outline_unbuffered_buffer, "2f", ["in_vert"])]
270-
)
259+
# ellipse/circle filled. Empty geometry. We generate it on the fly in the vertex shader.
260+
self.shape_ellipse_unbuffered_geometry: Geometry = self.geometry()
261+
# ellipse/circle outline. Empty geometry. We generate it on the fly in the vertex shader.
262+
self.shape_ellipse_outline_unbuffered_geometry: Geometry = self.geometry()
271263
# rectangle filled
272264
self.shape_rectangle_filled_unbuffered_buffer = self.buffer(reserve=8)
273265
# fmt: off

arcade/draw/circle.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import array
2-
3-
from arcade import gl
41
from arcade.types import Color, RGBOrA255
52
from arcade.window_commands import get_window
63

@@ -129,23 +126,31 @@ def draw_ellipse_filled(
129126
# Fail immediately if we have no window or context
130127
window = get_window()
131128
ctx = window.ctx
132-
ctx.enable(ctx.BLEND)
133129

134130
program = ctx.shape_ellipse_filled_unbuffered_program
135131
geometry = ctx.shape_ellipse_unbuffered_geometry
136-
buffer = ctx.shape_ellipse_unbuffered_buffer # type: ignore
137132

138133
# Normalize the color because this shader takes a float uniform
139134
color_normalized = Color.from_iterable(color).normalized
140135

136+
# Auto select number of segments if not specified
137+
if num_segments == -1:
138+
size = max(width, height)
139+
if size <= 12:
140+
num_segments = 6
141+
else:
142+
num_segments = int(size) // 2
143+
144+
if num_segments < 3:
145+
num_segments = 3
146+
141147
# Pass data to the shader
148+
program["center"] = center_x, center_y
142149
program["color"] = color_normalized
143150
program["shape"] = width / 2, height / 2, tilt_angle
144151
program["segments"] = num_segments
145-
buffer.orphan()
146-
buffer.write(data=array.array("f", (center_x, center_y)))
147-
148-
geometry.render(program, mode=gl.POINTS, vertices=1)
152+
ctx.enable(ctx.BLEND)
153+
geometry.render(program, mode=ctx.TRIANGLES, vertices=num_segments * 3)
149154
ctx.disable(ctx.BLEND)
150155

151156

@@ -190,20 +195,27 @@ def draw_ellipse_outline(
190195
ctx = window.ctx
191196
program = ctx.shape_ellipse_outline_unbuffered_program
192197
geometry = ctx.shape_ellipse_outline_unbuffered_geometry
193-
buffer = ctx.shape_ellipse_outline_unbuffered_buffer # type: ignore
194198

195199
# Normalize the color because this shader takes a float uniform
196200
color_normalized = Color.from_iterable(color).normalized
197201

198-
ctx.enable(ctx.BLEND)
202+
# Auto select number of segments if not specified
203+
if num_segments == -1:
204+
size = max(width, height)
205+
if size <= 12:
206+
num_segments = 6
207+
else:
208+
num_segments = int(size) // 2
209+
210+
if num_segments < 3:
211+
num_segments = 3
199212

200213
# Pass data to shader
214+
program["center"] = center_x, center_y
201215
program["color"] = color_normalized
202216
program["shape"] = width / 2, height / 2, tilt_angle, border_width
203217
program["segments"] = num_segments
204-
buffer.orphan()
205-
buffer.write(data=array.array("f", (center_x, center_y)))
206-
207-
geometry.render(program, mode=gl.POINTS, vertices=1)
208218

219+
ctx.enable(ctx.BLEND)
220+
geometry.render(program, mode=ctx.TRIANGLES, vertices=num_segments * 6)
209221
ctx.disable(ctx.BLEND)

arcade/resources/system/shaders/shapes/ellipse/filled_unbuffered_geo.glsl

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,37 @@
11
#version 330
22

3-
in vec2 in_vert;
3+
uniform WindowBlock {
4+
mat4 projection;
5+
mat4 view;
6+
} window;
7+
8+
uniform vec2 center;
9+
uniform int segments;
10+
// [w, h, tilt]
11+
uniform vec3 shape;
12+
13+
const float PI = 3.141592;
414

515
void main() {
6-
gl_Position = vec4(in_vert, 0.0, 1.0);
16+
int triangle_id = gl_VertexID / 3;
17+
int vertex_id = gl_VertexID % 3;
18+
19+
// Calculate rotation/tilt
20+
float angle = radians(shape.z);
21+
mat2 rot = mat2(
22+
cos(angle), -sin(angle),
23+
sin(angle), cos(angle)
24+
);
25+
// Calculate the positions for the full triangle in the current segment
26+
vec2 positions[3] = vec2[3](
27+
vec2(0.0, 0.0),
28+
vec2(sin((float(triangle_id) + 1.0) * (PI * 2.0 / float(segments))),
29+
cos((float(triangle_id) + 1.0) * (PI * 2.0 / float(segments)))) * shape.xy,
30+
vec2(sin(float(triangle_id) * (PI * 2.0 / float(segments))),
31+
cos(float(triangle_id) * (PI * 2.0 / float(segments)))) * shape.xy
32+
);
33+
34+
mat4 mvp = window.projection * window.view;
35+
vec4 pos = vec4(rot * positions[vertex_id] + center, 0.0, 1.0);
36+
gl_Position = mvp * pos;
737
}

arcade/resources/system/shaders/shapes/ellipse/outline_unbuffered_geo.glsl

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,45 @@
11
#version 330
22

3-
in vec2 in_vert;
3+
uniform WindowBlock {
4+
mat4 projection;
5+
mat4 view;
6+
} window;
7+
8+
uniform vec2 center;
9+
uniform int segments;
10+
// [w, h, tilt, thickness]
11+
uniform vec4 shape;
12+
13+
const float PI = 3.141592;
414

515
void main() {
6-
gl_Position = vec4(in_vert, 0.0, 1.0);
16+
// Two triangles per line segment of the outline
17+
int segment_id = gl_VertexID / 6;
18+
int vertex_id = gl_VertexID % 6;
19+
20+
// Calculate rotation/tilt
21+
float angle = radians(shape.z);
22+
mat2 rot = mat2(
23+
cos(angle), -sin(angle),
24+
sin(angle), cos(angle)
25+
);
26+
27+
// sin(v), cos(v) travels clockwise around the circle starting at 0, 1 (top of circle)
28+
float st = PI * 2.0 / float(segments);
29+
30+
// calculate the four points of the line segment
31+
// Inner and outer points for the start of line segment
32+
vec2 p0 = vec2(sin(float(segment_id) * st), cos(float(segment_id) * st)) * shape.xy;
33+
vec2 p1 = vec2(sin(float(segment_id) * st), cos(float(segment_id) * st)) * (shape.xy - vec2(shape.w));
34+
35+
// Inner and outer points for the end of line segment
36+
vec2 p2 = vec2(sin((float(segment_id) + 1.0) * st), cos((float(segment_id) + 1.0) * st)) * shape.xy;
37+
vec2 p3 = vec2(sin((float(segment_id) + 1.0) * st), cos((float(segment_id) + 1.0) * st)) * (shape.xy - vec2(shape.w));
38+
39+
vec2 position[6] = vec2[6](
40+
p1, p0, p2, // first triangle
41+
p1, p2, p3 // second triangle
42+
);
43+
mat4 mvp = window.projection * window.view;
44+
gl_Position = mvp * vec4(rot * position[vertex_id] + center, 0.0, 1.0);
745
}

arcade/resources/system/shaders/shapes/rectangle/filled_unbuffered_vs.glsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ void main() {
1919
);
2020
// vec2 size = shape.xy / 2.0;
2121
mat4 mvp = window.projection * window.view;
22-
vec2 pos = in_instance_pos + (in_vert * shape.xy);
23-
gl_Position = mvp * vec4(rot * pos, 0.0, 1.0);
22+
vec2 pos = in_instance_pos + (rot * (in_vert * shape.xy));
23+
gl_Position = mvp * vec4(pos, 0.0, 1.0);
2424
}

0 commit comments

Comments
 (0)