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
use crate::core::*;
use crate::renderer::*;

///
/// A set of sprites, ie. a set of quads that orients itself towards the camera.
///
/// The sprites will always orient themselves towards the camera, but if a direction is specified, the sprite normals will also always be orthogonal to that direction.
/// For example, if the up direction is specified, the sprites will rotate around the up direction trying to face the camera.
/// Sprites are also known as billboards in the case where no direction is specified.
///
pub struct Sprites {
    context: Context,
    position_buffer: VertexBuffer,
    uv_buffer: VertexBuffer,
    center_buffer: InstanceBuffer,
    transformation: Mat4,
    direction: Option<Vec3>,
}

impl Sprites {
    ///
    /// Create a new set of [Sprites] with the given centers. The centers also determines the number of sprites.
    /// The sprites will always orient themselves towards the camera, but if a direction is specified, the sprite normals will always be orthogonal to that direction.
    ///
    pub fn new(context: &Context, centers: &[Vec3], direction: Option<Vec3>) -> Self {
        let position_buffer = VertexBuffer::new_with_data(
            context,
            &[
                vec3(-1.0, -1.0, 0.0),
                vec3(1.0, -1.0, 0.0),
                vec3(1.0, 1.0, 0.0),
                vec3(1.0, 1.0, 0.0),
                vec3(-1.0, 1.0, 0.0),
                vec3(-1.0, -1.0, 0.0),
            ],
        );
        let uv_buffer = VertexBuffer::new_with_data(
            context,
            &[
                vec2(0.0, 0.0),
                vec2(1.0, 0.0),
                vec2(1.0, 1.0),
                vec2(1.0, 1.0),
                vec2(0.0, 1.0),
                vec2(0.0, 0.0),
            ],
        );
        Self {
            context: context.clone(),
            position_buffer,
            uv_buffer,
            center_buffer: InstanceBuffer::new_with_data(context, centers),
            transformation: Mat4::identity(),
            direction,
        }
    }

    ///
    /// Returns the local to world transformation applied to all sprites.
    ///
    pub fn transformation(&self) -> Mat4 {
        self.transformation
    }

    ///
    /// Set the local to world transformation applied to all sprites.
    ///
    pub fn set_transformation(&mut self, transformation: Mat4) {
        self.transformation = transformation;
    }

    ///
    /// Set a direction the sprite normals are always orthogonal to.
    ///
    pub fn set_direction(&mut self, direction: Option<Vec3>) {
        self.direction = direction;
    }

    ///
    /// Set the centers of the sprites. The centers also determines the number of sprites.
    ///
    pub fn set_centers(&mut self, centers: &[Vec3]) {
        self.center_buffer.fill(centers);
    }

    fn draw(&self, program: &Program, render_states: RenderStates, camera: &Camera) {
        program.use_uniform("eye", camera.position());
        program.use_uniform("viewProjection", camera.projection() * camera.view());
        program.use_uniform("transformation", self.transformation);
        program.use_vertex_attribute("position", &self.position_buffer);
        program.use_vertex_attribute("uv_coordinate", &self.uv_buffer);
        program.use_instance_attribute("center", &self.center_buffer);
        program.use_uniform("direction", self.direction.unwrap_or(vec3(0.0, 0.0, 0.0)));
        program.draw_arrays_instanced(
            render_states,
            camera.viewport(),
            6,
            self.center_buffer.instance_count(),
        )
    }
}

impl<'a> IntoIterator for &'a Sprites {
    type Item = &'a dyn Geometry;
    type IntoIter = std::iter::Once<&'a dyn Geometry>;

    fn into_iter(self) -> Self::IntoIter {
        std::iter::once(self)
    }
}

impl Geometry for Sprites {
    fn draw(
        &self,
        camera: &Camera,
        program: &Program,
        render_states: RenderStates,
        attributes: FragmentAttributes,
    ) {
        if !attributes.uv {
            todo!()
        }
        if attributes.normal || attributes.tangents {
            todo!()
        }
        self.draw(program, render_states, camera);
    }

    fn vertex_shader_source(&self, _required_attributes: FragmentAttributes) -> String {
        include_str!("shaders/sprites.vert").to_owned()
    }

    fn id(&self, _required_attributes: FragmentAttributes) -> u16 {
        0b1u16 << 15 | 0b100u16
    }

    fn render_with_material(
        &self,
        material: &dyn Material,
        camera: &Camera,
        lights: &[&dyn Light],
    ) {
        render_with_material(&self.context, camera, &self, material, lights);
    }

    fn render_with_effect(
        &self,
        material: &dyn Effect,
        camera: &Camera,
        lights: &[&dyn Light],
        color_texture: Option<ColorTexture>,
        depth_texture: Option<DepthTexture>,
    ) {
        render_with_effect(
            &self.context,
            camera,
            self,
            material,
            lights,
            color_texture,
            depth_texture,
        )
    }

    fn aabb(&self) -> AxisAlignedBoundingBox {
        AxisAlignedBoundingBox::INFINITE
    }
}