Skip to content

Commit

Permalink
feat(font): add Context::font_sprite for getting a single glyph fro…
Browse files Browse the repository at this point in the history
…m a font as a sprite
  • Loading branch information
tversteeg committed Oct 28, 2024
1 parent e00109f commit b96591f
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 125 deletions.
2 changes: 1 addition & 1 deletion examples/sprite_pivot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! (pivot: Middle)
//! ```
use chuot::{config::RotationAlgorithm, Config, Context, Game, Pivot};
use chuot::{Config, Context, Game, Pivot, config::RotationAlgorithm};

/// Define a game state for our example.
#[derive(Default)]
Expand Down
9 changes: 7 additions & 2 deletions examples/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ impl Game for GameState {
ctx.text("Beachball", "Hello world!")
// Use the UI camera which draws the center in the top left
.use_ui_camera()
// Draw at the middle of the screen
.translate((1.0, 40.0))
// Draw at the top of the screen
.translate((1.0, 1.0))
// Draw the text on the screen
.draw();

// We can also load a glyph from a font as a sprite asset and use it like a sprite would be used
ctx.font_glyph("Beachball", '@')
// Draw the sprite on the screen, it is centered because by default it uses the main camera
.draw();
}

/// Do nothing during the update loop.
Expand Down
2 changes: 1 addition & 1 deletion src/assets/loadable/sprite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use rgb::RGBA8;
use super::Loadable;
use crate::{
assets::{
loader::{png::PngLoader, ron::RonLoader},
Id,
loader::{png::PngLoader, ron::RonLoader},
},
context::ContextInner,
graphics::atlas::TextureRef,
Expand Down
61 changes: 61 additions & 0 deletions src/context/load.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! Define how assets can be loaded from different resources in contexts.
use std::rc::Rc;

use super::ContextInner;
use crate::assets::loadable::sprite::Sprite;

/// How a sprite should be loaded.
pub trait LoadMethod {
/// Return a reference to the actual sprite.
#[allow(private_interfaces)]
fn sprite(&self, ctx: &mut ContextInner) -> Rc<Sprite>;
}

/// Load a sprite from a path reference.
pub struct ByPath<'path>(&'path str);

impl<'path> ByPath<'path> {
/// Create from an existing path.
#[inline]
#[must_use]
pub const fn new(path: &'path str) -> Self {
Self(path)
}

/// Get the internal path.
#[inline]
#[must_use]
pub const fn path(&self) -> &'path str {
self.0
}
}

impl LoadMethod for ByPath<'_> {
#[inline]
#[must_use]
#[allow(private_interfaces)]
fn sprite(&self, ctx: &mut ContextInner) -> Rc<Sprite> {
ctx.sprite(self.0)
}
}

/// Load a sprite directly from memory.
pub struct FromMemory(Rc<Sprite>);

impl FromMemory {
/// Create from an existing sprite.
#[inline]
#[must_use]
pub(crate) fn new(sprite: Sprite) -> Self {
Self(Rc::new(sprite))
}
}

impl LoadMethod for FromMemory {
#[inline]
#[allow(private_interfaces)]
fn sprite(&self, _ctx: &mut ContextInner) -> Rc<Sprite> {
Rc::clone(&self.0)
}
}
2 changes: 2 additions & 0 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
pub mod audio;
pub mod camera;
pub(crate) mod extensions;
#[doc(hidden)]
pub mod load;
pub mod sprite;
pub mod text;

