Skip to main content

Renderer

Struct Renderer 

Source
pub struct Renderer {
    pub window: Arc<Window>,
    pub post_process: PostProcessStack,
    /* private fields */
}

Fields§

§window: Arc<Window>§post_process: PostProcessStack

Post-processing stack for “juice” effects.

Implementations§

Source§

impl Renderer

Source

pub async fn new( window: Arc<Window>, png_bytes: &[u8], tile_w: u32, tile_h: u32, use_scanlines: bool, ) -> Self

Source

pub fn load_sprite_folder(&mut self, path: &str, tile_w: u32, tile_h: u32)

Load all .png files from path (recursively) into the sprite atlas.

Source

pub fn get_sprite_data(&self, name: &str) -> Option<SpriteData>

Returns the metadata (UVs, spans) for a named sprite if it exists.

Source

pub fn resize(&mut self, new_size: PhysicalSize<u32>)

Source

pub fn update_camera(&mut self, uniform: &CameraUniform)

Upload a new camera view-projection matrix to the GPU.

Source

pub fn update_entity_offsets(&mut self, offsets: &[[f32; 4]])

Upload an array of [f32; 4] offsets for entity animation.

Source

pub fn surface_format(&self) -> TextureFormat

Source

pub fn set_mtsdf_distance_range(&mut self, range: f32)

Update the MTSDF distance range used by the text shader.

Call this once after registering a font via TextLayer::set_font, passing font.distance_range from the loaded Font. The atlas dimensions are taken from the actual GPU texture and do not need to be supplied again.

