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
use crate::prelude::*;
use crate::Context;

use crate::data::as_u8_slice;
use crate::data::as_u8_mut_slice;

use glow::HasContext;

use crate::TextureFormat;
use crate::ColorFormat;
use crate::Type;
use crate::Texture;

/// A `Texture3D` representation.

#[derive(Shrinkwrap)]
#[shrinkwrap(mutable)]
pub struct Texture3D {
    /// Base texture object.

    #[shrinkwrap(main_field)]
    pub texture : Texture,
    dimensions  : (usize,usize,usize)
}

impl Texture3D {
    fn new(context:&Context) -> Self {
        let format     = TextureFormat::new(ColorFormat::RGBA, Type::F32);
        let texture    = Texture::new(context,format,glow::TEXTURE_3D);
        let dimensions = (0,0,0);
        Self {texture,dimensions}
    }

    /// Gets the dimensions.

    pub fn dimensions(&self) -> (usize, usize, usize) {
        self.dimensions
    }

    /// Allocates a new `Texture3D` with the specified dimensions and `TextureFormat`.

    pub fn allocate
    (context:&Context, dimensions: (usize, usize, usize), format: &TextureFormat) -> Self {
        let mut texture = Self::new(context);
        texture.reallocate(dimensions, &format);
        texture
    }

    /// Creates a new `Texture3D` from a slice.

    pub fn from_data<T>
    (context:&Context, dimensions: (usize, usize, usize), format: &TextureFormat, data: &[T], data_format: &TextureFormat) -> Self {
        let mut texture = Self::new(context);
        texture.set_data(dimensions, &format, data, &data_format);
        texture
    }

    /// Reallocates the memory on the GPU side.

    pub fn reallocate(&mut self, dimensions: (usize, usize, usize), format: &TextureFormat) {
        self.dimensions = dimensions;
        self.format = format.clone();
        self.bind();
        unsafe {
            let tex_type        = self.typ();
            let internal_format = format.internal_format();
            self.gl.tex_storage_3d(tex_type, 1, internal_format, dimensions.0 as i32, dimensions.1 as
                i32, dimensions.2 as i32);
        }
    }

    /// Sets the data on the GPU side.

    pub fn set_data<T>(&mut self, dimensions: (usize, usize, usize), format: &TextureFormat,
                       data: &[T], data_format: &TextureFormat) {
        self.dimensions = dimensions;
        self.format = format.clone();
        self.bind();
        unsafe {
            let (color, ty)     = data_format.get_format_type();
            let internal_format = format.internal_format() as i32;
            let width           = dimensions.0 as i32;
            let height          = dimensions.1 as i32;
            let depth           = dimensions.2 as i32;
            let pixels          = Some(as_u8_slice(data));
            self.gl.tex_image_3d(self.typ(),0,internal_format,width,height,depth,0,color,ty,pixels);
        }
    }

    /// Gets a copy of the data on the GPU.

    pub fn data<T>(&self) -> Vec<T> {
        let (width,height,depth) = self.dimensions();
        let color_size           = self.format().color_format().size();
        let capacity             = width * height * depth * color_size;
        let mut data : Vec<T>    = Vec::with_capacity(capacity);
        let gl = &self.gl;
        unsafe {
            data.set_len(capacity);

            //FIXME: Pre-create a transfer framebuffer in Context.

            let fb = gl.create_framebuffer().expect("Couldn't create Framebuffer");

            for depth in 0..depth {
                gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fb));
                gl.framebuffer_texture_layer(glow::FRAMEBUFFER,
                                             glow::COLOR_ATTACHMENT0,
                                             Some(self.resource()),
                                             0,
                                             depth as i32);

                let (format, ty) = self.format().get_format_type();
                let offset = width * height * depth * color_size;
                let pixels = glow::PixelPackData::Slice(&mut as_u8_mut_slice(data.as_mut())[offset..]);
                let (width, height, _) = self.dimensions();
                gl.read_pixels(0, 0, width as i32, height as i32, format, ty, pixels);
            }

            gl.bind_framebuffer(glow::FRAMEBUFFER, None);
            gl.delete_framebuffer(fb);
        }
        data
    }
}