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

///
/// Adds additional functionality to write to and copy from both a [Texture2DArray]and
/// a [DepthTargetTexture2DArray] at the same time.
/// It purely adds functionality, so it can be created each time it is needed, the data is saved in the textures.
///
pub struct RenderTargetArray<'a, 'b> {
    context: Context,
    id: crate::context::Framebuffer,
    color_texture: Option<&'a mut Texture2DArray>,
    depth_texture: Option<&'b mut DepthTargetTexture2DArray>,
}
impl<'a, 'b> RenderTargetArray<'a, 'b> {
    ///
    /// Constructs a new render target that enables rendering into the given
    /// [DepthTargetTexture2DArray].
    ///
    pub fn new_depth(
        context: &Context,
        depth_texture: &'b mut DepthTargetTexture2DArray,
    ) -> ThreeDResult<Self> {
        Ok(Self {
            context: context.clone(),
            id: new_framebuffer(context)?,
            color_texture: None,
            depth_texture: Some(depth_texture),
        })
    }

    ///
    /// Constructs a new render target array that enables rendering into the given
    /// [Texture2DArray] and [DepthTargetTexture2DArray] array textures.
    ///
    pub fn new(
        context: &Context,
        color_texture: &'a mut Texture2DArray,
        depth_texture: &'b mut DepthTargetTexture2DArray,
    ) -> ThreeDResult<Self> {
        Ok(Self {
            context: context.clone(),
            id: new_framebuffer(context)?,
            color_texture: Some(color_texture),
            depth_texture: Some(depth_texture),
        })
    }

    ///
    /// Constructs a new render target array that enables rendering into the given
    /// [Texture2DArray].
    ///
    pub fn new_color(
        context: &Context,
        color_texture: &'a mut Texture2DArray,
    ) -> ThreeDResult<Self> {
        Ok(Self {
            context: context.clone(),
            id: new_framebuffer(context)?,
            color_texture: Some(color_texture),
            depth_texture: None,
        })
    }

    ///
    /// Renders whatever rendered in the `render` closure into the textures defined at construction
    /// and defined by the input parameters `color_layers` and `depth_layer`.
    /// Output at location *i* defined in the fragment shader is written to the color texture layer at the *ith* index in `color_layers`.
    /// The depth is written to the depth texture defined by `depth_layer`.
    /// Before writing, the textures are cleared based on the given clear state.
    ///
    pub fn write(
        &self,
        color_layers: &[u32],
        depth_layer: u32,
        clear_state: ClearState,
        render: impl FnOnce() -> ThreeDResult<()>,
    ) -> ThreeDResult<()> {
        self.bind(Some(color_layers), Some(depth_layer))?;
        clear(
            &self.context,
            &ClearState {
                red: self.color_texture.as_ref().and(clear_state.red),
                green: self.color_texture.as_ref().and(clear_state.green),
                blue: self.color_texture.as_ref().and(clear_state.blue),
                alpha: self.color_texture.as_ref().and(clear_state.alpha),
                depth: self.depth_texture.as_ref().and(clear_state.depth),
            },
        );
        render()?;
        if let Some(ref color_texture) = self.color_texture {
            color_texture.generate_mip_maps();
        }
        Ok(())
    }

    fn bind(&self, color_layers: Option<&[u32]>, depth_layer: Option<u32>) -> ThreeDResult<()> {
        unsafe {
            self.context
                .bind_framebuffer(crate::context::DRAW_FRAMEBUFFER, Some(self.id));
            if let Some(ref color_texture) = self.color_texture {
                if let Some(color_layers) = color_layers {
                    self.context.draw_buffers(
                        &(0..color_layers.len())
                            .map(|i| crate::context::COLOR_ATTACHMENT0 + i as u32)
                            .collect::<Vec<u32>>(),
                    );
                    for channel in 0..color_layers.len() {
                        color_texture.bind_as_color_target(color_layers[channel], channel as u32);
                    }
                }
            }
        }
        if let Some(ref depth_texture) = self.depth_texture {
            if let Some(depth_layer) = depth_layer {
                depth_texture.bind_as_depth_target(depth_layer);
            }
        }
        self.context.framebuffer_check()?;
        self.context.error_check()
    }
}

impl Drop for RenderTargetArray<'_, '_> {
    fn drop(&mut self) {
        unsafe {
            self.context.delete_framebuffer(self.id);
        }
    }
}