Examples found in repository?
examples/pathfinding.rs (line 123)
121    fn on_enter(&mut self, engine: &mut jEngine) {
122        if let Ok(font) = Font::from_mtsdf_json(DEFAULT_FONT_METADATA) {
123            engine.renderer.set_mtsdf_distance_range(font.distance_range);
124            engine.ui.text.set_font(font);
125        }
126        
127        // Load sound effects
128        engine.audio.load_sound("move", "resources/audio/UI_selection.wav");
129        engine.audio.load_sound("toggle", "resources/audio/UI_click.wav");
130    }
More examples
Hide additional examples
examples/stress_test.rs (line 152)
149    fn render(&mut self, engine: &mut jEngine) {
150        if !self.font_loaded {
151            if let Ok(font) = jengine::renderer::text::Font::from_mtsdf_json(DEFAULT_FONT_METADATA) {
152                engine.renderer.set_mtsdf_distance_range(font.distance_range);
153                engine.ui.text.set_font(font);
154            }
155            self.font_loaded = true;
156        }
157
158        engine.clear();
159
160        // ── Draw Entities (as small circles for speed) ──
161        for (_e, (_m, pos)) in self.world.query_multi::<(EntityMarker, Position)>() {
162            engine.draw_particle(pos.x, pos.y, Color([0.4, 0.7, 1.0, 1.0]), 4.0);
163        }
164
165        // ── Draw Particles ──
166        for (_e, (_l, pos)) in self.world.query_multi::<(Life, Position)>() {
167            engine.draw_particle(pos.x, pos.y, Color([1.0, 0.5, 0.2, 0.8]), 2.0);
168        }
169
170        // ── Stats UI ──
171        let th = engine.tile_height() as f32;
172        
173        engine.ui.ui_rect(0.0, 0.0, 350.0, th * 8.0, Color([0.0, 0.0, 0.0, 0.7]));
174        
175        let mut y = 10.0;
176        let lines = [
177            format!("STRESS TEST - [Space] for Nova"),
178            format!("Entities:  {} (Q/W to adj)", self.entity_target_count),
179            format!("P-Rate:    {} / frame (A/S to adj)", self.particle_spawn_rate),
180            format!("Total ECS: {}", self.world.entity_count()),
181            format!("FPS:       {:.1}", 1.0 / engine.dt().max(0.001)),
182        ];
183
184        for line in lines {
185            engine.ui.ui_text(10.0, y, &line, Color::WHITE, Color::TRANSPARENT, None);
186            y += th;
187        }
188    }
examples/particles.rs (line 266)
263    fn render(&mut self, engine: &mut jEngine) {
264        if !self.font_loaded {
265            if let Ok(font) = jengine::renderer::text::Font::from_mtsdf_json(DEFAULT_FONT_METADATA) {
266                engine.renderer.set_mtsdf_distance_range(font.distance_range);
267                engine.ui.text.set_font(font);
268            }
269            self.font_loaded = true;
270        }
271
272        engine.clear();
273
274        let sw = engine.renderer.window.inner_size().width as f32;
275        let sh = engine.renderer.window.inner_size().height as f32;
276
277        // Set dark navy background using the tile grid (Pass 1)
278        // This ensures particles (Pass 3) are drawn ON TOP of the background.
279        for y in 0..engine.grid_height() {
280            for x in 0..engine.grid_width() {
281                engine.set_background(x, y, Color([0.01, 0.01, 0.02, 1.0]));
282            }
283        }
284
285        // --- Render System ---
286        for (_e, (pos, life, p)) in self.world.query_multi::<(Position, Life, Particle)>() {
287            let t = (life.current / life.max).clamp(0.0, 1.0);
288            
289            // Lerp color
290            let mut c = [0.0; 4];
291            for i in 0..4 {
292                c[i] = p.color_end[i] + (p.color_start[i] - p.color_end[i]) * t;
293            }
294            
295            // Lerp size
296            let size = p.size_end + (p.size_start - p.size_end) * t;
297            
298            engine.draw_particle(pos.x, pos.y, Color(c), size);
299        }
300
301        // --- UI ---
302        let tw = engine.tile_width() as f32;
303        let th = engine.tile_height() as f32;
304        
305        engine.ui.ui_text(tw, th, "PARTICLE SHOWCASE", Color::WHITE, Color::TRANSPARENT, Some(48.0));
306        
307        let mut y = th * 4.0;
308        let help = [
309            "MOUSE LCLICK : Spawn Explosion",
310            "[S] KEY      : Spawn Slash (at mouse)",
311            "[G] KEY      : Spawn Glitch (at mouse)",
312            "CONTINUOUS   : Fire & Smoke (bottom)",
313            "[ESC]        : Quit demo"
314        ];
315        
316        for line in help {
317            engine.ui.ui_text(tw, y, line, Color([0.6, 0.7, 0.7, 1.0]), Color::TRANSPARENT, None);
318            y += th * 1.2;
319        }
320
321        y += th;
322        let pp_help = [
323            format!("[1] Scanlines: {}", if self.pp.scanlines { "ON" } else { "OFF" }),
324            format!("[2] Vignette:  {}", if self.pp.vignette  { "ON" } else { "OFF" }),
325            format!("[3] Chromatic: {}", if self.pp.chromatic { "ON" } else { "OFF" }),
326            format!("[4] Bloom:     {}", if self.pp.bloom     { "ON" } else { "OFF" }),
327        ];
328        for line in pp_help {
329            engine.ui.ui_text(tw, y, &line, Color([0.4, 0.9, 0.6, 1.0]), Color::TRANSPARENT, None);
330            y += th * 1.2;
331        }
332        
333        let count = self.world.entity_count();
334        engine.ui.ui_text(sw - tw * 18.0, sh - th * 2.5, &format!("Active Entities: {}", count), Color::CYAN, Color::TRANSPARENT, Some(20.0));
335    }
Source

pub fn render( &mut self, char_verts: &[TileVertex], sprite_verts: &[TileVertex], particle_verts: &[ParticleVertex], ui_verts: &[UIVertex], text_verts: &[TextVertex], text_indices: &[u16], ) -> Result<(), SurfaceError>

Render one frame.

Draw order within the single render pass:

  1. char_verts — character tile atlas (bg fills + char glyphs) [camera]
  2. sprite_verts — sprite atlas (static and animated sprites) [camera]
  3. particle_verts — particle pipeline [camera]
  4. ui_verts — UI solid fills (TileVertex, Layer 2) [screen]
  5. text_verts — MTSDF text (Labels + ui_char_at) [screen]

Passes 1–3 use the camera bind group (scroll/zoom). Passes 4–5 use the plain projection bind group (screen-fixed).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,