From d0e18bdb7924c71cdca8dd983711171d87ef28be Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 17 Jan 2022 23:12:32 -0500 Subject: glplanet, an OpenGL-based planetary renderer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glplanet draws Earth like it currently appears from space, putting nighttime areas in shadow and daytime areas in light. It’s modeled after Xplanet (http://xplanet.sourceforge.net/), but whereas Xplanet is entirely a CPU-resident program, glplanet draws using OpenGL. It’s thus much less resource-intensive, particularly when using high-resolution textures. --- src/gl/texture.h | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 src/gl/texture.h (limited to 'src/gl/texture.h') diff --git a/src/gl/texture.h b/src/gl/texture.h new file mode 100644 index 0000000..17541e0 --- /dev/null +++ b/src/gl/texture.h @@ -0,0 +1,259 @@ +// 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_ -- cgit v1.2.3