// Copyright (c) 2022 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "Texture.hpp" #include "assert/assertgl.hpp" #include "util/memory.hpp" using namespace Dawn; Texture::Texture() : ITexture() { useEffect([&]{ this->texturePropertiesNeedUpdating = true; }, { &this->wrapModeX, &this->wrapModeY, &this->filterModeMin, &this->filterModeMag, &this->mipmapFilterModeMin, &this->mipmapFilterModeMag }); } void Texture::bind(const textureslot_t slot) { assertTrue(this->id != -1, "Texture is not ready!"); glActiveTexture(GL_TEXTURE0 + slot); assertNoGLError(); glBindTexture(GL_TEXTURE_2D, this->id); assertNoGLError(); if(this->texturePropertiesNeedUpdating) { this->updateTextureProperties(); this->texturePropertiesNeedUpdating = false; } } int32_t Texture::getWidth() { return this->width; } int32_t Texture::getHeight() { return this->height; } void Texture::setSize( const int32_t width, const int32_t height, const enum TextureFormat format, const enum TextureDataFormat dataFormat ) { if(this->id != -1) { glDeleteTextures(1, &this->id); assertNoGLError(); this->id = -1; } int32_t maxSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); assertTrue(width > 0 && width <= maxSize, "Width is out of bounds!"); assertTrue(height > 0 && height <= maxSize, "Height is out of bounds!"); this->width = width; this->height = height; this->format = format; this->dataFormat = dataFormat; glGenTextures(1, &this->id); assertNoGLError(); if(this->id <= 0) assertUnreachable("Texture generation failed!"); // Initialize the texture to blank glActiveTexture(GL_TEXTURE0); assertNoGLError(); this->bufferRaw(NULL); } void Texture::fill(struct Color color) { auto pixels = memoryAllocate( sizeof(struct Color) * this->width * this->height ); this->buffer(pixels); memoryFree(pixels); } void Texture::fill(const uint8_t color) { auto pixels = memoryAllocate( sizeof(uint8_t) * this->width * this->height ); this->buffer(pixels); memoryFree(pixels); } bool_t Texture::isReady() { return this->id != -1; } void Texture::updateTextureProperties() { auto setWrapMode = [&](GLenum axis, enum TextureWrapMode wm) { switch(wm) { case TEXTURE_WRAP_MODE_REPEAT: glTexParameteri(GL_TEXTURE_2D, axis, GL_REPEAT); break; case TEXTURE_WRAP_MODE_MIRRORED_REPEAT: glTexParameteri(GL_TEXTURE_2D, axis, GL_MIRRORED_REPEAT); break; case TEXTURE_WRAP_MODE_CLAMP_TO_EDGE: glTexParameteri(GL_TEXTURE_2D, axis, GL_CLAMP_TO_EDGE); break; case TEXTURE_WRAP_MODE_CLAMP_TO_BORDER: glTexParameteri(GL_TEXTURE_2D, axis, GL_CLAMP_TO_BORDER); break; default: assertUnreachable("Unknown wrap mode!"); } assertNoGLError(); }; setWrapMode(GL_TEXTURE_WRAP_S, this->wrapModeX); setWrapMode(GL_TEXTURE_WRAP_T, this->wrapModeY); auto setFilterMode = [&]( GLenum minMag, enum TextureFilterMode filter, enum TextureFilterMode mapFilterMode ) { switch(filter) { case TEXTURE_FILTER_MODE_NEAREST: { glTexParameteri(GL_TEXTURE_2D, minMag, GL_NEAREST); break; } case TEXTURE_FILTER_MODE_LINEAR: { glTexParameteri(GL_TEXTURE_2D, minMag, GL_LINEAR); break; } default: { assertUnreachable("Unknown filter mode!"); } } assertNoGLError(); }; setFilterMode( GL_TEXTURE_MIN_FILTER, this->filterModeMin, this->mipmapFilterModeMin ); setFilterMode( GL_TEXTURE_MAG_FILTER, this->filterModeMag, this->mipmapFilterModeMag ); } void Texture::bufferRaw(const void *data) { assertTrue(this->isReady(), "Texture is not ready!"); GLenum format; switch(this->format) { case TEXTURE_FORMAT_R: format = GL_RED; break; case TEXTURE_FORMAT_RG: format = GL_RG; break; case TEXTURE_FORMAT_RGB: format = GL_RGB; break; case TEXTURE_FORMAT_RGBA: format = GL_RGBA; break; default: assertUnreachable("Unknown texture format!"); } GLenum dataFormat; switch(this->dataFormat) { case TEXTURE_DATA_FORMAT_UNSIGNED_BYTE: dataFormat = GL_UNSIGNED_BYTE; break; case TEXTURE_DATA_FORMAT_FLOAT: dataFormat = GL_FLOAT; break; default: assertUnreachable("Unknown texture data format!"); } glBindTexture(GL_TEXTURE_2D, this->id); assertNoGLError(); glTexImage2D( GL_TEXTURE_2D, 0, format, this->width, this->height, 0, format, dataFormat, data ); assertNoGLError(); glGenerateMipmap(GL_TEXTURE_2D); assertNoGLError(); this->texturePropertiesNeedUpdating = true; } void Texture::buffer(const struct ColorU8 pixels[]) { assertTrue( this->dataFormat == TEXTURE_DATA_FORMAT_UNSIGNED_BYTE, "Texture data format must be unsigned byte!" ); this->bufferRaw((void*)pixels); } void Texture::buffer(const struct Color pixels[]) { assertTrue( this->dataFormat == TEXTURE_DATA_FORMAT_FLOAT, "Texture data format must be float!" ); assertTrue( this->format == TEXTURE_FORMAT_RGBA, "Texture format must be RGBA!" ); this->bufferRaw((void*)pixels); } void Texture::buffer(const uint8_t pixels[]) { assertTrue( this->dataFormat == TEXTURE_DATA_FORMAT_UNSIGNED_BYTE, "Texture data format must be unsigned byte!" ); this->bufferRaw((void*)pixels); } Texture::~Texture() { if(this->id != -1) { glDeleteTextures(1, &this->id); assertNoGLError(); } }