1use wgpu::*;
2use wgpu::util::DeviceExt;
3use winit::window::Window;
4use cgmath::*;
5use bytemuck::{Pod, Zeroable};
6use crate::camera::Camera;
7use std::path::PathBuf;
8
9fn find_asset_path(relative_path: &str) -> PathBuf {
10 if let Ok(exe_path) = std::env::current_exe() {
12 if let Some(exe_dir) = exe_path.parent() {
13 let asset_path = exe_dir.join(relative_path);
14 if asset_path.exists() {
15 return asset_path;
16 }
17 }
18 }
19
20 PathBuf::from(relative_path)
22}
23
24#[repr(C)]
25#[derive(Copy, Clone, Debug, Pod, Zeroable)]
26struct Uniforms {
27 model: [[f32; 4]; 4],
28 view: [[f32; 4]; 4],
29 proj: [[f32; 4]; 4],
30}
31
32#[repr(C)]
33#[derive(Copy, Clone, Debug, Pod, Zeroable)]
34struct Vertex {
35 position: [f32; 3],
36 uv: [f32; 2],
37}
38
39pub struct Renderer {
40 window_id: winit::window::WindowId,
41 surface: wgpu::Surface<'static>,
42 device: Device,
43 queue: Queue,
44 config: SurfaceConfiguration,
45 pipeline: RenderPipeline,
46 vertex_buffer: Buffer,
47 index_buffer: Buffer,
48 uniform_buffer: Buffer,
49 uniform_bind_group: BindGroup,
50 texture_bind_group: BindGroup,
51 rotation: f32,
52 pub camera: Camera, }
54
55impl Renderer {
56 pub async fn new(window: &Window) -> Self {
57 let window_id = window.id();
58 let size = window.inner_size();
59
60 let instance = Instance::new(InstanceDescriptor {
61 backends: Backends::PRIMARY,
62 ..Default::default()
63 });
64
65 let surface_raw = instance.create_surface(window).unwrap();
66 let surface: wgpu::Surface<'static> = unsafe { std::mem::transmute(surface_raw) };
69
70 let adapter = instance
71 .request_adapter(&RequestAdapterOptions {
72 power_preference: PowerPreference::default(),
73 compatible_surface: Some(&surface),
74 force_fallback_adapter: false,
75 })
76 .await
77 .expect("Failed to find an appropriate adapter");
78
79 let (device, queue) = adapter
80 .request_device(
81 &DeviceDescriptor {
82 required_features: Features::empty(),
83 required_limits: Limits::default(),
84 label: None,
85 },
86 None,
87 )
88 .await
89 .expect("Failed to create device");
90
91 let surface_caps = surface.get_capabilities(&adapter);
92 let surface_format = surface_caps
93 .formats
94 .iter()
95 .copied()
96 .find(|f| f.is_srgb())
97 .unwrap_or(surface_caps.formats[0]);
98
99 let config = SurfaceConfiguration {
100 usage: TextureUsages::RENDER_ATTACHMENT,
101 format: surface_format,
102 width: size.width,
103 height: size.height,
104 present_mode: surface_caps.present_modes[0],
105 alpha_mode: surface_caps.alpha_modes[0],
106 view_formats: vec![],
107 desired_maximum_frame_latency: 2,
108 };
109
110 surface.configure(&device, &config);
111
112 let shader = device.create_shader_module(ShaderModuleDescriptor {
113 label: Some("Cube Shader"),
114 source: ShaderSource::Wgsl(include_str!("../shaders/cube.vert.wgsl").into()),
115 });
116
117 let fragment_shader = device.create_shader_module(ShaderModuleDescriptor {
118 label: Some("Cube Fragment Shader"),
119 source: ShaderSource::Wgsl(include_str!("../shaders/cube.frag.wgsl").into()),
120 });
121
122 let uniform_buffer = device.create_buffer(&BufferDescriptor {
123 label: Some("Uniform Buffer"),
124 size: std::mem::size_of::<Uniforms>() as u64,
125 usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
126 mapped_at_creation: false,
127 });
128
129 let uniform_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
130 entries: &[BindGroupLayoutEntry {
131 binding: 0,
132 visibility: ShaderStages::VERTEX,
133 ty: BindingType::Buffer {
134 ty: BufferBindingType::Uniform,
135 has_dynamic_offset: false,
136 min_binding_size: None,
137 },
138 count: None,
139 }],
140 label: Some("Uniform Bind Group Layout"),
141 });
142
143 let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor {
144 layout: &uniform_bind_group_layout,
145 entries: &[BindGroupEntry {
146 binding: 0,
147 resource: uniform_buffer.as_entire_binding(),
148 }],
149 label: Some("Uniform Bind Group"),
150 });
151
152 let texture_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
154 entries: &[
155 BindGroupLayoutEntry {
156 binding: 0,
157 visibility: ShaderStages::FRAGMENT,
158 ty: BindingType::Texture {
159 multisampled: false,
160 view_dimension: TextureViewDimension::D2,
161 sample_type: TextureSampleType::Float { filterable: true },
162 },
163 count: None,
164 },
165 BindGroupLayoutEntry {
166 binding: 1,
167 visibility: ShaderStages::FRAGMENT,
168 ty: BindingType::Sampler(SamplerBindingType::Filtering),
169 count: None,
170 },
171 ],
172 label: Some("Texture Bind Group Layout"),
173 });
174
175 use crate::texture::Texture;
177 let texture_path = find_asset_path("assets/textures/monkey.jxl");
178 let texture = Texture::from_jxl(&device, &queue, &texture_path)
179 .unwrap_or_else(|_| {
180 let width = 256;
182 let height = 256;
183 let mut rgba8_data = vec![0u8; (width * height * 4) as usize];
184 for y in 0..height {
186 for x in 0..width {
187 let idx = ((y * width + x) * 4) as usize;
188 let checker = ((x / 32) + (y / 32)) % 2;
189 rgba8_data[idx] = if checker == 0 { 255 } else { 128 };
190 rgba8_data[idx + 1] = if checker == 0 { 255 } else { 128 };
191 rgba8_data[idx + 2] = if checker == 0 { 255 } else { 128 };
192 rgba8_data[idx + 3] = 255;
193 }
194 }
195
196 let texture_size = Extent3d {
197 width,
198 height,
199 depth_or_array_layers: 1,
200 };
201
202 let texture = device.create_texture(&TextureDescriptor {
203 label: Some("Fallback Texture"),
204 size: texture_size,
205 mip_level_count: 1,
206 sample_count: 1,
207 dimension: TextureDimension::D2,
208 format: TextureFormat::Rgba8UnormSrgb,
209 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
210 view_formats: &[],
211 });
212
213 queue.write_texture(
214 ImageCopyTexture {
215 texture: &texture,
216 mip_level: 0,
217 origin: Origin3d::ZERO,
218 aspect: TextureAspect::All,
219 },
220 &rgba8_data,
221 ImageDataLayout {
222 offset: 0,
223 bytes_per_row: Some(4 * width),
224 rows_per_image: Some(height),
225 },
226 texture_size,
227 );
228
229 let view = texture.create_view(&TextureViewDescriptor::default());
230 let sampler = device.create_sampler(&SamplerDescriptor {
231 label: Some("Texture Sampler"),
232 address_mode_u: AddressMode::ClampToEdge,
233 address_mode_v: AddressMode::ClampToEdge,
234 address_mode_w: AddressMode::ClampToEdge,
235 mag_filter: FilterMode::Linear,
236 min_filter: FilterMode::Linear,
237 mipmap_filter: FilterMode::Nearest,
238 ..Default::default()
239 });
240
241 Texture {
242 texture,
243 view,
244 sampler,
245 width,
246 height,
247 }
248 });
249
250 let texture_bind_group = device.create_bind_group(&BindGroupDescriptor {
251 layout: &texture_bind_group_layout,
252 entries: &[
253 BindGroupEntry {
254 binding: 0,
255 resource: BindingResource::TextureView(&texture.view),
256 },
257 BindGroupEntry {
258 binding: 1,
259 resource: BindingResource::Sampler(&texture.sampler),
260 },
261 ],
262 label: Some("Texture Bind Group"),
263 });
264
265 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
266 label: Some("Render Pipeline Layout"),
267 bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout],
268 push_constant_ranges: &[],
269 });
270
271 let vertex_buffer = Self::create_cube_vertex_buffer(&device);
272 let index_buffer = Self::create_cube_index_buffer(&device);
273
274 let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
275 label: Some("Render Pipeline"),
276 layout: Some(&pipeline_layout),
277 vertex: VertexState {
278 module: &shader,
279 entry_point: "vs_main",
280 buffers: &[VertexBufferLayout {
281 array_stride: std::mem::size_of::<Vertex>() as u64,
282 step_mode: VertexStepMode::Vertex,
283 attributes: &[
284 VertexAttribute {
285 offset: 0,
286 shader_location: 0,
287 format: VertexFormat::Float32x3,
288 },
289 VertexAttribute {
290 offset: std::mem::size_of::<[f32; 3]>() as u64,
291 shader_location: 1,
292 format: VertexFormat::Float32x2,
293 },
294 ],
295 }],
296 compilation_options: PipelineCompilationOptions::default(),
297 },
298 fragment: Some(FragmentState {
299 module: &fragment_shader,
300 entry_point: "fs_main",
301 targets: &[Some(ColorTargetState {
302 format: config.format,
303 blend: Some(BlendState::REPLACE),
304 write_mask: ColorWrites::ALL,
305 })],
306 compilation_options: PipelineCompilationOptions::default(),
307 }),
308 primitive: PrimitiveState {
309 topology: PrimitiveTopology::TriangleList,
310 strip_index_format: None,
311 front_face: FrontFace::Ccw,
312 cull_mode: Some(Face::Back),
313 polygon_mode: PolygonMode::Fill,
314 unclipped_depth: false,
315 conservative: false,
316 },
317 depth_stencil: Some(DepthStencilState {
318 format: TextureFormat::Depth32Float,
319 depth_write_enabled: true,
320 depth_compare: CompareFunction::Less,
321 stencil: StencilState::default(),
322 bias: DepthBiasState::default(),
323 }),
324 multisample: MultisampleState {
325 count: 1,
326 mask: !0,
327 alpha_to_coverage_enabled: false,
328 },
329 multiview: None,
330 });
331
332 Self {
333 window_id,
334 surface,
335 device,
336 queue,
337 config,
338 pipeline,
339 vertex_buffer,
340 index_buffer,
341 uniform_buffer,
342 uniform_bind_group,
343 texture_bind_group,
344 rotation: 0.0,
345 camera: Camera::new(size.width, size.height),
346 }
347 }
348
349 fn create_cube_vertex_buffer(device: &Device) -> Buffer {
350 let vertices: [Vertex; 24] = [
353 Vertex { position: [-1.0, -1.0, -1.0], uv: [0.0, 1.0] },
355 Vertex { position: [1.0, -1.0, -1.0], uv: [1.0, 1.0] },
356 Vertex { position: [1.0, 1.0, -1.0], uv: [1.0, 0.0] },
357 Vertex { position: [-1.0, 1.0, -1.0], uv: [0.0, 0.0] },
358 Vertex { position: [1.0, -1.0, 1.0], uv: [0.0, 1.0] },
360 Vertex { position: [-1.0, -1.0, 1.0], uv: [1.0, 1.0] },
361 Vertex { position: [-1.0, 1.0, 1.0], uv: [1.0, 0.0] },
362 Vertex { position: [1.0, 1.0, 1.0], uv: [0.0, 0.0] },
363 Vertex { position: [-1.0, -1.0, 1.0], uv: [0.0, 1.0] },
365 Vertex { position: [-1.0, -1.0, -1.0], uv: [1.0, 1.0] },
366 Vertex { position: [-1.0, 1.0, -1.0], uv: [1.0, 0.0] },
367 Vertex { position: [-1.0, 1.0, 1.0], uv: [0.0, 0.0] },
368 Vertex { position: [1.0, -1.0, -1.0], uv: [0.0, 1.0] },
370 Vertex { position: [1.0, -1.0, 1.0], uv: [1.0, 1.0] },
371 Vertex { position: [1.0, 1.0, 1.0], uv: [1.0, 0.0] },
372 Vertex { position: [1.0, 1.0, -1.0], uv: [0.0, 0.0] },
373 Vertex { position: [-1.0, -1.0, 1.0], uv: [0.0, 1.0] },
375 Vertex { position: [1.0, -1.0, 1.0], uv: [1.0, 1.0] },
376 Vertex { position: [1.0, -1.0, -1.0], uv: [1.0, 0.0] },
377 Vertex { position: [-1.0, -1.0, -1.0], uv: [0.0, 0.0] },
378 Vertex { position: [-1.0, 1.0, -1.0], uv: [0.0, 1.0] },
380 Vertex { position: [1.0, 1.0, -1.0], uv: [1.0, 1.0] },
381 Vertex { position: [1.0, 1.0, 1.0], uv: [1.0, 0.0] },
382 Vertex { position: [-1.0, 1.0, 1.0], uv: [0.0, 0.0] },
383 ];
384
385 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
386 label: Some("Cube Vertex Buffer"),
387 contents: bytemuck::cast_slice(&vertices),
388 usage: BufferUsages::VERTEX,
389 })
390 }
391
392 fn create_cube_index_buffer(device: &Device) -> Buffer {
393 let indices: [u16; 36] = [
395 0, 1, 2, 2, 3, 0,
397 4, 5, 6, 6, 7, 4,
399 8, 9, 10, 10, 11, 8,
401 12, 13, 14, 14, 15, 12,
403 16, 17, 18, 18, 19, 16,
405 20, 21, 22, 22, 23, 20,
407 ];
408
409 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
410 label: Some("Cube Index Buffer"),
411 contents: bytemuck::cast_slice(&indices),
412 usage: BufferUsages::INDEX,
413 })
414 }
415
416 pub fn resize(&mut self, width: u32, height: u32) {
417 if width > 0 && height > 0 {
418 self.config.width = width;
419 self.config.height = height;
420 self.surface.configure(&self.device, &self.config);
421 }
422 }
423
424 pub fn update_rotation(&mut self, delta: f32) {
425 self.rotation += delta;
426 }
427
428 pub fn set_rotation(&mut self, rotation: f32) {
429 self.rotation = rotation;
430 }
431
432 pub fn window_id(&self) -> winit::window::WindowId {
433 self.window_id
434 }
435
436
437 pub fn render(&mut self) -> Result<(), SurfaceError> {
438 let output = self.surface.get_current_texture()?;
439 let texture_view = output.texture.create_view(&TextureViewDescriptor::default());
440
441 let depth_texture = self.device.create_texture(&TextureDescriptor {
442 size: Extent3d {
443 width: self.config.width,
444 height: self.config.height,
445 depth_or_array_layers: 1,
446 },
447 mip_level_count: 1,
448 sample_count: 1,
449 dimension: TextureDimension::D2,
450 format: TextureFormat::Depth32Float,
451 usage: TextureUsages::RENDER_ATTACHMENT,
452 view_formats: &[],
453 label: Some("Depth Texture"),
454 });
455
456 let depth_view = depth_texture.create_view(&TextureViewDescriptor::default());
457
458 let model = Matrix4::from_angle_y(Rad(self.rotation));
459 let view_matrix = self.camera.view_matrix();
460 let proj = self.camera.projection_matrix();
461
462 let uniforms = Uniforms {
463 model: model.into(),
464 view: view_matrix.into(),
465 proj: proj.into(),
466 };
467
468 self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
469
470 let mut encoder = self
471 .device
472 .create_command_encoder(&CommandEncoderDescriptor {
473 label: Some("Render Encoder"),
474 });
475
476 {
477 let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
478 label: Some("Render Pass"),
479 color_attachments: &[Some(RenderPassColorAttachment {
480 view: &texture_view,
481 resolve_target: None,
482 ops: Operations {
483 load: LoadOp::Clear(Color {
484 r: 0.1,
485 g: 0.1,
486 b: 0.1,
487 a: 1.0,
488 }),
489 store: StoreOp::Store,
490 },
491 })],
492 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
493 view: &depth_view,
494 depth_ops: Some(Operations {
495 load: LoadOp::Clear(1.0),
496 store: StoreOp::Store,
497 }),
498 stencil_ops: None,
499 }),
500 occlusion_query_set: None,
501 timestamp_writes: None,
502 });
503
504 render_pass.set_pipeline(&self.pipeline);
505 render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
506 render_pass.set_bind_group(1, &self.texture_bind_group, &[]);
507 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
508 render_pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint16);
509 render_pass.draw_indexed(0..36, 0, 0..1);
510 }
511
512 self.queue.submit(std::iter::once(encoder.finish()));
513 output.present();
514
515 Ok(())
516 }
517}