Expand Down
79 changes: 49 additions & 30 deletions src/context/sprite/draw.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
//! Different implementations for drawing a sprite.
use super::SpriteContext;
use crate::context::extensions::{
camera::IsUiCamera,
pivot::Pivot,
rotate::Rotation,
scale::Scaling,
translate::{PreviousTranslation, Translation},
Empty,
use crate::context::{
extensions::{
Empty,
camera::IsUiCamera,
pivot::Pivot,
rotate::Rotation,
scale::Scaling,
translate::{PreviousTranslation, Translation},
},
load::LoadMethod,
};

/// Nothing.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Empty, Empty, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Empty, Empty, Empty, Empty, O, C>
{
/// Draw the sprite to the screen at the zero coordinate of the camera.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand All @@ -23,7 +28,7 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Empty, Empty,
pub fn draw(self) {
self.ctx.write(|ctx| {
let (sprite, affine_matrix) =
ctx.sprite_with_base_affine_matrix(self.path, C::is_ui_camera(), self.pivot);
ctx.sprite_with_base_affine_matrix(&self.load, C::is_ui_camera(), self.pivot);

// Push the graphics
ctx.graphics
Expand Down Expand Up @@ -81,7 +86,9 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Empty, Empty,
}

/// Only translation.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Empty, Empty, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, Empty, Empty, Empty, O, C>
{
/// Draw the sprite to the screen.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand All @@ -93,7 +100,7 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Empty, E
pub fn draw(self) {
self.ctx.write(|ctx| {
let (sprite, mut affine_matrix) =
ctx.sprite_with_base_affine_matrix(self.path, C::is_ui_camera(), self.pivot);
ctx.sprite_with_base_affine_matrix(&self.load, C::is_ui_camera(), self.pivot);

// Translate the coordinates
affine_matrix.translation.x += self.translation.x;
Expand Down Expand Up @@ -155,8 +162,8 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Empty, E
}

/// Translation and previous translation.
impl<O: Pivot, C: IsUiCamera>
SpriteContext<'_, '_, Translation, PreviousTranslation, Empty, Empty, O, C>
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, PreviousTranslation, Empty, Empty, O, C>
{
/// Draw the sprite to the screen, interpolating the position in the render step.
///
Expand All @@ -169,7 +176,7 @@ impl<O: Pivot, C: IsUiCamera>
pub fn draw(self) {
self.ctx.write(|ctx| {
let (sprite, mut affine_matrix) =
ctx.sprite_with_base_affine_matrix(self.path, C::is_ui_camera(), self.pivot);
ctx.sprite_with_base_affine_matrix(&self.load, C::is_ui_camera(), self.pivot);

// Translate the coordinates
affine_matrix.translation.x += crate::math::lerp(
Expand Down Expand Up @@ -239,7 +246,9 @@ impl<O: Pivot, C: IsUiCamera>
}

/// Only rotation.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Rotation, Empty, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Empty, Empty, Rotation, Empty, O, C>
{
/// Draw the sprite rotated to the screen at the zero coordinate of the camera.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand Down Expand Up @@ -302,7 +311,9 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Rotation, Empt
}

/// Only scaling.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Empty, Scaling, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Empty, Empty, Empty, Scaling, O, C>
{
/// Draw the sprite scaled to the screen at the zero coordinate of the camera.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand Down Expand Up @@ -365,7 +376,9 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Empty, Scaling
}

/// Translation and rotation.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Rotation, Empty, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, Empty, Rotation, Empty, O, C>
{
/// Draw the sprite rotated to the screen.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand Down Expand Up @@ -428,8 +441,8 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Rotation
}

/// Translation, previous translation and rotation.
impl<O: Pivot, C: IsUiCamera>
SpriteContext<'_, '_, Translation, PreviousTranslation, Rotation, Empty, O, C>
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, PreviousTranslation, Rotation, Empty, O, C>
{
/// Draw the sprite rotated to the screen, interpolating in the render step.
///
Expand Down Expand Up @@ -493,7 +506,9 @@ impl<O: Pivot, C: IsUiCamera>
}

/// Translation and scaling.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Empty, Scaling, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, Empty, Empty, Scaling, O, C>
{
/// Draw the sprite scaled to the screen.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand Down Expand Up @@ -556,8 +571,8 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Empty, S
}

/// Translation, previous translation and scaling.
impl<O: Pivot, C: IsUiCamera>
SpriteContext<'_, '_, Translation, PreviousTranslation, Empty, Scaling, O, C>
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, PreviousTranslation, Empty, Scaling, O, C>
{
/// Draw the sprite scaled to the screen, interpolating in the render step.
///
Expand Down Expand Up @@ -621,7 +636,9 @@ impl<O: Pivot, C: IsUiCamera>
}

/// Rotation and scaling.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Rotation, Scaling, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Empty, Empty, Rotation, Scaling, O, C>
{
/// Draw the sprite rotated and scaled to the screen at the zero coordinate of the camera.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand Down Expand Up @@ -684,7 +701,9 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Empty, Empty, Rotation, Scal
}

/// Translation, rotation and scaling.
impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Rotation, Scaling, O, C> {
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, Empty, Rotation, Scaling, O, C>
{
/// Draw the sprite rotated and scaled to the screen.
///
/// Sprites that are drawn last are always shown on top of sprites that are drawn earlier.
Expand All @@ -696,7 +715,7 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Rotation
pub fn draw(self) {
self.ctx.write(|ctx| {
// Push the instance if the texture is already uploaded
let sprite = ctx.sprite(self.path);
let sprite = &self.load.sprite(ctx);

// Get the camera to draw the sprite with
let camera = ctx.camera(C::is_ui_camera());
Expand Down Expand Up @@ -768,7 +787,7 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Rotation
{
self.ctx.write(|ctx| {
// Push the instance if the texture is already uploaded
let sprite = ctx.sprite(self.path);
let sprite = &self.load.sprite(ctx);

// Get the camera to draw the sprite with
let camera = ctx.camera(C::is_ui_camera());
Expand Down Expand Up @@ -809,8 +828,8 @@ impl<O: Pivot, C: IsUiCamera> SpriteContext<'_, '_, Translation, Empty, Rotation
}

/// Translation, previous translation, rotation and scaling.
impl<O: Pivot, C: IsUiCamera>
SpriteContext<'_, '_, Translation, PreviousTranslation, Rotation, Scaling, O, C>
impl<L: LoadMethod, O: Pivot, C: IsUiCamera>
SpriteContext<'_, L, Translation, PreviousTranslation, Rotation, Scaling, O, C>
{
/// Draw the sprite rotated and scaled to the screen, interpolating the position in the render step.
///
Expand All @@ -823,7 +842,7 @@ impl<O: Pivot, C: IsUiCamera>
pub fn draw(self) {
self.ctx.write(|ctx| {
// Push the instance if the texture is already uploaded
let sprite = ctx.sprite(self.path);
let sprite = &self.load.sprite(ctx);

// Get the camera to draw the sprite with
let camera = ctx.camera(C::is_ui_camera());
Expand Down Expand Up @@ -895,7 +914,7 @@ impl<O: Pivot, C: IsUiCamera>
{
self.ctx.write(|ctx| {
// Push the instance if the texture is already uploaded
let sprite = ctx.sprite(self.path);
let sprite = &self.load.sprite(ctx);

// Get the camera to draw the sprite with
let camera = ctx.camera(C::is_ui_camera());
Expand Down
Loading

0 comments on commit b96591f

Please sign in to comment.