1use sfml::{
2 cpp::FBox,
3 graphics::{
4 Color, Drawable, Font, IntRect, PrimitiveType, RenderStates, RenderTarget, RenderTexture,
5 RenderWindow, Shader, ShaderType, Sprite, Text, Texture, Transformable, Vertex,
6 },
7 system::{Clock, Vector2f},
8 window::{Event, Key, Style},
9 SfResult,
10};
11
12include!("../example_common.rs");
13
14trait Effect: Drawable {
15 fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()>;
16 fn name(&self) -> &str;
17 fn as_drawable(&self) -> &dyn Drawable;
18}
19
20struct Pixelate<'t> {
21 sprite: Sprite<'t>,
22 shader: FBox<Shader<'static>>,
23}
24
25impl<'t> Pixelate<'t> {
26 fn new(texture: &'t Texture) -> SfResult<Self> {
27 let mut sprite = Sprite::new();
28 sprite.set_texture(texture, false);
29 Ok(Self {
30 sprite,
31 shader: Shader::from_file("pixelate.frag", ShaderType::Fragment)?,
32 })
33 }
34}
35
36impl Drawable for Pixelate<'_> {
37 fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
38 &'a self,
39 target: &mut dyn RenderTarget,
40 states: &RenderStates<'texture, 'shader, 'shader_texture>,
41 ) {
42 let mut states = *states;
43 states.shader = Some(&self.shader);
44 target.draw_with_renderstates(&self.sprite, &states);
45 }
46}
47
48impl Effect for Pixelate<'_> {
49 fn update(&mut self, _t: f32, x: f32, y: f32) -> SfResult<()> {
50 self.shader
51 .set_uniform_float("pixel_threshold", (x + y) / 30.0)
52 }
53 fn name(&self) -> &str {
54 "pixelate"
55 }
56 fn as_drawable(&self) -> &dyn Drawable {
57 self
58 }
59}
60
61struct WaveBlur<'fo> {
62 text: Text<'fo>,
63 shader: FBox<Shader<'static>>,
64}
65
66const WAVEBLUR_TEXT: &str = "\
67Praesent suscipit augue in velit pulvinar hendrerit varius purus aliquam.
68Mauris mi odio, bibendum quis fringilla a, laoreet vel orci. Proin vitae vulputate tortor.
69Praesent cursus ultrices justo, ut feugiat ante vehicula quis.
70Donec fringilla scelerisque mauris et viverra.
71Maecenas adipiscing ornare scelerisque. Nullam at libero elit.
72Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
73Nullam leo urna, tincidunt id semper eget, ultricies sed mi.
74Morbi mauris massa, commodo id dignissim vel, lobortis et elit.
75Fusce vel libero sed neque scelerisque venenatis.
76Integer mattis tincidunt quam vitae iaculis.
77Vivamus fringilla sem non velit venenatis fermentum.
78Vivamus varius tincidunt nisi id vehicula.
79Integer ullamcorper, enim vitae euismod rutrum, massa nisl semper ipsum,
80vestibulum sodales sem ante in massa.
81Vestibulum in augue non felis convallis viverra.
82Mauris ultricies dolor sed massa convallis sed aliquet augue fringilla.
83Duis erat eros, porta in accumsan in, blandit quis sem.
84In hac habitasse platea dictumst. Etiam fringilla est id odio dapibus sit amet semper dui laoreet.";
85
86impl<'fo> WaveBlur<'fo> {
87 fn new(font: &'fo Font) -> SfResult<Self> {
88 let mut text = Text::new(WAVEBLUR_TEXT, font, 22);
89 text.set_position((30., 20.));
90 Ok(Self {
91 text,
92 shader: Shader::from_file_vert_frag("wave.vert", "blur.frag")?,
93 })
94 }
95}
96
97impl Drawable for WaveBlur<'_> {
98 fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
99 &'a self,
100 target: &mut dyn RenderTarget,
101 states: &RenderStates<'texture, 'shader, 'shader_texture>,
102 ) {
103 let mut states = *states;
104 states.shader = Some(&self.shader);
105 target.draw_with_renderstates(&self.text, &states);
106 }
107}
108
109impl Effect for WaveBlur<'_> {
110 fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()> {
111 self.shader.set_uniform_float("wave_phase", t)?;
112 self.shader
113 .set_uniform_vec2("wave_amplitude", Vector2f::new(x * 40., y * 40.))?;
114 self.shader
115 .set_uniform_float("blur_radius", (x + y) * 0.008)
116 }
117 fn name(&self) -> &str {
118 "wave + blur"
119 }
120 fn as_drawable(&self) -> &dyn Drawable {
121 self
122 }
123}
124
125struct StormBlink {
126 points: Vec<Vertex>,
127 shader: FBox<Shader<'static>>,
128}
129
130impl StormBlink {
131 fn new() -> SfResult<Self> {
132 use rand::{thread_rng, Rng};
133 let mut rng = thread_rng();
134
135 let mut points = Vec::new();
136 for _ in 0..40_000 {
137 let x = rng.gen_range(0.0..800.);
138 let y = rng.gen_range(0.0..600.);
139 let (red, green, blue) = (rng.r#gen(), rng.r#gen(), rng.r#gen());
140 points.push(Vertex::with_pos_color(
141 Vector2f::new(x, y),
142 Color::rgb(red, green, blue),
143 ));
144 }
145
146 let shader = Shader::from_file_vert_frag("storm.vert", "blink.frag")?;
147 Ok(Self { points, shader })
148 }
149}
150
151impl Drawable for StormBlink {
152 fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
153 &'a self,
154 target: &mut dyn RenderTarget,
155 states: &RenderStates<'texture, 'shader, 'shader_texture>,
156 ) {
157 let mut states = *states;
158 states.shader = Some(&self.shader);
159 target.draw_primitives(&self.points, PrimitiveType::POINTS, &states);
160 }
161}
162
163impl Effect for StormBlink {
164 fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()> {
165 let radius = 200. + t.cos() * 150.;
166 self.shader
167 .set_uniform_vec2("storm_position", Vector2f::new(x * 800., y * 600.))?;
168 self.shader
169 .set_uniform_float("storm_inner_radius", radius / 3.)?;
170 self.shader
171 .set_uniform_float("storm_total_radius", radius)?;
172 self.shader
173 .set_uniform_float("blink_alpha", 0.5 + (t * 3.).cos() * 0.25)
174 }
175 fn name(&self) -> &str {
176 "storm + blink"
177 }
178 fn as_drawable(&self) -> &dyn Drawable {
179 self
180 }
181}
182
183struct Edge<'t> {
184 surface: FBox<RenderTexture>,
185 bg_sprite: Sprite<'t>,
186 entities: Vec<Sprite<'t>>,
187 shader: FBox<Shader<'static>>,
188}
189
190impl<'t> Edge<'t> {
191 fn new(bg_texture: &'t Texture, entity_texture: &'t Texture) -> SfResult<Self> {
192 let mut surface = RenderTexture::new(800, 600)?;
193 surface.set_smooth(true);
194 let mut bg_sprite = Sprite::with_texture(bg_texture);
195 bg_sprite.set_position((135., 100.));
196 let mut entities = Vec::new();
197
198 for i in 0..6 {
199 entities.push(Sprite::with_texture_and_rect(
200 entity_texture,
201 IntRect::new(96 * i, 0, 96, 96),
202 ));
203 }
204
205 let mut shader = Shader::from_file("edge.frag", ShaderType::Fragment)?;
206 shader.set_uniform_current_texture("texture")?;
207
208 Ok(Self {
209 surface,
210 bg_sprite,
211 entities,
212 shader,
213 })
214 }
215}
216
217impl Drawable for Edge<'_> {
218 fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
219 &'a self,
220 target: &mut dyn RenderTarget,
221 states: &RenderStates<'texture, 'shader, 'shader_texture>,
222 ) {
223 let mut states = *states;
224 states.shader = Some(&self.shader);
225 target.draw_with_renderstates(&Sprite::with_texture(self.surface.texture()), &states);
226 }
227}
228
229impl Effect for Edge<'_> {
230 fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()> {
231 self.shader
232 .set_uniform_float("edge_threshold", 1. - (x + y) / 2.)?;
233 let entities_len = self.entities.len() as f32;
234
235 for (i, en) in self.entities.iter_mut().enumerate() {
236 let pos = (
237 (0.25 * (t * i as f32 + (entities_len - i as f32))).cos() * 300. + 350.,
238 (0.25 * (t * (entities_len - i as f32) + i as f32)).cos() * 200. + 250.,
239 );
240 en.set_position(pos);
241 }
242 self.surface.clear(Color::WHITE);
243 self.surface.draw(&self.bg_sprite);
244 for en in &self.entities {
245 self.surface.draw(en);
246 }
247 self.surface.display();
248 Ok(())
249 }
250 fn as_drawable(&self) -> &dyn Drawable {
251 self
252 }
253 fn name(&self) -> &str {
254 "edge post-effect"
255 }
256}
257
258fn main() -> SfResult<()> {
259 example_ensure_right_working_dir();
260
261 let mut window = RenderWindow::new(
262 (800, 600),
263 "SFML Shader",
264 Style::TITLEBAR | Style::CLOSE,
265 &Default::default(),
266 )?;
267 window.set_vertical_sync_enabled(true);
268 let font = Font::from_file("sansation.ttf")?;
269 let bg = Texture::from_file("background.jpg")?;
270 let mut bg_texture = Texture::from_file("sfml.png")?;
271 bg_texture.set_smooth(true);
272 let mut entity_texture = Texture::from_file("devices.png")?;
273 entity_texture.set_smooth(true);
274 let effects: [&mut dyn Effect; 4] = [
275 &mut Pixelate::new(&bg)?,
276 &mut WaveBlur::new(&font)?,
277 &mut StormBlink::new()?,
278 &mut Edge::new(&bg_texture, &entity_texture)?,
279 ];
280 let mut current = 0;
281 let text_bg_texture = Texture::from_file("text-background.png")?;
282 let mut text_bg = Sprite::with_texture(&text_bg_texture);
283 text_bg.set_position((0., 520.));
284 text_bg.set_color(Color::rgba(255, 255, 255, 200));
285 let msg = format!("Current effect: {}", effects[current].name());
286 let mut desc = Text::new(&msg, &font, 20);
287 desc.set_position((10., 530.));
288 desc.set_fill_color(Color::rgb(80, 80, 80));
289 let msg = "Press left and right arrows to change the current shader";
290 let mut instructions = Text::new(msg, &font, 20);
291 instructions.set_position((280., 555.));
292 instructions.set_fill_color(Color::rgb(80, 80, 80));
293 let clock = Clock::start()?;
294
295 while window.is_open() {
296 while let Some(event) = window.poll_event() {
297 use crate::Event::*;
298 match event {
299 Closed => window.close(),
300 KeyPressed { code, .. } => match code {
301 Key::Escape => window.close(),
302 Key::Left => {
303 if current == 0 {
304 current = effects.len() - 1;
305 } else {
306 current -= 1;
307 }
308 desc.set_string(&format!("Current effect: {}", effects[current].name()));
309 }
310 Key::Right => {
311 if current == effects.len() - 1 {
312 current = 0;
313 } else {
314 current += 1;
315 }
316 desc.set_string(&format!("Current effect: {}", effects[current].name()));
317 }
318 _ => {}
319 },
320 _ => {}
321 }
322 }
323
324 let x = window.mouse_position().x as f32 / window.size().x as f32;
325 let y = window.mouse_position().y as f32 / window.size().y as f32;
326
327 effects[current].update(clock.elapsed_time().as_seconds(), x, y)?;
328
329 window.clear(Color::rgb(255, 128, 0));
330 window.draw(effects[current].as_drawable());
331 window.draw(&text_bg);
332 window.draw(&instructions);
333 window.draw(&desc);
334 window.display();
335 }
336 Ok(())
337}