// Copyright (c) 2022 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "Texture.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(textureslot_t slot) { assertTrue(this->id != -1, "Texture::bind: 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( int32_t width, int32_t height, enum TextureFormat format, enum TextureDataFormat dataFormat ) { if(this->id != -1) { glDeleteTextures(1, &this->id); assertNoGLError(); this->id = -1; } #if DAWN_DEBUG_BUILD int32_t maxSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); assertTrue(width > 0 && width <= maxSize, "Texture::setSize: Width is out of bounds!"); assertTrue(height > 0 && height <= maxSize, "Texture::setSize: Height is out of bounds!"); #endif this->width = width; this->height = height; this->format = format; this->dataFormat = dataFormat; glGenTextures(1, &this->id); assertNoGLError(); if(this->id <= 0) throw "Texture generation failed!"; // Initialize the texture to blank glActiveTexture(GL_TEXTURE0); assertNoGLError(); this->bufferRaw(NULL); } void Texture::fill(struct Color color) { struct Color *pixels = (struct Color *)memoryAllocate( sizeof(struct Color) * this->width * this->height ); this->buffer(pixels); memoryFree(pixels); } void Texture::fill(uint8_t color) { uint8_t *pixels = (uint8_t *)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("Texture::updateTextureProperties: 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); // switch(mapFilterMode) { // case TEXTURE_FILTER_MODE_NEAREST: // glTexParameteri(GL_TEXTURE_2D, minMag, GL_NEAREST_MIPMAP_NEAREST); // break; // case TEXTURE_FILTER_MODE_LINEAR: // glTexParameteri(GL_TEXTURE_2D, minMag, GL_LINEAR_MIPMAP_NEAREST); // break; // default: // assertUnreachable(); // } break; } case TEXTURE_FILTER_MODE_LINEAR: { glTexParameteri(GL_TEXTURE_2D, minMag, GL_LINEAR); // switch(mapFilterMode) { // case TEXTURE_FILTER_MODE_NEAREST: // glTexParameteri(GL_TEXTURE_2D, minMag, GL_NEAREST_MIPMAP_LINEAR); // break; // case TEXTURE_FILTER_MODE_LINEAR: // glTexParameteri(GL_TEXTURE_2D, minMag, GL_LINEAR_MIPMAP_LINEAR); // break; // default: // assertUnreachable(); // } break; } default: { assertUnreachable("Texture::updateTextureProperties: 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(void *data) { assertTrue(this->isReady(), "Texture::bufferRaw: 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("Texture::bufferRaw: 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("Texture::bufferRaw: 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(struct ColorU8 pixels[]) { assertTrue(this->dataFormat == TEXTURE_DATA_FORMAT_UNSIGNED_BYTE, "Texture::buffer: Texture data format must be unsigned byte!"); this->bufferRaw((void*)pixels); } void Texture::buffer(struct Color pixels[]) { assertTrue(this->dataFormat == TEXTURE_DATA_FORMAT_FLOAT, "Texture::buffer: Texture data format must be float!"); assertTrue(this->format == TEXTURE_FORMAT_RGBA, "Texture::buffer: Texture format must be RGBA!"); this->bufferRaw((void*)pixels); } void Texture::buffer(uint8_t pixels[]) { assertTrue(this->dataFormat == TEXTURE_DATA_FORMAT_UNSIGNED_BYTE, "Texture::buffer: Texture data format must be unsigned byte!"); this->bufferRaw((void*)pixels); } Texture::~Texture() { if(this->id != -1) { glDeleteTextures(1, &this->id); assertNoGLError(); } }