diff --git a/assets/gfx/test.png b/assets/gfx/test.png new file mode 100644 index 0000000..dea8eab Binary files /dev/null and b/assets/gfx/test.png differ diff --git a/assets/glsl/texture.frag b/assets/glsl/texture.frag new file mode 100644 index 0000000..056ef4a --- /dev/null +++ b/assets/glsl/texture.frag @@ -0,0 +1,12 @@ +#version 450 core + +layout(location = 0) out vec4 color; + +in vec2 v_texCoord; + +uniform sampler2D u_texture; + +void main() +{ + color = texture(u_texture, v_texCoord); +} diff --git a/assets/glsl/texture.vert b/assets/glsl/texture.vert new file mode 100644 index 0000000..ee0da5a --- /dev/null +++ b/assets/glsl/texture.vert @@ -0,0 +1,12 @@ +#version 450 core + +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec2 a_texCoord; + +out vec2 v_texCoord; + +void main() +{ + v_texCoord = a_texCoord; + gl_Position = vec4(a_position, 1.0f); +} diff --git a/inferno/src/inferno/application.cpp b/inferno/src/inferno/application.cpp index 646075b..2b91670 100644 --- a/inferno/src/inferno/application.cpp +++ b/inferno/src/inferno/application.cpp @@ -12,6 +12,7 @@ #include "inferno/render/context.h" #include "inferno/render/renderer.h" #include "inferno/render/shader.h" +#include "inferno/render/texture.h" #include "inferno/window.h" namespace Inferno { @@ -29,32 +30,68 @@ namespace Inferno { m_window = std::make_unique(); 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