From 1a4b8507e3886e02ab1b6589ed3823d7fafd1654 Mon Sep 17 00:00:00 2001 From: frank <420@shampoo.ooo> Date: Mon, 20 Sep 2021 02:32:15 -0400 Subject: [PATCH] GL texture class; logging functions changed to static --- src/Game.cpp | 4 +- src/Game.hpp | 6 +- src/Sprite.cpp | 7 --- src/Sprite.hpp | 5 ++ src/Texture.cpp | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Texture.hpp | 68 +++++++++++++++++++++++ 6 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 src/Texture.cpp create mode 100644 src/Texture.hpp diff --git a/src/Game.cpp b/src/Game.cpp index 284259e..803bab5 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -364,7 +364,7 @@ bool Game::link_shader(GLuint program) const /* Send an info priority message to SDL's log output function, which is overridden by our own member * function. Category will default to SDL's custom category. */ -void Game::log(const std::string& message, const int category) const +void Game::log(const std::string& message, const int category) { SDL_LogInfo(category, "%s", message.c_str()); } @@ -373,7 +373,7 @@ void Game::log(const std::string& message, const int category) const * function. Category will default to SDL's custom category. Using the default category will ensure * that debug level statements are handled according to the options in the global configuration. */ -void Game::debug(const std::string& message, const int category) const +void Game::debug(const std::string& message, const int category) { SDL_LogDebug(category, "%s", message.c_str()); } diff --git a/src/Game.hpp b/src/Game.hpp index 99acf5b..98ceb9d 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -95,10 +95,10 @@ public: void load_gl_context(); GLuint load_shader(const fs::path&, GLenum) const; bool link_shader(GLuint program) const; - void log(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY) const; - void debug(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY) const; + static void log(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY); + static void debug(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY); void log_renderer_info(SDL_RendererInfo&); - bool log_gl_errors(std::string = ""); + static bool log_gl_errors(std::string = ""); void log_display_mode(); void log_surface_format(SDL_Surface*, std::string = "surface"); std::string get_pixel_format_string(Uint32); diff --git a/src/Sprite.cpp b/src/Sprite.cpp index f8f6afe..a8089ce 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -1,10 +1,3 @@ -#include -#include - -#include "Pixels.hpp" -#include "Game.hpp" -#include "extension.hpp" -#include "Game.hpp" #include "Sprite.hpp" Sprite::Sprite() : Sprite(nullptr) {} diff --git a/src/Sprite.hpp b/src/Sprite.hpp index 87ce162..f266f74 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "SDL.h" #include "SDL_image.h" @@ -202,4 +203,8 @@ struct Frameset }; +#include "Pixels.hpp" +#include "Game.hpp" +#include "extension.hpp" + #endif diff --git a/src/Texture.cpp b/src/Texture.cpp new file mode 100644 index 0000000..b8d2824 --- /dev/null +++ b/src/Texture.cpp @@ -0,0 +1,144 @@ +#include "Texture.hpp" + +/* If an image path is passed at creation, the path will be stored in the class for later loading */ +Texture::Texture(fs::path path) +{ + 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; +} + +/* Generate a GL_TEXTURE_2D texture and allocate GL_RGB8 storage for the given size */ +void Texture::generate(glm::vec2 size) +{ + GLuint id; + glGenTextures(1, &id); + this->id(id); + bind(); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, size.x, size.y); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + Game::log_gl_errors(); +} + +/* Returns true if an ID has been generated for this Texture */ +bool Texture::generated() const +{ + return static_cast(texture_id); +} + +/* When called with no parameters, use the stored path variable */ +void Texture::load() +{ + load(path); +} + +/* Create an SDL Surface from the supplied path and forward it to the loading function which accepts + * a surface pointer */ +void Texture::load(fs::path path) +{ + /* open path as surface which will free itself when it goes out of scope (at the end of this function) */ + std::unique_ptr surface(IMG_Load(path.c_str()), SDL_FreeSurface); + std::unique_ptr flipped_surface(rotozoomSurfaceXY(surface.get(), 0, 1, -1, 0), SDL_FreeSurface); + load(flipped_surface.get()); +} + +/* Create an SDL Surface from the supplied RW pointer and forward it to the loading function which accepts + * a surface pointer */ +void Texture::load(SDL_RWops* rw) +{ + /* load RW as an SDL surface to translate image format into pixel data, flip, and get dimensions */ + std::unique_ptr surface(IMG_Load_RW(rw, 0), SDL_FreeSurface); + std::unique_ptr flipped_surface(rotozoomSurfaceXY(surface.get(), 0, 1, -1, 0), SDL_FreeSurface); + load(flipped_surface.get()); +} + +/* Forward pixels and size to the generic pixel loading function */ +void Texture::load(SDL_Surface* surface) +{ + load(surface->pixels, {surface->w, surface->h}, GL_RGBA, GL_UNSIGNED_BYTE); + std::ostringstream message; + message << "loaded " << path << " (" << surface->w << "x" << surface->h << ")"; + Game::log(message.str()); +} + +/* Bind texture and load pixel data using this class's generated ID */ +void Texture::load(void* pixels, glm::vec2 size, GLenum format, GLenum type) +{ + if (!generated()) + { + generate(size); + } + bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.x, size.y, format, type, pixels); + Game::log_gl_errors(); +} + +/* The texture must have been previously generated with a size to use this generic pixel data load function */ +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); + } +} + +/* Set the shared pointer to point to a new GLuint with specified ID value */ +void Texture::id(GLuint id) +{ + texture_id = std::shared_ptr(new GLuint, Texture::destroy); + *texture_id = id; +} + +/* Return the GL texture ID that was set for this texture */ +GLuint Texture::id() const +{ + return *texture_id; +} + +void Texture::bind() const +{ + glBindTexture(GL_TEXTURE_2D, this->id()); +} + +/* Return the size in pixels of mipmap level 0 (the only mipmap level supported by this class). If the texture hasn't been, + * generated, return {0, 0}. */ +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}; + } +} + +/* Delete this texture, freeing the memory */ +void Texture::destroy(GLuint* id) +{ + /* not sure why SDL_Log works here but SDL_LogDebug and SDL_LogInfo don't */ + SDL_Log("destroying texture ID %i", *id); + glDeleteTextures(1, id); +} + +/* Textures are considered equal if they have the same ID */ +bool Texture::operator==(const Texture& texture) const +{ + return id() == texture.id(); +} diff --git a/src/Texture.hpp b/src/Texture.hpp new file mode 100644 index 0000000..d23d88b --- /dev/null +++ b/src/Texture.hpp @@ -0,0 +1,68 @@ +/* /\ +--------------------------------------------------------------+ + ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | + +--\ ^__^ /--+ | | + | ~/ \~ | | - originally created at [http://nugget.fun] | + | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ + | SPACE ~~~~~ | / + | ~~~~~~~ BOX |/ + +--------------+ + + [Texture.hpp] + + The Texture class abstracts the file opening, data loading and binding steps of Open GL + texture creation. Currently it only supports loading GL_TEXTURE_2D with GL_RGB8 pixels + and automatic GL_NEAREST mipmapping. Support may be added for users to pass in their own + pixel data, to customize mipmapping, and more. + +*/ + +#ifndef Texture_h_ +#define Texture_h_ + +#include +#include +#include "SDL.h" +#include "SDL_image.h" +#include "sdl2-gfx/SDL2_rotozoom.h" +#include "filesystem.hpp" +#include "Game.hpp" + +/* include Open GL */ +#if defined(__EMSCRIPTEN__) +#include +#else +#include "glew/glew.h" +#endif + +class Texture +{ + +private: + + fs::path path = ""; + std::shared_ptr texture_id = nullptr; + +public: + + Texture() {}; + Texture(fs::path); + void associate(fs::path); + void generate(glm::vec2); + bool generated() const; + void load(); + void load(fs::path); + void load(SDL_RWops*); + void load(SDL_Surface*); + void load(void*, GLenum = GL_RGBA, GLenum = GL_UNSIGNED_BYTE); + void load(void*, glm::vec2, GLenum = GL_RGBA, GLenum = GL_UNSIGNED_BYTE); + void id(GLuint); + GLuint id() const; + void bind() const; + glm::vec2 size() const; + static void destroy(GLuint*); + bool operator==(const Texture&) const; + +}; + +#endif