diff --git a/CMakeLists.txt b/CMakeLists.txt index 8796e1e..7fc71fa 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ list(APPEND OGFX_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/Image.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/WindowBaseOutputHandler.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/PixelRenderer.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/Animation.cpp ) list(APPEND TEST_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/test.cpp diff --git a/extra/res/test.png b/extra/res/test.png new file mode 100644 index 0000000..ad258d3 Binary files /dev/null and b/extra/res/test.png differ diff --git a/other/build.nr b/other/build.nr index fe36e8b..e0fbadd 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -2009 +2020 diff --git a/src/ogfx/Animation.cpp b/src/ogfx/Animation.cpp new file mode 100644 index 0000000..a9f734c --- /dev/null +++ b/src/ogfx/Animation.cpp @@ -0,0 +1,97 @@ +#include "Animation.hpp" +#include + +namespace ogfx +{ + Animation::Animation(void) + { + create(0, 0, 0, 0, 0); + } + + Animation::Animation(int frames, int columns, int rows, int frame_width, int frame_height) + { + create(frames, columns, rows, frame_width, frame_height); + } + + Animation::Animation(AnimationData& ad) + { + create(ad); + } + + void Animation::create(AnimationData& ad) + { + create(ad.length, ad.columns, ad.rows, ad.frame_width, ad.frame_height); + m_backwards = ad.backwards; + m_still = ad.still; + m_column_offset = ad.column_offset; + m_row_offset = ad.row_offset; + m_frame_time = ad.speed; + m_random = false; + } + + void Animation::create(int frames, int columns, int rows, int frame_width, int frame_height) + { + m_frames = frames; + m_columns = columns; + m_frame_width = frame_width; + m_frame_height = frame_height; + m_rows = rows; + m_frame_time = 5; + m_current_time = 0; + m_current_frame = 0; + m_backwards = false; + m_back = false; + m_still = false; + m_random = false; + m_column_offset = 0; + m_row_offset = 0; + } + + void Animation::resetAnimation(void) + { + m_current_frame = 0; + m_current_time = 0; + } + + void Animation::update(void) + { + if (m_current_time++ > m_frame_time && !m_still) + { + m_current_time = 0; + if (m_random) + { + m_current_frame = ostd::Random::geti32(0, m_frames); + } + else if (m_backwards) + { + if (!m_back && m_current_frame >= m_frames - 1) + { + m_back = true; + m_current_frame--; + } + else if (!m_back) + m_current_frame++; + else if (m_back && m_current_frame <= 0) + { + m_back = false; + m_current_frame++; + } + else if (m_back) + m_current_frame--; + } + else + { + if (m_current_frame++ >= m_frames - 1) + m_current_frame = 0; + } + } + else if (m_still) + m_current_frame = 1; //TODO: Hardcoded value + int x = ((m_current_frame % m_columns) + m_column_offset) * m_frame_width; + int y = m_row_offset * m_frame_height; + if (m_rows > 1) + y = ((m_current_frame / m_columns) + m_row_offset) * m_frame_height; + + m_frame_rect = { static_cast(x), static_cast(y), static_cast(m_frame_width), static_cast(m_frame_height) }; + } +} diff --git a/src/ogfx/Animation.hpp b/src/ogfx/Animation.hpp new file mode 100644 index 0000000..04fe7e9 --- /dev/null +++ b/src/ogfx/Animation.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include + +namespace ogfx +{ + class Animation + { + public: struct AnimationData { + int32_t length; + int32_t speed; + int32_t still_frame; + + int32_t row_offset; + int32_t column_offset; + + int32_t rows; + int32_t columns; + + int32_t frame_width; + int32_t frame_height; + + bool still; + bool backwards; + bool random; + + AnimationData(void) + { + length = 3; + speed = 10; + row_offset = 0; + column_offset = 0; + rows = 1; + columns = 3; + frame_width = 32; + frame_height = 32; + still = false; + backwards = true; + random = false; + still_frame = 1; + } + }; + public: + Animation(void); + Animation(int32_t frames, int32_t columns, int32_t rows, int32_t frame_width, int32_t frame_height); + Animation(AnimationData& ad); + void create(int32_t frames, int32_t columns, int32_t rows, int32_t frame_width, int32_t frame_height); + void create(AnimationData& ad); + void update(void); + void resetAnimation(void); + + inline void setFrameNumber(int32_t n) { m_frames = n; } + inline void setColumnNumber(int32_t n) { m_columns = n; } + inline void setBackwards(bool b) { m_backwards = b; } + inline void setSpeed(int32_t d) { m_frame_time = d; } + inline void setColumnOffset(int32_t o) { m_column_offset = o; } + inline void setRowOffset(int32_t o) { m_row_offset = o; } + inline void setStill(bool s) { m_still = s; } + inline void setSpriteSheet(const Image& img) { m_spriteSheet = &img; } + + inline int32_t getFrameNumber(void) const { return m_frames; } + inline int32_t getColumnNumber(void) const { return m_columns; } + inline bool getBackwards(void) const { return m_backwards; } + inline int32_t getDelay(void) const { return m_frame_time; } + inline int32_t getColumnOffset(void) const { return m_column_offset; } + inline int32_t getRowOffset(void) const { return m_row_offset; } + inline bool isStill(void) const { return m_still; } + inline ostd::Rectangle getFrameRect(void) const { return m_frame_rect; } + inline const Image& getSpriteSheet(void) const { return (m_spriteSheet != nullptr ? *m_spriteSheet : InvalidImage); } + inline bool hasImage(void) const { return m_spriteSheet != nullptr; } + + private: + const Image* m_spriteSheet { nullptr }; + inline static const Image InvalidImage; + + int32_t m_frames; + int32_t m_rows; + int32_t m_columns; + int32_t m_frame_width; + int32_t m_frame_height; + + int32_t m_column_offset; + int32_t m_row_offset; + + int32_t m_frame_time; + int32_t m_current_time; + int32_t m_current_frame; + + bool m_backwards; + bool m_back; + bool m_still; + bool m_random; + + ostd::Rectangle m_frame_rect; + }; +} diff --git a/src/ogfx/BasicRenderer.cpp b/src/ogfx/BasicRenderer.cpp index 26d964f..40d5268 100644 --- a/src/ogfx/BasicRenderer.cpp +++ b/src/ogfx/BasicRenderer.cpp @@ -1,5 +1,6 @@ #include "BasicRenderer.hpp" #include "WindowBase.hpp" +#include namespace ogfx { @@ -22,7 +23,7 @@ namespace ogfx m_ttfr.setFontSize(fontSize); } - void BasicRenderer2D::drawImage(const ogfx::Image& image, const ostd::Vec2& position) + void BasicRenderer2D::drawImage(const ogfx::Image& image, const ostd::Vec2& position, const ostd::Rectangle& rect) { if (!m_initialized) return; if (!image.isLoaded()) return; @@ -30,8 +31,29 @@ namespace ogfx texr.x = position.x; texr.y = position.y; texr.w = image.getSize().x; - texr.h = image.getSize().y; - SDL_RenderCopy(m_window->getSDLRenderer(), image.getSDLTexture(), nullptr, &texr); + texr.h = image.getSize().y; + SDL_Rect srcRect; + srcRect.x = rect.x; + srcRect.y = rect.y; + srcRect.w = rect.w; + srcRect.h = rect.w; + if (srcRect.x == 0 && srcRect.y == 0 && srcRect.w == 0 && srcRect.h == 0) + SDL_RenderCopy(m_window->getSDLRenderer(), image.getSDLTexture(), nullptr, &texr); + else + { + texr.w = srcRect.w; + texr.h = srcRect.h; + SDL_RenderCopy(m_window->getSDLRenderer(), image.getSDLTexture(), &srcRect, &texr); + } + } + + void BasicRenderer2D::drawAnimation(const Animation& anim, const ostd::Vec2& position) + { + if (!m_initialized) return; + if (!anim.hasImage()) return; + const auto& img = anim.getSpriteSheet(); + if (!img.isLoaded() || !img.isValid()) return; + drawImage(img, position, anim.getFrameRect()); } void BasicRenderer2D::drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize) @@ -130,19 +152,19 @@ namespace ogfx fillRect(rect, fillColor); drawRect(rect, outlineColor, outlineThickness); } - + void BasicRenderer2D::outlinedRoundRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t radius, int32_t outlineThickness) { if (!m_initialized) return; fillRoundRect(rect, fillColor, radius); drawRoundRect(rect, outlineColor, radius, outlineThickness); } - + void BasicRenderer2D::outlinedCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness) { if (!m_initialized) return; fillCircle(center, radius, fillColor); drawCircle(center, radius, outlineColor, outlineThickness); } - + } diff --git a/src/ogfx/BasicRenderer.hpp b/src/ogfx/BasicRenderer.hpp index b8af068..51b99b9 100644 --- a/src/ogfx/BasicRenderer.hpp +++ b/src/ogfx/BasicRenderer.hpp @@ -20,6 +20,7 @@ #pragma once +#include "Animation.hpp" #include #include #include @@ -40,7 +41,8 @@ namespace ogfx void setFont(const ostd::String& fontFilePath); void setFontSize(int32_t fontSize); - void drawImage(const ogfx::Image& image, const ostd::Vec2& position); + void drawImage(const ogfx::Image& image, const ostd::Vec2& position, const ostd::Rectangle& rect = { 0, 0, 0, 0 }); + void drawAnimation(const Animation& anim, const ostd::Vec2& position); void drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize = 0); void drawCenteredString(const ostd::String& str, const ostd::Vec2& center, const ostd::Color& color, int32_t fontSize = 0); diff --git a/src/ogfx/Image.cpp b/src/ogfx/Image.cpp index 55c86ce..c456cb9 100644 --- a/src/ogfx/Image.cpp +++ b/src/ogfx/Image.cpp @@ -1,4 +1,5 @@ #include "Image.hpp" +#include "io/Logger.hpp" #include #include #include @@ -16,13 +17,18 @@ namespace ogfx Image& Image::loadFromFile(const ostd::String& filePath, BasicRenderer2D& gfx) { - if (!gfx.isInitialized()) - return *this; //TODO: Error - m_sdl_texture = IMG_LoadTexture(gfx.getWindow().getSDLRenderer(), filePath.c_str()); - SDL_QueryTexture(m_sdl_texture, nullptr, nullptr, &m_width, &m_height); - m_loaded = true; - setTypeName("ogfx::Image"); - validate(); - return *this; + if (!gfx.isInitialized()) + return *this; //TODO: Error + m_sdl_texture = IMG_LoadTexture(gfx.getWindow().getSDLRenderer(), filePath.c_str()); + if (!m_sdl_texture) + { + OX_ERROR("Failed to load Image: %s", IMG_GetError()); + return *this; + } + SDL_QueryTexture(m_sdl_texture, nullptr, nullptr, &m_width, &m_height); + m_loaded = true; + setTypeName("ogfx::Image"); + validate(); + return *this; } } diff --git a/src/ostd/utils/Async.hpp b/src/ostd/utils/Async.hpp new file mode 100644 index 0000000..007bf23 --- /dev/null +++ b/src/ostd/utils/Async.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include + +namespace ostd +{ + template + class AsyncJob + { + public: + AsyncJob() = default; + + // Start a new job (callable must return T) + template + void start(Callable&& func, Args&&... args) + { + // Reset state + m_done.store(false); + { + std::lock_guard lock(m_mutex); + m_result.reset(); + } + + // Launch detached worker thread + std::thread([this, func = std::forward(func), ... args = std::forward(args)]() mutable { + T value = func(args...); + { + std::lock_guard lock(m_mutex); + m_result = std::move(value); + } + m_done.store(true); + }).detach(); + } + + inline bool isReady(void) const { return m_done.load(); } + + // Retrieve the result (only valid if is_ready() == true) + inline T get(void) + { + std::lock_guard lock(m_mutex); + return *m_result; + } + + // Retrieve result as optional (safe even if not ready) + std::optional tryGet(void) + { + if (!m_done.load()) + return std::nullopt; + std::lock_guard lock(m_mutex); + return m_result; + } + + private: + std::atomic m_done { false }; + std::optional m_result; + mutable std::mutex m_mutex; + }; +} diff --git a/src/test.cpp b/src/test.cpp index f6a1fbb..bb33ee9 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -23,6 +23,8 @@ // #include // #include +#include "Image.hpp" +#include "utils/Utils.hpp" #include #include #include @@ -48,6 +50,10 @@ class Window : public ogfx::WindowBase m_textInput.setEventListener(m_sigHandler); // m_textInput.setCharacterFilter(m_numCharFilter); m_textInput.getTheme().extraPaddingTop = 3; + + m_testImg.loadFromFile("res/test.png", m_gfx); + out.p("Image loaded: ").p(STR_BOOL(m_testImg.isLoaded())).nl(); + out.p(" ").p(m_testImg.getSize().x).nl(); } inline void handleSignal(ostd::tSignal& signal) override @@ -79,6 +85,7 @@ class Window : public ogfx::WindowBase inline void onRender(void) override { m_textInput.render(m_gfx); + // m_gfx.drawImage(m_testImg, { 0, 0 });ยด } inline void onFixedUpdate(double frameTime_s) override @@ -88,7 +95,7 @@ class Window : public ogfx::WindowBase inline void onUpdate(void) override { - m_textInput.update (); + m_textInput.update(); } private: @@ -96,6 +103,8 @@ class Window : public ogfx::WindowBase ogfx::BasicRenderer2D m_gfx; ogfx::gui::RawTextInputEventListener m_sigHandler; ogfx::gui::RawTextInputNumberCharacterFilter m_numCharFilter; + + ogfx::Image m_testImg; }; // void test2(const std::string& str) {}