/* * Copyright (C) 2021 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 "gl/texture-2d.hpp" #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include #include namespace gl { 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_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F}, {GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8}, {GL_SRGB8, GL_SRGB8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F}, {GL_SRGB8, GL_SRGB8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F}, {GL_SRGB8, GL_SRGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, {GL_SRGB8, GL_SRGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, {GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}, {GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F} }; 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_BORDER, 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, gl::pixel_type type, gl::pixel_format format, gl::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, gl::pixel_type type, gl::pixel_format format, gl::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 == gl::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(gl::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); } } // namespace gl