You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
3.9 KiB
176 lines
3.9 KiB
/* |
|
* Copyright (C) 2022 Riyyi |
|
* |
|
* SPDX-License-Identifier: MIT |
|
*/ |
|
|
|
#include <climits> // UINT_MAX |
|
#include <cstdint> // uint8_t, uint32_t |
|
#include <memory> // std::shared_ptr |
|
#include <utility> // std::move |
|
|
|
#include "glad/glad.h" |
|
#include "ruc/format/log.h" |
|
#include "ruc/meta/assert.h" |
|
#define STB_IMAGE_IMPLEMENTATION |
|
#include "stb/stb_image.h" |
|
|
|
#include "inferno/render/texture.h" |
|
|
|
namespace Inferno { |
|
|
|
Texture::Texture(const std::string& path) |
|
: m_path(std::move(path)) |
|
{ |
|
unsigned char* data = nullptr; |
|
int width = 0; |
|
int height = 0; |
|
int channels = 0; |
|
|
|
// Load image data |
|
stbi_set_flip_vertically_on_load(1); |
|
data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default); |
|
VERIFY(data, "failed to load image: '{}'", path); |
|
|
|
init(data, width, height, channels); |
|
|
|
// Clean resources |
|
stbi_image_free(data); |
|
} |
|
|
|
Texture::Texture(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels) |
|
{ |
|
init(data, width, height, channels); |
|
} |
|
|
|
Texture::~Texture() |
|
{ |
|
glDeleteTextures(1, &m_id); |
|
} |
|
|
|
void Texture::bind(uint32_t unit) const |
|
{ |
|
// Set active unit |
|
glActiveTexture(GL_TEXTURE0 + unit); |
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id); |
|
|
|
// Reset unit |
|
glActiveTexture(GL_TEXTURE0); |
|
} |
|
|
|
void Texture::unbind() const |
|
{ |
|
glBindTexture(GL_TEXTURE_2D, 0); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
void Texture::init(unsigned char* data, uint32_t width, uint32_t height, uint8_t channels) |
|
{ |
|
m_width = width; |
|
m_height = height; |
|
|
|
if (channels == 4) { |
|
m_internalFormat = GL_RGBA8; |
|
m_dataFormat = GL_RGBA; |
|
} |
|
else if (channels == 3) { |
|
m_internalFormat = GL_RGB8; |
|
m_dataFormat = GL_RGB; |
|
} |
|
|
|
create(data); |
|
} |
|
|
|
void Texture::create(unsigned char* data) |
|
{ |
|
m_id = UINT_MAX; |
|
|
|
// Create texture object |
|
glGenTextures(1, &m_id); |
|
|
|
// Bind texture object |
|
glBindTexture(GL_TEXTURE_2D, m_id); |
|
|
|
// Set unpacking of pixel data to byte-alignment, |
|
// this prevents alignment issues when using a single byte for color |
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
|
|
|
// Generate texture |
|
glTexImage2D( |
|
GL_TEXTURE_2D, // Texture target |
|
0, // Midmap level, base starts at level 0 |
|
m_internalFormat, // Texture format |
|
m_width, m_height, // Image width/height |
|
0, // Always 0 (legacy) |
|
m_dataFormat, // Texture source format |
|
GL_UNSIGNED_BYTE, // Texture source datatype |
|
data); // Image data |
|
|
|
// Set the texture wrapping / filtering options |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // X |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Y |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Minify |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Magnify |
|
|
|
// Automatically generate all mipmap levels |
|
glGenerateMipmap(GL_TEXTURE_2D); |
|
|
|
// Unbind texture object |
|
glBindTexture(GL_TEXTURE_2D, 0); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
TextureManager::TextureManager(s) |
|
{ |
|
ruc::info("TextureManager initialized"); |
|
} |
|
|
|
TextureManager::~TextureManager() |
|
{ |
|
} |
|
|
|
void TextureManager::add(const std::string& path, std::shared_ptr<Texture> texture) |
|
{ |
|
// Construct (key, value) pair and insert it into the unordered_map |
|
m_textureList.emplace(std::move(path), std::move(texture)); |
|
} |
|
|
|
std::shared_ptr<Texture> TextureManager::load(const std::string& path) |
|
{ |
|
if (exists(path)) { |
|
return get(path); |
|
} |
|
|
|
std::shared_ptr<Texture> texture = std::make_shared<Texture>(path); |
|
add(path, texture); |
|
return get(path); |
|
} |
|
|
|
std::shared_ptr<Texture> TextureManager::get(const std::string& path) |
|
{ |
|
return exists(path) ? m_textureList.at(path) : nullptr; |
|
} |
|
|
|
bool TextureManager::exists(const std::string& path) |
|
{ |
|
return m_textureList.find(path) != m_textureList.end(); |
|
} |
|
|
|
void TextureManager::remove(const std::string& path) |
|
{ |
|
if (exists(path)) { |
|
m_textureList.erase(path); |
|
} |
|
} |
|
|
|
void TextureManager::remove(std::shared_ptr<Texture> texture) |
|
{ |
|
if (exists(texture->path())) { |
|
m_textureList.erase(texture->path()); |
|
} |
|
} |
|
|
|
} // namespace Inferno
|
|
|