From 9aee7e391d8d8d305fde31077242339edaf943a7 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Wed, 30 Dec 2020 11:33:56 +0100 Subject: [PATCH] Add texture class and texture test --- assets/gfx/test.png | Bin 0 -> 5846 bytes assets/glsl/texture.frag | 12 +++ assets/glsl/texture.vert | 12 +++ inferno/src/inferno/application.cpp | 72 ++++++++++--- inferno/src/inferno/application.h | 21 ++-- inferno/src/inferno/render/texture.cpp | 139 +++++++++++++++++++++++++ inferno/src/inferno/render/texture.h | 56 ++++++++++ 7 files changed, 291 insertions(+), 21 deletions(-) create mode 100644 assets/gfx/test.png create mode 100644 assets/glsl/texture.frag create mode 100644 assets/glsl/texture.vert create mode 100644 inferno/src/inferno/render/texture.cpp create mode 100644 inferno/src/inferno/render/texture.h diff --git a/assets/gfx/test.png b/assets/gfx/test.png new file mode 100644 index 0000000000000000000000000000000000000000..dea8eab78d1b56faa236c3d7c38700d888acce76 GIT binary patch literal 5846 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 m_window->setEventCallback(NF_BIND_EVENT(Application::onEvent)); - float vertices[] = { + TextureManager textureManager; + m_texture = textureManager.load("assets/gfx/test.png"); + +// ----------------------------------------- + + float verticesColor[] = { -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, }; - uint32_t indices[] = { + uint32_t indicesColor[] = { 0, 1, 2, 2, 3, 0 }; - m_vertexArray = std::make_shared(); + m_vertexArrayColor = std::make_shared(); - std::shared_ptr vertexBuffer = std::make_shared(vertices, sizeof(vertices)); - vertexBuffer->setLayout({ + std::shared_ptr vertexBufferColor = std::make_shared(verticesColor, sizeof(verticesColor)); + vertexBufferColor->setLayout({ { BufferElementType::Vec3, "a_position" }, { BufferElementType::Vec4, "a_color" }, }); - m_vertexArray->addVertexBuffer(vertexBuffer); + m_vertexArrayColor->addVertexBuffer(vertexBufferColor); + + std::shared_ptr indexBufferColor = std::make_shared(indicesColor, sizeof(indicesColor)); + + m_vertexArrayColor->setIndexBuffer(indexBufferColor); + +// ----------------------------------------- - std::shared_ptr indexBuffer = std::make_shared(indices, sizeof(indices)); + m_vertexArrayTexture = std::make_shared(); - m_vertexArray->setIndexBuffer(indexBuffer); + float verticesTexture[] = { + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, + }; - m_shader = std::make_unique("assets/glsl/simple.vert", "assets/glsl/simple.frag"); + uint32_t indicesTexture[] = { + 0, 1, 2, 2, 3, 0 + }; + + std::shared_ptr vertexBufferTexture = std::make_shared(verticesTexture, sizeof(verticesTexture)); + vertexBufferTexture->setLayout({ + { BufferElementType::Vec3, "a_position" }, + { BufferElementType::Vec2, "a_texCoord" }, + }); + + m_vertexArrayTexture->addVertexBuffer(vertexBufferTexture); + + std::shared_ptr indexBufferTexture = std::make_shared(indicesTexture, sizeof(indicesTexture)); + + m_vertexArrayTexture->setIndexBuffer(indexBufferTexture); + +// ----------------------------------------- + + m_shaderSimple = std::make_shared("assets/glsl/simple.vert", "assets/glsl/simple.frag"); + m_shaderTexture = std::make_shared("assets/glsl/texture.vert", "assets/glsl/texture.frag"); + m_shaderTexture->setInt("u_texture", m_texture->id()); } Application::~Application() @@ -74,10 +111,19 @@ namespace Inferno { Command::clearColor({ 0.2f, 0.3f, 0.3f, 1.0f }); Command::clear(); - Renderer::beginScene(); // camera, lights, environment - m_shader->bind(); - Renderer::submit(m_vertexArray); - Renderer::endScene(); + // Renderer::beginScene(); // camera, lights, environment + + // m_shaderSimple->bind(); + // Renderer::submit(m_vertexArrayColor); + // m_shaderSimple->unbind(); + + m_shaderTexture->bind(); + m_texture->bind(); + Renderer::submit(m_vertexArrayTexture); + m_texture->unbind(); + m_shaderTexture->unbind(); + + // Renderer::endScene(); m_window->update(); } diff --git a/inferno/src/inferno/application.h b/inferno/src/inferno/application.h index d2866d3..b045ae1 100644 --- a/inferno/src/inferno/application.h +++ b/inferno/src/inferno/application.h @@ -6,9 +6,11 @@ namespace Inferno { class Event; + class Texture; + class TextureManager; + class Window; class WindowCloseEvent; class WindowResizeEvent; - class Window; class VertexArray; class Shader; @@ -20,22 +22,25 @@ namespace Inferno { void run(); - void onEvent(Event &e); - bool onWindowClose(WindowCloseEvent &e); - bool onWindowResize(WindowResizeEvent &e); + void onEvent(Event& e); + bool onWindowClose(WindowCloseEvent& e); + bool onWindowResize(WindowResizeEvent& e); // ----------------------------------------- - inline Window &getWindow() { return *m_window; } + inline Window& getWindow() const { return *m_window; } - static inline Application &get() { return *s_instance; } + static inline Application& get() { return *s_instance; } private: std::unique_ptr m_window; // - std::shared_ptr m_vertexArray; - std::unique_ptr m_shader; + std::shared_ptr m_vertexArrayColor; + std::shared_ptr m_vertexArrayTexture; + std::shared_ptr m_shaderSimple; + std::shared_ptr m_shaderTexture; + std::shared_ptr m_texture; // static Application* s_instance; diff --git a/inferno/src/inferno/render/texture.cpp b/inferno/src/inferno/render/texture.cpp new file mode 100644 index 0000000..4ee7710 --- /dev/null +++ b/inferno/src/inferno/render/texture.cpp @@ -0,0 +1,139 @@ +#include // UINT_MAX +#include + +#include +#define STB_IMAGE_IMPLEMENTATION +#include + +#include "inferno/assertions.h" +#include "inferno/render/texture.h" + +namespace Inferno { + + Texture::Texture(const std::string& path) + { + int width; + int height; + int channels; + + // Load image data + stbi_set_flip_vertically_on_load(1); + unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, STBI_default); + + ASSERT(data, "Failed to load image: '{}'", path); + + 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); + + // Clean resources + stbi_image_free(data); + } + + Texture::~Texture() + { + glDeleteTextures(1, &m_id); + } + + void Texture::bind() const + { + glBindTexture(GL_TEXTURE_2D, m_id); + } + + void Texture::unbind() const + { + glBindTexture(GL_TEXTURE_2D, 0); + } + + 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); + } + +// ----------------------------------------- + + void TextureManager::add(const std::string& path, const std::shared_ptr& texture) + { + // Construct (key, value) pair and insert it into the unordered_map + m_textureList.emplace(path, texture); + } + + std::shared_ptr TextureManager::load(const std::string& path) + { + if (exists(path)) { + return get(path); + } + + std::shared_ptr texture = std::make_shared(path); + add(path, texture); + return get(path); + } + + std::shared_ptr 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(const std::shared_ptr& texture) + { + if (exists(texture->path())) { + m_textureList.erase(texture->path()); + } + } + +} diff --git a/inferno/src/inferno/render/texture.h b/inferno/src/inferno/render/texture.h new file mode 100644 index 0000000..8751144 --- /dev/null +++ b/inferno/src/inferno/render/texture.h @@ -0,0 +1,56 @@ +#ifndef TEXTURE_H +#define TEXTURE_H + +#include // std::uint32_t +#include // std::shared_ptr +#include // std::string +#include // std::unordered_map + +namespace Inferno { + + class Texture { + public: + Texture(const std::string& path); + virtual ~Texture(); + + void bind() const; + void unbind() const; + + inline std::string path() const { return m_path; } + inline uint32_t width() const { return m_width; } + inline uint32_t height() const { return m_height; } + inline uint32_t id() const { return m_id; } + inline uint32_t internalFormat() const { return m_internalFormat; } + inline uint32_t dataFormat() const { return m_dataFormat; } + + protected: + void create(unsigned char* data); + + private: + std::string m_path; + uint32_t m_width; + uint32_t m_height; + uint32_t m_id; + uint32_t m_internalFormat; + uint32_t m_dataFormat; + }; + +// ----------------------------------------- + + class TextureManager { + public: + void add(const std::string& path, const std::shared_ptr& texture); + std::shared_ptr load(const std::string& path); + std::shared_ptr get(const std::string& path); + bool exists(const std::string& path); + + void remove(const std::string& path); + void remove(const std::shared_ptr& texture); + + private: + std::unordered_map> m_textureList; + }; + +} + +#endif // TEXTURE_H