use crate::{ client::Client, skin_manager::{SkinManager, init_sprite_textures}, }; use anyhow::{Result, anyhow}; use log::warn; use pollster::FutureExt; use std::{ mem::swap, random::random, rc::Rc, sync::{Arc, mpsc::channel}, time::Instant, }; use twgame::{DdnetWorld, Map}; use twgpu::{ Camera, GpuCamera, TwRenderPass, blit::Blit, buffer::GpuBuffer, map::{GpuMapData, GpuMapRender, GpuMapStatic}, shared::{Clock, Rng}, sprites::{ ParticleData, ParticleGroup, SpriteRenderContext, SpriteTextures, SpritesData, SpritesStatic, TeeStorage, }, textures::Samplers, }; use twmap::TwMap; use twsnap::Snap; use vek::Vec2; use wgpu::{ Backends, Color, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, Instance, InstanceDescriptor, Limits, LoadOp, MaintainBase, MemoryHints, Operations, PowerPreference, Queue, RenderPassColorAttachment, RenderPassDescriptor, RequestAdapterOptions, StoreOp, Surface, SurfaceConfiguration, TextureFormat, TextureViewDescriptor, }; use winit::{dpi::PhysicalSize, window::Window}; pub struct Renderer<'a> { client: Arc, device: Arc, queue: Arc, window: &'a Window, surface: Surface<'a>, surface_configuration: SurfaceConfiguration, twmap: TwMap, map_render: GpuMapRender, map_data: GpuMapData, camera: Camera, sprites_data: SpritesData, particle_data: ParticleData, skin_manager: SkinManager, sprites_static: SpritesStatic, sprite_textures: SpriteTextures, gpu_camera: Arc>, start: Instant, rng: Rng, need_reconfigure: bool, clock: Clock, map: Rc, from_snap: Snap, to_snap: Snap, tees: TeeStorage, clock_set: bool, local_time_offset: i64, simulation: DdnetWorld, } impl<'a> Renderer<'a> { pub fn new(window: &'a Window, client: Arc) -> Result { let texture_format = TextureFormat::Bgra8Unorm; let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), ..Default::default() }); let surface = instance.create_surface(window)?; let adapter = instance .request_adapter(&RequestAdapterOptions { power_preference: PowerPreference::HighPerformance, compatible_surface: Some(&surface), force_fallback_adapter: false, }) .block_on() .ok_or(anyhow!("no adapter found"))?; eprintln!("Adapter {:#?}", adapter.limits()); let (device, queue) = adapter .request_device( &DeviceDescriptor { label: None, required_features: Features::default(), required_limits: Limits::default(), memory_hints: MemoryHints::Performance, }, None, ) .block_on()?; let device = Arc::new(device); let queue = Arc::new(queue); let mut twmap = TwMap::parse(include_bytes!( "/home/muffin/etc/ddnet-maps/types/novice/maps/Mint.map" ))?; twmap.load()?; let samplers = Arc::new(Samplers::new(&device)); let mut camera = Camera::new(1.); camera.position = Vec2 { x: 33., y: 84. }; let gpu_camera = Arc::new(GpuCamera::upload(&camera, &device)); let map_static = GpuMapStatic::new(texture_format, &device); let map_data = GpuMapData::upload(&twmap, &device, &queue); let map_render = map_static.prepare_render(&twmap, &map_data, &gpu_camera, &samplers, &device); let sprites_static = SpritesStatic::new(texture_format, &device); let sprites_data = SpritesData::new(&device); let particle_data = ParticleData::new(0.0, &device); let mut sprite_textures = SpriteTextures::new(&device, &queue, &gpu_camera, &samplers); init_sprite_textures(&mut sprite_textures, twmap.version, &device, &queue)?; let blit = Arc::new(Blit::new(&device)); let skin_manager = SkinManager::new(blit, &mut sprite_textures, device.clone(), queue.clone()); let mut surface_configuration = surface .get_default_config(&adapter, 256, 256) .ok_or(anyhow!("no surface config"))?; surface_configuration.format = texture_format; // surface_configuration.format.remove_srgb_suffix(); surface.configure(&device, &surface_configuration); let map = Rc::new(Map::try_from(&mut twmap).map_err(|e| anyhow!("{e}"))?); let (sender, _) = channel(); let (_, receiver) = channel(); let simulation = DdnetWorld::new_with_map(map.clone(), sender, receiver).map_err(|e| anyhow!("{e}"))?; Ok(Self { start: Instant::now(), map_data, sprites_static, window, device, queue, twmap, surface, surface_configuration, map_render, camera, gpu_camera, particle_data, skin_manager, sprites_data, sprite_textures, simulation, clock: Clock::default(), from_snap: Snap::default(), to_snap: Snap::default(), map, local_time_offset: 0, clock_set: false, tees: TeeStorage::default(), rng: Rng::new(random()), need_reconfigure: false, client, }) } pub fn resize(&mut self, size: PhysicalSize) { self.surface_configuration.width = size.width; self.surface_configuration.height = size.height; self.camera .switch_aspect_ratio(size.width as f32 / size.height as f32); self.reconfigure(); } pub fn reconfigure(&mut self) { self.surface .configure(&self.device, &self.surface_configuration); self.need_reconfigure = false; } pub fn redraw(&mut self) -> Result<()> { if self.need_reconfigure { self.reconfigure(); } let target = self.surface.get_current_texture()?; if target.suboptimal { warn!("suboptimal surface, need reconfigure"); self.need_reconfigure = true; } let time = self.start.elapsed().as_micros() as i64; let local_tick = (time + self.local_time_offset) as f64 / 1_000_000. * 50.; let size = Vec2::new( self.surface_configuration.width, self.surface_configuration.height, ); // self.simulation.sn if self.clock_set { self.clock.update(local_tick); } for (player_id, player) in self.from_snap.players.iter() { if player.local { if let Some(tees) = self.tees.lerp_tee( player_id, &self.clock, &self.from_snap, &self.to_snap, &self.map, ) { self.camera.position = self.clock.lerp_tee_vec(tees, |tee| tee.pos); } } } { let process = if let Ok((new_tick, new_snap)) = self.client.incoming.try_recv() { eprintln!("{new_tick} {}", new_snap.players.len()); if self.clock_set { self.clock.next_tick(new_tick); } else { self.clock = Clock::new(new_tick); self.clock_set = true; } self.local_time_offset = new_tick as i64 * 20_000 - time; self.skin_manager.queue_snap_skins(&new_snap); swap(&mut self.from_snap, &mut self.to_snap); self.to_snap = new_snap; true } else { false }; let mut ctx = SpriteRenderContext { clock: &self.clock, from_snap: &self.from_snap, to_snap: &self.to_snap, tees: &mut self.tees, map: &self.map, particles: &mut self.particle_data, textures: &self.sprite_textures, rng: &mut self.rng, }; if process { ctx.process_events(); } self.sprites_data.clear(); ctx.generate_all_sprites(&mut self.sprites_data); } self.map_data .update(&self.twmap, &self.camera, size, time, time, &self.queue); self.gpu_camera.update(&self.camera, &self.queue); self.skin_manager.poll_queued(&mut self.sprite_textures); self.sprites_data.upload(&self.device, &self.queue); self.particle_data.upload(&self.device, &self.queue); let target_view = target .texture .create_view(&TextureViewDescriptor::default()); let mut commands = self .device .create_command_encoder(&CommandEncoderDescriptor { label: None }); { let rpass = commands.begin_render_pass(&RenderPassDescriptor { label: None, color_attachments: &[Some(RenderPassColorAttachment { view: &target_view, resolve_target: None, ops: Operations { load: LoadOp::Clear(Color::BLACK), store: StoreOp::Store, }, })], ..Default::default() }); let mut rpass = TwRenderPass::new(rpass, size, &self.camera); self.map_render.render_background(&mut rpass); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::Trails, &self.sprite_textures, &mut rpass.render_pass, ); self.sprites_static.render( &self.sprites_data, &self.sprite_textures, &mut rpass.render_pass, ); self.map_render.render_foreground(&mut rpass); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::Explosions, &self.sprite_textures, &mut rpass.render_pass, ); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::Extra, &self.sprite_textures, &mut rpass.render_pass, ); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::General, &self.sprite_textures, &mut rpass.render_pass, ); } let submission = self.queue.submit(Some(commands.finish())); self.device .poll(MaintainBase::WaitForSubmissionIndex(submission)); target.present(); self.window.request_redraw(); Ok(()) } }