pico_rendering/render_pipeline/
mod.rs

1use pico_ecs::_component_storage::ComponentStorage;
2use pico_ecs::_world::World;
3use pico_transform::{_pivot::Pivot, _position::Position, _size::Size};
4use pico_engine_hardware::Display;
5
6use crate::components::_render_component::RendererComponent;
7use crate::types::_tile_map::TileMap;
8
9mod _constants;
10mod _sprite_renderer;
11mod _tilemap_renderer;
12
13pub use _sprite_renderer::{draw_sprite, shift_buffer};
14
15/// Render system - manages all rendering operations
16/// 
17/// This renderer uses several optimizations for embedded systems:
18/// - Buffer shifting for small camera movements (avoids full redraws)
19/// - Selective tile redrawing under moving entities
20/// - Strip-encoded sprite format for transparency
21/// - Unsafe pointer operations for maximum performance
22pub struct RenderPipeline {
23    prev_cam_x: u16,
24    prev_cam_y: u16,
25    first_frame: bool,
26}
27
28impl RenderPipeline {
29    pub fn new() -> Self {
30        Self {
31            prev_cam_x: 0,
32            prev_cam_y: 0,
33            first_frame: true,
34        }
35    }
36
37    /// Main render function - orchestrates the entire rendering process
38    /// 
39    /// Order of operations:
40    /// 1. Draw background tilemap
41    /// 2. Draw all entities (sprites)
42    /// 3. Draw foreground tilemap
43    /// 4. Flush to display
44    #[inline]
45    pub fn render_direct<const W: usize, const H: usize>(
46        &mut self,
47        world: &World,
48        position_storage: &ComponentStorage<Position>,
49        renderer_storage: &ComponentStorage<RendererComponent>,
50        size_storage: &ComponentStorage<Size>,
51        pivot_storage: &ComponentStorage<Pivot>,
52        background_tilemap: &'static TileMap<W, H>,
53        foreground_tilemap: &'static TileMap<W, H>,
54        display: &mut impl Display,
55    ) {
56        // Layer 1: Background tiles
57        _tilemap_renderer::render_tilemap_layer(
58            self.prev_cam_x,
59            self.prev_cam_y,
60            self.first_frame,
61            world,
62            position_storage,
63            size_storage,
64            pivot_storage,
65            background_tilemap,
66            display,
67            true,  // Allow buffer shifting
68        );
69
70        // Layer 2: Entities (sprites)
71        self.render_entities(
72            world,
73            position_storage,
74            renderer_storage,
75            size_storage,
76            pivot_storage,
77            display,
78        );
79
80        // Layer 3: Foreground tiles
81        _tilemap_renderer::render_tilemap_layer(
82            self.prev_cam_x,
83            self.prev_cam_y,
84            self.first_frame,
85            world,
86            position_storage,
87            size_storage,
88            pivot_storage,
89            foreground_tilemap,
90            display,
91            false, // Don't shift again (background already shifted)
92        );
93
94        // Update camera tracking
95        self.prev_cam_x = world.camera.x;
96        self.prev_cam_y = world.camera.y;
97        self.first_frame = false;
98
99        // Flush frame buffer to display
100        display.display_buffer();
101    }
102
103    /// Renders all visible entities (sprites)
104    #[inline]
105    fn render_entities(
106        &self,
107        world: &World,
108        position_storage: &ComponentStorage<Position>,
109        renderer_storage: &ComponentStorage<RendererComponent>,
110        size_storage: &ComponentStorage<Size>,
111        pivot_storage: &ComponentStorage<Pivot>,
112        display: &mut impl Display,
113    ) {
114        for (_entity, transform, renderer, size, pivot) in world.query4(
115            position_storage,
116            renderer_storage,
117            size_storage,
118            pivot_storage,
119        ) {
120            // Calculate sprite screen position (accounting for pivot and camera)
121            let sprite_x = transform.x as i16 - pivot.x as i16 - world.camera.x as i16;
122            let sprite_y = transform.y as i16 - pivot.y as i16 - world.camera.y as i16;
123
124            draw_sprite(
125                display,
126                &renderer.texture[renderer.sprite.data_start..renderer.sprite.data_end],
127                sprite_x,
128                sprite_y,
129                size.width as usize,
130                size.height as usize,
131                renderer.sprite.is_opaque,
132            );
133        }
134    }
135}