Skip to content

Commit cb7e457

Browse files
committed
GL: Transform emit example
1 parent cdfb0b2 commit cb7e457

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
"""
2+
Example showing how er can emit particles with the gpu.
3+
We work with transform shaders to do all the logic.
4+
5+
This example was created on a Raspberry PI 4
6+
"""
7+
import struct
8+
import random
9+
from array import array
10+
import arcade
11+
from arcade.gl import BufferDescription
12+
13+
14+
15+
class TransformEmit(arcade.Window):
16+
17+
def __init__(self):
18+
super().__init__(800, 600, "Transform Emit")
19+
self.time = 0
20+
21+
# Progam to simply draw the points
22+
self.visualize_points_program = self.ctx.program(
23+
vertex_shader="""
24+
#version 330
25+
26+
in vec2 in_pos;
27+
in vec3 in_col;
28+
29+
out vec3 color;
30+
31+
void main() {
32+
gl_Position = vec4(in_pos, 0.0, 1.0);
33+
color = in_col;
34+
}
35+
""",
36+
fragment_shader="""
37+
#version 330
38+
39+
out vec4 fragColor;
40+
in vec3 color;
41+
42+
void main() {
43+
fragColor = vec4(color, 1.0);
44+
}
45+
"""
46+
)
47+
48+
# Program to emit points into a buffer
49+
self.emit_program = self.ctx.program(
50+
vertex_shader="""
51+
#version 330
52+
#define M_PI 3.1415926535897932384626433832795
53+
uniform float time;
54+
55+
out vec2 out_pos;
56+
out vec2 out_vel;
57+
58+
float rand(float n) { return fract(sin(n) * 43758.5453123); }
59+
60+
void main() {
61+
float a = mod(time * float(gl_VertexID) + rand(time), M_PI * 2.0);
62+
float r = clamp(rand(time + float(gl_VertexID)), 0.1, 0.9);
63+
64+
out_pos = vec2(0.0);
65+
out_vel = vec2(sin(a), cos(a)) * r;
66+
}
67+
""",
68+
varyings=["out_pos", "out_vel"],
69+
varyings_capture_mode="separate",
70+
)
71+
72+
73+
self.move_program = self.ctx.program(
74+
vertex_shader="""
75+
#version 330
76+
77+
in vec2 in_pos;
78+
in vec2 in_vel;
79+
80+
out vec2 v_pos;
81+
out vec2 v_vel;
82+
83+
void main() {
84+
v_pos = in_pos;
85+
v_vel = in_vel;
86+
}
87+
""",
88+
geometry_shader="""
89+
#version 330
90+
91+
layout(points) in;
92+
layout(points, max_vertices = 1) out;
93+
94+
uniform float dt;
95+
96+
in vec2 v_pos[];
97+
in vec2 v_vel[];
98+
99+
out vec2 out_pos;
100+
out vec2 out_vel;
101+
102+
void main() {
103+
vec2 pos = v_pos[0];
104+
vec2 vel = v_vel[0];
105+
106+
if (pos.y > -1.0) {
107+
out_pos = pos + vel * dt;
108+
out_vel = vel - vec2(0.0, dt);
109+
EmitVertex();
110+
}
111+
112+
}
113+
""",
114+
varyings=["out_pos", "out_vel"],
115+
varyings_capture_mode="separate",
116+
)
117+
118+
# Configuration
119+
self.num_points = 10_000
120+
self.active_points = 0
121+
self.emit_max_points = 100
122+
123+
# First set of positons and velocities
124+
self.buffer_pos_1 = self.ctx.buffer(reserve=self.num_points * 8)
125+
self.buffer_vel_1 = self.ctx.buffer(reserve=self.num_points * 8)
126+
127+
# Second set of positons and velocities
128+
self.buffer_pos_2 = self.ctx.buffer(reserve=self.num_points * 8)
129+
self.buffer_vel_2 = self.ctx.buffer(reserve=self.num_points * 8)
130+
131+
self.buffer_colors = self.ctx.buffer(data=array('f', [random.random() for _ in range(self.num_points * 3)]))
132+
133+
# Geomtry definition for drawing the two sets of positions
134+
self.draw_geometry_1 = self.ctx.geometry(
135+
[
136+
BufferDescription(self.buffer_pos_1, "2f", ("in_pos",)),
137+
BufferDescription(self.buffer_colors, "3f", ("in_col",)),
138+
],
139+
mode=self.ctx.POINTS,
140+
)
141+
self.draw_geometry_2 = self.ctx.geometry(
142+
[
143+
BufferDescription(self.buffer_pos_2, "2f", ("in_pos",)),
144+
BufferDescription(self.buffer_colors, "3f", ("in_col",)),
145+
],
146+
mode=self.ctx.POINTS,
147+
)
148+
149+
# Geometry we use to move he points
150+
self.move_geometry_1 = self.ctx.geometry(
151+
[
152+
BufferDescription(self.buffer_pos_1, "2f", ("in_pos",)),
153+
BufferDescription(self.buffer_vel_1, "2f", ("in_vel",))
154+
],
155+
mode=self.ctx.POINTS,
156+
)
157+
self.move_geometry_2 = self.ctx.geometry(
158+
[
159+
BufferDescription(self.buffer_pos_2, "2f", ("in_pos",)),
160+
BufferDescription(self.buffer_vel_2, "2f", ("in_vel",))
161+
],
162+
mode=self.ctx.POINTS,
163+
)
164+
165+
self.emit_geometry = self.ctx.geometry()
166+
self.query = self.ctx.query(primitives=True)
167+
168+
def on_draw(self):
169+
self.clear()
170+
self.ctx.enable_only()
171+
172+
self.draw_geometry_2.render(self.visualize_points_program)
173+
174+
# Swap things around for the next frame
175+
self.draw_geometry_1, self.draw_geometry_2 = self.draw_geometry_2, self.draw_geometry_1
176+
self.buffer_pos_1, self.buffer_pos_2 = self.buffer_pos_2, self.buffer_pos_1
177+
self.buffer_vel_1, self.buffer_vel_2 = self.buffer_vel_2, self.buffer_vel_1
178+
179+
def on_update(self, delta_time):
180+
self.time += delta_time
181+
# print("active points", self.active_points)
182+
183+
# Do we have poins to emit?
184+
if self.active_points < self.num_points:
185+
186+
emit_count = min(self.num_points - self.active_points, self.emit_max_points)
187+
# print("Emitting", emit_count)
188+
189+
# Emit some particles
190+
self.emit_program["time"] = self.time
191+
self.emit_geometry.transform(
192+
self.emit_program,
193+
[self.buffer_pos_1, self.buffer_vel_1],
194+
vertices=emit_count,
195+
buffer_offset=self.active_points * 8,
196+
)
197+
self.active_points += emit_count
198+
199+
# Move the poins. This will also purge dead particles
200+
self.move_program["dt"] = delta_time
201+
with self.query:
202+
self.move_geometry_1.transform(
203+
self.move_program,
204+
[self.buffer_pos_2, self.buffer_vel_2],
205+
vertices=self.active_points,
206+
)
207+
self.active_points = self.query.primitives_generated
208+
# print("after moving", self.active_points)
209+
210+
211+
TransformEmit().run()

0 commit comments

Comments
 (0)