/*
* Copyright (C) 2020 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see .
*/
#include "rasterizer/texture-2d.hpp"
#include "rasterizer/texture-wrapping.hpp"
#include "rasterizer/texture-filter.hpp"
#include
#include
static constexpr GLenum pixel_format_lut[] =
{
GL_DEPTH_COMPONENT,
GL_DEPTH_STENCIL,
GL_RED,
GL_RG,
GL_RGB,
GL_BGR,
GL_RGBA,
GL_BGRA
};
static constexpr GLenum pixel_type_lut[] =
{
GL_BYTE,
GL_UNSIGNED_BYTE,
GL_SHORT,
GL_UNSIGNED_SHORT,
GL_INT,
GL_UNSIGNED_INT,
GL_HALF_FLOAT,
GL_FLOAT
};
static constexpr GLenum linear_internal_format_lut[][8] =
{
{GL_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F},
// Note: GL_DEPTH32F_STENCIL8 is actually a 64-bit format, 32 depth bits, 8 stencil bits, and 24 alignment bits.
{GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8},
{GL_R8, GL_R8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F},
{GL_RG8, GL_RG8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F},
{GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F},
{GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F},
{GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F},
{GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}
};
static constexpr GLenum srgb_internal_format_lut[][8] =
{
{GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8},
{GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8},
{GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8},
{GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8},
{GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8},
{GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8, GL_SRGB8},
{GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8},
{GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8}
};
static constexpr GLint swizzle_mask_lut[][4] =
{
{GL_RED, GL_RED, GL_RED, GL_ONE},
{GL_RED, GL_GREEN, GL_ZERO, GL_ONE},
{GL_RED, GL_RED, GL_RED, GL_ONE},
{GL_RED, GL_RED, GL_RED, GL_GREEN},
{GL_RED, GL_GREEN, GL_BLUE, GL_ONE},
{GL_RED, GL_GREEN, GL_BLUE, GL_ONE},
{GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA},
{GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}
};
static constexpr GLenum wrapping_lut[] =
{
GL_CLAMP_TO_EDGE,
GL_REPEAT,
GL_MIRRORED_REPEAT
};
static constexpr GLenum min_filter_lut[] =
{
GL_NEAREST,
GL_LINEAR,
GL_NEAREST_MIPMAP_NEAREST,
GL_LINEAR_MIPMAP_NEAREST,
GL_NEAREST_MIPMAP_LINEAR,
GL_LINEAR_MIPMAP_LINEAR
};
static constexpr GLenum mag_filter_lut[] =
{
GL_NEAREST,
GL_LINEAR
};
texture_2d::texture_2d(int width, int height, ::pixel_type type, ::pixel_format format, ::color_space color_space, const void* data):
gl_texture_id(0),
dimensions({0, 0}),
wrapping({texture_wrapping::repeat, texture_wrapping::repeat}),
filters({texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear}),
max_anisotropy(0.0f)
{
glGenTextures(1, &gl_texture_id);
resize(width, height, type, format, color_space, data);
set_wrapping(std::get<0>(wrapping), std::get<1>(wrapping));
set_filters(std::get<0>(filters), std::get<1>(filters));
set_max_anisotropy(max_anisotropy);
}
texture_2d::~texture_2d()
{
glDeleteTextures(1, &gl_texture_id);
}
void texture_2d::resize(int width, int height, ::pixel_type type, ::pixel_format format, ::color_space color_space, const void* data)
{
dimensions = {width, height};
pixel_type = type;
pixel_format = format;
this->color_space = color_space;
GLenum gl_internal_format;
if (color_space == ::color_space::srgb)
{
gl_internal_format = srgb_internal_format_lut[static_cast(format)][static_cast(type)];
}
else
{
gl_internal_format = linear_internal_format_lut[static_cast(format)][static_cast(type)];
}
GLenum gl_format = pixel_format_lut[static_cast(format)];
const GLint* gl_swizzle_mask = swizzle_mask_lut[static_cast(format)];
GLenum gl_type = pixel_type_lut[static_cast(type)];
// Special cases for depth + stencil pixel formats
if (gl_internal_format == GL_DEPTH24_STENCIL8)
gl_type = GL_UNSIGNED_INT_24_8;
else if (gl_internal_format == GL_DEPTH32F_STENCIL8)
gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, gl_texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask);
/// TODO: remove this
if (format == pixel_format::d)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
}
}
void texture_2d::set_wrapping(texture_wrapping wrap_s, texture_wrapping wrap_t)
{
wrapping = {wrap_s, wrap_t};
GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)];
GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)];
glBindTexture(GL_TEXTURE_2D, gl_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl_wrap_s);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl_wrap_t);
}
void texture_2d::set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter)
{
filters = {min_filter, mag_filter};
GLenum gl_min_filter = min_filter_lut[static_cast(min_filter)];
GLenum gl_mag_filter = mag_filter_lut[static_cast(mag_filter)];
glBindTexture(GL_TEXTURE_2D, gl_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_min_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_mag_filter);
}
void texture_2d::set_max_anisotropy(float anisotropy)
{
this->max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy));
// Lerp between 1.0f and GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
float gl_max_texture_max_anisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy);
float gl_max_anisotropy = 1.0f + this->max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f);
glBindTexture(GL_TEXTURE_2D, gl_texture_id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy);
}