spacebox/src/Texture.cpp

185 lines
6.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Texture.hpp"
using namespace sb;
/* Have to pass our deleter to abstract base class at instantiation */
Texture::Texture() : GLObject(texture_deleter) {}
/* If an image path is passed at creation, the path will be stored in the class for later loading */
Texture::Texture(fs::path path) : Texture()
{
associate(path);
}
/* Store an image path as a member variable for loading later. Each texture should have one image
* path (support for multiple mipmap levels may be added later) */
void Texture::associate(fs::path path)
{
this->path = path;
}
/* Forward the GL texture generate function to the base class */
void Texture::generate()
{
GLObject::generate(glGenTextures);
}
/* Generate a GL_TEXTURE_2D texture ID and allocate GL_RGBA8 storage for the given size */
void Texture::generate(glm::vec2 size)
{
generate();
bind();
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, size.x, size.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
sb::Log::gl_errors();
}
void Texture::load()
{
if (!path.empty())
{
load(path);
}
else
{
std::ostringstream message;
message << "Cannot load, no path has been specified yet for texture " << id();
sb::Log::log(message, sb::Log::WARN);
}
}
void Texture::load(fs::path path)
{
std::ostringstream message;
sb::Log::Level message_level;
if (path.has_filename())
{
/* Load file path as a surface object to access pixel data and flip into OpenGL orientation. Attach a destructor so it will free
* itself when it goes out of scope at the end of this function. */
std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> surface(IMG_Load(path.c_str()), SDL_FreeSurface);
std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> flipped_surface(rotozoomSurfaceXY(surface.get(), 0, 1, -1, 0), SDL_FreeSurface);
load(flipped_surface.get());
message << "Loading image at " << path;
message_level = sb::Log::INFO;
}
else
{
message << "Cannot load, " << path << " is not a vaild path to a file";
message_level = sb::Log::WARN;
}
sb::Log::log(message, message_level);
}
void Texture::load(SDL_RWops* rw)
{
/* Load RW object as path as a surface object to access pixel data and flip into OpenGL orientation. Attach a destructor so it will free
* itself when it goes out of scope at the end of this function. */
std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> surface(IMG_Load_RW(rw, 0), SDL_FreeSurface);
std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> flipped_surface(rotozoomSurfaceXY(surface.get(), 0, 1, -1, 0), SDL_FreeSurface);
load(flipped_surface.get());
}
void Texture::load(SDL_Surface* surface)
{
std::ostringstream message;
sb::Log::Level message_level;
if (surface->w > 0 && surface->h > 0)
{
message << "Loading image from SDL surface (" << surface->w << "×" << surface->h << ", " << SDL_GetPixelFormatName(surface->format->format) << ")";
load(surface->pixels, {surface->w, surface->h}, GL_RGBA, GL_UNSIGNED_BYTE);
message_level = sb::Log::DEBUG;
}
else
{
message << "Cannot load into texture, invalid image data without dimensions found";
message_level = sb::Log::WARN;
}
sb::Log::log(message, message_level);
}
void Texture::load(void* pixels, glm::vec2 size, GLenum format, GLenum type)
{
std::ostringstream message;
sb::Log::Level message_level;
if (size.x > 0 && size.y > 0)
{
if (!generated())
{
generate(size);
}
bind();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.x, size.y, format, type, pixels);
message << "Loaded " << size.x << "×" << size.y << " image into texture ID " << id();
message_level = sb::Log::DEBUG;
}
else
{
message << "Cannot load pixels with zero or negative size in either dimension";
message_level = sb::Log::WARN;
}
sb::Log::log(message, message_level);
sb::Log::gl_errors("after loading texture");
}
void Texture::bind() const
{
glBindTexture(GL_TEXTURE_2D, this->id());
}
/* glGetTexlevelparameteriv is not available in OpenGL ES 3.0 */
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(ANDROID)
void Texture::load(void* pixels, GLenum format, GLenum type)
{
if (!generated())
{
throw std::invalid_argument("This texture has not been generated and has no size property, so a size must be provided");
}
else
{
load(pixels, size(), format, type);
}
}
glm::vec2 Texture::size() const
{
if (generated())
{
bind();
int width, height;
int miplevel = 0;
glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_WIDTH, &width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_HEIGHT, &height);
return {width, height};
}
else
{
return {0, 0};
}
}
#endif
/* Textures are considered equal if they have the same ID */
bool Texture::operator==(const Texture& texture) const
{
return id() == texture.id();
}
/* This function gets passed to the abstract base class for deleting the texture data when the ID
* pointer goes out of scope (when all instances of this texture and its copies are out of scope) */
void sb::texture_deleter(GLuint* id)
{
/* not sure why SDL_Log works here at program end but SDL_LogDebug and SDL_LogInfo don't */
SDL_Log("destroying texture ID %i", *id);
glDeleteTextures(1, id);
}