1
use std::marker::PhantomData;
2
use std::ops::Deref;
3

            
4
use bytemuck::{Pod, Zeroable};
5
use easygpu::prelude::*;
6
use easygpu::wgpu::TextureFormat;
7
use figures::Vectorlike;
8

            
9
use super::{Normal, Srgb};
10
use crate::math::{Angle, Pixels, Point};
11

            
12
/// A pipeline for rendering shapes.
13
pub struct Pipeline<T> {
14
    core: PipelineCore,
15
    _phantom: PhantomData<T>,
16
}
17

            
18
#[repr(C)]
19
#[derive(Copy, Clone, Pod, Zeroable)]
20
/// The uniforms for the shader.
21
pub struct Uniforms {
22
    /// The orthographic projection matrix
23
    pub ortho: [f32; 16],
24
    /// The transformation matrix
25
    pub transform: [f32; 16],
26
}
27
#[repr(C)]
28
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
29
pub struct Vertex {
30
    pub position: [f32; 3],
31
    pub uv: [f32; 2],
32
    pub color: Rgba8,
33
    pub alpha: f32,
34
}
35

            
36
impl Vertex {
37
    pub fn rotate_by(mut self, angle: Option<Angle>, origin: Point<f32, Pixels>) -> Self {
38
        if let Some(angle) = angle {
39
            let origin = origin.to_vector();
40
            let position = Point::new(self.position[0], self.position[1]);
41
            let relative_position = position - origin;
42
            let rotated = angle.transform_point(relative_position) + origin;
43

            
44
            self.position[0] = rotated.x;
45
            self.position[1] = rotated.y;
46
        }
47

            
48
        self
49
    }
50
}
51

            
52
impl<T> Pipeline<T> {
53
    pub fn binding(
54
        &self,
55
        renderer: &Renderer,
56
        texture: &Texture,
57
        sampler: &Sampler,
58
    ) -> BindingGroup {
59
        renderer
60
            .device
61
            .create_binding_group(&self.pipeline.layout.sets[1], &[texture, sampler])
62
    }
63
}
64

            
65
impl<'a, T> AbstractPipeline<'a> for Pipeline<T>
66
where
67
    T: VertexShaderSource,
68
{
69
    type PrepareContext = ScreenTransformation<f32>;
70
    type Uniforms = self::Uniforms;
71

            
72
1
    fn description() -> PipelineDescription<'a> {
73
1
        PipelineDescription {
74
1
            vertex_layout: &[
75
1
                VertexFormat::Float3,
76
1
                VertexFormat::Float2,
77
1
                VertexFormat::UByte4,
78
1
                VertexFormat::Float,
79
1
            ],
80
1
            pipeline_layout: &[
81
1
                Set(&[Binding {
82
1
                    binding: BindingType::UniformBuffer,
83
1
                    stage: ShaderStages::VERTEX,
84
1
                }]),
85
1
                Set(&[
86
1
                    Binding {
87
1
                        binding: BindingType::SampledTexture {
88
1
                            multisampled: false,
89
1
                        },
90
1
                        stage: ShaderStages::FRAGMENT,
91
1
                    },
92
1
                    Binding {
93
1
                        binding: BindingType::Sampler,
94
1
                        stage: ShaderStages::FRAGMENT,
95
1
                    },
96
1
                ]),
97
1
            ],
98
1
            vertex_shader: T::shader(),
99
1
            fragment_shader: include_bytes!("shaders/sprite.frag.spv"),
100
1
        }
101
1
    }
102

            
103
1
    fn setup(pipeline: easygpu::pipeline::Pipeline, dev: &Device) -> Self {
104
1
        let transform = ScreenTransformation::identity().to_array();
105
1
        let ortho = ScreenTransformation::identity().to_array();
106
1
        let uniforms = dev.create_uniform_buffer(&[self::Uniforms { ortho, transform }]);
107
1
        let bindings = dev.create_binding_group(&pipeline.layout.sets[0], &[&uniforms]);
108
1

            
109
1
        Self {
110
1
            core: PipelineCore {
111
1
                pipeline,
112
1
                bindings,
113
1
                uniforms,
114
1
            },
115
1
            _phantom: PhantomData::default(),
116
1
        }
117
1
    }
118

            
119
1
    fn prepare(
120
1
        &'a self,
121
1
        ortho: ScreenTransformation<f32>,
122
1
    ) -> Option<(&'a UniformBuffer, Vec<self::Uniforms>)> {
123
1
        let ortho = ortho.to_array();
124
1
        let transform = ScreenTransformation::identity().to_array();
125
1
        Some((&self.uniforms, vec![self::Uniforms { ortho, transform }]))
126
1
    }
127
}
128

            
129
impl<T> Deref for Pipeline<T> {
130
    type Target = PipelineCore;
131

            
132
1
    fn deref(&self) -> &Self::Target {
133
1
        &self.core
134
1
    }
135
}
136

            
137
/// Defines a shader source for sprites.
138
pub trait VertexShaderSource {
139
    /// The corresponding shader source type in `easygpu_lyon` for shape
140
    /// rendering.
141
    type Lyon: easygpu_lyon::VertexShaderSource + Send + Sync;
142

            
143
    /// The shader executable.
144
    #[must_use]
145
    fn shader() -> &'static [u8];
146

            
147
    /// The texture format expected.
148
    #[must_use]
149
    fn texture_format() -> TextureFormat;
150

            
151
    /// The sampler format expected.
152
    #[must_use]
153
4
    fn sampler_format() -> TextureFormat {
154
4
        <Self::Lyon as easygpu_lyon::VertexShaderSource>::sampler_format()
155
4
    }
156
}
157

            
158
impl VertexShaderSource for Srgb {
159
    type Lyon = easygpu_lyon::Srgb;
160

            
161
1
    fn shader() -> &'static [u8] {
162
1
        include_bytes!("shaders/sprite-srgb.vert.spv")
163
1
    }
164

            
165
    fn texture_format() -> TextureFormat {
166
        TextureFormat::Rgba8UnormSrgb
167
    }
168
}
169

            
170
impl VertexShaderSource for Normal {
171
    type Lyon = easygpu_lyon::Normal;
172

            
173
    fn shader() -> &'static [u8] {
174
        include_bytes!("shaders/sprite.vert.spv")
175
    }
176

            
177
    fn texture_format() -> TextureFormat {
178
        TextureFormat::Rgba8Unorm
179
    }
180
}