341 lines
8.3 KiB
C++
341 lines
8.3 KiB
C++
#include "pch.h"
|
|
#include "log.h"
|
|
#include "texture.h"
|
|
#include "util.h"
|
|
#include "app.h"
|
|
|
|
std::map<uint16_t, Texture2D> TextureManager::m_textures;
|
|
std::array<int, 6> TextureCube::m_faces_map {
|
|
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, // front
|
|
GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // right
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // back
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X, // left
|
|
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // top
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // bottom
|
|
};
|
|
|
|
TextureCube::TextureCube(TextureCube&& other) noexcept
|
|
{
|
|
other.m_faces = std::move(other.m_faces);
|
|
m_cubetex_id = other.m_cubetex_id; other.m_cubetex_id = 0;
|
|
m_resolution = other.m_resolution; other.m_resolution = 0;
|
|
}
|
|
|
|
TextureCube::~TextureCube()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
void TextureCube::operator=(TextureCube&& other) noexcept
|
|
{
|
|
other.m_faces = std::move(other.m_faces);
|
|
m_cubetex_id = other.m_cubetex_id; other.m_cubetex_id = 0;
|
|
m_resolution = other.m_resolution; other.m_resolution = 0;
|
|
}
|
|
|
|
bool TextureCube::create(int resolution) noexcept
|
|
{
|
|
App::I->render_task([this, resolution]
|
|
{
|
|
destroy();
|
|
m_resolution = resolution;
|
|
glGenTextures(1, &m_cubetex_id);
|
|
|
|
if (!m_cubetex_id)
|
|
return;
|
|
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubetex_id);
|
|
for (GLuint i = 0; i < 6; i++)
|
|
{
|
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8,
|
|
m_resolution, m_resolution, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
}
|
|
});
|
|
return m_cubetex_id != 0;
|
|
}
|
|
|
|
void TextureCube::destroy() noexcept
|
|
{
|
|
if (m_cubetex_id)
|
|
{
|
|
App::I->render_task([f=m_faces, id=m_cubetex_id]
|
|
{
|
|
glDeleteTextures(f.size(), f.data());
|
|
glDeleteTextures(1, &id);
|
|
});
|
|
m_cubetex_id = 0;
|
|
m_faces.fill(0);
|
|
m_resolution = 0;
|
|
}
|
|
}
|
|
|
|
void TextureCube::bind() const noexcept
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubetex_id);
|
|
}
|
|
|
|
bool TextureManager::load(const char* path, bool generate_mipmaps)
|
|
{
|
|
uint16_t id = const_hash(path);
|
|
auto t = m_textures.find(id);
|
|
if (t == m_textures.end() || !m_textures[id].ready())
|
|
{
|
|
if (!m_textures[id].load(path))
|
|
return false;
|
|
}
|
|
if (generate_mipmaps && !m_textures[id].has_mips)
|
|
m_textures[id].create_mipmaps();
|
|
return true;
|
|
}
|
|
|
|
void TextureManager::assign(uint16_t id, GLuint tex, int w/* = -1*/, int h/* = -1*/, GLuint internal_format/* = GL_RGBA8*/, GLuint format/* = GL_RGBA*/)
|
|
{
|
|
m_textures[id].assign(tex, w, h, internal_format, format);
|
|
}
|
|
|
|
Texture2D& TextureManager::get(uint16_t id)
|
|
{
|
|
return m_textures[id];
|
|
}
|
|
|
|
void TextureManager::invalidate()
|
|
{
|
|
for (auto& t : m_textures)
|
|
{
|
|
t.second.destroy();
|
|
}
|
|
m_textures.clear();
|
|
}
|
|
|
|
Image Texture2D::get_image() const noexcept
|
|
{
|
|
Image ret;
|
|
ret.create(m_width, m_height);
|
|
App::I->render_task([&]
|
|
{
|
|
bind();
|
|
|
|
GLuint fboID;
|
|
glGenFramebuffers(1, &fboID);
|
|
if (fboID == 0)
|
|
return;
|
|
|
|
GLint oldFboID;
|
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldFboID);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fboID);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex, 0);
|
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if (status == GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, ret.m_data.get());
|
|
}
|
|
else
|
|
{
|
|
LOG("Texture2D::get_image() failed because framebuffer status = %d", (int)status);
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, oldFboID);
|
|
glDeleteFramebuffers(1, &fboID);
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
Texture2D::Texture2D(Texture2D&& other) noexcept
|
|
{
|
|
m_tex = other.m_tex;
|
|
m_width = other.m_width;
|
|
m_height = other.m_height;
|
|
m_format = other.m_format;
|
|
m_iformat = other.m_iformat;
|
|
has_mips = other.has_mips;
|
|
other.m_tex = false;
|
|
}
|
|
|
|
void Texture2D::operator=(Texture2D&& other) noexcept
|
|
{
|
|
m_tex = other.m_tex;
|
|
m_width = other.m_width;
|
|
m_height = other.m_height;
|
|
m_format = other.m_format;
|
|
m_iformat = other.m_iformat;
|
|
has_mips = other.has_mips;
|
|
other.m_tex = false;
|
|
}
|
|
|
|
bool Texture2D::create(int width, int height, GLint internal_format, GLint format, const uint8_t* data)
|
|
{
|
|
App::I->render_task([=]
|
|
{
|
|
destroy();
|
|
m_width = width;
|
|
m_height = height;
|
|
m_format = format;
|
|
m_iformat = internal_format;
|
|
glGenTextures(1, &m_tex);
|
|
//LOG("TEX create %d", m_tex);
|
|
bind();
|
|
auto ifmt = GL_UNSIGNED_BYTE;
|
|
if (internal_format == GL_RGBA32F) ifmt = GL_FLOAT;
|
|
if (internal_format == GL_RGBA16F) ifmt = GL_HALF_FLOAT;
|
|
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, ifmt, data);
|
|
unbind();
|
|
});
|
|
return true;
|
|
}
|
|
bool Texture2D::create(const Image& img)
|
|
{
|
|
static GLint formats[] = { GL_RED, GL_RG, GL_RGB, GL_RGBA };
|
|
static GLint iformats[] = { GL_R8, GL_RG8, GL_RGB8, GL_RGBA8 };
|
|
return create(img.width, img.height, iformats[img.comp - 1], formats[img.comp - 1], img.data());
|
|
}
|
|
|
|
void Texture2D::create_mipmaps()
|
|
{
|
|
App::I->render_task([this]
|
|
{
|
|
bind();
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
unbind();
|
|
has_mips = true;
|
|
});
|
|
}
|
|
|
|
void Texture2D::assign(GLuint tex, int w/* = -1*/, int h/* = -1*/, GLuint internal_format/* = GL_RGBA8*/, GLuint format/* = GL_RGBA*/)
|
|
{
|
|
m_tex = tex;
|
|
m_width = w;
|
|
m_height = h;
|
|
m_format = format;
|
|
m_iformat = internal_format;
|
|
}
|
|
|
|
bool Texture2D::load(std::string filename)
|
|
{
|
|
LOG("load texture %s", filename.c_str());
|
|
Image img;
|
|
if (!img.load(filename))
|
|
return false;
|
|
return create(img);
|
|
}
|
|
|
|
bool Texture2D::load_file(std::string filename)
|
|
{
|
|
LOG("load texture %s", filename.c_str());
|
|
Image img;
|
|
if (!img.load_file(filename))
|
|
return false;
|
|
return create(img);
|
|
}
|
|
|
|
void Texture2D::destroy()
|
|
{
|
|
if (m_tex)
|
|
{
|
|
App::I->render_task_async([id = m_tex]
|
|
{
|
|
glDeleteTextures(1, &id);
|
|
});
|
|
m_tex = 0;
|
|
}
|
|
}
|
|
|
|
void Texture2D::bind() const
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
glBindTexture(GL_TEXTURE_2D, m_tex);
|
|
}
|
|
|
|
void Texture2D::unbind() const
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void Texture2D::update(const uint8_t* data)
|
|
{
|
|
App::I->render_task([this, data]
|
|
{
|
|
bind();
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_format, GL_UNSIGNED_BYTE, data);
|
|
});
|
|
}
|
|
|
|
glm::vec2 Texture2D::size() const
|
|
{
|
|
return { m_width, m_height };
|
|
}
|
|
|
|
Texture2D::~Texture2D()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
bool Sampler::create(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/)
|
|
{
|
|
bool ret = false;
|
|
App::I->render_task([this, &ret, filter, wrap]
|
|
{
|
|
#if USE_SAMPLER
|
|
glGenSamplers(1, &id);
|
|
#endif // USE_SAMPLER
|
|
if (id == 0)
|
|
{
|
|
ret = false;
|
|
return;
|
|
}
|
|
set(filter, wrap);
|
|
ret = true;
|
|
});
|
|
return ret;
|
|
}
|
|
void Sampler::set(GLint filter /*= GL_LINEAR*/, GLint wrap /*= GL_CLAMP_TO_EDGE*/)
|
|
{
|
|
App::I->render_task([=]
|
|
{
|
|
#if USE_SAMPLER
|
|
glSamplerParameteri(id, GL_TEXTURE_WRAP_S, wrap);
|
|
glSamplerParameteri(id, GL_TEXTURE_WRAP_T, wrap);
|
|
glSamplerParameteri(id, GL_TEXTURE_WRAP_R, wrap);
|
|
glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter);
|
|
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter);
|
|
#endif // USE_SAMPLER
|
|
});
|
|
}
|
|
void Sampler::set_filter(GLint filter_min, GLint filter_mag)
|
|
{
|
|
App::I->render_task([=]
|
|
{
|
|
#if USE_SAMPLER
|
|
glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, filter_min);
|
|
glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, filter_mag);
|
|
#endif // USE_SAMPLER
|
|
});
|
|
}
|
|
void Sampler::set_border(glm::vec4 rgba)
|
|
{
|
|
App::I->render_task([this, rgba]
|
|
{
|
|
#if USE_SAMPLER && !defined(__GLES__)
|
|
glSamplerParameterfv(id, GL_TEXTURE_BORDER_COLOR, glm::value_ptr(rgba));
|
|
#endif // USE_SAMPLER
|
|
});
|
|
}
|
|
void Sampler::bind(int unit) const
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
current_unit = unit;
|
|
#if USE_SAMPLER
|
|
glBindSampler(unit, id);
|
|
#endif // USE_SAMPLER
|
|
}
|
|
void Sampler::unbind()
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
#if USE_SAMPLER
|
|
glBindSampler(current_unit, 0);
|
|
#endif // USE_SAMPLER
|
|
}
|
|
|