diff --git a/src/inferno/application.cpp b/src/inferno/application.cpp index a6ebc69..eff7f3c 100644 --- a/src/inferno/application.cpp +++ b/src/inferno/application.cpp @@ -274,6 +274,11 @@ bool Application::onKeyPress(KeyPressEvent& e) m_window->setShouldClose(true); } + if (e.getKey() == keyCode("GLFW_KEY_F12")) { + ruc::info("Taking screenshot.."); + Texture::saveScreenshotPNG("screenshot.png", m_window->getWidth(), m_window->getHeight()); + } + return true; } diff --git a/src/inferno/asset/texture.cpp b/src/inferno/asset/texture.cpp index bc432ea..2c1d19e 100644 --- a/src/inferno/asset/texture.cpp +++ b/src/inferno/asset/texture.cpp @@ -13,6 +13,8 @@ #include "ruc/meta/assert.h" #define STB_IMAGE_IMPLEMENTATION #include "stb/stb_image.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb/stb_image_write.h" #include "inferno/asset/texture.h" @@ -23,6 +25,51 @@ Texture::~Texture() glDeleteTextures(1, &m_id); } +// ----------------------------------------- + +void Texture::savePNG(std::string_view path, std::shared_ptr texture) +{ + texture->bind(); + + // Allocate memory + uint32_t dataFormat = texture->m_dataFormat == GL_RGBA ? 4 : 3; + size_t dataSize = texture->m_width * texture->m_height * dataFormat; + std::vector data(dataSize); + + // Read texture data from the GPU + glGetTexImage(GL_TEXTURE_2D, 0, texture->m_dataFormat, texture->m_dataType, data.data()); + + // Write image to a file + stbi_flip_vertically_on_write(1); + stbi_write_png( + path.data(), + texture->m_width, + texture->m_height, + dataFormat, + data.data(), + texture->m_width * dataFormat); + + texture->unbind(); +} + +void Texture::saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height) +{ + // Allocate memory + size_t dataSize = width * height * 4; + std::vector data(dataSize); + + // Set the pack alignment to 1 (to avoid row alignment issues) + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + // Read the pixels from the default framebuffer + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data()); + + stbi_flip_vertically_on_write(1); + stbi_write_png(path.data(), width, height, 4, data.data(), width * 4); +} + +// ----------------------------------------- + void Texture::init(uint32_t width, uint32_t height, uint8_t channels) { init(width, height, diff --git a/src/inferno/asset/texture.h b/src/inferno/asset/texture.h index 143455b..0fe74cf 100644 --- a/src/inferno/asset/texture.h +++ b/src/inferno/asset/texture.h @@ -22,6 +22,9 @@ class Texture : public Asset { public: virtual ~Texture(); + static void savePNG(std::string_view path, std::shared_ptr texture); + static void saveScreenshotPNG(std::string_view path, uint32_t width, uint32_t height); + void init(uint32_t width, uint32_t height, uint8_t channels); void init(uint32_t width, uint32_t height, uint32_t internalFormat, uint32_t dataFormat, uint32_t dataType); diff --git a/src/inferno/event/keyevent.h b/src/inferno/event/keyevent.h index 9427210..f536493 100644 --- a/src/inferno/event/keyevent.h +++ b/src/inferno/event/keyevent.h @@ -14,24 +14,27 @@ namespace Inferno { class KeyEvent : public Event { public: - inline int getKey() const { return m_key; } + int getKey() const { return m_key; } + int getMods() const { return m_mods; } EVENT_CLASS_CATEGORY(InputEventCategory | KeyEventCategory) protected: - KeyEvent(int key) + KeyEvent(int key, int mods) : m_key(key) + , m_mods(mods) { } private: - int m_key; + int m_key { 0 }; + int m_mods { 0 }; }; -class KeyPressEvent : public KeyEvent { +class KeyPressEvent final : public KeyEvent { public: - KeyPressEvent(int key) - : KeyEvent(key) + KeyPressEvent(int key, int mods) + : KeyEvent(key, mods) { } @@ -45,10 +48,10 @@ public: EVENT_CLASS_TYPE(KeyPress) }; -class KeyReleaseEvent : public KeyEvent { +class KeyReleaseEvent final : public KeyEvent { public: - KeyReleaseEvent(int key) - : KeyEvent(key) + KeyReleaseEvent(int key, int mods) + : KeyEvent(key, mods) { } @@ -59,13 +62,13 @@ public: return ss.str(); } - EVENT_CLASS_TYPE(KeyPress) + EVENT_CLASS_TYPE(KeyRelease) }; -class KeyRepeatEvent : public KeyEvent { +class KeyRepeatEvent final : public KeyEvent { public: - KeyRepeatEvent(int key) - : KeyEvent(key) + KeyRepeatEvent(int key, int mods) + : KeyEvent(key, mods) { } @@ -76,7 +79,7 @@ public: return ss.str(); } - EVENT_CLASS_TYPE(KeyPress) + EVENT_CLASS_TYPE(KeyRepeat) }; } // namespace Inferno diff --git a/src/inferno/keycodes.cpp b/src/inferno/keycodes.cpp index 07f9ab8..400be17 100644 --- a/src/inferno/keycodes.cpp +++ b/src/inferno/keycodes.cpp @@ -139,14 +139,33 @@ static std::unordered_map keys({ { MAP_KEY(GLFW_KEY_MENU) }, }); +static std::unordered_map modifiers({ + { MAP_KEY(GLFW_MOD_SHIFT) }, + { MAP_KEY(GLFW_MOD_CONTROL) }, + { MAP_KEY(GLFW_MOD_ALT) }, + { MAP_KEY(GLFW_MOD_SUPER) }, + { MAP_KEY(GLFW_MOD_CAPS_LOCK) }, // State, not really a modifier + { MAP_KEY(GLFW_MOD_NUM_LOCK) }, // State, not really a modifier +}); + // ----------------------------------------- +// Example usage: +// event.getKey() == keyCode("GLFW_KEY_ESCAPE") int keyCode(std::string_view name) { VERIFY(keys.find(name) != keys.end(), "could not find key code: {}", name); return keys.at(name); } +// Example usage: +// event.getMods() & keyMod("GLFW_MOD_SHIFT") +int keyMod(std::string_view name) +{ + VERIFY(modifiers.find(name) != modifiers.end(), "could not find key modifier: {}", name); + return modifiers.at(name); +} + std::string_view keyName(int key) { auto it = std::find_if(keys.begin(), keys.end(), [key](const auto& keybind) { diff --git a/src/inferno/keycodes.h b/src/inferno/keycodes.h index 015a1e8..80666cb 100644 --- a/src/inferno/keycodes.h +++ b/src/inferno/keycodes.h @@ -13,6 +13,8 @@ namespace Inferno { int keyCode(std::string_view name); -std::string_view keyName(int); +int keyMod(std::string_view name); + +std::string_view keyName(int key); } // namespace Inferno diff --git a/src/inferno/window.cpp b/src/inferno/window.cpp index 5b0ff01..59fcd9b 100644 --- a/src/inferno/window.cpp +++ b/src/inferno/window.cpp @@ -122,17 +122,17 @@ void Window::initialize() switch (action) { case GLFW_PRESS: { - KeyPressEvent event(key); + KeyPressEvent event(key, mods); w.m_eventCallback(event); break; } case GLFW_RELEASE: { - KeyReleaseEvent event(key); + KeyReleaseEvent event(key, mods); w.m_eventCallback(event); break; } case GLFW_REPEAT: { - KeyRepeatEvent event(key); + KeyRepeatEvent event(key, mods); w.m_eventCallback(event); break; }