Files
fennec/include/fennec/renderers/opengl/lib/texture.h

332 lines
14 KiB
C++

// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 - 2026 Medusa Slockbower
//
// This program 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.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file fennec/renderers/opengl/lib/texture.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_RENDERERS_OPENGL_LIB_TEXTURE_H
#define FENNEC_RENDERERS_OPENGL_LIB_TEXTURE_H
/* Because our implementation targets minimum OpenGL ES 3.2,
* we are guaranteed to have the following relevant extensions (Starting from OpenGL ES 3.0):
*
* OES_texture_compression_astc
* EXT_texture_border_clamp
* OES_EGL_image_external_essl3
* ARB_shader_image_load_store
* ARB_stencil_texturing
* ARG_shader_image_size
* ARB_texture_multisample
* ARB_texture_storage_multisample
* ARB_sample_locations
* OES_texture_view
* NV_image_formats
* EXT_render_snorm
* EXT_render_norm16
* EXT_color_buffer_float
* OES_copy_image
* OES_shader_image_atomic
* OES_texture_border_clamp
* OES_texture_buffer
* OES_texture_cube_map_array
* OES_texture_stencil8
* OES_texture_storage_multisample_2d_array
*/
#include <fennec/math/common.h>
#include <fennec/renderers/opengl/lib/fwd.h>
#include <fennec/renderers/opengl/lib/enum.h>
namespace fennec
{
namespace gl
{
template<GLint FormatV, GLboolean ImmutableV> using texture1d = texture<TEXTURE_1D, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture1d_array = texture<TEXTURE_1D_ARRAY, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture2d = texture<TEXTURE_2D, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture2d_array = texture<TEXTURE_2D_ARRAY, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture_rect = texture<TEXTURE_RECTANGLE, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture2d_ms = texture<TEXTURE_2D_MS, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture2d_ms_array = texture<TEXTURE_2D_MS_ARRAY, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using cubemap = texture<CUBEMAP, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using cubemap_array = texture<CUBEMAP_ARRAY, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using texture3d = texture<TEXTURE_3D, FormatV, ImmutableV>;
template<GLint FormatV, GLboolean ImmutableV> using buffer_texture = texture<BUFFER_TEXTURE, FormatV, ImmutableV>;
///
/// \brief Wrapper for OpenGL Texture Objects
/// \tparam TypeV The type of the texture
/// \tparam FormatV The internal pixel format
/// \tparam ImmutableV Mutability
///
/// \details Immutable textures require EXT_texture_storage or ARB_texture_storage
/// Immutable multisample textures require ARB_texture_storage_multisample of OES_texture_storage_multisample_2d_array
template<GLenum TypeV, GLint FormatV, GLboolean ImmutableV>
class texture {
// Constants ===========================================================================================================
public:
static constexpr GLenum type = TypeV;
static constexpr GLint format = FormatV;
static constexpr GLboolean immutable = ImmutableV;
static constexpr GLboolean is_rect = type == TEXTURE_RECTANGLE;
static constexpr GLboolean is_buffered = type == BUFFER_TEXTURE;
static constexpr GLboolean sampled = type == TEXTURE_2D_MS or type == TEXTURE_2D_MS_ARRAY;
static constexpr GLboolean cubemap = type == CUBEMAP or type == CUBEMAP_ARRAY;
static constexpr GLboolean is_1d = type == TEXTURE_1D or type == TEXTURE_1D_ARRAY;
static constexpr GLboolean is_2d = sampled or is_rect or type == TEXTURE_2D or type == TEXTURE_2D_ARRAY or cubemap;
static constexpr GLboolean is_array = type == TEXTURE_1D_ARRAY or type == TEXTURE_2D_ARRAY or type == TEXTURE_2D_MS_ARRAY or type == CUBEMAP_ARRAY;
static constexpr GLboolean is_3d = type == TEXTURE_3D;
static constexpr GLboolean has_mipmaps = not(sampled or is_rect or is_buffered);
static constexpr GLboolean use_1d = is_1d and not is_array;
static constexpr GLboolean use_2d = ((is_2d and not is_array) or (is_1d and is_array)) and not sampled;
static constexpr GLboolean use_3d = (is_3d or (is_2d and is_array)) and not (sampled or cubemap);
static constexpr GLint cubemap_faces = 6;
static constexpr GLenum base_cubemap_face = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
static constexpr GLboolean compressed = type == RGB_DXT1 or type == RGBA_DXT1 or type == RGBA_DXT3 or type == RGBA_DXT5
or type == SRGB_DXT1 or type == SRGBA_DXT1 or type == SRGBA_DXT3 or type == SRGBA_DXT5;
// Constructors ========================================================================================================
///
/// \brief 1D Texture Constructor
/// \param width The width of the texture
/// \param mips The number of mipmap levels
/// \param data A pointer to a buffer containing pixel values, used as an offset if a PIXEL_UNPACK_BUFFER is bound.
/// \param component The type of each component
/// \param layout The layout of components in each pixel
/// \param size The size of the image data in bytes, for compressed pixel formats
texture(GLsizei width, GLint mips,
void* data = nullptr, GLenum component = BYTE, GLenum layout = R, GLsizei size = 0) requires use_1d
: _handle(NULL)
, _width(width), _height(1), _depth(1)
, _samples(1), _mips(mips) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage1D(type, _mips, format, _width);
glTexSubImage1D(type, 0, 0, _width, layout, component, data);
} else if constexpr(compressed) {
glCompressedTexImage1D(type, 0, format, _width, 0, size, data);
} else {
glTexImage1D(type, 0, format, _width, 0, layout, component, data);
}
genmips();
}
///
/// \brief 2D Texture Constructor
/// \param width The width of the texture
/// \param height The height of the texture, or number of layers for arrays
/// \param mips The number of mipmap levels
/// \param data A pointer to a buffer containing pixel values, used as an offset if a PIXEL_UNPACK_BUFFER is bound.
/// \param component The type of each component
/// \param layout The layout of components in each pixel
/// \param size The size of the image data in bytes, for compressed pixel formats
texture(GLsizei width, GLsizei height, GLint mips,
void* data = nullptr, GLenum component = BYTE, GLenum layout = R, GLsizei size = 0) requires use_2d and not cubemap
: _handle(NULL)
, _width(width), _height(height), _depth(1)
, _samples(1), _mips(mips) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage2D(type, _mips, format, _width, _height);
glTexSubImage2D(type, 0, 0, 0, _width, _height, layout, component, data);
} else if constexpr(compressed) {
glCompressedTexImage2D(type, 0, format, _width, _height, 0, size, data);
} else {
glTexImage2D(type, 0, format, _width, _height, 0, layout, component, data);
}
}
///
/// \brief 2D Texture Constructor
/// \param width The width of the texture
/// \param height The height of the texture
/// \param depth The depth of the texture, or number of layers for arrays
/// \param mips The number of mipmap levels
/// \param data A pointer to a buffer containing pixel values, used as an offset if a PIXEL_UNPACK_BUFFER is bound.
/// \param component The type of each component
/// \param layout The layout of components in each pixel
/// \param size The size of the image data in bytes, for compressed pixel formats
texture(GLsizei width, GLsizei height, GLsizei depth, GLsizei mips,
void* data = nullptr, GLenum component = BYTE, GLenum layout = R, GLsizei size = 0) requires use_3d and not cubemap
: _handle(NULL)
, _width(width), _height(height), _depth(depth)
, _samples(1), _mips(mips) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage3D(type, _mips, format, _width, _height, _depth);
glTexSubImage3D(type, 0, 0, 0, 0, _width, _height, _depth, layout, component, data);
} else if constexpr(compressed) {
glCompressedTexImage3D(type, 0, format, _width, _height, _depth, 0, size, data);
} else {
glTexImage3D(type, 0, format, _width, _height, _depth, 0, layout, component, data);
}
}
///
/// \brief 2D Multisample Texture Constructor
/// \param width The width of the texture
/// \param height The height of the texture
/// \param samples The number of samples per pixel
/// \param fixed When true, a fixed set of sample locations is used
texture(GLsizei width, GLsizei height, GLsizei samples, GLboolean fixed = true) requires sampled and not is_array
: _handle(NULL)
, _width(width), _height(height), _depth(1)
, _samples(samples), _mips(0) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage2DMultisample(type, _samples, format, _width, _height, fixed);
} else {
glTexImage2DMultisample(type, _samples, format, _width, _height, fixed);
}
}
///
/// \brief 2D Multisample Array Texture Constructor
/// \param width The width of the texture
/// \param height The height of the texture
/// \param depth The number of layers in the array
/// \param samples The number of samples per pixel
/// \param fixed When true, a fixed set of sample locations is used
texture(GLsizei width, GLsizei height, GLsizei depth, GLsizei samples, GLboolean fixed = true) requires sampled and is_array
: _handle(NULL)
, _width(width), _height(height), _depth(depth)
, _samples(samples), _mips(0) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage3DMultisample(type, _samples, format, _width, _height, _depth, fixed);
} else {
glTexImage3DMultisample(type, _samples, format, _width, _height, _depth, fixed);
}
}
///
/// \brief Cubemap Constructor
/// \param size The size of each face texture
/// \param mips The number of mipmap layers
/// \param faces An array of pointers to textures containing pixel data for each face
/// \param component The component type of the data
/// \param layout The layout of the components in the pixel
/// \param bytes The size of the image data in bytes, for compressed pixel formats
texture(GLsizei size, GLsizei mips,
const void* faces[6], GLenum component = BYTE, GLenum layout = R, GLsizei bytes = 0) requires is_2d and cubemap
: _handle(NULL)
, _width(size), _height(size), _depth(1)
, _samples(1), _mips(mips) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage2D(type, _mips, format, _width, _height);
for (int i = 0; i < cubemap_faces; ++i) {
glTexSubImage2D(base_cubemap_face + i, 0, 0, 0, _width, _height, layout, component, faces[i]);
}
} else if constexpr(compressed) {
for (int i = 0; i < cubemap_faces; ++i) {
glCompressedTexImage2D(base_cubemap_face + i, 0, format, _width, _height, 0, bytes, faces[i]);
}
} else {
for (int i = 0; i < cubemap_faces; ++i) {
glTexImage2D(base_cubemap_face + i, 0, format, _width, _height, 0, layout, component, faces[i]);
}
}
}
///
/// \brief Cubemap Array Constructor
/// \param size The size of each face texture
/// \param depth The number of layers in the array
/// \param mips The number of mipmap layers
/// \param data A pointer to a buffer containing image data
/// \param component The component type of the data
/// \param layout The layout of the components in the pixel
/// \param bytes The size of the image data in bytes, for compressed pixel formats
///
/// \details Requires OES_texture_cube_map_array
texture(GLsizei size, GLsizei depth, GLsizei mips,
const void* data, GLenum component = BYTE, GLenum layout = R, GLsizei bytes = 0) requires is_2d and cubemap
: _handle(NULL)
, _width(size), _height(size), _depth(depth)
, _samples(1), _mips(mips) {
glGenTextures(1, &_handle);
start();
if constexpr(immutable) {
glTexStorage3D(type, _mips, format, _width, _height, _depth * 6);
glTexSubImage3D(type, 0, 0, 0, 0, _width, _height, _depth * 6, layout, component, data);
} else if constexpr(compressed) {
for (int i = 0; i < cubemap_faces; ++i) {
glCompressedTexImage3D(type, 0, format, _width, _height, _depth * 6, 0, bytes, data);
}
} else {
glTexImage3D(type, 0, format, _width, _height, _depth * 6, 0, layout, component, data);
}
}
// Basic Functions =====================================================================================================
void use() {
glBindTexture(type, _handle);
}
void bind(GLint i) {
glActiveTexture(GL_TEXTURE0 + i);
use();
}
void genmips() {
glGenerateMipmap(type);
}
private:
GLuint _handle;
GLsizei _width;
GLsizei _height;
GLsizei _depth;
GLsizei _samples;
GLint _mips;
};
}
}
#endif // FENNEC_RENDERERS_OPENGL_LIB_TEXTURE_H