// Copyright 2021 Benjamin Barenblat // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. // Texture objects. #ifndef GLPLANET_SRC_GL_TEXTURE_H_ #define GLPLANET_SRC_GL_TEXTURE_H_ #include "src/gl/error.h" #include "src/util.h" #include "third_party/glew/include/GL/glew.h" namespace gl { class Texture2d; // A generic texture. You can't instantiate this directly; instead, instantiate // one of its derived classes with an explicit dimensionality. class Texture { public: // Options for the texture's sample format. enum class Format : GLenum { kR8 = GL_R8, kR8Snorm = GL_R8_SNORM, kR16 = GL_R16, kR16Snorm = GL_R16_SNORM, kRg8 = GL_RG8, kRg8Snorm = GL_RG8_SNORM, kRg16 = GL_RG16, kRg16Snorm = GL_RG16_SNORM, kR3G3B2 = GL_R3_G3_B2, kRgb4 = GL_RGB4, kRgb5 = GL_RGB5, kRgb8 = GL_RGB8, kRgb8Snorm = GL_RGB8_SNORM, kRgb10 = GL_RGB10, kRgb12 = GL_RGB12, kRgb16Snorm = GL_RGB16_SNORM, kRgba2 = GL_RGBA2, kRgba4 = GL_RGBA4, kRgb5A1 = GL_RGB5_A1, kRgba8 = GL_RGBA8, kRgba8Snorm = GL_RGBA8_SNORM, kRgb10A2 = GL_RGB10_A2, kRgb10A2Ui = GL_RGB10_A2UI, kRgba12 = GL_RGBA12, kRgba16 = GL_RGBA16, kSrgb8 = GL_SRGB8, kSrgb8Alpha8 = GL_SRGB8_ALPHA8, kR16f = GL_R16F, kRg16f = GL_RG16F, kRgb16f = GL_RGB16F, kRgba16f = GL_RGBA16F, kR32f = GL_R32F, kRg32f = GL_RG32F, kRgb32f = GL_RGB32F, kRgba32f = GL_RGBA32F, kR11fG11fB10f = GL_R11F_G11F_B10F, kRgb9E5 = GL_RGB9_E5, kR8i = GL_R8I, kR8ui = GL_R8UI, kR16i = GL_R16I, kR16ui = GL_R16UI, kR32i = GL_R32I, kR32ui = GL_R32UI, kRg8i = GL_RG8I, kRg8ui = GL_RG8UI, kRg16i = GL_RG16I, kRg16ui = GL_RG16UI, kRg32i = GL_RG32I, kRg32ui = GL_RG32UI, kRgb8i = GL_RGB8I, kRgb8ui = GL_RGB8UI, kRgb16i = GL_RGB16I, kRgb16ui = GL_RGB16UI, kRgb32i = GL_RGB32I, kRgb32ui = GL_RGB32UI, kRgba8i = GL_RGBA8I, kRgba8ui = GL_RGBA8UI, kRgba16i = GL_RGBA16I, kRgba16ui = GL_RGBA16UI, kRgba32i = GL_RGBA32I, kRgba32ui = GL_RGBA32UI, }; // Pixel order. enum class PixelFormat : GLenum { kRed = GL_RED, kRg = GL_RG, kRgb = GL_RGB, kBgr = GL_BGR, kRgba = GL_RGBA, kBgra = GL_BGRA, }; // Bits per pixel and subpixel. enum class PixelType : GLenum { kUnsignedByte = GL_UNSIGNED_BYTE, kByte = GL_BYTE, kUnsignedShort = GL_UNSIGNED_SHORT, kShort = GL_SHORT, kUnsignedInt = GL_UNSIGNED_INT, kInt = GL_INT, kFloat = GL_FLOAT, kUnsignedByte332 = GL_UNSIGNED_BYTE_3_3_2, kUnsignedByte233Rev = GL_UNSIGNED_BYTE_2_3_3_REV, kUnsignedShort565 = GL_UNSIGNED_SHORT_5_6_5, kUnsignedShort565Rev = GL_UNSIGNED_SHORT_5_6_5_REV, kUnsignedShort4444 = GL_UNSIGNED_SHORT_4_4_4_4, kUnsignedShort4444Rev = GL_UNSIGNED_SHORT_4_4_4_4_REV, kUnsignedShort5551 = GL_UNSIGNED_SHORT_5_5_5_1, kUnsignedShort1555Rev = GL_UNSIGNED_SHORT_1_5_5_5_REV, kUnsignedInt8888 = GL_UNSIGNED_INT_8_8_8_8, kUnsignedInt8888Rev = GL_UNSIGNED_INT_8_8_8_8_REV, kUnsignedInt10x10x10x2 = GL_UNSIGNED_INT_10_10_10_2, kUnsignedInt2x10x10x10Rev = GL_UNSIGNED_INT_2_10_10_10_REV, }; // Filtering techniques. enum class MinFilter : GLenum { kNearest = GL_NEAREST, kLinear = GL_LINEAR, kNearestMipmapNearest = GL_NEAREST_MIPMAP_NEAREST, kLinearMipmapNearest = GL_LINEAR_MIPMAP_NEAREST, kNearestMipmapLinear = GL_NEAREST_MIPMAP_LINEAR, kLinearMipmapLinear = GL_LINEAR_MIPMAP_LINEAR, }; enum class MagFilter : GLenum { kNearest = GL_NEAREST, kLinear = GL_LINEAR, }; // Wrapping techniques. enum class Wrap : GLenum { kClampToEdge = GL_CLAMP_TO_EDGE, kClampToBorder = GL_CLAMP_TO_BORDER, kMirroredRepeat = GL_MIRRORED_REPEAT, kRepeat = GL_REPEAT, kMirrorClampToEdge = GL_MIRROR_CLAMP_TO_EDGE, }; Texture(Texture&&) noexcept = default; Texture& operator=(Texture&&) noexcept = default; virtual ~Texture() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glDeleteTextures(1, &texture_); gl_internal::UnnecessaryErrorCheck(); } // The GL identifier for this texture. unsigned int id() const noexcept { return texture_; } protected: int levels_; GLuint texture_; private: friend class Texture2d; explicit Texture(int levels) noexcept(!gl_internal::kThreadSafetyChecks) : levels_(levels) { gl_internal::CheckThreadSafety(); } }; // A two-dimensional texture. class Texture2d final : public Texture { public: enum class Dimension : GLenum { kS = GL_TEXTURE_WRAP_S, kT = GL_TEXTURE_WRAP_T, }; // Creates a two-dimensional texture with the specified pixel format, width, // and height. The number of levels is equal to the number of mipmap layers // for this texture; if you're not using mipmaps, you can leave it as 1. explicit Texture2d(Format format, int width, int height, int levels = 1) : Texture(levels), width_(width), height_(height) { glCreateTextures(GL_TEXTURE_2D, 1, &texture_); gl_internal::UnnecessaryErrorCheck(); glTextureStorage2D(texture_, levels, FromEnum(format), width_, height_); // This error check is necessary because `format` could have been passed as // an invalid GLenum. gl_internal::ErrorCheck(); } int width() const noexcept { return width_; } int height() const noexcept { return height_; } // Copies data from the specified buffer into GPU memory and associates it // with this texture. width, height, format, and type describe the formatting // of the data buffer, not this texture! // // There are no alignment restrictions on the buffer. It should consist of // densely packed rows of pixels, left to right and top to bottom. // // As in the constructor, the optional level argument specifies which mipmap // layer to populate. If you're not using mipmaps, you can leave it as 0. void LoadSubimage(int width, int height, PixelFormat format, PixelType type, const void* pixels, int level = 0); // Fills all texture layers except layer 0 with automatically generated // mipmaps. These mipmaps will generally be of lower quality than you would // get if you generated them manually and loaded them with LoadSubimage. // However, using them will yield a higher quality render than not using // mipmaps at all. void GenerateMipmaps() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glGenerateTextureMipmap(texture_); gl_internal::UnnecessaryErrorCheck(); } void SetWrap(Dimension dimension, Wrap wrap) { gl_internal::CheckThreadSafety(); glTexParameteri(GL_TEXTURE_2D, FromEnum(dimension), FromEnum(wrap)); // This error check is necessary because `dimension` or `wrap` could have // been passed as an invalid GLenum. gl_internal::ErrorCheck(); } void SetMinFilter(MinFilter filter) { gl_internal::CheckThreadSafety(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, FromEnum(filter)); // This error check is necessary because `filter` could have been passed as // an invalid GLenum. gl_internal::ErrorCheck(); } void SetMagFilter(MagFilter filter) { gl_internal::CheckThreadSafety(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, FromEnum(filter)); // This error check is necessary because `filter` could have been passed as // an invalid GLenum. gl_internal::ErrorCheck(); } private: int width_; int height_; }; } // namespace gl #endif // GLPLANET_SRC_GL_TEXTURE_H_