use gl;
use gl::types::*;
use texture::{self, Texture};
use features::traits::{ScanLines, Image, CubemapImage};
use std::mem;
use std::ops::Index;
use ::Result;
use ::Error;
use ::ErrorKind;
use sampler::Sampler;
use state::StateRef;
#[derive(Debug, Eq, PartialEq)]
pub struct CubeMap{
texture: Texture,
gl: StateRef,
}
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub struct Format{
pub internal_format: GLenum,
pub width: u32,
pub height: u32,
pub levels: u32,
}
#[derive(Clone)]
pub struct Faces<I: Image>{
pub positive_x: I,
pub negative_x: I,
pub positive_y: I,
pub negative_y: I,
pub positive_z: I,
pub negative_z: I,
}
pub struct FacesIter<'a, I:Image + 'a>{
face: GLenum,
faces: &'a Faces<I>,
}
impl<I: Image> Faces<I>{
pub fn iter(&self) -> FacesIter<I>{
FacesIter{
face: gl::TEXTURE_CUBE_MAP_POSITIVE_X,
faces: self
}
}
}
impl<'a, I: Image> Iterator for FacesIter<'a, I>{
type Item = (GLenum, &'a I);
fn next(&mut self) -> Option<(GLenum, &'a I)>{
if self.face > gl::TEXTURE_CUBE_MAP_NEGATIVE_Z{
None
}else{
let curr_face = self.face;
self.face += 1;
Some((curr_face, self.faces.index(curr_face)))
}
}
}
impl<I: Image> Index<GLenum> for Faces<I>{
type Output = I;
fn index(&self, index: GLenum) -> &I{
match index{
gl::TEXTURE_CUBE_MAP_POSITIVE_X => &self.positive_x,
gl::TEXTURE_CUBE_MAP_NEGATIVE_X => &self.negative_x,
gl::TEXTURE_CUBE_MAP_POSITIVE_Y => &self.positive_y,
gl::TEXTURE_CUBE_MAP_NEGATIVE_Y => &self.negative_y,
gl::TEXTURE_CUBE_MAP_POSITIVE_Z => &self.positive_z,
gl::TEXTURE_CUBE_MAP_NEGATIVE_Z => &self.negative_z,
_ => panic!("Trying to access innexisting cubemap face")
}
}
}
impl Format{
fn to_texture_format(self) -> ::texture::Format{
texture::Format::Custom{
target: gl::TEXTURE_CUBE_MAP,
internal_format: self.internal_format,
width: self.width,
height: self.height,
depth: 0,
levels: self.levels,
samples: 0,
}
}
}
pub struct Builder<'a>(pub(crate) &'a StateRef);
impl<'a> Builder<'a>{
pub fn from_format(&self, format: Format) -> Result<CubeMap>{
let mut tex = texture::Builder(self.0).from_format(format.to_texture_format())?;
tex.set_wrap_s(gl::CLAMP_TO_EDGE);
tex.set_wrap_t(gl::CLAMP_TO_EDGE);
tex.set_wrap_r(gl::CLAMP_TO_EDGE);
if format.levels > 1 {
tex.set_min_mag_filters(gl::NEAREST_MIPMAP_NEAREST, gl::NEAREST);
}else{
tex.set_min_mag_filters(gl::LINEAR, gl::LINEAR);
}
tex.set_max_level(format.levels as i32 - 1);
Ok(CubeMap{
texture: tex,
gl : self.0.clone(),
})
}
pub fn from_faces<I: ::Image>(&self, faces: &Faces<I>) -> Result<CubeMap>{
let img0 = &faces.positive_x;
let internal_format = img0.gl_internal().ok_or(ErrorKind::FormatNotSupported)?;
let w = img0.width(0);
let h = img0.height(0);
let mipcount = img0.levels();
let mut cubemap = self.from_format(Format{
internal_format,
width: w as u32,
height: h as u32,
levels: mipcount as u32,
})?;
cubemap.load_faces(faces)?;
cubemap.texture.set_wrap_s(gl::CLAMP_TO_EDGE);
cubemap.texture.set_wrap_t(gl::CLAMP_TO_EDGE);
cubemap.texture.set_wrap_r(gl::CLAMP_TO_EDGE);
if mipcount > 1 {
cubemap.texture.set_min_mag_filters(gl::NEAREST_MIPMAP_NEAREST, gl::NEAREST);
}else{
cubemap.texture.set_min_mag_filters(gl::LINEAR, gl::LINEAR);
}
Ok(cubemap)
}
pub fn from_cubemap_image<I: CubemapImage>(&self, dds: &I) -> Result<CubeMap>
where <I as Image>::DataType: Copy
{
let internal_format = dds.gl_internal().ok_or(ErrorKind::FormatNotSupported)?;
let mipcount = dds.levels();
let w = dds.width(0);
let h = dds.height(0);
let mut cubemap = self.from_format(Format{
internal_format,
width: w as u32,
height: h as u32,
levels: mipcount as u32})?;
cubemap.load_image(dds)?;
cubemap.texture.set_wrap_r(gl::CLAMP_TO_EDGE);
if mipcount > 1{
cubemap.texture.set_min_mag_filters(gl::NEAREST_MIPMAP_NEAREST, gl::NEAREST);
}else{
cubemap.texture.set_min_mag_filters(gl::LINEAR, gl::LINEAR);
}
cubemap.set_max_level(mipcount as i32 - 1);
Ok(cubemap)
}
fn from_cubemap_image_level_levels<I: CubemapImage>(&self, dds: &I, level: usize, allocate_levels: bool) -> Result<CubeMap>
where <I as Image>::DataType: Copy
{
let (internal, format, ty) = dds.gl_internal_format_type()
.ok_or(ErrorKind::FormatNotSupported)?;
let compressed = dds.is_gpu_compressed();
let w = dds.width(level);
let h = dds.height(level);
let levels = if allocate_levels { dds.max_levels() } else { 1 };
if w != h { return Err(Error::new(ErrorKind::NotSquareImage, None)) };
let mut cubemap = self.from_format(Format{
internal_format: internal,
width: w as u32,
height: h as u32,
levels: levels as u32})?;
for face in 0..6{
let data = dds.mipmap_face(level, face)
.ok_or_else(|| Error::new(ErrorKind::FormatNotSupported,
format!("Couldn't get level {} face {}", level, face).as_ref()))?;
let linewidth = w * dds.bytes_per_pixel() / std::mem::size_of::<<I as Image>::DataType>();
let data = data
.chunks(linewidth)
.rev()
.flat_map(|line| line.iter().copied())
.collect::<Vec<<I as Image>::DataType>>();
let side = gl::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32;
if compressed{
cubemap.load_compressed_data(&data, side, 0, internal, w as GLint, h as GLint);
}else{
cubemap.load_data(data.as_ptr(), side, 0, w as GLint, h as GLint, format, ty);
}
}
cubemap.texture.set_wrap_r(gl::CLAMP_TO_EDGE);
cubemap.texture.set_min_mag_filters(gl::LINEAR, gl::LINEAR);
cubemap.set_max_level(levels as i32 - 1);
Ok(cubemap)
}
pub fn from_cubemap_image_level<I: CubemapImage>(&self, dds: &I, level: usize) -> Result<CubeMap>
where <I as Image>::DataType: Copy
{
self.from_cubemap_image_level_levels(dds, level, false)
}
pub fn from_cubemap_image_level_allocate_mips<I: CubemapImage>(&self, dds: &I, level: usize) -> Result<CubeMap>
where <I as Image>::DataType: Copy
{
self.from_cubemap_image_level_levels(dds, level, true)
}
}
impl CubeMap{
pub fn generate_mipmaps(&mut self){
self.texture.generate_mipmaps();
}
pub fn width(&self) -> u32{
self.texture.width()
}
pub fn height(&self) -> u32{
self.texture.height()
}
pub fn levels(&self) -> u32{
self.texture.levels()
}
pub fn id(&self) -> GLuint{
self.texture.id()
}
pub fn internal_format(&self) -> GLenum{
self.texture.internal_format()
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn load_data<T>(&mut self, data: *const T, side: GLenum, level: i32, w: GLint, h: GLint, format: GLenum, ty: GLenum){
unsafe{
self.gl.GetError();
if self.texture.is_dsa(){
let face = side - gl::TEXTURE_CUBE_MAP_POSITIVE_X;
self.gl.TextureSubImage3D(self.texture.id(), level, 0, 0, face as GLsizei, w, h, 1, format, ty, mem::transmute(data));
}else{
self.gl.BindTexture(gl::TEXTURE_CUBE_MAP, self.texture.id());
self.gl.TexSubImage2D(side, level, 0, 0, w, h, format, ty, mem::transmute(data));
}
let err = self.gl.GetError();
if err > 0{
let mut max = 0;
self.gl.GetIntegerv(gl::MAX_TEXTURE_SIZE, &mut max);
error!("error loading cubemap data {}, w: {}, h: {}, side: {}, level: {}, format: {}, ty: {}", err, w, h, side, level, format, ty);
}
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
fn load_data<T>(&mut self, data: *const T, side: GLenum, level: i32, w: GLint, h: GLint, format: GLenum, ty: GLenum){
unsafe{
self.gl.GetError();
self.gl.BindTexture(gl::TEXTURE_CUBE_MAP, self.texture.id());
self.gl.TexSubImage2D(side, level, 0, 0, w, h, format, ty, mem::transmute(data));
let err = self.gl.GetError();
if err > 0{
error!("error loading cubemap data {}, w: {}, h: {}, side: {}, level: {}, format: {}, ty: {}", err, w, h, side, level, format, ty);
}
}
}
fn load_compressed_data<T>(&mut self, data: &[T], side: GLenum, level: i32, internal: GLenum, w: GLint, h: GLint){
unsafe{
self.gl.GetError();
self.gl.CompressedTexImage2D(side, level, internal, w, h, 0, data.len() as i32, mem::transmute(data.as_ptr()));
let err = self.gl.GetError();
if err > 0{
error!("error loading compressed cubemap data {}, w: {}, h: {}, side: {}, level: {}, internal: {}", err, w, h, side, level, internal);
}
}
}
fn load_img_data<I: ::Image>(&mut self, img: &I, target: GLenum, level: GLint) -> Result<()>{
let bytes_pp = img.bytes_per_pixel();
let (_internal,format,ty) = img.gl_internal_format_type()
.ok_or(ErrorKind::FormatNotSupported)?;
unsafe{
self.gl.state().set_pixel_store((img.width(0)*bytes_pp) as i32);
}
let w = img.width(0) as i32;
let h = img.height(0) as i32;
if img.pitch_bytes(0) != img.width(0)*bytes_pp{
let mut pixels = Vec::new();
let scanlines = ScanLines::new(img, 0);
for line in scanlines{
pixels.extend(line.iter());
}
self.load_data(pixels.as_ptr(), target, level, w, h, format, ty);
}else{
self.load_data(img.data().as_ptr(), target, level, w, h, format, ty);
}
unsafe{
self.gl.PixelStorei (gl::UNPACK_ALIGNMENT, 4);
}
Ok(())
}
pub fn load_level_from_faces<I: Image>(&mut self, faces: &Faces<I>, level: GLint) -> Result<()>{
let img0 = &faces.positive_x;
let bpp = img0.bytes_per_pixel();
let ift = img0.gl_internal_format_type().ok_or(ErrorKind::FormatNotSupported)?;
let w = img0.width(0);
let h = img0.height(0);
if w != h { return Err(Error::new(ErrorKind::NotSquareImage, None)) };
if !faces.iter().all(|(_side, img)|
img.bytes_per_pixel()==bpp &&
img.gl_internal_format_type().is_some() &&
img.gl_internal_format_type().unwrap()==ift) {
return Err(Error::new(ErrorKind::DifferentFormatsPerImage, None))
};
if !faces.iter().all(|(_side, img)| img.width(0)==w && img.height(0)==h){
return Err(Error::new(ErrorKind::DifferentDimensionsPerImage, None))
};
for (side, img) in faces.iter(){
self.load_img_data(img, side, level)?;
}
Ok(())
}
pub fn load_faces<I: Image>(&mut self, faces: &Faces<I>) -> Result<()>{
let img0 = &faces.positive_x;
let (internal, format, ty) = img0.gl_internal_format_type()
.ok_or(ErrorKind::FormatNotSupported)?;
let compressed = img0.is_gpu_compressed();
let w = img0.width(0);
let h = img0.height(0);
let mipcount = img0.levels();
if w != h { return Err(Error::new(ErrorKind::NotSquareImage, None)) };
if !faces.iter().all(|(_side, img)|
img.gl_internal().is_some() &&
img.gl_internal().unwrap()==internal){
return Err(Error::new(ErrorKind::DifferentFormatsPerImage,
"All images need to have the same format"))
};
if !faces.iter().all(|(_side, img)| img.width(0)==w && img.height(0)==h){
return Err(Error::new(ErrorKind::DifferentDimensionsPerImage,
"All images need to have the same dimensions"))
};
for (side, img) in faces.iter(){
for level in 0..mipcount{
let data = img.mipmap(level)
.ok_or_else(|| Error::new(ErrorKind::FormatNotSupported,
format!("Error retrieving mipmap, reports {} but got None for {}", mipcount, level)
.as_ref()))?;
let w = img.width(level);
let h = img.height(level);
if compressed{
self.load_compressed_data(data, side, level as GLint, internal, w as GLint, h as GLint);
}else{
self.load_data(data.as_ptr(), side, level as GLint, w as GLint, h as GLint, format, ty);
}
}
}
self.set_max_level(mipcount as i32 - 1);
Ok(())
}
pub fn load_level_from_cubemap_image<I: CubemapImage>(&mut self, dds: &I, level: GLint) -> Result<()>{
let (internal, format, ty) = dds.gl_internal_format_type()
.ok_or(ErrorKind::FormatNotSupported)?;
let compressed = dds.is_gpu_compressed();
let w = dds.width(0);
let h = dds.height(0);
if w != h { return Err(Error::new(ErrorKind::NotSquareImage, None)) };
for face in 0..6{
let data = dds.mipmap_face(0, face)
.ok_or_else(|| Error::new(ErrorKind::FormatNotSupported,
format!("Couldn't get level {} face {}", level, face).as_ref()))?;
let side = gl::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32;
if compressed{
self.load_compressed_data(data, side, level as GLint, internal, w as GLint, h as GLint);
}else{
self.load_data(data.as_ptr(), side, level as GLint, w as GLint, h as GLint, format, ty);
}
}
self.texture.set_wrap_r(gl::CLAMP_TO_EDGE);
Ok(())
}
pub fn load_image<I: CubemapImage>(&mut self, dds: &I) -> Result<()>
where <I as Image>::DataType: Copy
{
let (internal, format, ty) = dds.gl_internal_format_type()
.ok_or(ErrorKind::FormatNotSupported)?;
let compressed = dds.is_gpu_compressed();
let mut w = dds.width(0);
let mut h = dds.height(0);
if w != h { return Err(Error::new(ErrorKind::NotSquareImage, None)) };
let mipcount = dds.levels();
for level in 0..mipcount{
for face in 0..6{
let data = dds.mipmap_face(level, face)
.ok_or_else(|| Error::new(ErrorKind::FormatNotSupported,
format!("Couldn't get level {} face {}", level, face).as_ref()))?;
let linewidth = w * dds.bytes_per_pixel() / std::mem::size_of::<<I as Image>::DataType>();
let data = data
.chunks(linewidth)
.rev()
.flat_map(|line| line.iter().copied())
.collect::<Vec<<I as Image>::DataType>>();
let side = gl::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32;
if compressed{
self.load_compressed_data(&data, side, level as GLint, internal, w as GLint, h as GLint);
}else{
self.load_data(data.as_ptr(), side, level as GLint, w as GLint, h as GLint, format, ty);
}
}
w /= 2;
h /= 2;
}
Ok(())
}
#[cfg(not(feature="webgl"))]
pub fn set_anisotropy_level(&mut self, level: f32){
self.texture.set_anisotropy_level(level)
}
pub fn set_base_level(&mut self, level: GLint){
self.texture.set_base_level(level);
}
pub fn set_max_level(&mut self, level: GLint){
self.texture.set_max_level(level);
}
pub fn set_min_lod(&mut self, level: GLint){
self.texture.set_min_lod(level);
}
pub fn set_max_lod(&mut self, level: GLint){
self.texture.set_max_lod(level);
}
pub fn set_min_mag_filters(&mut self, min: GLuint, max: GLuint){
self.texture.set_min_mag_filters(min, max);
}
pub fn set_wrap_s(&mut self, wrap: GLenum){
self.texture.set_wrap_s(wrap);
}
pub fn set_wrap_t(&mut self, wrap: GLenum){
self.texture.set_wrap_t(wrap);
}
pub fn set_wrap_r(&mut self, wrap: GLenum){
self.texture.set_wrap_r(wrap);
}
pub fn set_compare_mode(&mut self, mode: GLenum){
self.texture.set_compare_mode(mode)
}
pub fn set_compare_func(&mut self, mode: GLenum){
self.texture.set_compare_func(mode)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_width(&self, level: u32) -> u32{
self.texture.level_width(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_height(&self, level: u32) -> u32{
self.texture.level_height(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_internal_format(&self, level: u32) -> GLenum{
self.texture.level_internal_format(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_red_type(&self, level: u32) -> GLenum{
self.texture.level_red_type(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_green_type(&self, level: u32) -> GLenum{
self.texture.level_green_type(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_blue_type(&self, level: u32) -> GLenum{
self.texture.level_blue_type(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_alpha_type(&self, level: u32) -> GLenum{
self.texture.level_alpha_type(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn is_level_compressed(&self, level: u32) -> bool{
self.texture.is_level_compressed(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_compressed_image_len(&self, level: u32) -> usize{
self.texture.level_compressed_image_len(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_red_size(&self, level: u32) -> usize{
self.texture.level_red_size(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_green_size(&self, level: u32) -> usize{
self.texture.level_green_size(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_blue_size(&self, level: u32) -> usize{
self.texture.level_blue_size(level)
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn level_alpha_size(&self, level: u32) -> usize{
self.texture.level_alpha_size(level)
}
#[cfg(not(feature="webgl"))]
pub fn depth_stencil_texture_mode(&self) -> GLenum{
self.texture.depth_stencil_texture_mode()
}
pub fn mag_filter(&self) -> GLenum{
self.texture.mag_filter()
}
pub fn min_filter(&self) -> GLenum{
self.texture.min_filter()
}
pub fn min_lod(&self) -> i32{
self.texture.min_lod()
}
pub fn max_lod(&self) -> i32{
self.texture.max_lod()
}
pub fn base_level(&self) -> i32{
self.texture.base_level()
}
pub fn max_level(&self) -> i32{
self.texture.max_level()
}
#[cfg(not(feature = "webgl"))]
pub fn swizzle_r(&self) -> texture::Component{
self.texture.swizzle_r()
}
#[cfg(not(feature = "webgl"))]
pub fn swizzle_g(&self) -> texture::Component{
self.texture.swizzle_g()
}
#[cfg(not(feature = "webgl"))]
pub fn swizzle_b(&self) -> texture::Component{
self.texture.swizzle_b()
}
#[cfg(not(feature = "webgl"))]
pub fn swizzle_a(&self) -> texture::Component{
self.texture.swizzle_a()
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn swizzles(&self) -> texture::Swizzles{
self.texture.swizzles()
}
pub fn wrap_s(&self) -> GLenum{
self.texture.wrap_s()
}
pub fn wrap_t(&self) -> GLenum{
self.texture.wrap_t()
}
pub fn wrap_r(&self) -> GLenum{
self.texture.wrap_r()
}
#[cfg(not(feature = "webgl"))]
pub fn border_color(&self) -> [f32;4]{
self.texture.border_color()
}
pub fn compare_mode(&self) -> GLenum{
self.texture.compare_mode()
}
pub fn compare_func(&self) -> GLenum{
self.texture.compare_func()
}
#[cfg(not(feature = "webgl"))]
pub fn image_format_compatibility_type(&self) -> GLenum{
self.texture.image_format_compatibility_type()
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn seamless(&self) -> bool {
unsafe{
self.texture.get_parameteri(gl::TEXTURE_CUBE_MAP_SEAMLESS) != 0
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn set_seamless(&mut self, seamless: bool) {
unsafe{
self.texture.set_parameteri(gl::TEXTURE_CUBE_MAP_SEAMLESS, seamless as u8 as i32)
}
}
pub unsafe fn set_parameteri(&mut self, parameter: GLenum, value: GLint){
self.texture.set_parameteri(parameter, value);
}
pub unsafe fn set_parameterf(&mut self, parameter: GLenum, value: GLfloat){
self.texture.set_parameterf(parameter, value);
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub unsafe fn get_level_parameteriv(&self, parameter: GLenum, level: u32) -> GLint{
self.texture.get_level_parameteriv(parameter, level)
}
pub unsafe fn get_parameteri(&self, parameter: GLenum) -> GLint{
self.texture.get_parameteri(parameter)
}
pub unsafe fn get_parameterf(&self, parameter: GLenum) -> GLfloat{
self.texture.get_parameterf(parameter)
}
pub fn cubemap_sampler<'a>(&'a self, sampler: &'a Sampler) -> CubeMapSampler<'a>{
CubeMapSampler{
cubemap: self,
sampler
}
}
}
#[derive(Clone, Copy)]
pub struct CubeMapSampler<'a>{
cubemap: &'a CubeMap,
sampler: &'a Sampler,
}
impl<'a> CubeMapSampler<'a>{
pub fn width(&self) -> u32{
self.cubemap.width()
}
pub fn height(&self) -> u32{
self.cubemap.height()
}
pub fn levels(&self) -> u32{
self.cubemap.levels()
}
pub fn cubemap_id(&self) -> GLuint{
self.cubemap.id()
}
pub fn sampler_id(&self) -> GLuint{
self.sampler.id()
}
pub fn internal_format(&self) -> GLenum{
self.cubemap.internal_format()
}
}