From f77cfde13edd6f70a6838883e1516066f11529f5 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Tue, 19 Jul 2022 16:47:52 +0800 Subject: [PATCH] Add 1D and 3D texture types. Add support for loading 1D textures --- src/entity/components/model.hpp | 1 - src/game/state/nuptial-flight.cpp | 6 +- src/gl/shader-input.cpp | 98 +++++++++++ src/gl/shader-input.hpp | 8 + src/gl/shader-program.cpp | 13 ++ src/gl/shader-variable-type.hpp | 2 + src/gl/texture-1d.cpp | 41 +++++ src/gl/texture-1d.hpp | 48 ++++++ src/gl/texture-2d.cpp | 188 +-------------------- src/gl/texture-2d.hpp | 132 ++------------- src/gl/texture-3d.cpp | 41 +++++ src/gl/texture-3d.hpp | 48 ++++++ src/gl/texture-cube.hpp | 1 - src/gl/texture-filter.hpp | 6 + src/gl/texture.cpp | 267 ++++++++++++++++++++++++++++++ src/gl/texture.hpp | 212 ++++++++++++++++++++++++ src/render/material-property.hpp | 14 ++ src/resources/material-loader.cpp | 42 ++++- src/resources/texture-loader.cpp | 116 +++++++++++++ 19 files changed, 973 insertions(+), 311 deletions(-) create mode 100644 src/gl/texture-1d.cpp create mode 100644 src/gl/texture-1d.hpp create mode 100644 src/gl/texture-3d.cpp create mode 100644 src/gl/texture-3d.hpp create mode 100644 src/gl/texture.cpp create mode 100644 src/gl/texture.hpp diff --git a/src/entity/components/model.hpp b/src/entity/components/model.hpp index 3aac87a..e3d6c80 100644 --- a/src/entity/components/model.hpp +++ b/src/entity/components/model.hpp @@ -38,4 +38,3 @@ struct model } // namespace entity #endif // ANTKEEPER_ENTITY_COMPONENT_MODEL_HPP - diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index 2003680..319c972 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -58,8 +58,8 @@ nuptial_flight::nuptial_flight(game::context& ctx): ant::breed breed; // Load morphological traits - breed.head = ctx.resource_manager->load("square-harvester-head.dna"); - breed.mandibles = ctx.resource_manager->load("harvester-mandibles.dna"); + breed.head = ctx.resource_manager->load("miller-head.dna"); + breed.mandibles = ctx.resource_manager->load("miller-mandibles.dna"); breed.antennae = ctx.resource_manager->load("slender-antennae.dna"); breed.eyes = ctx.resource_manager->load("oval-eyes.dna"); breed.mesosoma = ctx.resource_manager->load("humpback-mesosoma.dna"); @@ -264,7 +264,7 @@ void nuptial_flight::setup_camera() ctx.entity_registry->assign(camera_eid, constraint_stack); } - float ev100 = 14.0f; + float ev100 = 14.5f; ctx.surface_camera->set_exposure(ev100); } diff --git a/src/gl/shader-input.cpp b/src/gl/shader-input.cpp index 35e4d34..930ee0a 100644 --- a/src/gl/shader-input.cpp +++ b/src/gl/shader-input.cpp @@ -18,7 +18,9 @@ */ #include "gl/shader-input.hpp" +#include "gl/texture-1d.hpp" #include "gl/texture-2d.hpp" +#include "gl/texture-3d.hpp" #include "gl/texture-cube.hpp" #include @@ -211,6 +213,21 @@ bool shader_input::upload(const float4x4& value) const return true; } +bool shader_input::upload(const texture_1d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location, texture_unit); + + return true; +} + bool shader_input::upload(const texture_2d* value) const { if (gl_uniform_location == -1) @@ -226,6 +243,21 @@ bool shader_input::upload(const texture_2d* value) const return true; } +bool shader_input::upload(const texture_3d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location, texture_unit); + + return true; +} + bool shader_input::upload(const texture_cube* value) const { if (gl_uniform_location == -1) @@ -415,6 +447,21 @@ bool shader_input::upload(std::size_t index, const float4x4& value) const return true; } +bool shader_input::upload(std::size_t index, const texture_1d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); + glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); + + return true; +} + bool shader_input::upload(std::size_t index, const texture_2d* value) const { if (gl_uniform_location == -1) @@ -430,6 +477,21 @@ bool shader_input::upload(std::size_t index, const texture_2d* value) const return true; } +bool shader_input::upload(std::size_t index, const texture_3d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); + glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); + + return true; +} + bool shader_input::upload(std::size_t index, const texture_cube* value) const { if (gl_uniform_location == -1) @@ -636,6 +698,24 @@ bool shader_input::upload(std::size_t index, const float4x4* values, std::size_t return true; } +bool shader_input::upload(std::size_t index, const texture_1d** values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + for (std::size_t i = 0; i < count; ++i) + { + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); + glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); + } + + return true; +} + bool shader_input::upload(std::size_t index, const texture_2d** values, std::size_t count) const { if (gl_uniform_location == -1) @@ -654,6 +734,24 @@ bool shader_input::upload(std::size_t index, const texture_2d** values, std::siz return true; } +bool shader_input::upload(std::size_t index, const texture_3d** values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + for (std::size_t i = 0; i < count; ++i) + { + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); + glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); + } + + return true; +} + bool shader_input::upload(std::size_t index, const texture_cube** values, std::size_t count) const { if (gl_uniform_location == -1) diff --git a/src/gl/shader-input.hpp b/src/gl/shader-input.hpp index 2cfd7de..56c5951 100644 --- a/src/gl/shader-input.hpp +++ b/src/gl/shader-input.hpp @@ -26,7 +26,9 @@ namespace gl { class shader_program; +class texture_1d; class texture_2d; +class texture_3d; class texture_cube; enum class shader_variable_type; @@ -77,7 +79,9 @@ public: bool upload(const float2x2& value) const; bool upload(const float3x3& value) const; bool upload(const float4x4& value) const; + bool upload(const texture_1d* value) const; bool upload(const texture_2d* value) const; + bool upload(const texture_3d* value) const; bool upload(const texture_cube* value) const; ///@} @@ -108,7 +112,9 @@ public: bool upload(std::size_t index, const float2x2& value) const; bool upload(std::size_t index, const float3x3& value) const; bool upload(std::size_t index, const float4x4& value) const; + bool upload(std::size_t index, const texture_1d* value) const; bool upload(std::size_t index, const texture_2d* value) const; + bool upload(std::size_t index, const texture_3d* value) const; bool upload(std::size_t index, const texture_cube* value) const; ///@} @@ -140,7 +146,9 @@ public: bool upload(std::size_t index, const float2x2* values, std::size_t count) const; bool upload(std::size_t index, const float3x3* values, std::size_t count) const; bool upload(std::size_t index, const float4x4* values, std::size_t count) const; + bool upload(std::size_t index, const texture_1d** values, std::size_t count) const; bool upload(std::size_t index, const texture_2d** values, std::size_t count) const; + bool upload(std::size_t index, const texture_3d** values, std::size_t count) const; bool upload(std::size_t index, const texture_cube** values, std::size_t count) const; ///@} diff --git a/src/gl/shader-program.cpp b/src/gl/shader-program.cpp index 2bbad68..02acb9f 100644 --- a/src/gl/shader-program.cpp +++ b/src/gl/shader-program.cpp @@ -267,6 +267,13 @@ void shader_program::find_inputs() case GL_FLOAT_MAT4: variable_type = shader_variable_type::float4x4; break; + + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + variable_type = shader_variable_type::texture_1d; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; case GL_SAMPLER_2D: case GL_SAMPLER_2D_SHADOW: @@ -275,6 +282,12 @@ void shader_program::find_inputs() available_texture_unit += uniform_size; break; + case GL_SAMPLER_3D: + variable_type = shader_variable_type::texture_3d; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; + case GL_SAMPLER_CUBE: variable_type = shader_variable_type::texture_cube; texture_unit = available_texture_unit; diff --git a/src/gl/shader-variable-type.hpp b/src/gl/shader-variable-type.hpp index 2aa3a58..43233dd 100644 --- a/src/gl/shader-variable-type.hpp +++ b/src/gl/shader-variable-type.hpp @@ -43,7 +43,9 @@ enum class shader_variable_type float2x2, float3x3, float4x4, + texture_1d, texture_2d, + texture_3d, texture_cube }; diff --git a/src/gl/texture-1d.cpp b/src/gl/texture-1d.cpp new file mode 100644 index 0000000..993fec3 --- /dev/null +++ b/src/gl/texture-1d.cpp @@ -0,0 +1,41 @@ +/* + * 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-1d.hpp" + +namespace gl { + +texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, type, format, color_space, data) +{} + +texture_1d::~texture_1d() +{} + +void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + texture::resize(width, type, format, color_space, data); +} + +void texture_1d::set_wrapping(gl::texture_wrapping wrap_s) +{ + texture::set_wrapping(wrap_s); +} + +} // namespace gl diff --git a/src/gl/texture-1d.hpp b/src/gl/texture-1d.hpp new file mode 100644 index 0000000..e9c3863 --- /dev/null +++ b/src/gl/texture-1d.hpp @@ -0,0 +1,48 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_1D_HPP +#define ANTKEEPER_GL_TEXTURE_1D_HPP + +#include "gl/texture.hpp" + +namespace gl { + +/** + * A 1D texture which can be uploaded to shaders via shader inputs. + */ +class texture_1d: public texture +{ +public: + /// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + + /// Destructs a 1D texture. + virtual ~texture_1d(); + + /// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + + /// @copydoc texture::set_wrapping(gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s); +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_1D_HPP diff --git a/src/gl/texture-2d.cpp b/src/gl/texture-2d.cpp index 4ab6e6f..5e8d55b 100644 --- a/src/gl/texture-2d.cpp +++ b/src/gl/texture-2d.cpp @@ -18,198 +18,24 @@ */ #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(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, height, type, format, color_space, data) +{} 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) +void texture_2d::resize(std::uint16_t width, std::uint16_t 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); - } + texture::resize(width, height, type, format, color_space, data); } 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); + texture::set_wrapping(wrap_s, wrap_t); } } // namespace gl diff --git a/src/gl/texture-2d.hpp b/src/gl/texture-2d.hpp index e3d3dd1..4f5c824 100644 --- a/src/gl/texture-2d.hpp +++ b/src/gl/texture-2d.hpp @@ -20,141 +20,29 @@ #ifndef ANTKEEPER_GL_TEXTURE_2D_HPP #define ANTKEEPER_GL_TEXTURE_2D_HPP -#include -#include -#include "gl/color-space.hpp" -#include "gl/pixel-format.hpp" -#include "gl/pixel-type.hpp" +#include "gl/texture.hpp" namespace gl { -class framebuffer; -class shader_input; -enum class texture_mag_filter; -enum class texture_min_filter; -enum class texture_wrapping; - /** * A 2D texture which can be uploaded to shaders via shader inputs. */ -class texture_2d +class texture_2d: public texture { public: - /** - * Creates a 2D texture. - * - * @param color_space Specifies the color space of the pixel data. - * - * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. - */ - texture_2d(int width, int height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - - /** - * Destroys a 2D texture. - */ - ~texture_2d(); - - /** - * Resizes the texture. - * - * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. - */ - void resize(int width, int height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - - /** - * Sets the texture wrapping modes. - * - * @param wrap_s Wrapping mode for s-coordinates. - * @param wrap_t Wrapping mode for t-coordinates. - */ - void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); - - /** - * Sets the texture filtering modes. - * - * @param min_filter Texture minification filter. - * @param mag_filter Texture magnification filter. - */ - void set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter); - - /** - * Sets the maximum anisotropy. - * - * @param level Max anisotropy on `[0.0, 1.0]`, with `0.0` indicating normal filtering, and `1.0` indicating maximum anisotropic filtering. - */ - void set_max_anisotropy(float anisotropy); - - /// Returns the dimensions of the texture, in pixels. - const std::array& get_dimensions() const; + /// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - /// Returns the pixel type enumeration. - const pixel_type& get_pixel_type() const; + /// Destructs a 2D texture. + virtual ~texture_2d(); - /// Returns the pixel format enumeration. - const pixel_format& get_pixel_format() const; + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - /// Returns the color space enumeration. - const color_space& get_color_space() const; - - /// Returns the wrapping modes of the texture. - const std::tuple get_wrapping() const; - - /// Returns the filtering modes of the texture. - const std::tuple get_filters() const; - - /// Returns the maximum anisotropy. - float get_max_anisotropy() const; - -private: - friend class framebuffer; - friend class shader_input; - - unsigned int gl_texture_id; - std::array dimensions; - gl::pixel_type pixel_type; - gl::pixel_format pixel_format; - gl::color_space color_space; - std::tuple wrapping; - std::tuple filters; - float max_anisotropy; + /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); }; -inline const std::array& texture_2d::get_dimensions() const -{ - return dimensions; -} - -inline const pixel_type& texture_2d::get_pixel_type() const -{ - return pixel_type; -} - -inline const pixel_format& texture_2d::get_pixel_format() const -{ - return pixel_format; -} - -inline const color_space& texture_2d::get_color_space() const -{ - return color_space; -} - -inline const std::tuple texture_2d::get_wrapping() const -{ - return wrapping; -} - -inline const std::tuple texture_2d::get_filters() const -{ - return filters; -} - -inline float texture_2d::get_max_anisotropy() const -{ - return max_anisotropy; -} - } // namespace gl #endif // ANTKEEPER_GL_TEXTURE_2D_HPP - diff --git a/src/gl/texture-3d.cpp b/src/gl/texture-3d.cpp new file mode 100644 index 0000000..a380954 --- /dev/null +++ b/src/gl/texture-3d.cpp @@ -0,0 +1,41 @@ +/* + * 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-3d.hpp" + +namespace gl { + +texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, height, depth, type, format, color_space, data) +{} + +texture_3d::~texture_3d() +{} + +void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + texture::resize(width, height, depth, type, format, color_space, data); +} + +void texture_3d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r) +{ + texture::set_wrapping(wrap_s, wrap_t, wrap_r); +} + +} // namespace gl diff --git a/src/gl/texture-3d.hpp b/src/gl/texture-3d.hpp new file mode 100644 index 0000000..14dd380 --- /dev/null +++ b/src/gl/texture-3d.hpp @@ -0,0 +1,48 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_3D_HPP +#define ANTKEEPER_GL_TEXTURE_3D_HPP + +#include "gl/texture.hpp" + +namespace gl { + +/** + * A 3D texture which can be uploaded to shaders via shader inputs. + */ +class texture_3d: public texture +{ +public: + /// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + + /// Destructs a 3D texture. + virtual ~texture_3d(); + + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + + /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping, gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r); +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_3D_HPP diff --git a/src/gl/texture-cube.hpp b/src/gl/texture-cube.hpp index 009d012..895889b 100644 --- a/src/gl/texture-cube.hpp +++ b/src/gl/texture-cube.hpp @@ -58,4 +58,3 @@ inline int texture_cube::get_face_size() const } // namespace gl #endif // ANTKEEPER_GL_TEXTURE_CUBE_HPP - diff --git a/src/gl/texture-filter.hpp b/src/gl/texture-filter.hpp index d65a48c..7357b54 100644 --- a/src/gl/texture-filter.hpp +++ b/src/gl/texture-filter.hpp @@ -22,6 +22,9 @@ namespace gl { +/** + * Texture minification filter modes. + */ enum class texture_min_filter { nearest, @@ -32,6 +35,9 @@ enum class texture_min_filter linear_mipmap_linear }; +/** + * Texture magnification filter modes. + */ enum class texture_mag_filter { nearest, diff --git a/src/gl/texture.cpp b/src/gl/texture.cpp new file mode 100644 index 0000000..a316cb3 --- /dev/null +++ b/src/gl/texture.cpp @@ -0,0 +1,267 @@ +/* + * 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.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::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + gl_texture_target((depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D), + gl_texture_id(0), + dimensions({0, 0, 0}), + wrapping({texture_wrapping::repeat, 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, depth, type, format, color_space, data); + set_wrapping(wrapping[0], wrapping[1], wrapping[2]); + set_filters(std::get<0>(filters), std::get<1>(filters)); + set_max_anisotropy(max_anisotropy); +} + +texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, height, 0, type, format, color_space, data) +{} + +texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, 0, 0, type, format, color_space, data) +{} + +texture::~texture() +{ + glDeleteTextures(1, &gl_texture_id); +} + +void texture::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_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); + glTexParameteri(gl_texture_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); +} + +void texture::set_max_anisotropy(float anisotropy) +{ + max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy)); + + // Get the maximum supported anisotropy value + float gl_max_texture_max_anisotropy; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy); + + // Lerp between 1.0 and GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + float gl_max_anisotropy = 1.0f + max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f); + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameterf(gl_texture_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); +} + +void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r) +{ + wrapping = {wrap_s, wrap_t, wrap_r}; + + GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; + GLenum gl_wrap_r = wrapping_lut[static_cast(wrap_r)]; + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_R, gl_wrap_r); +} + +void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t) +{ + std::get<0>(wrapping) = wrap_s; + std::get<1>(wrapping) = 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_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); +} + +void texture::set_wrapping(gl::texture_wrapping wrap_s) +{ + std::get<0>(wrapping) = wrap_s; + + GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); +} + +void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + dimensions = {width, height, depth}; + 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_target, gl_texture_id); + if (depth) + glTexImage3D(gl_texture_target, 0, gl_internal_format, width, height, depth, 0, gl_format, gl_type, data); + else if (height) + glTexImage2D(gl_texture_target, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data); + else if (width) + glTexImage1D(gl_texture_target, 0, gl_internal_format, width, 0, gl_format, gl_type, data); + + glGenerateMipmap(gl_texture_target); + glTexParameteriv(gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); + + /// TODO: remove this + if (format == pixel_format::d) + { + glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS); + glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + } +} + +void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + resize(width, height, 0, type, format, color_space, data); +} + +void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + resize(width, 0, 0, type, format, color_space, data); +} + +} // namespace gl diff --git a/src/gl/texture.hpp b/src/gl/texture.hpp new file mode 100644 index 0000000..22ec830 --- /dev/null +++ b/src/gl/texture.hpp @@ -0,0 +1,212 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_HPP +#define ANTKEEPER_GL_TEXTURE_HPP + +#include "gl/color-space.hpp" +#include "gl/pixel-format.hpp" +#include "gl/pixel-type.hpp" +#include "gl/texture-filter.hpp" +#include "gl/texture-wrapping.hpp" +#include +#include +#include + +namespace gl { + +class framebuffer; +class shader_input; + +/** + * Abstract base class for 1D, 2D, 3D, and cube textures which can be uploaded to shaders via shader inputs. + */ +class texture +{ +public: + /** + * Constructs a texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. For 2D or 3D textures. + * @param depth Texture depth, in pixels. For 3D textures only. + * @param type Pixel component data type. + * @param format Pixel format. + * @param color_space Color space of the pixel data. + * @param data Pointer to pixel data. + * + * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. + */ + /// @{ + texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + /// @} + + /** + * Destructs a texture. + */ + virtual ~texture() = 0; + + /** + * Sets the texture filtering modes. + * + * @param min_filter Texture minification filter mode. + * @param mag_filter Texture magnification filter mode. + */ + void set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter); + + /** + * Sets the maximum anisotropy. + * + * @param level Max anisotropy on `[0.0, 1.0]`, with `0.0` indicating normal filtering, and `1.0` indicating maximum anisotropic filtering. + */ + void set_max_anisotropy(float anisotropy); + + /// Returns the dimensions of the texture, in pixels. + const std::array& get_dimensions() const; + + /// Returns the width of the texture, in pixels. + const std::uint16_t& get_width() const; + + /// Returns the height of the texture, in pixels. + const std::uint16_t& get_height() const; + + /// Returns the depth of the texture, in pixels. + const std::uint16_t& get_depth() const; + + /// Returns the pixel type enumeration. + const pixel_type& get_pixel_type() const; + + /// Returns the pixel format enumeration. + const pixel_format& get_pixel_format() const; + + /// Returns the color space enumeration. + const color_space& get_color_space() const; + + /// Returns the wrapping modes of the texture. + const std::array& get_wrapping() const; + + /// Returns the filtering modes of the texture. + const std::tuple& get_filters() const; + + /// Returns the maximum anisotropy. + float get_max_anisotropy() const; + +protected: + /** + * Sets the texture wrapping modes. + * + * @param wrap_s Wrapping mode for s-coordinates. + * @param wrap_t Wrapping mode for t-coordinates. + * @param wrap_r Wrapping mode for r-coordinates. + */ + /// @{ + virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r); + virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t); + virtual void set_wrapping(gl::texture_wrapping wrap_s); + /// @} + + /** + * Resizes the texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. For 2D or 3D textures. + * @param depth Texture depth, in pixels. For 3D textures only. + * @param type Pixel component data type. + * @param format Pixel format. + * @param color_space Color space of the pixel data. + * @param data Pointer to pixel data. + * + * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. + */ + /// @{ + virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + /// @} + +private: + friend class framebuffer; + friend class shader_input; + + unsigned int gl_texture_target; + unsigned int gl_texture_id; + std::array dimensions; + gl::pixel_type pixel_type; + gl::pixel_format pixel_format; + gl::color_space color_space; + std::array wrapping; + std::tuple filters; + float max_anisotropy; +}; + +inline const std::array& texture::get_dimensions() const +{ + return dimensions; +} + +inline const std::uint16_t& texture::get_width() const +{ + return dimensions[0]; +} + +inline const std::uint16_t& texture::get_height() const +{ + return dimensions[1]; +} + +inline const std::uint16_t& texture::get_depth() const +{ + return dimensions[2]; +} + +inline const pixel_type& texture::get_pixel_type() const +{ + return pixel_type; +} + +inline const pixel_format& texture::get_pixel_format() const +{ + return pixel_format; +} + +inline const color_space& texture::get_color_space() const +{ + return color_space; +} + +inline const std::array& texture::get_wrapping() const +{ + return wrapping; +} + +inline const std::tuple& texture::get_filters() const +{ + return filters; +} + +inline float texture::get_max_anisotropy() const +{ + return max_anisotropy; +} + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_HPP diff --git a/src/render/material-property.hpp b/src/render/material-property.hpp index 6261858..2f3823b 100644 --- a/src/render/material-property.hpp +++ b/src/render/material-property.hpp @@ -26,7 +26,9 @@ #include "math/interpolation.hpp" #include "utility/fundamental-types.hpp" #include "gl/shader-program.hpp" +#include "gl/texture-1d.hpp" #include "gl/texture-2d.hpp" +#include "gl/texture-3d.hpp" #include "gl/texture-cube.hpp" #include @@ -415,12 +417,24 @@ inline gl::shader_variable_type material_property::get_data_type() con return gl::shader_variable_type::float4x4; } +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::texture_1d; +} + template <> inline gl::shader_variable_type material_property::get_data_type() const { return gl::shader_variable_type::texture_2d; } +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::texture_3d; +} + template <> inline gl::shader_variable_type material_property::get_data_type() const { diff --git a/src/resources/material-loader.cpp b/src/resources/material-loader.cpp index f11d512..a469037 100644 --- a/src/resources/material-loader.cpp +++ b/src/resources/material-loader.cpp @@ -39,6 +39,40 @@ static bool read_value(T* value, const nlohmann::json& json, const std::string& return false; } +static bool load_texture_1d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) +{ + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + render::material_property* property = material->add_property(name, array_size); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + std::string filename = element.get(); + const gl::texture_1d* texture = resource_manager->load(filename); + property->set_value(i++, texture); + } + } + else + { + // Create property + render::material_property* property = material->add_property(name); + + // Load texture + std::string filename = json.get(); + const gl::texture_1d* texture = resource_manager->load(filename); + property->set_value(texture); + } + + return true; +} + static bool load_texture_2d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) { // If JSON element is an array @@ -315,12 +349,14 @@ render::material* resource_loader::load(resource_manager* reso // Ignore valueless properties continue; - // If property type is a 2D texture - if (type == "texture_2d") + if (type == "texture_1d") + { + load_texture_1d_property(resource_manager, material, name, value_element.value()); + } + else if (type == "texture_2d") { load_texture_2d_property(resource_manager, material, name, value_element.value()); } - // If property type is a cubic texture else if (type == "texture_cube") { load_texture_cube_property(resource_manager, material, name, value_element.value()); diff --git a/src/resources/texture-loader.cpp b/src/resources/texture-loader.cpp index c4c7059..749bfd0 100644 --- a/src/resources/texture-loader.cpp +++ b/src/resources/texture-loader.cpp @@ -23,13 +23,127 @@ #include "gl/pixel-type.hpp" #include "gl/pixel-format.hpp" #include "gl/color-space.hpp" +#include "gl/texture-1d.hpp" #include "gl/texture-2d.hpp" #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include +#include #include #include +template <> +gl::texture_1d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + nlohmann::json json = nlohmann::json::parse(buffer); + + // Read image filename + std::string image_filename; + if (auto element = json.find("image"); element != json.end()) + image_filename = element.value().get(); + + // Read color space + gl::color_space color_space = gl::color_space::linear; + if (auto element = json.find("color_space"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "linear") + color_space = gl::color_space::linear; + else if (value == "srgb") + color_space = gl::color_space::srgb; + } + + // Read extension mode + gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; + if (auto element = json.find("extension"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "clip") + wrapping = gl::texture_wrapping::clip; + else if (value == "extend") + wrapping = gl::texture_wrapping::extend; + else if (value == "repeat") + wrapping = gl::texture_wrapping::repeat; + else if (value == "mirrored_repeat") + wrapping = gl::texture_wrapping::mirrored_repeat; + } + + // Read interpolation mode + gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; + gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; + if (auto element = json.find("interpolation"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + min_filter = gl::texture_min_filter::linear_mipmap_linear; + mag_filter = gl::texture_mag_filter::linear; + } + else if (value == "closest") + { + min_filter = gl::texture_min_filter::nearest_mipmap_nearest; + mag_filter = gl::texture_mag_filter::nearest; + } + } + + // Read max anisotropy + float max_anisotropy = 0.0f; + if (auto element = json.find("max_anisotropy"); element != json.end()) + max_anisotropy = element.value().get(); + + // Load image + ::image* image = resource_manager->load<::image>(image_filename); + + // Determine pixel type + gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; + + // Determine pixel format + gl::pixel_format format; + if (image->get_channel_count() == 1) + { + format = gl::pixel_format::r; + } + else if (image->get_channel_count() == 2) + { + format = gl::pixel_format::rg; + } + else if (image->get_channel_count() == 3) + { + format = gl::pixel_format::rgb; + } + else if (image->get_channel_count() == 4) + { + format = gl::pixel_format::rgba; + } + else + { + std::stringstream stream; + stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); + delete image; + throw std::runtime_error(stream.str().c_str()); + } + + // Create texture + gl::texture_1d* texture = new gl::texture_1d(image->get_width(), type, format, color_space, image->get_pixels()); + + // Set wrapping and filtering + texture->set_wrapping(wrapping); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + // Free loaded image + resource_manager->unload(image_filename); + + return texture; +} + template <> gl::texture_2d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { @@ -141,3 +255,5 @@ gl::texture_2d* resource_loader::load(resource_manager* resource return texture; } + +