Dawn/src/dawnopengl/display/Texture.cpp

239 lines
5.8 KiB
C++

// 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<struct Color>(
sizeof(struct Color) * this->width * this->height
);
this->buffer(pixels);
memoryFree(pixels);
}
void Texture::fill(const uint8_t color) {
auto pixels = memoryAllocate<uint8_t>(
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();
}
}