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 - renders entities only (no tilemaps)
38    /// 
39    /// Order of operations:
40    /// 1. Clear buffer
41    /// 2. Draw all entities (sprites)
42    /// 3. Flush to display
43    /// 
44    /// Use this when you don't need tilemap layers
45    #[inline]
46    pub fn render_direct(
47        &mut self,
48        world: &World,
49        position_storage: &ComponentStorage<Position>,
50        renderer_storage: &ComponentStorage<RendererComponent>,
51        size_storage: &ComponentStorage<Size>,
52        pivot_storage: &ComponentStorage<Pivot>,
53        display: &mut impl Display,
54    ) {
55        // Clear the frame buffer (fill with black)
56        for pixel in display.frame_buffer().iter_mut() {
57            *pixel = 0x03E0;
58        }
59
60        // Render entities
61        self.render_entities(
62            world,
63            position_storage,
64            renderer_storage,
65            size_storage,
66            pivot_storage,
67            display,
68        );
69
70        // Flush frame buffer to display
71        display.display_buffer();
72    }
73
74    /// Full render function with tilemap layers
75    /// 
76    /// Order of operations:
77    /// 1. Draw background tilemap
78    /// 2. Draw all entities (sprites)
79    /// 3. Draw foreground tilemap
80    /// 4. Flush to display
81    #[inline]
82    pub fn render_with_tiles<const W: usize, const H: usize>(
83        &mut self,
84        world: &World,
85        position_storage: &ComponentStorage<Position>,
86        renderer_storage: &ComponentStorage<RendererComponent>,
87        size_storage: &ComponentStorage<Size>,
88        pivot_storage: &ComponentStorage<Pivot>,
89        background_tilemap: &'static TileMap<W, H>,
90        foreground_tilemap: &'static TileMap<W, H>,
91        display: &mut impl Display,
92    ) {
93        // Layer 1: Background tiles
94        _tilemap_renderer::render_tilemap_layer(
95            self.prev_cam_x,
96            self.prev_cam_y,
97            self.first_frame,
98            world,
99            position_storage,
100            size_storage,
101            pivot_storage,
102            background_tilemap,
103            display,
104            true,  // Allow buffer shifting
105        );
106
107        // Layer 2: Entities (sprites)
108        self.render_entities(
109            world,
110            position_storage,
111            renderer_storage,
112            size_storage,
113            pivot_storage,
114            display,
115        );
116
117        // Layer 3: Foreground tiles
118        _tilemap_renderer::render_tilemap_layer(
119            self.prev_cam_x,
120            self.prev_cam_y,
121            self.first_frame,
122            world,
123            position_storage,
124            size_storage,
125            pivot_storage,
126            foreground_tilemap,
127            display,
128            false, // Don't shift again (background already shifted)
129        );
130
131        // Update camera tracking
132        self.prev_cam_x = world.camera.x;
133        self.prev_cam_y = world.camera.y;
134        self.first_frame = false;
135
136        // Flush frame buffer to display
137        display.display_buffer();
138    }
139
140    /// Renders all visible entities (sprites)
141    #[inline]
142    fn render_entities(
143        &self,
144        world: &World,
145        position_storage: &ComponentStorage<Position>,
146        renderer_storage: &ComponentStorage<RendererComponent>,
147        size_storage: &ComponentStorage<Size>,
148        pivot_storage: &ComponentStorage<Pivot>,
149        display: &mut impl Display,
150    ) {
151        for (_entity, transform, renderer, size, pivot) in world.query4(
152            position_storage,
153            renderer_storage,
154            size_storage,
155            pivot_storage,
156        ) {
157            // Calculate sprite screen position (accounting for pivot and camera)
158            let sprite_x = transform.x as i16 - pivot.x as i16 - world.camera.x as i16;
159            let sprite_y = transform.y as i16 - pivot.y as i16 - world.camera.y as i16;
160
161            draw_sprite(
162                display,
163                &renderer.texture[renderer.sprite.data_start..renderer.sprite.data_end],
164                sprite_x,
165                sprite_y,
166                size.width as usize,
167                size.height as usize,
168                renderer.sprite.is_opaque,
169            );
170        }
171    }
172}