1use crate::ecs::World;
2use crate::math::{Color, Transform, Vec2};
3use crate::ui::{UIPanel, UIProgressBar, UITransform};
4use std::sync::Arc;
5use wgpu::util::DeviceExt;
6use winit::window::Window;
7use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
8
9pub struct Renderer {
10 surface: wgpu::Surface<'static>,
11 device: wgpu::Device,
12 queue: wgpu::Queue,
13 config: wgpu::SurfaceConfiguration,
14 size: winit::dpi::PhysicalSize<u32>,
15 render_pipeline: wgpu::RenderPipeline,
16 #[allow(dead_code)]
17 font_system: FontSystem,
18 #[allow(dead_code)]
19 swash_cache: SwashCache,
20 #[allow(dead_code)]
21 text_atlas: TextAtlas,
22 #[allow(dead_code)]
23 text_renderer: TextRenderer,
24}
25
26#[repr(C)]
27#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
28struct Vertex {
29 position: [f32; 2],
30 color: [f32; 4],
31}
32
33impl Vertex {
34 fn desc() -> wgpu::VertexBufferLayout<'static> {
35 wgpu::VertexBufferLayout {
36 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
37 step_mode: wgpu::VertexStepMode::Vertex,
38 attributes: &[
39 wgpu::VertexAttribute {
40 offset: 0,
41 shader_location: 0,
42 format: wgpu::VertexFormat::Float32x2,
43 },
44 wgpu::VertexAttribute {
45 offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
46 shader_location: 1,
47 format: wgpu::VertexFormat::Float32x4,
48 },
49 ],
50 }
51 }
52}
53
54impl Renderer {
55 pub async fn new(window: Arc<Window>) -> Self {
56 let size = window.inner_size();
57
58 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
59 backends: wgpu::Backends::all(),
60 ..Default::default()
61 });
62
63 let surface = instance.create_surface(window).unwrap();
64
65 let adapter = instance
66 .request_adapter(&wgpu::RequestAdapterOptions {
67 power_preference: wgpu::PowerPreference::default(),
68 compatible_surface: Some(&surface),
69 force_fallback_adapter: false,
70 })
71 .await
72 .unwrap();
73
74 let (device, queue) = adapter
75 .request_device(
76 &wgpu::DeviceDescriptor {
77 label: None,
78 required_features: wgpu::Features::empty(),
79 required_limits: wgpu::Limits::default(),
80 },
81 None,
82 )
83 .await
84 .unwrap();
85
86 let surface_caps = surface.get_capabilities(&adapter);
87 let surface_format = surface_caps
88 .formats
89 .iter()
90 .copied()
91 .find(|f| f.is_srgb())
92 .unwrap_or(surface_caps.formats[0]);
93
94 let config = wgpu::SurfaceConfiguration {
95 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
96 format: surface_format,
97 width: size.width,
98 height: size.height,
99 present_mode: surface_caps.present_modes[0],
100 alpha_mode: surface_caps.alpha_modes[0],
101 view_formats: vec![],
102 desired_maximum_frame_latency: 2,
103 };
104
105 surface.configure(&device, &config);
106
107 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
108 label: Some("Shader"),
109 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/basic.wgsl").into()),
110 });
111
112 let render_pipeline_layout =
113 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
114 label: Some("Render Pipeline Layout"),
115 bind_group_layouts: &[],
116 push_constant_ranges: &[],
117 });
118
119 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
120 label: Some("Render Pipeline"),
121 layout: Some(&render_pipeline_layout),
122 vertex: wgpu::VertexState {
123 module: &shader,
124 entry_point: "vs_main",
125 buffers: &[Vertex::desc()],
126 },
127 fragment: Some(wgpu::FragmentState {
128 module: &shader,
129 entry_point: "fs_main",
130 targets: &[Some(wgpu::ColorTargetState {
131 format: config.format,
132 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
133 write_mask: wgpu::ColorWrites::ALL,
134 })],
135 }),
136 primitive: wgpu::PrimitiveState {
137 topology: wgpu::PrimitiveTopology::TriangleList,
138 strip_index_format: None,
139 front_face: wgpu::FrontFace::Ccw,
140 cull_mode: Some(wgpu::Face::Back),
141 polygon_mode: wgpu::PolygonMode::Fill,
142 unclipped_depth: false,
143 conservative: false,
144 },
145 depth_stencil: None,
146 multisample: wgpu::MultisampleState {
147 count: 1,
148 mask: !0,
149 alpha_to_coverage_enabled: false,
150 },
151 multiview: None,
152 });
153
154 let font_system = FontSystem::new();
155 let swash_cache = SwashCache::new();
156 let mut cache = TextAtlas::new(&device, &queue, config.format);
157 let text_renderer = TextRenderer::new(&mut cache, &device, wgpu::MultisampleState::default(), None);
158
159 Self {
160 surface,
161 device,
162 queue,
163 config,
164 size,
165 render_pipeline,
166 font_system,
167 swash_cache,
168 text_atlas: cache,
169 text_renderer,
170 }
171 }
172
173 pub fn size(&self) -> winit::dpi::PhysicalSize<u32> {
174 self.size
175 }
176
177 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
178 if new_size.width > 0 && new_size.height > 0 {
179 self.size = new_size;
180 self.config.width = new_size.width;
181 self.config.height = new_size.height;
182 self.surface.configure(&self.device, &self.config);
183 }
184 }
185
186 pub fn render(&mut self, world: &World) -> Result<(), wgpu::SurfaceError> {
187 let output = self.surface.get_current_texture()?;
188 let view = output
189 .texture
190 .create_view(&wgpu::TextureViewDescriptor::default());
191
192 let mut encoder = self
193 .device
194 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
195 label: Some("Render Encoder"),
196 });
197
198 let mut buffers = Vec::new();
200
201 for entity in world.entities() {
203 if let (Some(transform), Some(sprite)) =
204 (entity.get::<Transform>(), entity.get::<Sprite>())
205 {
206 let vertices = self.create_sprite_vertices(transform, sprite);
207 let vertex_buffer =
208 self.device
209 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
210 label: Some("Vertex Buffer"),
211 contents: bytemuck::cast_slice(&vertices),
212 usage: wgpu::BufferUsages::VERTEX,
213 });
214
215 let indices: &[u16] = &[0, 1, 2, 0, 2, 3];
216 let index_buffer =
217 self.device
218 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
219 label: Some("Index Buffer"),
220 contents: bytemuck::cast_slice(indices),
221 usage: wgpu::BufferUsages::INDEX,
222 });
223
224 buffers.push((vertex_buffer, index_buffer));
225 }
226 }
227
228 for entity in world.entities() {
230 if let (Some(ui_transform), Some(panel)) =
231 (entity.get::<UITransform>(), entity.get::<UIPanel>())
232 {
233 let pos = ui_transform.get_screen_position(self.size.width as f32, self.size.height as f32);
234 let transform = Transform::new(
235 pos.x + ui_transform.size.x / 2.0 - self.size.width as f32 / 2.0,
236 pos.y + ui_transform.size.y / 2.0 - self.size.height as f32 / 2.0
237 );
238 let sprite = Sprite {
239 size: ui_transform.size,
240 color: panel.background_color,
241 };
242
243 let vertices = self.create_sprite_vertices(&transform, &sprite);
244 let vertex_buffer =
245 self.device
246 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
247 label: Some("UI Vertex Buffer"),
248 contents: bytemuck::cast_slice(&vertices),
249 usage: wgpu::BufferUsages::VERTEX,
250 });
251
252 let indices: &[u16] = &[0, 1, 2, 0, 2, 3];
253 let index_buffer =
254 self.device
255 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
256 label: Some("UI Index Buffer"),
257 contents: bytemuck::cast_slice(indices),
258 usage: wgpu::BufferUsages::INDEX,
259 });
260
261 buffers.push((vertex_buffer, index_buffer));
262 }
263 }
264
265 for entity in world.entities() {
267 if let (Some(ui_transform), Some(bar)) =
268 (entity.get::<UITransform>(), entity.get::<UIProgressBar>())
269 {
270 let pos = ui_transform.get_screen_position(self.size.width as f32, self.size.height as f32);
271
272 let bg_transform = Transform::new(
274 pos.x + ui_transform.size.x / 2.0 - self.size.width as f32 / 2.0,
275 pos.y + ui_transform.size.y / 2.0 - self.size.height as f32 / 2.0
276 );
277 let bg_sprite = Sprite {
278 size: ui_transform.size,
279 color: bar.background_color,
280 };
281
282 let vertices = self.create_sprite_vertices(&bg_transform, &bg_sprite);
283 let vertex_buffer =
284 self.device
285 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
286 label: Some("UI Bar BG Vertex Buffer"),
287 contents: bytemuck::cast_slice(&vertices),
288 usage: wgpu::BufferUsages::VERTEX,
289 });
290
291 let indices: &[u16] = &[0, 1, 2, 0, 2, 3];
292 let index_buffer =
293 self.device
294 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
295 label: Some("UI Bar BG Index Buffer"),
296 contents: bytemuck::cast_slice(indices),
297 usage: wgpu::BufferUsages::INDEX,
298 });
299
300 buffers.push((vertex_buffer, index_buffer));
301
302 let fill_width = ui_transform.size.x * bar.get_fill_percentage();
304 if fill_width > 0.0 {
305 let fill_transform = Transform::new(
306 pos.x + fill_width / 2.0 - self.size.width as f32 / 2.0,
307 pos.y + ui_transform.size.y / 2.0 - self.size.height as f32 / 2.0
308 );
309 let fill_sprite = Sprite {
310 size: Vec2::new(fill_width, ui_transform.size.y),
311 color: bar.fill_color,
312 };
313
314 let vertices = self.create_sprite_vertices(&fill_transform, &fill_sprite);
315 let vertex_buffer =
316 self.device
317 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
318 label: Some("UI Bar Fill Vertex Buffer"),
319 contents: bytemuck::cast_slice(&vertices),
320 usage: wgpu::BufferUsages::VERTEX,
321 });
322
323 let indices: &[u16] = &[0, 1, 2, 0, 2, 3];
324 let index_buffer =
325 self.device
326 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
327 label: Some("UI Bar Fill Index Buffer"),
328 contents: bytemuck::cast_slice(indices),
329 usage: wgpu::BufferUsages::INDEX,
330 });
331
332 buffers.push((vertex_buffer, index_buffer));
333 }
334 }
335 }
336
337 {
338 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
339 label: Some("Render Pass"),
340 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
341 view: &view,
342 resolve_target: None,
343 ops: wgpu::Operations {
344 load: wgpu::LoadOp::Clear(wgpu::Color {
345 r: 0.1,
346 g: 0.1,
347 b: 0.1,
348 a: 1.0,
349 }),
350 store: wgpu::StoreOp::Store,
351 },
352 })],
353 depth_stencil_attachment: None,
354 occlusion_query_set: None,
355 timestamp_writes: None,
356 });
357
358 render_pass.set_pipeline(&self.render_pipeline);
359
360 for (vertex_buffer, index_buffer) in &buffers {
362 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
363 render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);
364 render_pass.draw_indexed(0..6, 0, 0..1);
365 }
366 }
367
368 self.queue.submit(std::iter::once(encoder.finish()));
369 output.present();
370
371 Ok(())
372 }
373
374 fn create_sprite_vertices(&self, transform: &Transform, sprite: &Sprite) -> Vec<Vertex> {
375 let half_width = sprite.size.x / 2.0;
376 let half_height = sprite.size.y / 2.0;
377
378 let _aspect = self.size.width as f32 / self.size.height as f32;
379
380 let x = transform.position.x / (self.size.width as f32 / 2.0);
381 let y = -transform.position.y / (self.size.height as f32 / 2.0);
382 let w = half_width / (self.size.width as f32 / 2.0);
383 let h = half_height / (self.size.height as f32 / 2.0);
384
385 let color = [sprite.color.r, sprite.color.g, sprite.color.b, sprite.color.a];
386
387 vec![
388 Vertex {
389 position: [x - w, y - h],
390 color,
391 },
392 Vertex {
393 position: [x + w, y - h],
394 color,
395 },
396 Vertex {
397 position: [x + w, y + h],
398 color,
399 },
400 Vertex {
401 position: [x - w, y + h],
402 color,
403 },
404 ]
405 }
406}
407
408#[derive(Clone, Debug)]
409pub struct Sprite {
410 pub size: Vec2,
411 pub color: Color,
412}
413
414impl Sprite {
415 pub fn new(width: f32, height: f32) -> Self {
416 Self {
417 size: Vec2::new(width, height),
418 color: Color::WHITE,
419 }
420 }
421
422 pub fn with_color(mut self, color: Color) -> Self {
423 self.color = color;
424 self
425 }
426}
427
428impl crate::ecs::Component for Sprite {}