From d7fa13d8752b74cd45bffd121442281c98743c39 Mon Sep 17 00:00:00 2001 From: OmniaX-dev Date: Fri, 29 Mar 2024 22:36:16 +0100 Subject: [PATCH] Started work on new library --- .vscode/settings.json | 19 +++- CMakeLists.txt | 15 ++- README.md | 7 +- build.nr | 2 +- compile | 9 ++ src/ogfx/BasicRenderer.cpp | 137 +++++++++++++++++++++++ src/ogfx/BasicRenderer.hpp | 44 ++++++++ src/ogfx/FontUtils.cpp | 218 +++++++++++++++++++++++++++++++++++++ src/ogfx/FontUtils.hpp | 57 ++++++++++ src/ogfx/SDLInclude.hpp | 9 ++ src/ogfx/WindowBase.cpp | 169 ++++++++++++++++++++++++++++ src/ogfx/WindowBase.hpp | 115 +++++++++++++++++++ src/ostd/Geometry.hpp | 50 +++++++++ src/ostd/QuadTree.cpp | 2 +- src/ostd/String.cpp | 10 +- temp_install.sh | 10 ++ 16 files changed, 864 insertions(+), 9 deletions(-) create mode 100644 src/ogfx/BasicRenderer.cpp create mode 100644 src/ogfx/BasicRenderer.hpp create mode 100644 src/ogfx/FontUtils.cpp create mode 100644 src/ogfx/FontUtils.hpp create mode 100644 src/ogfx/SDLInclude.hpp create mode 100644 src/ogfx/WindowBase.cpp create mode 100644 src/ogfx/WindowBase.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 61116ba..6acb32c 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -139,5 +139,22 @@ ], "editor.insertSpaces": false, - "workbench.list.openMode": "doubleClick" + "workbench.list.openMode": "doubleClick", + "files.autoSave": "afterDelay", + "files.autoSaveDelay": 200, + "workbench.activityBar.location": "hidden", + "workbench.editor.openPositioning": "last", + "workbench.editor.tabActionCloseVisibility": false, + "window.density.editorTabHeight": "compact", + "window.zoomLevel": 1, + "explorer.autoReveal": false, + "editor.stickyScroll.enabled": false, + "workbench.statusBar.visible": false, + "breadcrumbs.enabled": false, + "workbench.tree.enableStickyScroll": false, + + "cmake.configureOnOpen": false, + "cmake.options.statusBarVisibility": "hidden", + + "git.openRepositoryInParentFolders": "never" } diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b31dfc..87606fc 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ set(PROJ_NAME omnia-framework) set(MAJOR_VER 0) set(MINOR_VER 1) set(OMNIA_STD_LIB ostd) +set(OMNIA_GFX_LIB ogfx) set(TEST_TARGET ostd_test) set(RUN_CONFIG O_F_DEBUG) #----------------------------------------------------------------------------------------- @@ -26,6 +27,7 @@ message("** Building ${PROJ_NAME} ${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}") list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/src/ostd + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx ${CMAKE_CURRENT_LIST_DIR}/src/ostd/vendor ) list(APPEND OSTD_SOURCE_FILES @@ -52,6 +54,11 @@ list(APPEND OSTD_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/ostd/TextStyleParser.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ostd/Utils.cpp ) +list(APPEND OGFX_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/FontUtils.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/WindowBase.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/ogfx/BasicRenderer.cpp +) list(APPEND TEST_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/test.cpp ) @@ -60,12 +67,16 @@ list(APPEND TEST_SOURCE_FILES #Targets #----------------------------------------------------------------------------------------- add_library(${OMNIA_STD_LIB} SHARED ${OSTD_SOURCE_FILES}) +add_library(${OMNIA_GFX_LIB} SHARED ${OGFX_SOURCE_FILES}) add_executable(${TEST_TARGET} ${TEST_SOURCE_FILES}) target_compile_definitions(${OMNIA_STD_LIB} PUBLIC ${RUN_CONFIG} BUILD_NR=${BUILD_NUMBER} MAJ_V=${MAJOR_VER} MIN_V=${MINOR_VER} VERSION_STR="${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}") +target_compile_definitions(${OMNIA_GFX_LIB} PUBLIC ${RUN_CONFIG} BUILD_NR=${BUILD_NUMBER} MAJ_V=${MAJOR_VER} MIN_V=${MINOR_VER} VERSION_STR="${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}") target_include_directories(${OMNIA_STD_LIB} PUBLIC ${INCLUDE_DIRS}) +target_include_directories(${OMNIA_GFX_LIB} PUBLIC ${INCLUDE_DIRS}) target_include_directories(${TEST_TARGET} PUBLIC ${INCLUDE_DIRS}) +target_link_libraries(${OMNIA_GFX_LIB} PUBLIC ${OMNIA_STD_LIB}) target_link_libraries(${TEST_TARGET} PUBLIC ${OMNIA_STD_LIB}) #TODO: Different flags for Release/Debug @@ -73,7 +84,9 @@ add_compile_options(-O3 -m32 -MMD -MP -Wall -ggdb -fsanitize=address) if (UNIX) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") target_link_libraries(${OMNIA_STD_LIB} PUBLIC tinfo boost_regex) + target_link_libraries(${OMNIA_GFX_LIB} PUBLIC xcb xcb-randr) endif (UNIX) +target_link_libraries(${OMNIA_GFX_LIB} PUBLIC SDL2main SDL2 SDL2_gfx SDL2_ttf) #----------------------------------------------------------------------------------------- #Finalize @@ -84,5 +97,5 @@ add_custom_command(TARGET ${OMNIA_STD_LIB} POST_BUILD VERBATIM ) add_custom_target(FinalMessage ALL ${CMAKE_COMMAND} -E cmake_echo_color --cyan "[100%] Built ${PROJ_NAME} ${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}") -add_dependencies(FinalMessage ${OMNIA_STD_LIB}) +add_dependencies(FinalMessage ${OMNIA_GFX_LIB}) #----------------------------------------------------------------------------------------- diff --git a/README.md b/README.md index d74864c..e223bd9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ download MSYS2 from https://www.msys2.org/ and install it run MSYS2, and in the terminal run: ``` pacman -Syuu -pacman -S --needed base-devel mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-gdb mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-make mingw-w64-ucrt-x86_64-boost +pacman -S --needed base-devel mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-gdb mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-make mingw-w64-ucrt-x86_64-boost mingw-w64-ucrt-x86_64-SDL2 mingw-w64-ucrt-x86_64-SDL2_mixer mingw-w64-ucrt-x86_64-SDL2_image mingw-w64-ucrt-x86_64-SDL2_ttf +``` ``` **Step 3:** @@ -28,7 +29,7 @@ execute this command: open a terminal and run: ``` sudo pacman -Syuu -sudo pacman -S --needed base-devel clang gdb cmake make boost +sudo pacman -S --needed base-devel clang gdb cmake make boost sdl2 sdl2_mixer sdl2_image sdl2_ttf ``` **Step 2:** @@ -41,7 +42,7 @@ open a terminal inside the root directory of the project and run this command: open a terminal and run: ``` sudo apt update && sudo apt upgrade -sudo apt install clang gdb make cmake libboost-all-dev +sudo apt install clang gdb make cmake libboost-all-dev libsdl2-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-ttf-dev libxcb-randr0-dev ``` **Step 2:** diff --git a/build.nr b/build.nr index eebf2dd..c2d7082 100644 --- a/build.nr +++ b/build.nr @@ -1 +1 @@ -1824 +1850 diff --git a/compile b/compile index 8a90994..93e2235 100755 --- a/compile +++ b/compile @@ -12,9 +12,12 @@ if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then mkdir -p ./Release/lib cp ./bin/libostd.so ./Release/lib + cp ./bin/libogfx.so ./Release/lib mkdir -p ./Release/include/ostd/vendor + mkdir -p ./Release/include/ogfx/ find ./src/ostd -maxdepth 1 -name "*.hpp" -exec cp {} ./Release/include/ostd \; + find ./src/ogfx -maxdepth 1 -name "*.hpp" -exec cp {} ./Release/include/ogfx \; find ./src/ostd/vendor -maxdepth 1 -name "*.hpp" -exec cp {} ./Release/include/ostd/vendor \; cp -r ./licences ./Release @@ -37,9 +40,15 @@ elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then cp C:/msys64/ucrt64/bin/libgcc_s_seh-1.dll ./Release/lib cp C:/msys64/ucrt64/bin/libstdc++-6.dll ./Release/lib cp C:/msys64/ucrt64/bin/libwinpthread-1.dll ./Release/lib + cp C:/msys64/ucrt64/bin/SDL2.dll ./Release/lib + cp C:/msys64/ucrt64/bin/SDL2_image.dll ./Release/lib + cp C:/msys64/ucrt64/bin/SDL2_ttf.dll ./Release/lib + cp C:/msys64/ucrt64/bin/SDL2_gfx.dll ./Release/lib mkdir -p ./Release/include/ostd/vendor + mkdir -p ./Release/include/ogfx/ find ./src/ostd -maxdepth 1 -name "*.hpp" -exec cp {} ./Release/include/ostd \; + find ./src/ogfx -maxdepth 1 -name "*.hpp" -exec cp {} ./Release/include/ogfx \; find ./src/ostd/vendor -maxdepth 1 -name "*.hpp" -exec cp {} ./Release/include/ostd/vendor \; cp -r ./licences ./Release diff --git a/src/ogfx/BasicRenderer.cpp b/src/ogfx/BasicRenderer.cpp new file mode 100644 index 0000000..2858290 --- /dev/null +++ b/src/ogfx/BasicRenderer.cpp @@ -0,0 +1,137 @@ +#include "BasicRenderer.hpp" +#include "WindowBase.hpp" +#include + +namespace ogfx +{ + void BasicRenderer2D::init(WindowBase& window) + { + m_window = &window; + m_ttfr.init(window.getSDLRenderer()); + m_initialized = true; + } + + void BasicRenderer2D::setFont(const ostd::String& fontFilePath) + { + if (!m_initialized) return; + m_ttfr.openFont(fontFilePath, m_ttfr.getFontSize()); + } + + void BasicRenderer2D::setFontSize(int32_t fontSize) + { + if (!m_initialized) return; + m_ttfr.setFontSize(fontSize); + } + + void BasicRenderer2D::drawString(const ostd::String& str, const ostd::Vec2& position, const ostd::Color& color, int32_t fontSize) + { + if (!m_initialized) return; + if (!m_ttfr.isValid()) return; + m_ttfr.renderText(str, (int32_t)std::round(position.x), (int32_t)std::round(position.y), color, fontSize); + } + + void BasicRenderer2D::drawCenteredString(const ostd::String& str, const ostd::Vec2& center, const ostd::Color& color, int32_t fontSize) + { + if (!m_initialized) return; + if (!m_ttfr.isValid()) return; + m_ttfr.renderCenteredText(str, (int32_t)std::round(center.x), (int32_t)std::round(center.y), color, fontSize); + } + + void BasicRenderer2D::drawLine(const ostd::FLine& line, const ostd::Color& color, int32_t thickness) + { + if (!m_initialized) return; + thickLineRGBA(m_window->getSDLRenderer(), (int32_t)std::round(line.p1.x), (int32_t)std::round(line.p1.y), (int32_t)std::round(line.p2.x), (int32_t)std::round(line.p2.y), thickness, color.r, color.g, color.b, color.a); + } + + void BasicRenderer2D::drawRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t thickness) + { + if (!m_initialized) return; + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; + if (thickness == 1) + rectangleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(x1), (int32_t)std::round(y1), (int32_t)std::round(x2), (int32_t)std::round(y2), color.r, color.g, color.b, color.a); + else if (thickness > 1) + { + for (int32_t i = 0; i < thickness; i++) + rectangleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(x1 + i), (int32_t)std::round(y1 + i), (int32_t)std::round(x2 - i), (int32_t)std::round(y2 - i), color.r, color.g, color.b, color.a); + } + } + + void BasicRenderer2D::drawRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t radius, int32_t thickness) + { + if (!m_initialized) return; + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; + if (thickness == 1) + roundedRectangleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(x1), (int32_t)std::round(y1), (int32_t)std::round(x2), (int32_t)std::round(y2), radius, color.r, color.g, color.b, color.a); + else if (thickness > 1) + { + for (int32_t i = 0; i < thickness; i++) + roundedRectangleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(x1 + i), (int32_t)std::round(y1 + i), (int32_t)std::round(x2 - i), (int32_t)std::round(y2 - i), radius, color.r, color.g, color.b, color.a); + } + } + + void BasicRenderer2D::drawCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& color, int32_t thickness) + { + if (!m_initialized) return; + if (thickness == 1) + circleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(center.x), (int32_t)std::round(center.y), radius, color.r, color.g, color.b, color.a); + else if (thickness > 1) + { + for (int32_t i = 0; i < thickness; i++) + circleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(center.x), (int32_t)std::round(center.y), radius - i, color.r, color.g, color.b, color.a); + } + } + + void BasicRenderer2D::fillRect(const ostd::Rectangle& rect, const ostd::Color& color) + { + if (!m_initialized) return; + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; + boxRGBA(m_window->getSDLRenderer(), (int32_t)std::round(x1), (int32_t)std::round(y1), (int32_t)std::round(x2), (int32_t)std::round(y2), color.r, color.g, color.b, color.a); + } + + void BasicRenderer2D::fillRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t radius) + { + if (!m_initialized) return; + float x1 = rect.x; + float y1 = rect.y; + float x2 = rect.x + rect.w; + float y2 = rect.y + rect.h; + roundedBoxRGBA(m_window->getSDLRenderer(), (int32_t)std::round(x1), (int32_t)std::round(y1), (int32_t)std::round(x2), (int32_t)std::round(y2), radius, color.r, color.g, color.b, color.a); + } + + void BasicRenderer2D::fillCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& color) + { + if (!m_initialized) return; + filledCircleRGBA(m_window->getSDLRenderer(), (int32_t)std::round(center.x), (int32_t)std::round(center.y), radius, color.r, color.g, color.b, color.a); + } + + void BasicRenderer2D::outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness) + { + if (!m_initialized) return; + 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); + } + +} \ No newline at end of file diff --git a/src/ogfx/BasicRenderer.hpp b/src/ogfx/BasicRenderer.hpp new file mode 100644 index 0000000..4d923f1 --- /dev/null +++ b/src/ogfx/BasicRenderer.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +namespace ogfx +{ + class WindowBase; + class BasicRenderer2D + { + public: + BasicRenderer2D(void) = default; + void init(WindowBase& window); + + inline TTFRenderer& getTTFRenderer(void) { return m_ttfr; } + inline ostd::IPoint getStringSize(const ostd::String str, int32_t fontSize = 0) { return m_ttfr.getStringDimensions(str, fontSize); } + inline WindowBase& getWindow(void) { return *m_window; } + inline bool isInitialized(void) { return m_initialized; } + void setFont(const ostd::String& fontFilePath); + void setFontSize(int32_t fontSize); + + 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); + + void drawLine(const ostd::FLine& line, const ostd::Color& color, int32_t thickness = 1); + + void drawRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t thickness = 1); + void drawRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t radius, int32_t thickness = 1); + void drawCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& color, int32_t thickness = 1); + + void fillRect(const ostd::Rectangle& rect, const ostd::Color& color); + void fillRoundRect(const ostd::Rectangle& rect, const ostd::Color& color, int32_t radius); + void fillCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& color); + + void outlinedRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1); + void outlinedRoundRect(const ostd::Rectangle& rect, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t radius, int32_t outlineThickness = 1); + void outlinedCircle(const ostd::Vec2& center, int32_t radius, const ostd::Color& fillColor, const ostd::Color& outlineColor, int32_t outlineThickness = 1); + + private: + TTFRenderer m_ttfr; + WindowBase* m_window { nullptr }; + bool m_initialized { false }; + }; +} \ No newline at end of file diff --git a/src/ogfx/FontUtils.cpp b/src/ogfx/FontUtils.cpp new file mode 100644 index 0000000..1cb98b2 --- /dev/null +++ b/src/ogfx/FontUtils.cpp @@ -0,0 +1,218 @@ +#include "FontUtils.hpp" + +namespace ogfx +{ + TTFRenderer::~TTFRenderer(void) + { + closeFont(); + } + + void TTFRenderer::renderText(const ostd::String& message, int32_t x, int32_t y, const ostd::Color& color, int32_t fontSize) + { + if (!TTFRenderer::m_initialized) + { + set_error_state(tErrors::Uninitialized); + return; + } + if (!m_fontOpen) + { + set_error_state(tErrors::NoFont); + return; + } + if (m_renderer == nullptr) + { + set_error_state(tErrors::NullRenderer); + return; + } + if (m_font == nullptr) + { + set_error_state(tErrors::NullFont); + return; + } + setFontSize(fontSize); + SDL_Surface* surf = TTF_RenderText_Blended(m_font, message.c_str(), { color.r, color.g, color.b, color.a }); + if (surf == nullptr) + { + set_error_state(tErrors::TTFRenderTextBlendedFail); + print_ttf_error("TTF_RenderText_Blended"); + return; + } + SDL_Texture* texture = SDL_CreateTextureFromSurface(m_renderer, surf); + if (texture == nullptr) + { + set_error_state(tErrors::TTFCreateTextureFromSurfaceFail); + print_ttf_error("SDL_CreateTextureFromSurface"); + return; + } + SDL_FreeSurface(surf); + + int32_t w = 0, h = 0; + SDL_QueryTexture(texture, NULL, NULL, &w, &h); + + SDL_Rect dst; + dst.x = x; + dst.y = y; + dst.w = w; + dst.h = h; + SDL_RenderCopy(m_renderer, texture, NULL, &dst); + SDL_DestroyTexture(texture); + } + + void TTFRenderer::renderCenteredText(const ostd::String& message, int32_t center_x, int32_t center_y, const ostd::Color& color, int32_t fontSize) + { + if (!TTFRenderer::m_initialized) + { + set_error_state(tErrors::Uninitialized); + return; + } + if (!m_fontOpen) + { + set_error_state(tErrors::NoFont); + return; + } + if (m_renderer == nullptr) + { + set_error_state(tErrors::NullRenderer); + return; + } + if (m_font == nullptr) + { + set_error_state(tErrors::NullFont); + return; + } + setFontSize(fontSize); + SDL_Surface* surf = TTF_RenderText_Blended(m_font, message.c_str(), { color.r, color.g, color.b, color.a }); + if (surf == nullptr) + { + set_error_state(tErrors::TTFRenderTextBlendedFail); + print_ttf_error("TTF_RenderText_Blended"); + return; + } + SDL_Texture* texture = SDL_CreateTextureFromSurface(m_renderer, surf); + if (texture == nullptr) + { + set_error_state(tErrors::TTFCreateTextureFromSurfaceFail); + print_ttf_error("SDL_CreateTextureFromSurface"); + return; + } + SDL_FreeSurface(surf); + + int32_t w = 0, h = 0; + SDL_QueryTexture(texture, NULL, NULL, &w, &h); + + SDL_Rect dst; + dst.x = center_x - (w / 2); + dst.y = center_y - (h / 2); + dst.w = w; + dst.h = h; + SDL_RenderCopy(m_renderer, texture, NULL, &dst); + SDL_DestroyTexture(texture); + } + + ostd::IPoint TTFRenderer::getStringDimensions(const ostd::String& message, int32_t fontSize) + { + if (!TTFRenderer::m_initialized) + { + set_error_state(tErrors::Uninitialized); + return { 0, 0 }; + } + if (!m_fontOpen) + { + set_error_state(tErrors::NoFont); + return { 0, 0 }; + } + if (m_renderer == nullptr) + { + set_error_state(tErrors::NullRenderer); + return { 0, 0 }; + } + if (m_font == nullptr) + { + set_error_state(tErrors::NullFont); + return { 0, 0 }; + } + setFontSize(fontSize); + SDL_Surface* surf = TTF_RenderText_Blended(m_font, message.c_str(), { 0, 0, 0, 255 }); + if (surf == nullptr) + { + set_error_state(tErrors::TTFRenderTextBlendedFail); + print_ttf_error("TTF_RenderText_Blended"); + return { 0, 0 }; + } + SDL_Texture* texture = SDL_CreateTextureFromSurface(m_renderer, surf); + if (texture == nullptr) + { + set_error_state(tErrors::TTFCreateTextureFromSurfaceFail); + print_ttf_error("SDL_CreateTextureFromSurface"); + return { 0, 0 }; + } + SDL_FreeSurface(surf); + + int32_t w = 0, h = 0; + SDL_QueryTexture(texture, NULL, NULL, &w, &h); + SDL_DestroyTexture(texture); + return { w, h }; + } + + int32_t TTFRenderer::init(SDL_Renderer* renderer) + { + if (TTFRenderer::m_initialized) return set_error_state(tErrors::NoError); + if (renderer == nullptr) return set_error_state(tErrors::NullRenderer); + if (TTF_Init() != 0) + { + print_ttf_error("TTF_Init"); + SDL_Quit(); + return set_error_state(tErrors::FailedToLoad); + } + m_renderer = renderer; + TTFRenderer::m_initialized = true; + return set_error_state(tErrors::NoError); + } + + void TTFRenderer::closeFont(void) + { + if (!TTFRenderer::m_initialized || !m_fontOpen) return; + if (m_font != nullptr) + TTF_CloseFont(m_font); + m_font = nullptr; + m_fontOpen = false; + m_fontSize = DefaultFontSize; + set_error_state(tErrors::NoError); + } + + int32_t TTFRenderer::openFont(const ostd::String& fontPath, int32_t fontSize) + { + if (!TTFRenderer::m_initialized) return set_error_state(tErrors::Uninitialized); + if (m_fontOpen) closeFont(); + if (fontSize <= 0) + fontSize = m_fontSize; + else + m_fontSize = fontSize; + m_font = TTF_OpenFont(fontPath.c_str(), fontSize); + if (m_font == nullptr) + { + print_ttf_error("TTF_OpenFont"); + return set_error_state(tErrors::FailedToOpenFontFile); + } + m_fontOpen = true; + return set_error_state(tErrors::NoError); + } + + int32_t TTFRenderer::setFontSize(int32_t fontSize) + { + if (!TTFRenderer::m_initialized) + return set_error_state(tErrors::Uninitialized); + if (!m_fontOpen) + return set_error_state(tErrors::NoFont); + if (m_font == nullptr) + return set_error_state(tErrors::NullFont); + if (fontSize == m_fontSize || fontSize <= 0) return set_error_state(tErrors::NoError); + TTF_SetFontSize(m_font, fontSize); + return set_error_state(tErrors::NoError); + } + + void TTFRenderer::print_ttf_error(const ostd::String& funcName) + { + m_out.fg(ostd::ConsoleColors::Red).p(funcName).p("(...) failed: ").p(ostd::String(TTF_GetError())).reset().nl(); + } +} \ No newline at end of file diff --git a/src/ogfx/FontUtils.hpp b/src/ogfx/FontUtils.hpp new file mode 100644 index 0000000..ff191c0 --- /dev/null +++ b/src/ogfx/FontUtils.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +namespace ogfx +{ + class TTFRenderer + { + public: struct tErrors + { + inline static constexpr int32_t NoError = 0; + inline static constexpr int32_t FailedToLoad = 1; + inline static constexpr int32_t FailedToOpenFontFile = 2; + inline static constexpr int32_t Uninitialized = 3; + inline static constexpr int32_t NullRenderer = 4; + inline static constexpr int32_t InvalidState = 5; + inline static constexpr int32_t TTFRenderTextBlendedFail = 6; + inline static constexpr int32_t TTFCreateTextureFromSurfaceFail = 7; + inline static constexpr int32_t NullFont = 8; + inline static constexpr int32_t NoFont = 9; + }; + public: + TTFRenderer(void) = default; + ~TTFRenderer(void); + inline TTFRenderer(SDL_Renderer* renderer) { init(renderer); } + int32_t init(SDL_Renderer* renderer); + void closeFont(void); + int32_t openFont(const ostd::String& fontPath, int32_t fontSize = 0); + int32_t setFontSize(int32_t fontSize); + void renderText(const ostd::String& message, int32_t x, int32_t y, const ostd::Color& color, int32_t fontSize = 0); + void renderCenteredText(const ostd::String& message, int32_t center_x, int32_t center_y, const ostd::Color& color, int32_t fontSize = 0); + ostd::IPoint getStringDimensions(const ostd::String& message, int32_t fontSize = 0); + inline bool isInitialized(void) { return TTFRenderer::m_initialized; } + inline bool hasOpenFont(void) { return m_fontOpen; } + inline TTF_Font* getSDLFont(void) { return m_font; } + inline bool isValid(void) { return TTFRenderer::m_initialized && m_fontOpen && m_font != nullptr && m_renderer != nullptr; } + inline int32_t geterrorState(void) { return m_errorState; } + inline int32_t getFontSize(void) { return m_fontSize; } + + private: + void print_ttf_error(const ostd::String& funcName); + inline int32_t set_error_state(int32_t err) { m_errorState = err; return m_errorState; } + + private: + ostd::ConsoleOutputHandler m_out; + inline static bool m_initialized { false }; + bool m_fontOpen { false }; + TTF_Font* m_font { nullptr }; + SDL_Renderer* m_renderer { nullptr }; + int32_t m_errorState { tErrors::NoError }; + int32_t m_fontSize { DefaultFontSize }; + + inline static constexpr int32_t DefaultFontSize { 16 }; + }; +} \ No newline at end of file diff --git a/src/ogfx/SDLInclude.hpp b/src/ogfx/SDLInclude.hpp new file mode 100644 index 0000000..6408d40 --- /dev/null +++ b/src/ogfx/SDLInclude.hpp @@ -0,0 +1,9 @@ +#pragma once + +#ifdef _WIN32 +#undef __linux__ +#endif + +#include +#include +#include \ No newline at end of file diff --git a/src/ogfx/WindowBase.cpp b/src/ogfx/WindowBase.cpp new file mode 100644 index 0000000..f125471 --- /dev/null +++ b/src/ogfx/WindowBase.cpp @@ -0,0 +1,169 @@ +#include "WindowBase.hpp" + +namespace ogfx +{ + WindowBase::~WindowBase(void) + { + onDestroy(); + SDL_DestroyRenderer(m_renderer); + SDL_DestroyWindow(m_window); + // IMG_Quit(); + SDL_Quit(); + TTF_Quit(); + } + + void WindowBase::initialize(int32_t width, int32_t height, const ostd::String& windowTitle) + { + if (m_initialized) return; + m_windowWidth = width; + m_windowHeight = height; + m_title = windowTitle; + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + printf( "SDL could not initialize! Error: %s\n", SDL_GetError() ); + exit(1); + } + // int imgFlags = IMG_INIT_PNG; + // if (!(IMG_Init(imgFlags) & imgFlags)) + // { + // printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() ); + // exit(2); + // } + m_window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_windowWidth, m_windowHeight, SDL_WINDOW_RESIZABLE); + SDL_SetWindowResizable(m_window, SDL_FALSE); + m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED); + SDL_SetWindowMinimumSize(m_window, m_windowWidth, m_windowHeight); + SDL_SetWindowTitle(m_window, m_title.c_str()); + + m_initialized = true; + m_running = true; + + setTypeName("dragon::WindowBase"); + enableSignals(true); + validate(); + + onInitialize(); + } + + void WindowBase::update(void) + { + if (!m_initialized) return; + Uint64 start = SDL_GetPerformanceCounter(); + handleEvents(); + SDL_SetRenderDrawColor(m_renderer, m_clearColor.r, m_clearColor.g, m_clearColor.b, m_clearColor.a); + if (m_refreshScreen) + SDL_RenderClear(m_renderer); + onUpdate(); + onRender(); + SDL_RenderPresent(m_renderer); + Uint64 end = SDL_GetPerformanceCounter(); + float elapsed = (end - start) / (float)SDL_GetPerformanceFrequency(); + m_redrawAccumulator += elapsed; + if (m_redrawAccumulator >= 0.2f) + { + onFixedUpdate(); + m_redrawAccumulator = 0.0f; + } + end = SDL_GetPerformanceCounter(); + elapsed = (end - start) / (float)SDL_GetPerformanceFrequency(); + m_timeAccumulator += elapsed; + if (m_timeAccumulator >= 0.5f) + { + onSlowUpdate(); + m_fps = (int32_t)(1.0f / elapsed); + m_timeAccumulator = 0.0f; + } + } + + void WindowBase::setSize(int32_t width, int32_t height) + { + if (!isInitialized()) return; + SDL_SetWindowSize(m_window, width, height); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowResized, ostd::tSignalPriority::RealTime); + } + + void WindowBase::setTitle(const ostd::String& title) + { + if (!isInitialized()) return; + m_title = title; + SDL_SetWindowTitle(m_window, m_title.c_str()); + } + + void WindowBase::handleEvents(void) + { + if (!m_initialized) return; + auto l_getMouseState = [this](void) -> MouseEventData { + int32_t mx = 0, my = 0; + uint32_t btn = SDL_GetMouseState(&mx, &my); + MouseEventData::eButton button = MouseEventData::eButton::None; + switch (btn) + { + case SDL_BUTTON(1): button = MouseEventData::eButton::Left; break; + case SDL_BUTTON(2): button = MouseEventData::eButton::Middle; break; + case SDL_BUTTON(3): button = MouseEventData::eButton::Right; break; + default: button = MouseEventData::eButton::None; break; + } + MouseEventData mmd(*this, mx, my, button); + return mmd; + }; + SDL_Event event; + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + m_running = false; + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowClosed, ostd::tSignalPriority::Normal, *this); + } + else if (event.type == SDL_WINDOWEVENT) + { + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + WindowResizedData wrd(*this, m_windowWidth, m_windowHeight, 0, 0); + SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight); + wrd.new_width = m_windowWidth; + wrd.new_height = m_windowHeight; + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowResized, ostd::tSignalPriority::RealTime, wrd); + } + } + else if (event.type == SDL_MOUSEMOTION) + { + MouseEventData mmd = l_getMouseState(); + if (isMouseDragEventEnabled() && mmd.button != MouseEventData::eButton::None) + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowClosed, ostd::tSignalPriority::RealTime, mmd); + else + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MouseMoved, ostd::tSignalPriority::RealTime, mmd); + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + MouseEventData mmd = l_getMouseState(); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MousePressed, ostd::tSignalPriority::RealTime, mmd); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + MouseEventData mmd = l_getMouseState(); + switch (event.button.button) + { + case SDL_BUTTON(1): mmd.button = MouseEventData::eButton::Left; break; + case SDL_BUTTON(2): mmd.button = MouseEventData::eButton::Middle; break; + case SDL_BUTTON(3): mmd.button = MouseEventData::eButton::Right; break; + default: mmd.button = MouseEventData::eButton::None; break; + } + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MouseReleased, ostd::tSignalPriority::RealTime, mmd); + } + else if (event.type == SDL_KEYDOWN) + { + KeyEventData ked(*this, (int32_t)event.key.keysym.sym, 0, KeyEventData::eKeyEvent::Pressed); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::KeyPressed, ostd::tSignalPriority::RealTime, ked); + } + else if (event.type == SDL_KEYUP) + { + KeyEventData ked(*this, (int32_t)event.key.keysym.sym, 0, KeyEventData::eKeyEvent::Released); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::KeyReleased, ostd::tSignalPriority::RealTime, ked); + } + else if (event.type == SDL_TEXTINPUT) + { + KeyEventData ked(*this, 0, event.text.text[0], KeyEventData::eKeyEvent::Text); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::TextEntered, ostd::tSignalPriority::RealTime, ked); + } + } + } +} \ No newline at end of file diff --git a/src/ogfx/WindowBase.hpp b/src/ogfx/WindowBase.hpp new file mode 100644 index 0000000..41a8b44 --- /dev/null +++ b/src/ogfx/WindowBase.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include + +namespace ogfx +{ + class WindowBase : public ostd::BaseObject + { + public: + inline WindowBase(void) { } + ~WindowBase(void); + inline WindowBase(int32_t width, int32_t height, const ostd::String& windowTitle) { initialize(width, height, windowTitle); } + void initialize(int32_t width, int32_t height, const ostd::String& windowTitle); + + void update(void); + void setSize(int32_t width, int32_t height); + void setTitle(const ostd::String& title); + + inline virtual void onRender(void) { } + inline virtual void onUpdate(void) { } + inline virtual void onFixedUpdate(void) { } + inline virtual void onSlowUpdate(void) { } + inline virtual void onInitialize(void) { } + inline virtual void onDestroy(void) { } + + inline bool isInitialized(void) const { return m_initialized; } + inline bool isRunning(void) const { return m_running; } + inline void hide(void) { SDL_HideWindow(m_window); } + inline void show(void) { SDL_ShowWindow(m_window); } + inline ostd::String getTitle(void) const { return m_title; } + inline int32_t getFPS(void) const { return m_fps; } + inline int32_t getWindowWidth(void) const { return m_windowWidth; } + inline int32_t getWindowHeight(void) const { return m_windowHeight; } + inline SDL_Renderer* getSDLRenderer(void) const { return m_renderer; } + inline bool isMouseDragEventEnabled(void) const { return m_deagEventEnabled; } + inline void enableMouseDragEvent(bool enable = true) { m_deagEventEnabled = enable; } + inline ostd::Color getClearColor(void) const { return m_clearColor; } + inline void setClearColor(const ostd::Color& color) { m_clearColor = color; } + + private: + void handleEvents(void); + + protected: + ostd::ConsoleOutputHandler out; + + SDL_Window* m_window { nullptr }; + SDL_Renderer* m_renderer { nullptr }; + bool m_refreshScreen { true }; + + private: + int32_t m_windowWidth { 0 }; + int32_t m_windowHeight { 0 }; + ostd::String m_title { "" }; + int32_t m_fps { 0 }; + + ostd::Color m_clearColor { 10, 10, 10, 255 }; + + float m_timeAccumulator { 0.0f }; + float m_redrawAccumulator { 0.0f }; + + bool m_deagEventEnabled { false }; + bool m_running { false }; + bool m_initialized { false }; + }; + class WindowResizedData : public ostd::BaseObject + { + public: + inline WindowResizedData(WindowBase& parent, int32_t _oldx, int32_t _oldy, int32_t _newx, int32_t _newy) : parentWindow(parent), old_width(_oldx), old_height(_oldy), new_width(_newx), new_height(_newy) + { + setTypeName("ogfx::WindowResizedData"); + validate(); + } + + public: + int32_t new_width; + int32_t new_height; + int32_t old_width; + int32_t old_height; + WindowBase& parentWindow; + }; + class MouseEventData : public ostd::BaseObject + { + public: enum class eButton { None = 0, Left, Middle, Right }; + public: + inline MouseEventData(WindowBase& parent, int32_t mousex, int32_t mousey, eButton btn) : parentWindow(parent), position_x(mousex), position_y(mousey), button(btn) + { + setTypeName("ogfx::MouseEventData"); + validate(); + } + + public: + int32_t position_x; + int32_t position_y; + eButton button; + WindowBase& parentWindow; + }; + class KeyEventData : public ostd::BaseObject + { + public: enum class eKeyEvent { Pressed = 0, Released, Text }; + public: + inline KeyEventData(WindowBase& parent, int32_t key, char _text, eKeyEvent evt) : parentWindow(parent), keyCode(key), text(_text), eventType(evt) + { + setTypeName("ogfx::KeyEventData"); + validate(); + } + + public: + int32_t keyCode; + char text; + eKeyEvent eventType; + WindowBase& parentWindow; + }; +} \ No newline at end of file diff --git a/src/ostd/Geometry.hpp b/src/ostd/Geometry.hpp index abebc19..f8ecf70 100755 --- a/src/ostd/Geometry.hpp +++ b/src/ostd/Geometry.hpp @@ -353,6 +353,56 @@ namespace ostd float w; float h; }; + + template + class Rect + { + public: + Rect(void) = default; + inline Rect(T pos_x, T pos_y, T size_x, T size_y) : position(pos_x, pos_y), size(size_x, size_y) { } + inline Rect(Point pos, Point _size) : position(pos), size(_size) { } + template inline Rect(Rect copy) { position = { (T2)(copy.position.x), (T2)(copy.position.y) }; size = { (T2)(copy.size.x), (T2)(copy.size.y) }; } + + public: + Point position; + Point size; + }; + + typedef Rect FRect; + typedef Rect DRect; + typedef Rect UIRect; + typedef Rect UI64Rect; + typedef Rect UI16Rect; + typedef Rect UI8Rect; + typedef Rect IRect; + typedef Rect I64Rect; + typedef Rect I16Rect; + typedef Rect I8Rect; + + template + class Line + { + public: + Line(void) = default; + inline Line(T x1, T y1, T x2, T y2) : p1(x1, y1), p2(x2, y2) { } + inline Line(Point _p1, Point _p2) : p1(_p1), p2(_p2) { } + template inline Line(Line copy) { p1 = { (T2)(copy.p1.x), (T2)(copy.p1.y) }; p2 = { (T2)(copy.p2.x), (T2)(copy.p2.y) }; } + + public: + Point p1; + Point p2; + }; + + typedef Line FLine; + typedef Line DLine; + typedef Line UILine; + typedef Line UI64Line; + typedef Line UI16Line; + typedef Line UI8Line; + typedef Line ILine; + typedef Line I64Line; + typedef Line I16Line; + typedef Line I8Line; } #endif diff --git a/src/ostd/QuadTree.cpp b/src/ostd/QuadTree.cpp index 8b08745..1f16a20 100755 --- a/src/ostd/QuadTree.cpp +++ b/src/ostd/QuadTree.cpp @@ -42,7 +42,7 @@ namespace ostd m_sw = nullptr; m_ne = nullptr; m_se = nullptr; - setTypeName("ox::QuadTree"); + setTypeName("ostd::QuadTree"); setValid(true); } diff --git a/src/ostd/String.cpp b/src/ostd/String.cpp index 29763e7..a4649a6 100644 --- a/src/ostd/String.cpp +++ b/src/ostd/String.cpp @@ -240,7 +240,10 @@ namespace ostd String& String::add(float f, uint8_t precision) { std::stringstream stream; - stream << std::fixed << std::setprecision(precision) << f; + if (precision != 0) + stream << std::fixed << std::setprecision(precision) << f; + else + stream << f; cpp_string s = stream.str(); return add(s); } @@ -248,7 +251,10 @@ namespace ostd String& String::add(double f, uint8_t precision) { std::stringstream stream; - stream << std::fixed << std::setprecision(precision) << f; + if (precision != 0) + stream << std::fixed << std::setprecision(precision) << f; + else + stream << f; cpp_string s = stream.str(); return add(s); } diff --git a/temp_install.sh b/temp_install.sh index 4012d1e..4dc8208 100755 --- a/temp_install.sh +++ b/temp_install.sh @@ -2,14 +2,24 @@ if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then sudo rm -r /usr/include/ostd + sudo rm -r /usr/include/ogfx sudo rm -r /usr/lib/libostd.so + sudo rm -r /usr/lib/libogfx.so sudo cp -r ./Release/include/ostd /usr/include/ + sudo cp -r ./Release/include/ogfx /usr/include/ sudo cp -r ./Release/lib/libostd.so /usr/lib/ + sudo cp -r ./Release/lib/libogfx.so /usr/lib/ elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then rm -r C:/msys64/ucrt64/include/ostd + rm -r C:/msys64/ucrt64/include/ogfx rm -r C:/msys64/ucrt64/bin/libostd.dll rm -r C:/msys64/ucrt64/lib/libostd.dll.a + rm -r C:/msys64/ucrt64/bin/libogfx.dll + rm -r C:/msys64/ucrt64/lib/libogfx.dll.a cp -r ./Release/include/ostd C:/msys64/ucrt64/include/ + cp -r ./Release/include/ogfx C:/msys64/ucrt64/include/ cp -r ./Release/lib/libostd.dll C:/msys64/ucrt64/bin/ cp -r ./Release/lib/libostd.dll.a C:/msys64/ucrt64/lib/ + cp -r ./Release/lib/libogfx.dll C:/msys64/ucrt64/bin/ + cp -r ./Release/lib/libogfx.dll.a C:/msys64/ucrt64/lib/ fi