#include <gltexture.hpp> #include <algorithm> #include <ostream> #include <utility> #include <globject.hpp> // NOLINTNEXTLINE #define STR_EXCEPTION GLObject<>::Exception #include <str.hpp> /// Special member functions GLTexture::GLTexture( // NOLINTNEXTLINE std::string object_label, GLenum target, GLenum binding, GLenum internal_format, GLenum wrap, GLenum min_filter, GLenum mag_filter ) : GLObject(STR_JOIN(" ", it, it, { str_target_(target), str_internal_format_(internal_format), std::move(object_label) })), target_ {target}, binding_ {binding}, min_filter_{min_filter}, unit_ {0} { // check_internal_format_(internal_format); unit(true); if (wrap) { glTexParameteri(target_, GL_TEXTURE_WRAP_S, (GLint)wrap); glTexParameteri(target_, GL_TEXTURE_WRAP_T, (GLint)wrap); glTexParameteri(target_, GL_TEXTURE_WRAP_R, (GLint)wrap); } if (min_filter) glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, (GLint)min_filter); if (mag_filter) glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, (GLint)mag_filter); if (min_filter_mipmap_()) if (!supported({3, 0}, "GL_ARB_framebuffer_object")) glTexParameteri(target_, GL_GENERATE_MIPMAP, GL_TRUE); if (target_ == GL_TEXTURE_2D) // NOLINTNEXTLINE if (supported({4, 6}, "GL_ARB_texture_filter_anisotropic")) // TODO(rcrnstn): Remove the `_EXT` suffix when the headers are // updated. glTexParameterf(target_, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy_); } /// Core GLOBJECT_THREAD(GLTexture::anisotropy_, {1.0F}) GLint GLTexture::unit(bool force_active) const try { auto static const unit_count = (GLuint)integer( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ); auto static units = std::vector<GLuint>(unit_count); auto static textures = std::vector<GLuint>(unit_count); auto static targets = std::vector<GLenum>(unit_count); auto static unit_next = GLuint{1}; auto static unit_active = GLuint{0}; if (!unit_ || textures[unit_] != object()) { if (unit_next < unit_count) units.back() = unit_ = unit_next++; else unit_ = units.back(); std::rotate(units.begin(), units.end() - 1, units.end()); } if (force_active || unit_active != unit_) { glActiveTexture(GL_TEXTURE0 + unit_); unit_active = unit_; } if (textures[unit_] != object()) { GLOBJECT_DEBUG_IF(1) check_unit_active_(); GLOBJECT_DEBUG_IF(1) { glBindTexture(targets[unit_], 0); targets[unit_] = target_; } glBindTexture(target_, object()); textures[unit_] = object(); } GLOBJECT_DEBUG_IF(1) check_unit_texture_(); return (GLint)unit_; } catch (...) { fail_action_("allocate texture unit for"); } bool GLTexture::min_filter_mipmap_() const { switch (min_filter_) { case GL_NEAREST_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_LINEAR: return true; default: return false; } } /// Path GLOBJECT_THREAD(GLTexture::prefix_, {"assets/textures"}) /// Check void GLTexture::check_unit_active_() const { auto unit_active = (GLuint)integer(GL_ACTIVE_TEXTURE) - GL_TEXTURE0; if (unit_active != unit_) STR_THROW( "Expected active unit " << unit_ << ", " << "got " << unit_active << "." ); } void GLTexture::check_unit_texture_() const { auto unit_texture = (GLuint)integer(binding_); if (unit_texture != object()) STR_THROW( "Expected unit " << unit_ << " " << "to be bound to " << debug_name() << ", " << "got " << unit_texture << "." ); } void GLTexture::check_data_size_(std::size_t data_size) const { if (data_size != data_size_()) STR_THROW( "Expected data size " << data_size_() << ", " << "got " << data_size << "." ); } /// String std::string GLTexture::str_target_(GLenum target) { switch (target) { STR_CASE(GL_TEXTURE_1D) STR_CASE(GL_TEXTURE_2D) STR_CASE(GL_TEXTURE_3D) STR_CASE(GL_TEXTURE_1D_ARRAY) STR_CASE(GL_TEXTURE_2D_ARRAY) STR_CASE(GL_TEXTURE_RECTANGLE) STR_CASE(GL_TEXTURE_CUBE_MAP) STR_CASE(GL_TEXTURE_CUBE_MAP_ARRAY) STR_CASE(GL_TEXTURE_BUFFER) STR_CASE(GL_TEXTURE_2D_MULTISAMPLE) STR_CASE(GL_TEXTURE_2D_MULTISAMPLE_ARRAY) default: return str_enum_(target); } }