239 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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();
 | 
						|
  }
 | 
						|
} |