Skip to main content

arcane_engine/renderer/
mod.rs

1mod gpu;
2mod sprite;
3mod texture;
4mod camera;
5mod tilemap;
6mod lighting;
7pub mod font;
8
9pub use gpu::GpuContext;
10pub use sprite::{SpriteCommand, SpritePipeline};
11pub use texture::{TextureId, TextureStore};
12pub use camera::Camera2D;
13pub use tilemap::{Tilemap, TilemapStore};
14pub use lighting::{LightingState, LightingUniform, PointLight, LightData, MAX_LIGHTS};
15
16use anyhow::Result;
17
18/// Top-level renderer that owns the GPU context, sprite pipeline, and textures.
19pub struct Renderer {
20    pub gpu: GpuContext,
21    pub sprites: SpritePipeline,
22    pub textures: TextureStore,
23    pub camera: Camera2D,
24    pub lighting: LightingState,
25    /// Sprite commands queued for the current frame.
26    pub frame_commands: Vec<SpriteCommand>,
27}
28
29impl Renderer {
30    /// Create a new renderer attached to a winit window.
31    pub fn new(window: std::sync::Arc<winit::window::Window>) -> Result<Self> {
32        let gpu = GpuContext::new(window)?;
33        let sprites = SpritePipeline::new(&gpu);
34        let textures = TextureStore::new();
35        let camera = Camera2D::default();
36        Ok(Self {
37            gpu,
38            sprites,
39            textures,
40            camera,
41            lighting: LightingState::default(),
42            frame_commands: Vec::new(),
43        })
44    }
45
46    /// Render the current frame's sprite commands and present.
47    pub fn render_frame(&mut self) -> Result<()> {
48        let output = self.gpu.surface.get_current_texture()?;
49        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
50
51        let mut encoder = self.gpu.device.create_command_encoder(
52            &wgpu::CommandEncoderDescriptor { label: Some("frame_encoder") },
53        );
54
55        // Sort by layer, then by texture for batching
56        self.frame_commands.sort_by(|a, b| {
57            a.layer.cmp(&b.layer).then(a.texture_id.cmp(&b.texture_id))
58        });
59
60        let lighting_uniform = self.lighting.to_uniform();
61
62        self.sprites.render(
63            &self.gpu,
64            &self.textures,
65            &self.camera,
66            &lighting_uniform,
67            &self.frame_commands,
68            &view,
69            &mut encoder,
70        );
71
72        self.gpu.queue.submit(std::iter::once(encoder.finish()));
73        output.present();
74
75        self.frame_commands.clear();
76        Ok(())
77    }
78
79    /// Resize the surface when the window size changes.
80    pub fn resize(&mut self, width: u32, height: u32) {
81        if width > 0 && height > 0 {
82            self.gpu.config.width = width;
83            self.gpu.config.height = height;
84            self.gpu.surface.configure(&self.gpu.device, &self.gpu.config);
85            self.camera.viewport_size = [width as f32, height as f32];
86        }
87    }
88}