1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use crate::{math::*, wgpu_ext::*, *};

/////////////////////////////////////////////////////////////////////////////
// struct SpriteRenderCommand
/////////////////////////////////////////////////////////////////////////////

/// Builder for a [`Sprite`] render call
///
/// Defaults:
///
/// * **Pivot**: `(0,0)`
/// * **Scale**: `(1,1)`
/// * **Angle**: `0`
/// * **Diffuse Color**: `Color::WHITE`
///
/// The location refers to the location of the pivot of the sprite. The pivot
/// of the sprite is relative to the top left of the sprite.
///
/// # Example
///
/// ```no_run
/// # use riddle::{common::Color, image::*, platform::*, renderer::*, math::*, *};
/// # fn main() -> Result<(), RiddleError> {
/// # let rdl =  RiddleLib::new()?;
/// # let window = WindowBuilder::new().build(rdl.context())?;
/// # let renderer = Renderer::new_from_window(&window)?;
/// # let img = Image::new(100, 100);
/// let sprite = SpriteBuilder::new(img).build(&renderer)?;
///
/// let mut render_ctx = renderer.begin_render()?;
/// render_ctx.clear(Color::WHITE)?;
///
/// // Render the sprite
/// SpriteRenderCommand::new(vec2(0.0, 0.0))
///     .with_scale(vec2(1.0, 2.0))
///     .with_color(Color::RED)
///     .render(&mut render_ctx, &sprite)?;
///
/// render_ctx.present()?;
/// # Ok(()) }
/// ```
#[derive(Clone, Debug)]
pub struct SpriteRenderCommand {
    pub(crate) location: Vector2<f32>,
    pub(crate) pivot: Vector2<f32>,
    pub(crate) scale: Vector2<f32>,
    pub(crate) angle: f32,
    pub(crate) diffuse_color: Color<f32>,
}

impl SpriteRenderCommand {
    /// New render command with default args, at the specified location
    pub fn new<T: Into<Vector2<f32>>>(location: T) -> Self {
        let mut args = Self::default();
        args.at(location);
        args
    }

    /// Set the location of the sprite, specifying where the pivot should
    /// be placed.
    #[inline]
    pub fn at<T: Into<Vector2<f32>>>(&mut self, location: T) -> &mut Self {
        self.location = location.into();
        self
    }

    /// Set the pivot of the sprite, relative to the top left of the sprite
    #[inline]
    pub fn with_pivot<T: Into<Vector2<f32>>>(&mut self, pivot: T) -> &mut Self {
        self.pivot = pivot.into();
        self
    }

    /// Set the scale at which the sprite will be rendered
    pub fn with_scale<T: Into<Vector2<f32>>>(&mut self, scale: T) -> &mut Self {
        self.scale = scale.into();
        self
    }

    /// Set the angle at which the sprite will be rendered, in radians.
    pub fn with_angle(&mut self, angle: f32) -> &mut Self {
        self.angle = angle;
        self
    }

    /// Set the diffuse color of the sprite, which will be multiplied by the sprite
    /// colors.
    pub fn with_color(&mut self, color: Color<f32>) -> &mut Self {
        self.diffuse_color = color;
        self
    }

    /// Invoke the render command, for the given sprite, in the specified context
    pub fn render<Device>(
        &self,
        render_ctx: &mut impl RenderContext,
        sprite: &WGPUSprite<Device>,
    ) -> Result<()>
    where
        Device: WGPUDevice,
    {
        sprite.render(render_ctx, self)
    }
}

impl Default for SpriteRenderCommand {
    fn default() -> Self {
        SpriteRenderCommand {
            location: [0.0, 0.0].into(),
            pivot: [0.0, 0.0].into(),
            angle: 0.0,
            scale: [1.0, 1.0].into(),
            diffuse_color: Color::WHITE,
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// struct SpriteBuilder
/////////////////////////////////////////////////////////////////////////////

/// Builder to construct new [`Sprite`]s from `riddle_image::Image`s.
///
/// # Example
///
/// ```no_run
/// # use riddle::{common::Color, image::*, platform::*, renderer::*, math::*, *};
/// # fn main() -> Result<(), RiddleError> {
/// # let rdl =  RiddleLib::new()?;
/// # let window = WindowBuilder::new().build(rdl.context())?;
/// let renderer = Renderer::new_from_window(&window)?;
///
/// let img = Image::new(100, 100);
/// let sprite = SpriteBuilder::new(img)
///     .with_filter_modes(FilterMode::Linear, FilterMode::Linear)
///     .build(&renderer)?;
/// # Ok(()) }
/// ```
pub struct SpriteBuilder {
    pub(crate) img: image::Image,
    pub(crate) mag_filter: FilterMode,
    pub(crate) min_filter: FilterMode,
}

impl SpriteBuilder {
    /// Create a new builder for the given image
    pub fn new(img: image::Image) -> Self {
        Self {
            img,
            mag_filter: Default::default(),
            min_filter: Default::default(),
        }
    }

    /// Specify the min and mag filters used when rendering the sprite
    pub fn with_filter_modes(mut self, mag_filter: FilterMode, min_filter: FilterMode) -> Self {
        self.mag_filter = mag_filter;
        self.min_filter = min_filter;
        self
    }

    /// Build the sprite for the given renderer
    pub fn build<Device: WGPUDevice>(
        self,
        renderer: &WGPURenderer<Device>,
    ) -> Result<WGPUSprite<Device>> {
        WGPUSprite::new_from_image(renderer, self.img, self.mag_filter, self.min_filter)
    }
}