diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0833848 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "external/msdf-atlas-gen"] + path = external/msdf-atlas-gen + url = https://github.com/Chlumsky/msdf-atlas-gen.git +[submodule "external/imgui"] + path = external/imgui + url = https://github.com/ocornut/imgui.git +[submodule "external/nfd-extended"] + path = external/nfd-extended + url = https://github.com/btzy/nativefiledialog-extended.git diff --git a/.templates/sdlkConfig.hpp.in b/.templates/sdlkConfig.hpp.in index 795918f..fe94e6a 100644 --- a/.templates/sdlkConfig.hpp.in +++ b/.templates/sdlkConfig.hpp.in @@ -1 +1,3 @@ +#pragma once + #define SDLK_VERSION "@SDLK_VERSION@" diff --git a/CMakeLists.txt b/CMakeLists.txt index c1e12ae..c2ae66e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,63 +8,69 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # SDL2 -find_package(Freetype REQUIRED) find_package(glm CONFIG REQUIRED) find_package(SDL2 CONFIG REQUIRED) find_package(SDL2_image CONFIG REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) find_path(EARCUT_HPP_INCLUDE_DIRS "mapbox/earcut.hpp") - +find_package(Freetype REQUIRED) set(SDLK_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SDLK_IS_MAIN_PROJECT OFF) -if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - set(SDLK_IS_MAIN_PROJECT ON) -endif() +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(SDLK_IS_MAIN_PROJECT ON) +endif () # target names set(SDLK_LIB_NAME sdlk) set(SDLK_LIB_VERSION ${PROJECT_VERSION}) set(SDLK_LIB_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) + +set(SDLK_ENGINE_NAME sdlk_engine) +set(SDLK_ENGINE_VERSION ${PROJECT_VERSION}) +set(SDLK_ENGINE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) + set(SDLK_VERSION ${PROJECT_VERSION}) set(SDLK_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) # list of options -option(BUILD_SHARED_LIBS "Build rcli as a shared library" ON) -option(SDLK_LIB_BUILD "Build sdlk lib" ON) +option(BUILD_SHARED_LIBS "Build sdlk as a shared library" ON) option(SDLK_LIB_INSTALL "Install sdlk lib" OFF) -option(SDLK_BUILD_EXAMPLE "Build sdlk example" OFF) +option(SDLK_BUILD_EXAMPLES "Build sdlk example" ON) + +option(SDLK_ENGINE_BUILD "Build sdlk engine lib" ON) +option(SDLK_ENGINE_INSTALL "Install sdlk engine lib" OFF) # configure build type -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") -endif() +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif () -if(CMAKE_BUILD_TYPE STREQUAL "Release") - set(SDLK_TEST OFF) - set(SDLK_BUILD_EXAMPLE OFF) -endif() +if (CMAKE_BUILD_TYPE STREQUAL "Release") + set(SDLK_TEST OFF) + set(SDLK_BUILD_EXAMPLES OFF) +endif () -if(SDLK_IS_MAIN_PROJECT) - configure_file(${SDLK_SOURCE_DIR}/.templates/PKGBUILD.in - ${SDLK_SOURCE_DIR}/PKGBUILD @ONLY) +if (SDLK_IS_MAIN_PROJECT) + configure_file(${SDLK_SOURCE_DIR}/.templates/PKGBUILD.in + ${SDLK_SOURCE_DIR}/PKGBUILD @ONLY) - configure_file(${SDLK_SOURCE_DIR}/.templates/install.sh.in - ${SDLK_SOURCE_DIR}/install.sh @ONLY) + configure_file(${SDLK_SOURCE_DIR}/.templates/install.sh.in + ${SDLK_SOURCE_DIR}/install.sh @ONLY) - configure_file(${SDLK_SOURCE_DIR}/.templates/create-release.sh.in - ${SDLK_SOURCE_DIR}/create-release.sh @ONLY) + configure_file(${SDLK_SOURCE_DIR}/.templates/create-release.sh.in + ${SDLK_SOURCE_DIR}/create-release.sh @ONLY) - configure_file( - ${SDLK_SOURCE_DIR}/.templates/${SDLK_LIB_NAME}Config.hpp.in - ${SDLK_SOURCE_DIR}/include/${SDLK_LIB_NAME}/${SDLK_LIB_NAME}Config.hpp - @ONLY) -endif() + configure_file( + ${SDLK_SOURCE_DIR}/.templates/${SDLK_LIB_NAME}Config.hpp.in + ${SDLK_SOURCE_DIR}/include/${SDLK_LIB_NAME}/${SDLK_LIB_NAME}Config.hpp + @ONLY) +endif () +add_subdirectory(external/nfd-extended) +add_subdirectory(external/msdf-atlas-gen) add_subdirectory(sources) - -if(SDLK_BUILD_EXAMPLE) - add_subdirectory(examples) -endif() +add_subdirectory(examples) # CPack configuration set(CPACK_PACKAGE_VENDOR "RickaPrincy ") diff --git a/CMakePresets.json b/CMakePresets.json index 89e643f..11ceeb6 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -6,7 +6,9 @@ "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + "MSDF_ATLAS_USE_SKIA": "OFF", + "MSDF_ATLAS_USE_VCPKG": "ON", + "CMAKE_TOOLCHAIN_FILE": "/home/ricka/Apk/vcpkg/scripts/buildsystems/vcpkg.cmake" } }, { @@ -15,7 +17,7 @@ "displayName": "Build with vcpkg (Debug)", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "SDLK_BUILD_EXAMPLE": "ON" + "SDLK_BUILD_EXAMPLES": "ON" } }, { @@ -24,7 +26,7 @@ "displayName": "Build with vcpkg (Release)", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", - "SDLK_BUILD_EXAMPLE": "OFF" + "SDLK_BUILD_EXAMPLES": "OFF" } } ], diff --git a/cmake/sdlk_resources.cmake b/cmake/sdlk_resources.cmake new file mode 100644 index 0000000..e571aef --- /dev/null +++ b/cmake/sdlk_resources.cmake @@ -0,0 +1,38 @@ +function(set_sdlk_resources TARGET) + set(options) + set(oneValueArgs DESTINATION) + set(multiValueArgs FILES DIRECTORIES) + + cmake_parse_arguments(SDLK + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + + if (NOT SDLK_DESTINATION) + set(SDLK_DESTINATION "resources") + endif() + + foreach(file ${SDLK_FILES}) + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${file} + $/${SDLK_DESTINATION}/user/${file} + ) + endforeach() + + foreach(dir ${SDLK_DIRECTORIES}) + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${dir} + $/${SDLK_DESTINATION}/user/${dir} + ) + endforeach() + + add_custom_command(TARGET ${TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${SDLK_ENGINE_RESOURCE_DIR} + $/${SDLK_DESTINATION}/engine + ) +endfunction() \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index be7c554..48298e6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,6 @@ -add_executable(example main.cpp) +add_executable(example main.cpp + ../include/sdlk/core/imgui/imgui_drawer_utils.hpp + ../include/sdlk/extra/nfd_wrapper.hpp) target_include_directories(example PRIVATE ${SDLK_SOURCE_DIR}/include) diff --git a/examples/main.cpp b/examples/main.cpp index ba3d737..377c695 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,60 +1,76 @@ -#include - +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include using namespace sdlk; -static constexpr int WINDOW_WIDTH = 1000; -static constexpr int WINDOW_HEIGHT = 1000; +auto home() -> std::shared_ptr; +auto text_hello_world() -> std::shared_ptr; +auto rectangle_with_texture() -> std::shared_ptr; +auto rectangle_with_uniform_color() -> std::shared_ptr; +auto rectangle_with_vertex_color() -> std::shared_ptr; -auto main(int argc, char** argv) -> int +auto main(const int argc, char** argv) -> int { - app myapp("Hello World", WINDOW_WIDTH, WINDOW_HEIGHT); + app rc_engine("RC_Engine", 1200, 800); + + rc_engine.add_view("home", home()); - auto image = texture::from_file("./resources/assets/images/image.png"); - textured_shape shape( - std::move(polygon(quad::make(image->get_width() / 2, image->get_height() / 2))), - quad::make(image->get_width() / 2, image->get_height() / 2), - image); + rc_engine.add_event_listener( + event_type::key_up, [&](const SDL_Event& event) { std::cout << "Clicked \n"; }); - textured_shape shape2( - std::move(polygon(quad::make(image->get_width() / 2, image->get_height() / 2))), - quad::make(image->get_width() / 2, image->get_height() / 2), - image); + return rc_engine.run("home", argc, argv); +} - shape2.translate({ 40.f, 50.f }); +auto home() -> std::shared_ptr +{ + auto home = std::make_shared(); - colored_shape test(polygon({ - { 0, 0 }, - { 50, 0 }, - { 50, 50 }, - { 0, 50 }, - }), - { 255, 0, 0, 255 }, - true); + home->add_child(rectangle_with_uniform_color()); + home->add_child(text_hello_world()); - auto font = freetype_font::make("./resources/assets/font/arial.ttf"); + return home; +} + +auto text_hello_world() -> std::shared_ptr +{ + auto font = msdf_font::make("./resources/assets/fonts/arial.ttf"); + return std::make_shared(U"Hello World helloƩ", + sdlk2d::text_style{ .m_size = 20.0f, .m_fg_color = color::white() }, + font); +} - text_shape blibli("hello world", font, { 255, 0, 0, 255 }); +auto rectangle_with_uniform_color() -> std::shared_ptr +{ + return std::make_shared( + sdlk2d::type::point{ 0.0f, 0.0f }, 100.0f, 100.0f, color::red()); +} - myapp.add_renderable(&shape); - myapp.add_renderable(&shape2); - myapp.add_renderable(&test); - myapp.add_renderable(&blibli); +auto rectangle_with_vertex_color() -> std::shared_ptr +{ + sdlk2d::type::polygon triangle_with_color = { sdlk2d::type::vertex( + { -1.0f, -1.0f }, color::blue()), + sdlk2d::type::vertex({ 0.0f, 1.0f }, color::red()), + sdlk2d::type::vertex({ 1.0f, -1.0f }, color::green()) }; + return std::make_shared(triangle_with_color); +} - myapp.add_event_listener( - event_type::key_down, [&](const SDL_Event& event) { shape.translate({ 50, 50 }); }); +auto rectangle_with_texture() -> std::shared_ptr +{ + const auto texture = gl_texture::from_file("./resources/assets/images/image.png"); - myapp.add_event_listener(event_type::mouse_button_down, - [&](const SDL_Event& event) { shape.translate({ 100, 100 }); }); + sdlk2d::type::polygon rect_with_uv = { sdlk2d::type::vertex( + { 0.5f, 1.0f }, glm::vec2{ 0.0f, 0.0f }), + sdlk2d::type::vertex({ 1.0f, 1.0f }, glm::vec2{ 1.0f, 0.0f }), + sdlk2d::type::vertex({ 1.0f, 0.5f }, glm::vec2{ 1.0f, 1.0f }), - myapp.add_event_listener( - event_type::mouse_button_up, [&](const SDL_Event&) { shape.scale(1.1f); }); + sdlk2d::type::vertex({ 0.5f, 1.0f }, glm::vec2{ 0.0f, 0.0f }), + sdlk2d::type::vertex({ 1.0f, 0.5f }, glm::vec2{ 1.0f, 1.0f }), + sdlk2d::type::vertex({ 0.5f, 0.5f }, glm::vec2{ 0.0f, 1.0f }) }; - return myapp.run(argc, argv); -} + return std::make_shared(rect_with_uv, texture); +} \ No newline at end of file diff --git a/examples/resources/assets/font/arial.ttf b/examples/resources/assets/fonts/arial.ttf similarity index 100% rename from examples/resources/assets/font/arial.ttf rename to examples/resources/assets/fonts/arial.ttf diff --git a/examples/resources/assets/images/image.png b/examples/resources/assets/images/image.png index ce1cce8..65f3c3f 100644 Binary files a/examples/resources/assets/images/image.png and b/examples/resources/assets/images/image.png differ diff --git a/external/imgui b/external/imgui new file mode 160000 index 0000000..5166bec --- /dev/null +++ b/external/imgui @@ -0,0 +1 @@ +Subproject commit 5166bec5d8293021d7396ab1d623aabe329b0d0a diff --git a/external/msdf-atlas-gen b/external/msdf-atlas-gen new file mode 160000 index 0000000..77804c2 --- /dev/null +++ b/external/msdf-atlas-gen @@ -0,0 +1 @@ +Subproject commit 77804c2f4108f49eb73491cda9296a2afb714d4c diff --git a/external/nfd-extended b/external/nfd-extended new file mode 160000 index 0000000..fc168e8 --- /dev/null +++ b/external/nfd-extended @@ -0,0 +1 @@ +Subproject commit fc168e8605bfa51aaec22ab0c4e46b9de665a437 diff --git a/include/sdlk/core/app.hpp b/include/sdlk/core/app.hpp index 9f752e7..3ada44e 100644 --- a/include/sdlk/core/app.hpp +++ b/include/sdlk/core/app.hpp @@ -1,68 +1,64 @@ #pragma once -#include -#include -#include FT_FREETYPE_H - #include -#include -#include -#include #include -#include +#include +#include #include -#include +#include #include -#include namespace sdlk { + class renderable; struct app_options { - unsigned int fps = 30; - std::string vertex_source = ""; - std::string fragment_source = ""; - SDL_Color background_color = { 0, 0, 0, 255 }; + bool m_vsync = false; + unsigned int m_fps{ 30 }; + color m_background{ color::black() }; }; class app : public observer { - private: + Uint32 _frame_start{ 0 }; app_options _options{}; - Uint32 _frame_start = 0; - unsigned int _frame_delay_ms = 0; + unsigned int _frame_delay_ms{ 0 }; - static FT_Library s_ft_library; - static unsigned int s_window_width, s_window_height; + std::shared_ptr _imgui_wrapper{}; protected: - camera m_camera; - GLuint m_shader_program{}; + std::shared_ptr m_camera{}; + std::shared_ptr m_program{}; + + SDL_Window *p_window{}; + SDL_GLContext m_opengl_context{ nullptr }; - SDL_Window *p_window = nullptr; - std::vector m_childs{}; - SDL_GLContext m_opengl_context = nullptr; + auto handle_event(SDL_Event &event) const -> void; + virtual auto limit_fps() -> void; + app(const std::string &window_title, + const int &width, + const int &height, + const app_options &options, + Uint32 sdl_init_flags); public: - app(std::string window_title, - int width, - int height, - app_options options = {}, - Uint32 sdl_init_flags = SDL_INIT_EVERYTHING); + std::shared_ptr m_view{}; - virtual auto limit_fps() -> void; - auto run(int argc, char **argv) -> int; - auto add_renderable(renderable *child) -> void; + static auto make(const std::string &window_title, + const int &width, + const int &height, + const app_options &options = {}, + Uint32 sdl_init_flags = SDL_INIT_EVERYTHING) -> std::shared_ptr; - [[nodiscard]] auto get_camera() -> camera *; + static auto get() -> std::shared_ptr; - [[nodiscard]] static auto get_width() -> int const; - [[nodiscard]] static auto get_height() -> int const; - [[nodiscard]] static auto get_ft_library() -> FT_Library &; - [[nodiscard]] auto get_event_listener() -> std::shared_ptr const; + auto run(const std::string &default_view, int, char **) -> int; + auto add_view(const std::string &name, const std::shared_ptr &child) const -> void; + auto switch_to(const std::string &name, const std::shared_ptr &context = nullptr) const -> void; - virtual ~app(); + app() = delete; + ~app() override; }; }; // namespace sdlk diff --git a/include/sdlk/core/camera.hpp b/include/sdlk/core/camera.hpp index 36331bb..4803a3f 100644 --- a/include/sdlk/core/camera.hpp +++ b/include/sdlk/core/camera.hpp @@ -1,15 +1,13 @@ #pragma once -#include - -#include -#include -#include +#include namespace sdlk { class camera { + unsigned int _width, _height; + protected: float m_zoom{ 1.0f }; glm::vec2 m_position{ 0.0f }; @@ -17,13 +15,13 @@ namespace sdlk glm::mat4 m_projection{ 1.0f }; glm::mat4 m_view{ 1.0f }; - void recalculate(int width, int height); + auto recalculate(int width, int height) -> void; public: camera(int width, int height); - [[nodiscard]] auto get_zoom() -> float; - [[nodiscard]] auto get_position() -> glm::vec2; + [[nodiscard]] auto get_zoom() const -> float; + [[nodiscard]] auto get_position() const -> glm::vec2; auto set_position(const glm::vec2& pos) -> void; auto translate(const glm::vec2& delta) -> void; @@ -34,11 +32,9 @@ namespace sdlk auto update(int width, int height) -> void; // Must be called after any change - auto load_uniforms(GLuint* shader_program, - std::string view_name = "uView", - std::string proj_name = "uProjection") const -> void const; + auto load_uniforms(const std::shared_ptr& program) const -> void; [[nodiscard]] auto get_projection() const -> const glm::mat4&; [[nodiscard]] auto get_view() const -> const glm::mat4&; }; -} // namespace sdlk +} // namespace sdlk \ No newline at end of file diff --git a/include/sdlk/core/color.hpp b/include/sdlk/core/color.hpp new file mode 100644 index 0000000..a190e01 --- /dev/null +++ b/include/sdlk/core/color.hpp @@ -0,0 +1,60 @@ +// +// Created by ricka on 2026-01-25. +// +#pragma once + +#include + +#include +#include + +namespace sdlk +{ + struct color + { + SDL_Color m_sdl_color{}; + glm::vec4 m_ndc_color{}; + + explicit color(const SDL_Color& color) + { + this->m_sdl_color = color; + this->m_ndc_color = converter::sdl_color_to_ndc(color); + } + + [[nodiscard]] auto ndc() const -> glm::vec4 + { + return this->m_ndc_color; + } + + color() = default; + color(const color&) = default; + color(color&&) noexcept = default; + color& operator=(const color&) = default; + color& operator=(color&&) noexcept = default; + + static color red() + { + return color(SDL_Color{ .r = 255, .g = 0, .b = 0, .a = 255 }); + } + + static color green() + { + return color(SDL_Color{ .r = 0, .g = 255, .b = 0, .a = 255 }); + } + + static color blue() + { + return color(SDL_Color{ .r = 0, .g = 0, .b = 255, .a = 255 }); + } + + static color black() + { + return color(SDL_Color{ .r = 0, .g = 0, .b = 0, .a = 255 }); + } + + static color white() + { + return color(SDL_Color{ .r = 255, .g = 255, .b = 255, .a = 255 }); + } + }; +} // namespace sdlk diff --git a/include/sdlk/core/component.hpp b/include/sdlk/core/component.hpp deleted file mode 100644 index b66c06d..0000000 --- a/include/sdlk/core/component.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace sdlk -{ - class component : public observer, public renderable - { - protected: - renderable *p_renderable = nullptr; - component *p_parent = nullptr; - std::vector m_childs{}; - - auto append_child(component *child) -> void; - - public: - component(class app *parent, renderable *); - component(component *parent, renderable *); - - virtual auto bind() -> void const override; - - virtual auto translate(glm::vec2 pixel_offset) -> void override; - virtual auto scale(float scale) -> void override; - virtual auto rotate(float angle_radians) -> void override; - virtual auto add_translate(glm::vec2 pixel_offset) -> void override; - virtual auto add_scale(float scale) -> void override; - virtual auto add_rotation(float angle_radians) -> void override; - virtual auto set_transformation_model(glm::mat4 transformation_model) -> void override; - - virtual auto render(GLuint *program) -> void override; - }; -} // namespace sdlk diff --git a/include/sdlk/core/components/2d/fonts/msdf_font.hpp b/include/sdlk/core/components/2d/fonts/msdf_font.hpp new file mode 100644 index 0000000..d8ddb14 --- /dev/null +++ b/include/sdlk/core/components/2d/fonts/msdf_font.hpp @@ -0,0 +1,92 @@ +// +// Created by ricka on 2026-01-18. +// + +#pragma once + +#include + +#include +#include +#include + +#define MIN_SIDE 256 + +namespace sdlk +{ + using namespace msdf_atlas; + + using charset = Charset; + using font_geometry = FontGeometry; + using font_metrics = msdfgen::FontMetrics; + using glyph_geometry = GlyphGeometry; + using font_handle = msdfgen::FontHandle; + + using bitmap = msdfgen::Bitmap; + using bitmap_ref = msdfgen::BitmapRef; + using bitmap_const_ref = msdfgen::BitmapConstRef; + using msdf_dynamic_atlas = + DynamicAtlas>>; + + struct msdf_font_conf + { + const float m_pixel_range = 2; + const float m_glyph_scale = 32.0; + const float m_miter_limit = 1.0; + const float m_max_corner_angle = 3.0; + }; + + struct glyph_storage + { + font_handle *m_font_handle{}; + std::vector m_data{}; + font_geometry m_font_geometry{ &m_data }; + std::unordered_map m_glyphs{}; + + [[nodiscard]] auto size() const -> size_t; + [[nodiscard]] auto is_loaded(const char32_t &character) const -> bool; + [[nodiscard]] auto slice(size_t start_index) -> glyph_geometry *; + [[nodiscard]] auto get(const char32_t &character) + -> std::optional>; + + auto invalidate_glyphs_map() -> void; + auto load(const charset &charset) -> void; + + ~glyph_storage(); + }; + + class msdf_font final + { + msdf_font_conf m_conf{}; + bitmap_const_ref m_bitmap{}; + msdf_dynamic_atlas m_atlas{ 256 }; + + bool m_texture_initialized{ false }; + std::shared_ptr m_texture{}; + + glyph_storage m_glyph_storage{ nullptr }; + + auto update_texture(msdf_dynamic_atlas::ChangeFlags flags) -> void; + auto add_to_atlas(size_t start_index) -> msdf_dynamic_atlas::ChangeFlags; + + public: + explicit msdf_font(const std::string &font_path, msdf_font_conf &&conf = {}); + + static auto make(const std::string &font_path) -> std::shared_ptr; + + [[nodiscard]] auto get(const char32_t &c) + -> std::optional>; + + [[nodiscard]] auto get_conf() const -> msdf_font_conf; + [[nodiscard]] auto get_bitmap() const -> bitmap_const_ref; + [[nodiscard]] auto is_loaded(const char32_t &c) const -> bool; + [[nodiscard]] auto get_texture() const -> std::shared_ptr; + [[nodiscard]] auto get_metrics() const -> font_metrics; + + auto load(const Charset &charset) -> void; + auto load(const char32_t &character) -> void; + auto load(const std::u32string &text) -> void; + + ~msdf_font() = default; + }; +} // namespace sdlk diff --git a/include/sdlk/core/components/2d/shape/polygon_shape.hpp b/include/sdlk/core/components/2d/shape/polygon_shape.hpp new file mode 100644 index 0000000..b9673d3 --- /dev/null +++ b/include/sdlk/core/components/2d/shape/polygon_shape.hpp @@ -0,0 +1,31 @@ +// +// Created by ricka on 2025-12-31. +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace sdlk2d +{ + class polygon_shape : public sdlk::renderable + { + shape_style m_style{}; + + public: + explicit polygon_shape(const type::polygon &polygon); + explicit polygon_shape(const type::points &points, const sdlk::color &uniform_color); + explicit polygon_shape(const type::polygon &polygon, const sdlk::color &uniform_color); + + explicit polygon_shape(const type::polygon &polygon, + const std::shared_ptr &texture); + + auto render(const std::shared_ptr &program) -> void override; + + ~polygon_shape() override = default; + }; +} // namespace sdlk2d \ No newline at end of file diff --git a/include/sdlk/core/components/2d/shape/rectangle_shape.hpp b/include/sdlk/core/components/2d/shape/rectangle_shape.hpp new file mode 100644 index 0000000..af29427 --- /dev/null +++ b/include/sdlk/core/components/2d/shape/rectangle_shape.hpp @@ -0,0 +1,21 @@ +// +// Created by ricka on 2026-01-18. +// + +#pragma once + +#include + +namespace sdlk2d +{ + class rectangle_shape : public polygon_shape + { + public: + rectangle_shape(const type::point &origin, + const float &width, + const float &height, + const sdlk::color &color); + + ~rectangle_shape() override = default; + }; +} // namespace sdlk2d diff --git a/include/sdlk/core/components/2d/shape/shape_style.hpp b/include/sdlk/core/components/2d/shape/shape_style.hpp new file mode 100644 index 0000000..0749501 --- /dev/null +++ b/include/sdlk/core/components/2d/shape/shape_style.hpp @@ -0,0 +1,51 @@ +// +// Created by ricka on 2026-01-18. +// + +#pragma once + +#include +#include +#include +#include + +namespace sdlk2d +{ + struct text_style + { + float m_size{ 12.0f }; + sdlk::color m_fg_color{ sdlk::color::white() }; + sdlk::color m_bg_color{ SDL_Color{ .r = 0, .g = 0, .b = 0, .a = 0 } }; + }; + + struct shape_style + { + enum class type + { + uniform, + vertex, + texture, + }; + + type m_type{ type::uniform }; + std::shared_ptr m_texture{}; + std::optional m_uniform_color{}; + + explicit shape_style() + { + this->m_type = type::vertex; + } + + explicit shape_style(const sdlk::color &color) + { + this->m_type = type::uniform; + this->m_uniform_color = color; + } + + explicit shape_style(const std::shared_ptr &texture) + { + this->m_type = type::texture; + this->m_texture = texture; + } + }; +} // namespace sdlk2d \ No newline at end of file diff --git a/include/sdlk/core/components/2d/shape/text_shape.hpp b/include/sdlk/core/components/2d/shape/text_shape.hpp new file mode 100644 index 0000000..a02175d --- /dev/null +++ b/include/sdlk/core/components/2d/shape/text_shape.hpp @@ -0,0 +1,39 @@ +// +// Created by ricka on 2026-01-24. +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace sdlk2d +{ + struct char_vertex + { + glm::vec3 m_pos; + glm::vec2 m_uv; + }; + + class text_shape : public sdlk::renderable + { + std::vector m_vertices{}; + + protected: + text_style m_style{}; + std::u32string m_text{}; + std::shared_ptr m_font{}; + + auto update_vertices() -> void; + + public: + explicit text_shape(std::u32string text, + const text_style &text_style, + const std::shared_ptr &font); + + auto render(const std::shared_ptr &program) -> void override; + }; +} // namespace sdlk2d \ No newline at end of file diff --git a/include/sdlk/core/components/2d/type.hpp b/include/sdlk/core/components/2d/type.hpp new file mode 100644 index 0000000..8b3a998 --- /dev/null +++ b/include/sdlk/core/components/2d/type.hpp @@ -0,0 +1,41 @@ +// +// Created by ricka on 2025-12-31. +// + +#pragma once + +#include +#include +#include + +namespace sdlk2d::type +{ + using point = glm::vec2; + + struct vertex + { + point m_position{}; + std::optional m_uv{}; + std::optional m_color{}; + + explicit vertex(const point &position) + { + this->m_position = position; + } + + explicit vertex(const point &position, const point &uv) + { + this->m_uv = uv; + this->m_position = position; + } + + explicit vertex(const point &position, const sdlk::color &color) + { + this->m_color = color; + this->m_position = position; + } + }; + + using polygon = std::vector; + using points = std::vector; +} // namespace sdlk2d::type diff --git a/include/sdlk/core/components/component.hpp b/include/sdlk/core/components/component.hpp new file mode 100644 index 0000000..734a0d0 --- /dev/null +++ b/include/sdlk/core/components/component.hpp @@ -0,0 +1,28 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include +#include +#include +#include + +namespace sdlk +{ + class component : public renderable, public observer + { + protected: + std::vector> m_childs{}; + + public: + explicit component(std::vector> childs = {}); + + auto add_child(const std::shared_ptr &child) -> void; + + auto render(const std::shared_ptr &program) -> void override; + + ~component() override = default; + }; +} // namespace sdlk diff --git a/include/sdlk/core/components/multiple_view.hpp b/include/sdlk/core/components/multiple_view.hpp new file mode 100644 index 0000000..56bcc70 --- /dev/null +++ b/include/sdlk/core/components/multiple_view.hpp @@ -0,0 +1,32 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include +#include +#include + +namespace sdlk +{ + class multiple_view : public renderable + { + protected: + std::shared_ptr m_current{}; + std::map> m_childs{}; + + public: + explicit multiple_view( + const std::map> &childs = {}); + + [[nodiscard]] auto get_current() const -> std::shared_ptr; + + auto add_view(const std::string &name, std::shared_ptr child) -> void; + auto switch_to(const std::string &name, const std::shared_ptr &ctx = nullptr) -> void; + + auto render(const std::shared_ptr &program) -> void override; + + ~multiple_view() override = default; + }; +} // namespace sdlk diff --git a/include/sdlk/core/components/renderable.hpp b/include/sdlk/core/components/renderable.hpp new file mode 100644 index 0000000..ff6d905 --- /dev/null +++ b/include/sdlk/core/components/renderable.hpp @@ -0,0 +1,86 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sdlk +{ + struct renderable_context + { + std::unordered_map props{}; + + template + auto get(const std::string& key) -> std::optional + { + if (has(key)) + { + return std::any_cast(props[key]); + } + + return std::nullopt; + } + + template + auto set(const std::string &key, T value) + { + this->props[key] = value; + } + + auto has(const std::string &key) -> bool + { + return this->props.find(key) != this->props.end(); + } + + renderable_context() = default; + }; + + class renderable + { + protected: + std::shared_ptr m_geometry{}; + std::shared_ptr m_transform{std::make_shared()}; + std::shared_ptr m_context{std::make_shared()}; + + public: + renderable() = default; + + explicit renderable(const std::shared_ptr &geometry); + + virtual auto render(const std::shared_ptr &program) -> void; + + auto get_context() -> std::shared_ptr; + auto set_context(const std::shared_ptr &context) -> void; + + template + auto set_context(const std::string name, const T &value) -> void + { + if (!this->m_context) + { + this->m_context = std::make_shared(); + } + this->m_context->set(name, value); + } + + template + auto get_context(const std::string &name) -> std::optional + { + if (!this->m_context) + { + return std::nullopt; + } + + return this->m_context->get(name); + } + + virtual ~renderable() = default; + }; +} \ No newline at end of file diff --git a/include/sdlk/core/converter.hpp b/include/sdlk/core/converter.hpp index f4176a0..2941a69 100644 --- a/include/sdlk/core/converter.hpp +++ b/include/sdlk/core/converter.hpp @@ -1,14 +1,9 @@ #pragma once #include - -#include -#include -#include +#include namespace sdlk::converter { - auto sdl_color_to_ndc(SDL_Color color) -> std::array; - auto pixel_uv_to_ndc(std::vector pixels, int texture_width, int texture_height) - -> std::vector; + auto sdl_color_to_ndc(const SDL_Color &color) -> glm::vec4; } // namespace sdlk::converter diff --git a/include/sdlk/core/events/event_listener.hpp b/include/sdlk/core/events/event_listener.hpp index 775d8dd..30c461a 100644 --- a/include/sdlk/core/events/event_listener.hpp +++ b/include/sdlk/core/events/event_listener.hpp @@ -3,22 +3,27 @@ #include #include +#include #include namespace sdlk { class event_listener { - private: - std::map> m_event_listeners; + protected: + std::map> m_event_listeners{}; public: event_listener() = default; + virtual ~event_listener() = default; + virtual void notify_event(const SDL_Event &event); static auto get_event_type_value(const SDL_Event &event) -> event_type; + [[nodiscard]] static auto instance() -> std::shared_ptr; + friend class observer; }; } // namespace sdlk diff --git a/include/sdlk/core/events/observer.hpp b/include/sdlk/core/events/observer.hpp index 7925e80..356a952 100644 --- a/include/sdlk/core/events/observer.hpp +++ b/include/sdlk/core/events/observer.hpp @@ -1,22 +1,18 @@ #pragma once -#include -#include #include namespace sdlk { class observer { - protected: - std::shared_ptr m_event_listener = nullptr; - public: - observer(std::shared_ptr event_listener); + virtual ~observer() = default; + explicit observer() = default; - virtual void add_event_listener(event_type event_type, + virtual auto add_event_listener(event_type event_type, event_callback callback, - bool stop_propagation = false); + bool stop_propagation = false) -> void; friend class app; }; diff --git a/include/sdlk/core/events/types.hpp b/include/sdlk/core/events/types.hpp index 8fba2cd..a0d9210 100644 --- a/include/sdlk/core/events/types.hpp +++ b/include/sdlk/core/events/types.hpp @@ -1,8 +1,6 @@ #pragma once #include -#include - #include #include diff --git a/include/sdlk/core/freetype_font.hpp b/include/sdlk/core/freetype_font.hpp deleted file mode 100644 index 377d538..0000000 --- a/include/sdlk/core/freetype_font.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include FT_FREETYPE_H - -namespace sdlk -{ - - struct character - { - GLuint texture_id; // ID handle of the glyph texture - glm::ivec2 size; // Size of glyph - glm::ivec2 bearing; // Offset from baseline to left/top of glyph - unsigned int advance; // Offset to advance to next glyph - }; - - class freetype_font - { - private: - int _ascender{}; - std::map _characters{}; - - protected: - FT_Face m_face; - - public: - [[nodiscard]] auto get_face() -> FT_Face &; - [[nodiscard]] auto get_character(const char &c) -> character &; - [[nodiscard]] auto get_ascender() const -> int; - [[nodiscard]] static auto make(std::string font_path, - int font_size = 20, - int char_to_load = 128) -> std::shared_ptr; - - freetype_font(std::string font_path, int font_size = 20, int char_to_load = 128); - virtual ~freetype_font(); - }; -} // namespace sdlk diff --git a/include/sdlk/core/geometry.hpp b/include/sdlk/core/geometry.hpp new file mode 100644 index 0000000..f5d6ecb --- /dev/null +++ b/include/sdlk/core/geometry.hpp @@ -0,0 +1,31 @@ +// +// Created by ricka on 2025-12-31. +// + +#pragma once + +#include +#include + +namespace sdlk +{ + class geometry + { + protected: + std::shared_ptr m_vbo{}; + std::shared_ptr m_ebo{}; + std::shared_ptr m_vao{}; + bool m_is_indexed{ false }; + + public: + geometry() = default; + geometry(std::shared_ptr vao, + std::shared_ptr vbo, + std::shared_ptr ebo); + + auto render() const -> void; + + [[nodiscard]] auto get_vbo() const -> std::shared_ptr; + [[nodiscard]] auto get_vao() const -> std::shared_ptr; + }; +} // namespace sdlk diff --git a/include/sdlk/core/gl/gl_buffer.hpp b/include/sdlk/core/gl/gl_buffer.hpp new file mode 100644 index 0000000..f57385f --- /dev/null +++ b/include/sdlk/core/gl/gl_buffer.hpp @@ -0,0 +1,37 @@ +// +// Created by ricka on 2025-12-31. +// + +#pragma once + +#include + +#include +#include + +namespace sdlk +{ + class gl_buffer + { + GLuint m_id{ 0 }; + GLenum m_target{}; + std::size_t m_count{ 0 }; + std::size_t m_element_size{ 0 }; + + public: + explicit gl_buffer(GLenum target); + + virtual ~gl_buffer(); + + auto bind() const -> void; + auto unbind() const -> void; + + auto set_data(const void* data, GLenum usage, std::size_t count, std::size_t element_size) + -> void; + + [[nodiscard]] static auto make(GLenum target) -> std::shared_ptr; + + [[nodiscard]] auto id() -> GLuint&; + [[nodiscard]] auto get_count() const -> std::size_t; + }; +} // namespace sdlk diff --git a/include/sdlk/core/gl/gl_program.hpp b/include/sdlk/core/gl/gl_program.hpp new file mode 100644 index 0000000..fd6aeb4 --- /dev/null +++ b/include/sdlk/core/gl/gl_program.hpp @@ -0,0 +1,35 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include +#include +#include +#include + +namespace sdlk +{ + class gl_program + { + protected: + GLuint m_id{}; + std::shared_ptr m_uniform{}; + + public: + explicit gl_program(const std::shared_ptr &vertex_shader, + const std::shared_ptr &fragment_shader); + + auto use() -> void; + + [[nodiscard]] auto get_id() const -> GLuint; + [[nodiscard]] auto get_uniform() const -> std::shared_ptr; + + [[nodiscard]] static auto from_files(const std::string &vertex_path, + const std::string &fragment_path) -> std::shared_ptr; + + virtual ~gl_program(); + }; + +} // namespace sdlk \ No newline at end of file diff --git a/include/sdlk/core/gl/gl_shader.hpp b/include/sdlk/core/gl/gl_shader.hpp new file mode 100644 index 0000000..2cc3880 --- /dev/null +++ b/include/sdlk/core/gl/gl_shader.hpp @@ -0,0 +1,28 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include + +#include +#include + +namespace sdlk +{ + class gl_shader + { + protected: + GLuint m_id{}; + public: + explicit gl_shader(GLenum type, const std::string& source); + + [[nodiscard]] auto get_id() const -> GLuint; + + virtual ~gl_shader(); + + [[nodiscard]] static auto from_file(GLenum type, const std::string &file_path) -> std::shared_ptr; + }; + +} // namespace sdlk diff --git a/include/sdlk/core/texture.hpp b/include/sdlk/core/gl/gl_texture.hpp similarity index 64% rename from include/sdlk/core/texture.hpp rename to include/sdlk/core/gl/gl_texture.hpp index 6dbbc1d..ad604fd 100644 --- a/include/sdlk/core/texture.hpp +++ b/include/sdlk/core/gl/gl_texture.hpp @@ -8,24 +8,26 @@ namespace sdlk { - class texture + class gl_texture { - private: GLuint m_id{ 0 }; int m_width{}, m_height{}; public: - texture(GLuint id, int width, int height); + gl_texture() = default; + gl_texture(GLuint id, int width, int height); auto bind(GLuint unit = 0) const -> void; + [[nodiscard]] auto get_id() -> GLuint&; [[nodiscard]] auto get_width() const -> int; [[nodiscard]] auto get_height() const -> int; + [[nodiscard]] auto get_ratio() const -> float; - ~texture(); + ~gl_texture(); - static auto from_file(const std::string& path) -> std::shared_ptr; - static auto from_surface(SDL_Surface* surface) -> std::shared_ptr; + static auto from_file(const std::string& path) -> std::shared_ptr; + static auto from_surface(SDL_Surface* surface) -> std::shared_ptr; }; -} // namespace sdlk +} // namespace sdlk2d \ No newline at end of file diff --git a/include/sdlk/core/gl/gl_uniform.hpp b/include/sdlk/core/gl/gl_uniform.hpp new file mode 100644 index 0000000..16bea5f --- /dev/null +++ b/include/sdlk/core/gl/gl_uniform.hpp @@ -0,0 +1,45 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include +#include + +#include +#include + +namespace sdlk +{ + class gl_program; + + class gl_uniform + { + protected: + gl_program *m_program{}; + + public: + explicit gl_uniform(gl_program *program); + + [[nodiscard]] auto get_loc(const std::string &name) const -> GLint; + + // ---- scalar ---- + auto set(const std::string &name, int value) const -> void; + auto set(const std::string &name, float value) const -> void; + auto set(const std::string &name, bool value) const -> void; + + // ---- vectors ---- + auto set(const std::string &name, const glm::vec2 &v) const -> void; + auto set(const std::string &name, const glm::vec3 &v) const -> void; + auto set(const std::string &name, const glm::vec4 &v) const -> void; + + // ---- matrices ---- + auto set(const std::string &name, const glm::mat3 &m) const -> void; + auto set(const std::string &name, const glm::mat4 &m) const -> void; + + [[nodiscard]] static auto from(gl_program *program) -> std::shared_ptr; + + virtual ~gl_uniform() = default; + }; +} // namespace sdlk diff --git a/include/sdlk/core/gl/gl_vertex_array.hpp b/include/sdlk/core/gl/gl_vertex_array.hpp new file mode 100644 index 0000000..d0588b3 --- /dev/null +++ b/include/sdlk/core/gl/gl_vertex_array.hpp @@ -0,0 +1,36 @@ +// +// Created by ricka on 2025-12-31. +// + +#pragma once + +#include + +#include + +namespace sdlk +{ + class gl_vertex_array + { + GLuint m_id{ 0 }; + + public: + gl_vertex_array(); + + virtual ~gl_vertex_array(); + + auto bind() const -> void; + auto unbind() const -> void; + + auto enable_attrib(GLuint index) -> void; + auto attrib_pointer(GLuint index, + GLint size, + GLenum type, + GLboolean normalized, + GLsizei stride, + const void* offset) -> void; + + [[nodiscard]] auto id() -> GLuint&; + [[nodiscard]] static auto make() -> std::shared_ptr; + }; +} // namespace sdlk \ No newline at end of file diff --git a/include/sdlk/core/imgui/imgui_drawer_utils.hpp b/include/sdlk/core/imgui/imgui_drawer_utils.hpp new file mode 100644 index 0000000..70fd3ec --- /dev/null +++ b/include/sdlk/core/imgui/imgui_drawer_utils.hpp @@ -0,0 +1,20 @@ +// +// Created by ricka on 2026-02-06. +// + +#pragma once + +#include + +#include +#include +#include + +namespace sdlk::imgui_drawer_utils +{ + auto centered_window(const std::function &size_getter, + const std::function &renderable) -> void; + + auto spacing(int iteration = 1) -> void; + auto image(const std::shared_ptr &texture, const ImVec2 &size) -> void; +} // namespace sdlk::imgui_drawer_utils diff --git a/include/sdlk/core/imgui/imgui_renderable.hpp b/include/sdlk/core/imgui/imgui_renderable.hpp new file mode 100644 index 0000000..047ad1e --- /dev/null +++ b/include/sdlk/core/imgui/imgui_renderable.hpp @@ -0,0 +1,23 @@ +// +// Created by ricka on 2026-02-04. +// + +#pragma once + +#include +#include + +namespace sdlk +{ + class imgui_renderable : public renderable + { + std::function _render; + + public: + explicit imgui_renderable(const std::function &render); + + auto render(const std::shared_ptr &program) -> void override; + + static auto make(const std::function &render) -> std::shared_ptr; + }; +} // namespace sdlk diff --git a/include/sdlk/core/opengl_utils.hpp b/include/sdlk/core/opengl_utils.hpp deleted file mode 100644 index c8ecd7b..0000000 --- a/include/sdlk/core/opengl_utils.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#include - -namespace sdlk -{ - [[nodiscard]] auto get_uniform_loc(GLuint *program, std::string uniform_name) -> GLuint; - [[nodiscard]] auto compile_shader(GLenum type, std::string source) -> GLuint; - [[nodiscard]] auto create_shader_program(std::string vertex_source, std::string fragment_source) - -> GLuint; -}; // namespace sdlk diff --git a/include/sdlk/core/quad.hpp b/include/sdlk/core/quad.hpp deleted file mode 100644 index 96e3190..0000000 --- a/include/sdlk/core/quad.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace sdlk::quad -{ - auto make(int width, int height) -> std::vector; - - auto indices() -> std::vector; -} // namespace sdlk::quad diff --git a/include/sdlk/core/renderable.hpp b/include/sdlk/core/renderable.hpp deleted file mode 100644 index f9d76f4..0000000 --- a/include/sdlk/core/renderable.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace sdlk -{ - class renderable - { - protected: - transformation m_transformation{}; - - public: - virtual auto render(GLuint *program) -> void; - - virtual auto bind() -> void const; - - virtual auto translate(glm::vec2 pixel_offset) -> void; - virtual auto scale(float scale) -> void; - virtual auto rotate(float angle_radians) -> void; - virtual auto add_translate(glm::vec2 pixel_offset) -> void; - virtual auto add_scale(float scale) -> void; - virtual auto add_rotation(float angle_radians) -> void; - virtual auto set_transformation_model(glm::mat4 transformation_model) -> void; - - [[nodiscard]] virtual auto get_scale() -> float; - [[nodiscard]] virtual auto get_rotation() -> float; - [[nodiscard]] virtual auto get_translation() -> glm::vec2; - - renderable() = default; - virtual ~renderable() = default; - }; -} // namespace sdlk diff --git a/include/sdlk/core/resource.hpp b/include/sdlk/core/resource.hpp new file mode 100644 index 0000000..ed5d86f --- /dev/null +++ b/include/sdlk/core/resource.hpp @@ -0,0 +1,14 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +namespace sdlk +{ + class resource + { + public: + resource() = default; + }; +} \ No newline at end of file diff --git a/include/sdlk/core/shape.hpp b/include/sdlk/core/shape.hpp deleted file mode 100644 index c2bf931..0000000 --- a/include/sdlk/core/shape.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sdlk -{ - - struct shape_config - { - bool use_vec_color = false; - bool use_texture = false; - bool use_text_rendering = false; - }; - - class shape : public renderable - { - protected: - std::array m_ndc_color{}; - SDL_Color m_color{}; - polygon m_polygon{}; - shape_config m_config{}; - unsigned int m_indices_count{}; - - GLuint m_vao{}, m_vbo{}, m_ebo{}; - - shape(polygon polygon, std::vector indices, shape_config config = {}); - - public: - auto set_color(SDL_Color color) -> void; - - [[nodiscard]] auto get_color() -> SDL_Color; - - virtual auto bind() -> void const override; - - virtual auto render(GLuint *program) -> void override; - - virtual ~shape(); - }; - - class text_shape : public shape - { - protected: - std::string m_text{}; - std::shared_ptr m_font = nullptr; - - public: - auto set_text(std::string text) -> void; - [[nodiscard]] auto get_text() -> std::string; - - virtual auto bind() -> void const override; - virtual auto render(GLuint *program) -> void override; - - text_shape(std::string text, std::shared_ptr font, SDL_Color color); - ~text_shape() = default; - }; - - class colored_shape : public shape - { - public: - colored_shape(polygon geometry, SDL_Color color, bool use_vec_color = false); - colored_shape(polygon geometry, - SDL_Color color, - std::vector indices, - bool use_vec_color = false); - - colored_shape(polygon geometry, std::vector color); - colored_shape(polygon geometry, - std::vector colors, - std::vector indices); - - virtual ~colored_shape() = default; - }; - - class textured_shape : public shape - { - protected: - std::shared_ptr m_texture = nullptr; - - public: - textured_shape(polygon geometry, std::vector uv, std::shared_ptr texture); - textured_shape(polygon geometry, - std::vector uv, - std::shared_ptr texture, - std::vector indices); - - virtual auto render(GLuint *program) -> void override; - virtual auto bind() -> const void override; - - virtual ~textured_shape() = default; - }; - - class image_shape : public textured_shape - { - public: - image_shape(std::string path, int width, int height); - image_shape(std::string path, int width, int height, std::vector uv); - - virtual ~image_shape() = default; - }; -} // namespace sdlk diff --git a/include/sdlk/core/transform.hpp b/include/sdlk/core/transform.hpp new file mode 100644 index 0000000..a7a49f5 --- /dev/null +++ b/include/sdlk/core/transform.hpp @@ -0,0 +1,41 @@ +// +// Created by ricka on 2025-12-31. +// + +#pragma once + +#include +#include + +namespace sdlk +{ + class transform + { + protected: + float m_scale{ 1.0f }; + float m_rotation_degrees{ 0.0f }; + glm::vec3 m_translation{ glm::vec3(0.0f) }; + glm::mat4 m_model{ glm::mat4(1.0f) }; + + auto compose() -> void; + + public: + transform() = default; + + auto set_scale(float scale) -> void; + auto set_model(const glm::mat4 &model) -> void; + auto set_translation(glm::vec2 offset) -> void; + auto set_rotation(float angle_degrees) -> void; + + auto add_scale(float scale) -> void; + auto add_translation(glm::vec2 offset) -> void; + auto add_rotation(float angle_degrees) -> void; + + [[nodiscard]] virtual auto get_model() const -> glm::mat4; + [[nodiscard]] virtual auto get_scale() const -> float; + [[nodiscard]] virtual auto get_rotation() const -> float; + [[nodiscard]] virtual auto get_translation() const -> glm::vec3; + + virtual ~transform() = default; + }; +} // namespace sdlk \ No newline at end of file diff --git a/include/sdlk/core/transformation.hpp b/include/sdlk/core/transformation.hpp deleted file mode 100644 index 97959e7..0000000 --- a/include/sdlk/core/transformation.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -namespace sdlk -{ - class transformation - { - private: - float m_scale{ 1.0f }; - float m_rotation_radians{ 0.0f }; - glm::vec3 m_translation{ glm::vec3(0.0f) }; - glm::mat4 m_model{ glm::mat4(1.0f) }; - - auto compose() -> void; - - public: - transformation() = default; - - auto translate(glm::vec2 offset) -> void; - auto scale(float scale) -> void; - auto rotate(float angle_radians) -> void; - - auto add_translate(glm::vec2 offset) -> void; - auto add_scale(float scale) -> void; - auto add_rotation(float angle_radians) -> void; - - [[nodiscard]] virtual auto get_scale() -> float; - [[nodiscard]] virtual auto get_rotation() -> float; - [[nodiscard]] virtual auto get_translation() -> glm::vec2; - - auto set_model(glm::mat4 model) -> void; - - auto load_uniforms(GLuint *shader_program, std::string model_name = "uModel") const - -> void const; - }; -} // namespace sdlk diff --git a/include/sdlk/core/types.hpp b/include/sdlk/core/types.hpp deleted file mode 100644 index f2fa4dd..0000000 --- a/include/sdlk/core/types.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -namespace sdlk -{ - using point = std::array; - using ring = std::vector; - - class polygon - { - private: - std::vector m_data{}; - - public: - polygon() = default; - explicit polygon(ring outer, std::vector holes = {}); - - [[nodiscard]] auto data() const -> const std::vector &; - [[nodiscard]] auto flattened() const -> std::vector; - }; -} // namespace sdlk diff --git a/include/sdlk/extra/nfd_wrapper.hpp b/include/sdlk/extra/nfd_wrapper.hpp new file mode 100644 index 0000000..c5eb1c6 --- /dev/null +++ b/include/sdlk/extra/nfd_wrapper.hpp @@ -0,0 +1,52 @@ +// +// Created by ricka on 2026-02-07. +// + +#pragma once + +#include +#include +#include +#include + +namespace sdlk +{ + struct nfd_filter + { + std::string m_name{}; + std::string m_extensions{}; + }; + + struct nfd_open_dialog_result + { + enum class type + { + canceled, + success, + error + }; + + type m_type{ type::canceled }; + std::optional m_path{}; + + explicit nfd_open_dialog_result(type type, + const std::optional &path = std::nullopt) + { + this->m_path = path; + this->m_type = type; + } + }; + + class nfd_wrapper + { + public: + explicit nfd_wrapper(); + + virtual ~nfd_wrapper(); + + [[nodiscard]] static auto instance() -> std::shared_ptr; + [[nodiscard]] auto open_file_dialog(const std::vector &filters) + -> nfd_open_dialog_result; + [[nodiscard]] auto open_folder_dialog() -> nfd_open_dialog_result; + }; +} // namespace sdlk diff --git a/include/sdlk/sdlkConfig.hpp b/include/sdlk/sdlkConfig.hpp index a023c02..326fc37 100644 --- a/include/sdlk/sdlkConfig.hpp +++ b/include/sdlk/sdlkConfig.hpp @@ -1 +1,3 @@ +#pragma once + #define SDLK_VERSION "0.0.4" diff --git a/sources/CMakeLists.txt b/sources/CMakeLists.txt index 56d3242..666c442 100644 --- a/sources/CMakeLists.txt +++ b/sources/CMakeLists.txt @@ -1,101 +1,5 @@ -set(LIB_SOURCES - core/events/event_listener.cpp - core/events/observer.cpp - core/glad.cpp - core/quad.cpp - core/renderable.cpp - core/types.cpp - core/transformation.cpp - core/opengl_utils.cpp - core/static_resource.cpp - core/converter.cpp - core/camera.cpp - core/app.cpp - core/component.cpp - core/shape.cpp - core/texture.cpp - core/image_shape.cpp - core/text_shape.cpp - core/textured_shape.cpp - core/freetype_font.cpp - core/colored_shape.cpp -) +add_subdirectory(lib) -# Create the library -if(BUILD_SHARED_LIBS) - add_library(${SDLK_LIB_NAME} SHARED ${LIB_SOURCES}) -else() - add_library(${SDLK_LIB_NAME} STATIC ${LIB_SOURCES}) -endif() - -file(COPY core/shaders DESTINATION ${CMAKE_BINARY_DIR}/bin/resources) - -# Set include directories for the library -target_include_directories( - ${SDLK_LIB_NAME} PUBLIC $) - -target_include_directories(${SDLK_LIB_NAME} PRIVATE $ - $,SDL2::SDL2,SDL2::SDL2-static> -) - -target_link_libraries(${SDLK_LIB_NAME} PUBLIC glm::glm-header-only) - -target_link_libraries(${SDLK_LIB_NAME} PUBLIC Freetype::Freetype) - -target_link_libraries(${SDLK_LIB_NAME} PUBLIC $,SDL2_image::SDL2_image,SDL2_image::SDL2_image-static>) - -# set properties -set_target_properties( - ${SDLK_LIB_NAME} - PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" - SOVERSION ${SDLK_LIB_VERSION_MAJOR} - VERSION ${SDLK_LIB_VERSION}) - -# lib install configuration -if(SDLK_LIB_INSTALL) - include(CMakePackageConfigHelpers) - write_basic_package_version_file( - ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Version.cmake - VERSION ${SDLK_VERSION} - COMPATIBILITY SameMajorVersion) - - include(GNUInstallDirs) - install(DIRECTORY ${SDLK_SOURCE_DIR}/include/${SDLK_LIB_NAME} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - install( - TARGETS ${SDLK_LIB_NAME} - EXPORT ${SDLK_LIB_NAME}Targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development) - - # Install the Config and ConfigVersion files - install( - EXPORT ${SDLK_LIB_NAME}Targets - FILE ${SDLK_LIB_NAME}Targets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDLK_LIB_NAME}) - - # Configure the Config file - configure_package_config_file( - ${SDLK_SOURCE_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake.in - ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDLK_LIB_NAME} - PATH_VARS SDLK_SOURCE_DIR - ) - - configure_file(${SDLK_SOURCE_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake.in - ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake @ONLY) - - # Install the Config, ConfigVersion, and custom Find module files - install(FILES ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake - ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Version.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDLK_LIB_NAME}) - -endif() +if(SDLK_ENGINE_BUILD) + add_subdirectory(engine) +endif() \ No newline at end of file diff --git a/sources/core/app.cpp b/sources/core/app.cpp deleted file mode 100644 index c35cfe7..0000000 --- a/sources/core/app.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "static_resource.hpp" - -namespace sdlk -{ - unsigned int app::s_window_width = 0; - unsigned int app::s_window_height = 0; - FT_Library app::s_ft_library = 0; - - // TO handle ctrl + c or something else that can stop the application - static bool is_running = true; - static void signal_handler(int signal) - { - is_running = false; - } - - auto app::run(int argc, char **argv) -> int - { - std::signal(SIGINT, signal_handler); - SDL_Event event; - - auto ndc_background_color = converter::sdl_color_to_ndc(this->_options.background_color); - - glClearColor(ndc_background_color[0], - ndc_background_color[1], - ndc_background_color[2], - ndc_background_color[3]); - - try - { - while (is_running) - { - // Event - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: is_running = false; break; - default: - if (event.type == SDL_WINDOWEVENT && - event.window.event == SDL_WINDOWEVENT_RESIZED) - { - app::s_window_width = event.window.data1; - app::s_window_height = event.window.data2; - glViewport(0, 0, event.window.data1, event.window.data2); - } - - this->m_event_listener->notify_event(event); - break; - } - } - - glUseProgram(this->m_shader_program); - - // Update - glClear(GL_COLOR_BUFFER_BIT); - - this->m_camera.load_uniforms(&this->m_shader_program); - for (const auto &child : this->m_childs) - { - child->bind(); - child->render(&this->m_shader_program); - } - SDL_GL_SwapWindow(this->p_window); - - // FPS Limit - this->limit_fps(); - } - } - catch (const std::runtime_error &e) - { - std::cerr << "[ ERROR ] : " << e.what() << "\n"; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; - } - - app::app(std::string window_title, - int width, - int height, - app_options options, - Uint32 window_init_flags) - : observer(nullptr), - m_camera(std::move(camera(width, height))), - _options(std::move(options)) - { - if (FT_Init_FreeType(&app::s_ft_library)) - { - throw std::runtime_error("ERROR::FREETYPE: Could not init FreeType Library"); - } - - this->_frame_delay_ms = 1000 / this->_options.fps; - - if (SDL_Init(window_init_flags) != 0) - { - FT_Done_FreeType(app::s_ft_library); - throw std::runtime_error("Cannot init sdl"); - } - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - - this->p_window = SDL_CreateWindow(window_title.c_str(), - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - width, - height, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN); - - app::s_window_width = std::move(width); - app::s_window_height = std::move(height); - - this->m_opengl_context = SDL_GL_CreateContext(this->p_window); - - if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) - { - FT_Done_FreeType(app::s_ft_library); - SDL_DestroyWindow(this->p_window); - SDL_GL_DeleteContext(this->m_opengl_context); - throw std::runtime_error("Failed to initialize GLAD"); - } - - this->_options.vertex_source = this->_options.vertex_source.empty() - ? resource::s_vertex_source - : this->_options.vertex_source; - - this->_options.fragment_source = this->_options.fragment_source.empty() - ? resource::s_fragment_source - : this->_options.fragment_source; - - this->m_shader_program = create_shader_program( - std::move(this->_options.vertex_source), std::move(this->_options.fragment_source)); - - this->m_event_listener = std::make_shared(); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - auto app::limit_fps() -> void - { - auto frame_time = SDL_GetTicks() - this->_frame_start; - - if (frame_time < this->_frame_delay_ms) - { - SDL_Delay(this->_frame_delay_ms - frame_time); - } - - this->_frame_start = SDL_GetTicks(); - } - - auto app::add_renderable(renderable *child) -> void - { - this->m_childs.push_back(child); - } - - auto app::get_ft_library() -> FT_Library & - { - return app::s_ft_library; - } - - auto app::get_width() -> int const - { - return app::s_window_width; - } - - auto app::get_height() -> int const - { - return app::s_window_height; - } - - auto app::get_camera() -> camera * - { - return &this->m_camera; - } - - auto app::get_event_listener() -> std::shared_ptr const - { - return this->m_event_listener; - } - - app::~app() - { - if (this->m_shader_program) - { - glDeleteProgram(this->m_shader_program); - } - - if (this->p_window) - { - SDL_DestroyWindow(this->p_window); - } - - SDL_GL_DeleteContext(this->m_opengl_context); - - FT_Done_FreeType(app::s_ft_library); - - std::cout << "clean app\n"; - } -} // namespace sdlk diff --git a/sources/core/camera.cpp b/sources/core/camera.cpp deleted file mode 100644 index 7aed2fa..0000000 --- a/sources/core/camera.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include -#include -#include - -namespace sdlk -{ - camera::camera(int width, int height) - { - this->update(std::move(width), std::move(height)); - } - - auto camera::get_projection() const -> const glm::mat4& - { - return this->m_projection; - } - - auto camera::get_view() const -> const glm::mat4& - { - return this->m_view; - } - - auto camera::translate(const glm::vec2& delta) -> void - { - m_position += delta; - this->update(app::get_width(), app::get_height()); - } - - auto camera::set_position(const glm::vec2& pos) -> void - { - m_position = pos; - this->update(app::get_width(), app::get_height()); - } - - auto camera::set_zoom(float zoom) -> void - { - this->m_zoom = glm::max(zoom, 0.01f); // Avoid negative/zero zoom - this->update(app::get_width(), app::get_height()); - } - - auto camera::focus_on(const glm::vec2& target) -> void - { - this->set_position(target); - this->update(app::get_width(), app::get_height()); - } - - auto camera::update(int width, int height) -> void - { - this->recalculate(std::move(width), std::move(height)); - } - - auto camera::recalculate(int width, int height) -> void - { - float visible_width = width / m_zoom; - float visible_height = height / m_zoom; - - this->m_projection = glm::ortho(0.0f, visible_width, visible_height, 0.0f, -1.0f, 1.0f); - this->m_view = glm::translate(glm::mat4(1.0f), glm::vec3(-m_position, 0.0f)); - } - - auto camera::get_zoom() -> float - { - return this->m_zoom; - } - - auto camera::get_position() -> glm::vec2 - { - return this->m_position; - } - - auto camera::load_uniforms(GLuint* shader_program, - std::string view_name, - std::string proj_name) const -> void const - { - auto u_view_loc = get_uniform_loc(shader_program, view_name); - glUniformMatrix4fv(u_view_loc, 1, GL_FALSE, glm::value_ptr(this->m_view)); - - auto u_projection_loc = get_uniform_loc(shader_program, proj_name); - glUniformMatrix4fv(u_projection_loc, 1, GL_FALSE, glm::value_ptr(this->m_projection)); - } -} // namespace sdlk diff --git a/sources/core/colored_shape.cpp b/sources/core/colored_shape.cpp deleted file mode 100644 index fe44880..0000000 --- a/sources/core/colored_shape.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace sdlk -{ - static auto load_colored_vertices(GLuint *vao, - GLuint *vbo, - const std::vector &vertices, - bool use_vec_color) -> void; - - colored_shape::colored_shape(polygon geometry, SDL_Color color, bool use_vec_color) - : colored_shape(geometry, - std::move(color), - std::move(mapbox::earcut(std::move(geometry.data()))), - use_vec_color) - { - } - - colored_shape::colored_shape(polygon geometry, - SDL_Color color, - std::vector indices, - bool use_vec_color) - : shape(std::move(geometry), std::move(indices), { use_vec_color, false, false }) - { - this->set_color(color); - auto positions = this->m_polygon.flattened(); - - std::vector vertices{}; - for (size_t i = 0; i < positions.size(); i++) - { - vertices.push_back(positions[i][0]); - vertices.push_back(positions[i][1]); - - if (this->m_config.use_vec_color) - { - vertices.push_back(this->m_ndc_color[0]); // r - vertices.push_back(this->m_ndc_color[1]); // g - vertices.push_back(this->m_ndc_color[2]); // b - vertices.push_back(this->m_ndc_color[3]); // a - } - } - - load_colored_vertices(&this->m_vao, &this->m_vbo, vertices, this->m_config.use_vec_color); - } - - colored_shape::colored_shape(polygon geometry, std::vector colors) - : colored_shape(geometry, - std::move(colors), - std::move(mapbox::earcut(std::move(geometry.data())))) - { - } - - colored_shape::colored_shape(polygon geometry, - std::vector colors, - std::vector indices) - : shape(std::move(geometry), std::move(indices), { true, false, false }) - { - auto positions = this->m_polygon.flattened(); - if (positions.size() != colors.size()) - { - throw std::runtime_error("Some colors are missing for vertex"); - } - - std::vector vertices{}; - for (size_t i = 0; i < positions.size(); i++) - { - vertices.push_back(positions[i][0]); - vertices.push_back(positions[i][1]); - - auto ndc_color = converter::sdl_color_to_ndc(std::move(colors[i])); - vertices.push_back(ndc_color[0]); // r - vertices.push_back(ndc_color[1]); // g - vertices.push_back(ndc_color[2]); // b - vertices.push_back(ndc_color[3]); // a - } - - load_colored_vertices(&this->m_vao, &this->m_vbo, vertices, this->m_config.use_vec_color); - } - - static auto load_colored_vertices(GLuint *vao, - GLuint *vbo, - const std::vector &vertices, - bool use_vec_color) -> void - { - auto vertex_size_count = use_vec_color ? 6 : 2; - glBindVertexArray(*vao); - glBindBuffer(GL_ARRAY_BUFFER, *vbo); - glBufferData( - GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW); - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, vertex_size_count * sizeof(float), (void *)0); - glEnableVertexAttribArray(0); - - glDisableVertexAttribArray(1); // texture attribute - - if (use_vec_color) - { - glVertexAttribPointer(2, - 4, - GL_FLOAT, - GL_FALSE, - vertex_size_count * sizeof(float), - (void *)(2 * sizeof(float))); - glEnableVertexAttribArray(2); - } - else - { - glDisableVertexAttribArray(2); - } - - glBindVertexArray(0); - } - -} // namespace sdlk diff --git a/sources/core/component.cpp b/sources/core/component.cpp deleted file mode 100644 index 126c8cf..0000000 --- a/sources/core/component.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include - -namespace sdlk -{ - component::component(app *app, renderable *renderable) - : observer(app->get_event_listener()), - p_renderable(renderable) - { - app->add_renderable(this); - } - - component::component(component *parent, renderable *renderable) - : observer(parent->m_event_listener), - p_renderable(renderable) - { - parent->append_child(this); - } - - auto component::render(GLuint *program) -> void - { - this->p_renderable->render(program); - - for (const auto &child : this->m_childs) - { - child->render(program); - } - } - - auto component::append_child(component *child) -> void - { - this->m_childs.push_back(child); - } - - auto component::bind() -> void const - { - this->p_renderable->bind(); - } - - auto component::translate(glm::vec2 pixel_offset) -> void - { - this->p_renderable->translate(std::move(pixel_offset)); - } - - auto component::scale(float scale) -> void - { - this->p_renderable->scale(std::move(scale)); - } - - auto component::rotate(float angle_radians) -> void - { - this->p_renderable->rotate(std::move(angle_radians)); - } - - auto component::add_translate(glm::vec2 translation) -> void - { - this->p_renderable->add_translate(std::move(translation)); - } - - auto component::add_rotation(float angle) -> void - { - this->p_renderable->add_rotation(std::move(angle)); - } - - auto component::add_scale(float scale) -> void - { - this->p_renderable->add_rotation(std::move(scale)); - } - - auto component::set_transformation_model(glm::mat4 transformation_model) -> void - { - this->p_renderable->set_transformation_model(std::move(transformation_model)); - } -} // namespace sdlk diff --git a/sources/core/converter.cpp b/sources/core/converter.cpp deleted file mode 100644 index 6b85506..0000000 --- a/sources/core/converter.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include - -namespace sdlk::converter -{ - auto sdl_color_to_ndc(SDL_Color color) -> std::array - { - return { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; - } - - auto pixel_uv_to_ndc(std::vector pixels, int texture_width, int texture_height) - -> std::vector - { - std::vector uv_coords; - uv_coords.reserve(pixels.size()); - - for (const auto& p : pixels) - { - float u = p[0] / static_cast(texture_width); - float v = p[1] / static_cast(texture_height); - uv_coords.push_back({ u, v }); - } - - return uv_coords; - } -} // namespace sdlk::converter diff --git a/sources/core/events/event_listener.cpp b/sources/core/events/event_listener.cpp deleted file mode 100644 index ee00b18..0000000 --- a/sources/core/events/event_listener.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include - -void sdlk::event_listener::notify_event(const SDL_Event &event) -{ - const auto actual_type = get_event_type_value(event); - std::vector actions{}; - - for (const auto &[etype, actionList] : m_event_listeners) - { - if (etype == actual_type || etype == event_type::all) - { - actions.insert(actions.end(), actionList.begin(), actionList.end()); - } - } - - for (const auto &action : actions) - { - action.m_callback(event); - if (action.m_stop_propagation) - { - break; - } - }; -} - -auto sdlk::event_listener::get_event_type_value(const SDL_Event &event) -> sdlk::event_type -{ - switch (event.type) - { - case SDL_KEYDOWN: return sdlk::event_type::key_down; - case SDL_KEYUP: return sdlk::event_type::key_up; - case SDL_WINDOWEVENT: return sdlk::event_type::window_event; - case SDL_MOUSEMOTION: return sdlk::event_type::mouse_motion; - case SDL_MOUSEBUTTONDOWN: return sdlk::event_type::mouse_button_down; - case SDL_MOUSEBUTTONUP: return sdlk::event_type::mouse_button_up; - case SDL_MOUSEWHEEL: return sdlk::event_type::mouse_wheel; - default: return sdlk::event_type::all; - } -} diff --git a/sources/core/events/observer.cpp b/sources/core/events/observer.cpp deleted file mode 100644 index 5d7337e..0000000 --- a/sources/core/events/observer.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -namespace sdlk -{ - void observer::add_event_listener(event_type event_type, - event_callback callback, - bool stop_propagation) - { - if (this->m_event_listener) - { - this->m_event_listener->m_event_listeners[event_type].push_back( - { std::move(callback), stop_propagation }); - } - } - - observer::observer(std::shared_ptr event_listener) - : m_event_listener(event_listener) - { - } -} // namespace sdlk diff --git a/sources/core/freetype_font.cpp b/sources/core/freetype_font.cpp deleted file mode 100644 index 162c45d..0000000 --- a/sources/core/freetype_font.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace sdlk -{ - freetype_font::freetype_font(std::string font_path, int font_size, int char_to_load) - { - if (FT_New_Face(app::get_ft_library(), font_path.c_str(), 0, &this->m_face)) - { - throw std::runtime_error("Cannot load font " + font_path); - } - - FT_Set_Pixel_Sizes(this->m_face, 0, font_size); - GLint previous_alignment; - glGetIntegerv(GL_UNPACK_ALIGNMENT, &previous_alignment); - - // Set the desired alignment (e.g., for tightly packed data) - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - FT_Face &face = this->m_face; - for (unsigned char c = 0; c < char_to_load; c++) - { - // load character glyph - if (FT_Load_Char(this->m_face, c, FT_LOAD_RENDER)) - { - std::cerr << "ERROR::FREETYTPE: Failed to load Glyph \n" << std::flush; - continue; - } - - // generate texture - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RED, - face->glyph->bitmap.width, - face->glyph->bitmap.rows, - 0, - GL_RED, - GL_UNSIGNED_BYTE, - face->glyph->bitmap.buffer); - // set texture options - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // now store character for later use - character new_character; - new_character.texture_id = texture; - new_character.size = glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), - new_character.bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), - new_character.advance = face->glyph->advance.x; - - this->_characters.insert(std::pair(c, std::move(new_character))); - this->_ascender = this->m_face->size->metrics.ascender >> 6; - } - - // Restore the previous alignment - glPixelStorei(GL_UNPACK_ALIGNMENT, previous_alignment); - }; - - auto freetype_font::get_ascender() const -> int - { - return this->_ascender; - } - - auto freetype_font::get_face() -> FT_Face & - { - return this->m_face; - } - - auto freetype_font::get_character(const char &c) -> character & - { - return this->_characters[c]; - } - - auto freetype_font::make(std::string font_path, int font_size, int char_to_load) - -> std::shared_ptr - { - return std::make_shared( - std::move(font_path), std::move(font_size), std::move(char_to_load)); - } - - freetype_font::~freetype_font() - { - for (const auto &[c, ch] : this->_characters) - { - if (ch.texture_id) - { - glDeleteTextures(1, &ch.texture_id); - } - } - - this->_characters.clear(); - FT_Done_Face(this->m_face); - } -} // namespace sdlk diff --git a/sources/core/image_shape.cpp b/sources/core/image_shape.cpp deleted file mode 100644 index 155a388..0000000 --- a/sources/core/image_shape.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace sdlk -{ - image_shape::image_shape(std::string image_path, int width, int height, std::vector uv) - : textured_shape(std::move(polygon(quad::make(std::move(width), std::move(height)))), - std::move(uv), - std::move(texture::from_file(std::move(image_path))), - std::move(quad::indices())) - { - } - - image_shape::image_shape(std::string image_path, int width, int height) - : image_shape(std::move(image_path), - width, - height, - std::move(quad::make(std::move(width), std::move(height)))) - { - } -} // namespace sdlk diff --git a/sources/core/opengl_utils.cpp b/sources/core/opengl_utils.cpp deleted file mode 100644 index 2fee417..0000000 --- a/sources/core/opengl_utils.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include - -namespace sdlk -{ - auto compile_shader(GLenum type, std::string source) -> GLuint - { - auto source_ctr = source.c_str(); - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &source_ctr, nullptr); - glCompileShader(shader); - - GLint success{}; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) - { - char infoLog[512]; - glGetShaderInfoLog(shader, 512, nullptr, infoLog); - throw std::runtime_error(std::string("Shader compilation failed: ") + infoLog); - } - return shader; - } - - auto create_shader_program(std::string vertex_source, std::string fragment_source) -> GLuint - { - GLuint vertexShader = compile_shader(GL_VERTEX_SHADER, vertex_source.c_str()); - GLuint fragmentShader = compile_shader(GL_FRAGMENT_SHADER, fragment_source.c_str()); - - GLuint program = glCreateProgram(); - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - glLinkProgram(program); - - GLint success{}; - glGetProgramiv(program, GL_LINK_STATUS, &success); - if (!success) - { - char infoLog[512]; - glGetProgramInfoLog(program, 512, nullptr, infoLog); - throw std::runtime_error(std::string("Program linking failed: ") + infoLog); - } - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - - return program; - } - - [[nodiscard]] auto get_uniform_loc(GLuint *program, std::string uniform_name) -> GLuint - { - auto loc = glGetUniformLocation(*program, uniform_name.c_str()); - - if (loc == -1) - { - throw std::runtime_error("Cannot get the location of " + uniform_name); - } - - return loc; - } - -} // namespace sdlk diff --git a/sources/core/quad.cpp b/sources/core/quad.cpp deleted file mode 100644 index 67310ea..0000000 --- a/sources/core/quad.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -namespace sdlk::quad -{ - auto make(int width, int height) -> std::vector - { - auto f_width = (float)width; - auto f_height = (float)height; - return { { 0, 0 }, { f_width, 0 }, { f_width, f_height }, { 0, f_height } }; - } - - auto indices() -> std::vector - { - return { 0, 1, 2, 2, 3, 0 }; - } -} // namespace sdlk::quad diff --git a/sources/core/renderable.cpp b/sources/core/renderable.cpp deleted file mode 100644 index ae49574..0000000 --- a/sources/core/renderable.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include - -namespace sdlk -{ - auto renderable::render(GLuint *program) -> void - { - this->m_transformation.load_uniforms(program); - } - - auto renderable::bind() -> void const - { - // TODO: nothing here - } - - auto renderable::translate(glm::vec2 pixel_offset) -> void - { - this->m_transformation.translate(std::move(pixel_offset)); - } - - auto renderable::scale(float pixel_scale) -> void - { - this->m_transformation.scale(std::move(pixel_scale)); - } - - auto renderable::rotate(float angle_radians) -> void - { - this->m_transformation.rotate(std::move(angle_radians)); - } - - auto renderable::add_translate(glm::vec2 offset) -> void - { - this->m_transformation.add_translate(std::move(offset)); - } - - auto renderable::add_rotation(float angle_radians) -> void - { - this->m_transformation.add_rotation(std::move(angle_radians)); - } - - auto renderable::add_scale(float scale) -> void - { - this->m_transformation.add_scale(std::move(scale)); - } - - auto renderable::set_transformation_model(glm::mat4 transformation_model) -> void - { - this->m_transformation.set_model(std::move(transformation_model)); - } - - auto renderable::get_scale() -> float - { - return this->m_transformation.get_scale(); - } - - auto renderable::get_rotation() -> float - { - return this->m_transformation.get_rotation(); - } - - auto renderable::get_translation() -> glm::vec2 - { - return this->m_transformation.get_translation(); - } - -} // namespace sdlk diff --git a/sources/core/shaders/fragment.glsl b/sources/core/shaders/fragment.glsl deleted file mode 100644 index 079a26b..0000000 --- a/sources/core/shaders/fragment.glsl +++ /dev/null @@ -1,24 +0,0 @@ -#version 330 core -out vec4 FragColor; - -in vec2 TexCoord; -in vec4 OutColor; - -uniform sampler2D uTexture; -uniform bool uUseTexture; -uniform bool uUseTextRendering; - -void main() -{ - if (uUseTexture) - { - if (uUseTextRendering) { - vec4 sampled = vec4(1.0, 1.0, 1.0, texture(uTexture, TexCoord).r); - FragColor = OutColor * sampled; - } else { - FragColor = texture(uTexture, TexCoord); - } - } else { - FragColor = OutColor; - } -} diff --git a/sources/core/shaders/vertex.glsl b/sources/core/shaders/vertex.glsl deleted file mode 100644 index b62ca2a..0000000 --- a/sources/core/shaders/vertex.glsl +++ /dev/null @@ -1,22 +0,0 @@ -#version 330 core -layout(location = 0) in vec2 aPos; -layout(location = 1) in vec2 aTexCoord; -layout(location = 2) in vec4 aColor; - -uniform mat4 uModel; -uniform mat4 uView; -uniform mat4 uProjection; -uniform vec4 uColor; - -uniform bool uUseTexture; -uniform bool uUseVecColor; - -out vec2 TexCoord; -out vec4 OutColor; - -void main() -{ - gl_Position = uProjection * uView * uModel * vec4(aPos, 0.0, 1.0); - TexCoord = uUseTexture ? aTexCoord : vec2(1.0); - OutColor = uUseVecColor ? aColor : uColor; -} diff --git a/sources/core/shape.cpp b/sources/core/shape.cpp deleted file mode 100644 index a2420a7..0000000 --- a/sources/core/shape.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace sdlk -{ - shape::shape(polygon polygon, std::vector indices, shape_config config) - : m_polygon(polygon), - m_config(std::move(config)) - { - this->m_indices_count = static_cast(indices.size()); - - glGenVertexArrays(1, &this->m_vao); - glGenBuffers(1, &this->m_vbo); - glGenBuffers(1, &this->m_ebo); - - glBindVertexArray(this->m_vao); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - indices.size() * sizeof(uint32_t), - indices.data(), - GL_STATIC_DRAW); - } - - auto shape::bind() -> void const - { - renderable::bind(); - glBindVertexArray(this->m_vao); - } - - auto shape::set_color(SDL_Color color) -> void - { - this->m_color = color; - this->m_ndc_color = converter::sdl_color_to_ndc(std::move(color)); - } - - auto shape::render(GLuint *program) -> void - { - renderable::render(program); - - if (!this->m_indices_count) - { - return; - } - - auto use_vec_color_loc = get_uniform_loc(program, "uUseVecColor"); - auto use_texture_loc = get_uniform_loc(program, "uUseTexture"); - auto use_text_rendering_loc = get_uniform_loc(program, "uUseTextRendering"); - auto u_color_loc = get_uniform_loc(program, "uColor"); - - glUniform1i(use_vec_color_loc, this->m_config.use_vec_color ? 1 : 0); - glUniform1i(use_texture_loc, this->m_config.use_texture ? 1 : 0); - glUniform1i(use_text_rendering_loc, this->m_config.use_text_rendering ? 1 : 0); - glUniform4fv(u_color_loc, 1, this->m_ndc_color.data()); - - glDrawElements(GL_TRIANGLES, this->m_indices_count, GL_UNSIGNED_INT, 0); - } - - auto shape::get_color() -> SDL_Color - { - return this->m_color; - } - - shape::~shape() - { - if (this->m_ebo) - { - glDeleteBuffers(1, &m_ebo); - } - - if (this->m_vbo) - { - glDeleteBuffers(1, &m_vbo); - } - - if (this->m_vao) - { - glDeleteVertexArrays(1, &m_vao); - } - std::cout << "clean shape\n"; - } -} // namespace sdlk diff --git a/sources/core/static_resource.cpp b/sources/core/static_resource.cpp deleted file mode 100644 index db053a7..0000000 --- a/sources/core/static_resource.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "static_resource.hpp" - -#include -#include -#include - -namespace sdlk -{ - static std::string load_file(const std::string& path) - { - std::ifstream file(path); - if (!file) - { - throw std::runtime_error("Cannot open shader file: " + path); - } - - std::ostringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); - } - - std::string resource::s_vertex_source = load_file("./resources/shaders/vertex.glsl"); - std::string resource::s_fragment_source = load_file("./resources/shaders/fragment.glsl"); -} // namespace sdlk diff --git a/sources/core/static_resource.hpp b/sources/core/static_resource.hpp deleted file mode 100644 index a9bac13..0000000 --- a/sources/core/static_resource.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include - -namespace sdlk -{ - class resource - { - private: - static std::string s_vertex_source, s_fragment_source; - - public: - friend class app; - }; -} // namespace sdlk diff --git a/sources/core/text_shape.cpp b/sources/core/text_shape.cpp deleted file mode 100644 index 033c28e..0000000 --- a/sources/core/text_shape.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include - -namespace sdlk -{ - text_shape::text_shape(std::string text, std::shared_ptr font, SDL_Color color) - : shape(polygon(), {}, { false, true, true }), - m_text(std::move(text)), - m_font(std::move(font)) - { - this->set_color(color); - glBindVertexArray(this->m_vao); - glBindBuffer(GL_ARRAY_BUFFER, this->m_vbo); - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); - glEnableVertexAttribArray(0); - - glVertexAttribPointer( - 1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); - glEnableVertexAttribArray(1); - - glDisableVertexAttribArray(2); // for color texture - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - } - - auto text_shape::bind() -> void const - { - shape::bind(); - glActiveTexture(GL_TEXTURE0); - } - - auto text_shape::set_text(std::string text) -> void - { - m_text = std::move(text); - } - - auto text_shape::get_text() -> std::string - { - return m_text; - } - - auto text_shape::render(GLuint *program) -> void - { - auto use_vec_color_loc = get_uniform_loc(program, "uUseVecColor"); - glUniform1i(use_vec_color_loc, 0); - - auto u_color_loc = get_uniform_loc(program, "uColor"); - glUniform4fv(u_color_loc, 1, this->m_ndc_color.data()); - - auto use_text_rendering_loc = get_uniform_loc(program, "uUseTextRendering"); - glUniform1i(use_text_rendering_loc, 1); - - auto use_texture_loc = get_uniform_loc(program, "uUseTexture"); - glUniform1i(use_texture_loc, 1); - - auto u_texture_loc = get_uniform_loc(program, "uTexture"); - glUniform1i(u_texture_loc, 0); // TODO: handle texture unit$ - - std::string::const_iterator c; - int x = 0; - int y = this->m_font->get_ascender(); - - for (c = this->m_text.begin(); c != this->m_text.end(); c++) - { - auto ch = this->m_font->get_character(*c); - - float xpos = x + ch.bearing.x; - float ypos = y - ch.bearing.y; - - float w = ch.size.x; - float h = ch.size.y; - - float vertices[6][4] = { - { xpos, ypos, 0.0f, 0.0f }, // top-left - { xpos, ypos + h, 0.0f, 1.0f }, // bottom-left - { xpos + w, ypos + h, 1.0f, 1.0f }, // bottom-right - - { xpos, ypos, 0.0f, 0.0f }, - { xpos + w, ypos + h, 1.0f, 1.0f }, - { xpos + w, ypos, 1.0f, 0.0f } // top-right - }; - // render glyph texture over quad - glBindTexture(GL_TEXTURE_2D, ch.texture_id); - - // update content of VBO memory - glBindBuffer(GL_ARRAY_BUFFER, this->m_vbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - // render quad - glDrawArrays(GL_TRIANGLES, 0, 6); - - // now advance cursors for next glyph (note that advance is number of 1/64 pixels) - x += (ch.advance >> 6); // bitshift by 6 to get value in pixels (2^6 = 64) - } - } -} // namespace sdlk diff --git a/sources/core/textured_shape.cpp b/sources/core/textured_shape.cpp deleted file mode 100644 index 2954b76..0000000 --- a/sources/core/textured_shape.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sdlk -{ - static auto load_textured_vertices(GLuint *vao, GLuint *vbo, const std::vector &vertices) - -> void; - - textured_shape::textured_shape(polygon geometry, - std::vector uv, - std::shared_ptr texture) - : textured_shape(geometry, - std::move(uv), - std::move(texture), - std::move(mapbox::earcut(geometry.data()))) - { - } - - textured_shape::textured_shape(polygon geometry, - std::vector uv, - std::shared_ptr texture, - std::vector indices) - : shape(std::move(geometry), std::move(indices), { false, true, false }), - m_texture(std::move(texture)) - { - auto positions = this->m_polygon.flattened(); - if (positions.size() != uv.size()) - { - throw std::runtime_error("Some uv are missing for vertex"); - } - - std::vector vertices{}; - auto ndc_uv = converter::pixel_uv_to_ndc( - uv, this->m_texture->get_width(), this->m_texture->get_height()); - for (size_t i = 0; i < positions.size(); i++) - { - vertices.push_back(positions[i][0]); - vertices.push_back(positions[i][1]); - vertices.push_back(ndc_uv[i][0]); - vertices.push_back(ndc_uv[i][1]); - } - - load_textured_vertices(&this->m_vao, &this->m_vbo, vertices); - } - - auto textured_shape::bind() -> const void - { - shape::bind(); - this->m_texture->bind(0); // TODO: handle texture unit - } - - auto textured_shape::render(GLuint *program) -> void - { - auto u_texture_loc = get_uniform_loc(program, "uTexture"); - glUniform1i(u_texture_loc, 0); // TODO: handle texture unit - - shape::render(program); - } - - static auto load_textured_vertices(GLuint *vao, GLuint *vbo, const std::vector &vertices) - -> void - { - glBindVertexArray(*vao); - glBindBuffer(GL_ARRAY_BUFFER, *vbo); - glBufferData( - GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW); - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); - glEnableVertexAttribArray(0); - - glVertexAttribPointer( - 1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); - glEnableVertexAttribArray(1); - - glDisableVertexAttribArray(2); // for color texture - glBindVertexArray(0); - } -} // namespace sdlk diff --git a/sources/core/transformation.cpp b/sources/core/transformation.cpp deleted file mode 100644 index 1e8f812..0000000 --- a/sources/core/transformation.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace sdlk -{ - auto transformation::compose() -> void - { - glm::mat4 T = glm::translate(glm::mat4(1.0f), m_translation); - glm::mat4 R = glm::rotate(glm::mat4(1.0f), m_rotation_radians, glm::vec3(0, 0, 1)); - glm::mat4 S = glm::scale(glm::mat4(1.0f), glm::vec3(m_scale)); - - m_model = T * R * S; - } - - void transformation::translate(glm::vec2 pos) - { - this->m_translation = std::move(glm::vec3(std::move(pos), 0.0f)); - this->compose(); - } - - void transformation::rotate(float angle_radians) - { - this->m_rotation_radians = std::move(angle_radians); - this->compose(); - } - - void transformation::scale(float scale) - { - this->m_scale = std::move(scale); - this->compose(); - } - - void transformation::add_translate(glm::vec2 delta) - { - this->m_translation += std::move(glm::vec3(std::move(delta), 0.0f)); - this->compose(); - } - - void transformation::add_rotation(float delta_angle) - { - this->m_rotation_radians += std::move(delta_angle); - this->compose(); - } - - void transformation::add_scale(float delta_scale) - { - this->m_scale += std::move(delta_scale); - - if (m_scale < 0.01f) - { - this->m_scale = 0.01f; // prevent negative or zero scale - } - - this->compose(); - } - - auto transformation::set_model(glm::mat4 model) -> void - { - this->m_model = std::move(model); - } - - auto transformation::load_uniforms(GLuint* shader_program, std::string model_name) const - -> void const - { - auto u_model_loc = get_uniform_loc(shader_program, model_name); - glUniformMatrix4fv(u_model_loc, 1, GL_FALSE, glm::value_ptr(this->m_model)); - } - - auto transformation::get_scale() -> float - { - return this->m_scale; - } - - auto transformation::get_rotation() -> float - { - return this->m_rotation_radians; - } - - auto transformation::get_translation() -> glm::vec2 - { - return this->m_translation; - } -} // namespace sdlk diff --git a/sources/core/types.cpp b/sources/core/types.cpp deleted file mode 100644 index 6b2a970..0000000 --- a/sources/core/types.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include - -namespace sdlk -{ - polygon::polygon(ring outer, std::vector holes) - { - holes.insert(holes.begin(), outer); - - if (holes.empty()) - { - throw std::runtime_error("Polygon requires at least one ring (outer)"); - } - m_data = std::move(holes); - } - - auto polygon::data() const -> const std::vector& - { - return m_data; - } - - auto polygon::flattened() const -> std::vector - { - std::vector result; - for (const auto& r : m_data) - { - result.insert(result.end(), r.begin(), r.end()); - } - return result; - } -} // namespace sdlk diff --git a/sources/engine/CMakeLists.txt b/sources/engine/CMakeLists.txt new file mode 100644 index 0000000..d4da990 --- /dev/null +++ b/sources/engine/CMakeLists.txt @@ -0,0 +1,29 @@ +set(IMGUI_DIR ${SDLK_SOURCE_DIR}/external/imgui) + +set(SDLK_ENGINE_SOURCES + main.cpp + types/project.cpp + views/home/home.cpp + views/home/draw_project_card.cpp + conf/sdlk_engine_conf.cpp + utils/os.cpp + views/project_editor/project_editor.cpp +) + +add_executable(${SDLK_ENGINE_NAME} ${SDLK_ENGINE_SOURCES}) + +file(COPY resources/images DESTINATION ${CMAKE_BINARY_DIR}/bin/resources) + +# Set include directories for the library +target_include_directories( + ${SDLK_ENGINE_NAME} PRIVATE $) + +target_include_directories(${SDLK_ENGINE_NAME} PRIVATE ${SDLK_SOURCE_DIR}/include) + +target_link_libraries(${SDLK_ENGINE_NAME} PRIVATE ${SDLK_LIB_NAME}) + +set_target_properties(${SDLK_ENGINE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + +if (SDLK_ENGINE_INSTALL) + install(TARGETS ${SDLK_ENGINE_NAME} DESTINATION bin) +endif () \ No newline at end of file diff --git a/sources/engine/conf/sdlk_engine_conf.cpp b/sources/engine/conf/sdlk_engine_conf.cpp new file mode 100644 index 0000000..03588b7 --- /dev/null +++ b/sources/engine/conf/sdlk_engine_conf.cpp @@ -0,0 +1,113 @@ +// +// Created by ricka on 2026-05-17. +// + +#include "sdlk_engine_conf.hpp" + +#include + +#include "../utils/os.hpp" +#include "../utils/json_reader.hpp" +#include +#include + +#define SDLK_CONF_DIRECTORY ".sdlk-engine" +#define SDLK_CONF_FILE "sdlk-engine.conf.json" + +using json = nlohmann::json; + +sdlk_engine_conf::sdlk_engine_conf(std::string version, const std::vector &last_project) + : m_version(std::move(version)), m_last_project_paths(last_project) +{} + +auto sdlk_engine_conf::get_file_path() -> std::filesystem::path +{ + const auto home_path = std::filesystem::path(os::get_home_dir()); + return std::filesystem::path(home_path / SDLK_CONF_DIRECTORY) / SDLK_CONF_FILE; +} + +auto sdlk_engine_conf::is_valid(const std::filesystem::path& file_conf_path) -> bool +{ + if (!std::filesystem::exists(file_conf_path)) + return false; + + try + { + auto j = json_reader::read(file_conf_path); + if (!j.is_object()) + { + return false; + } + + if (!j.contains("version") || !j["version"].is_string()) + { + return false; + } + + if (!j.contains("last_project_paths") || !j["last_project_paths"].is_array()) + { + return false; + } + return true; + } + catch (...) + { + return false; + } +} + +auto sdlk_engine_conf::load_or_init() -> sdlk_engine_conf* +{ + const auto path = get_file_path(); + static sdlk_engine_conf conf; + + if (is_valid(path)) + { + auto j = json_reader::read(path); + + conf.m_version = j.value("version", SDLK_VERSION); + conf.m_last_project_paths = + j["last_project_paths"].get>(); + + return &conf; + } + + conf.save(); + return &conf; +} + +auto sdlk_engine_conf::save() const -> void +{ + const auto path = get_file_path(); + std::filesystem::create_directories(path.parent_path()); + + json j; + j["version"] = m_version; + j["last_project_paths"] = m_last_project_paths; + + std::ofstream file(path); + if (!file.is_open()) + { + throw std::runtime_error("Cannot write config file: " + path.string()); + } + + file << j.dump(2); +} + +auto sdlk_engine_conf::add_recent_project(const std::string& path) -> void +{ + m_last_project_paths.erase(std::remove(m_last_project_paths.begin(), m_last_project_paths.end(), path),m_last_project_paths.end()); + m_last_project_paths.insert(m_last_project_paths.begin(), path); + + if (m_last_project_paths.size() > MAX_RECENTS) + { + m_last_project_paths.resize(MAX_RECENTS); + } + + save(); +} + +auto sdlk_engine_conf::get_last_project_paths() const -> std::vector +{ + return m_last_project_paths; +} diff --git a/sources/engine/conf/sdlk_engine_conf.hpp b/sources/engine/conf/sdlk_engine_conf.hpp new file mode 100644 index 0000000..5bd456a --- /dev/null +++ b/sources/engine/conf/sdlk_engine_conf.hpp @@ -0,0 +1,30 @@ +// +// Created by ricka on 2026-05-17. +// + +#pragma once + +#include +#include +#include +#include + +class sdlk_engine_conf +{ + std::string m_version{SDLK_VERSION}; + std::vector m_last_project_paths{}; +public: + static constexpr size_t MAX_RECENTS = 6; + + [[nodiscard]] static auto get_file_path() -> std::filesystem::path; + [[nodiscard]] static auto load_or_init() -> sdlk_engine_conf*; + + auto save() const -> void; + auto add_recent_project(const std::string& path) -> void; + auto get_last_project_paths() const -> std::vector; + + [[nodiscard]] static auto is_valid(const std::filesystem::path &file_conf_path) -> bool; + + sdlk_engine_conf(std::string version, const std::vector &last_projects); + sdlk_engine_conf() = default; +}; diff --git a/sources/engine/main.cpp b/sources/engine/main.cpp new file mode 100644 index 0000000..1ec469d --- /dev/null +++ b/sources/engine/main.cpp @@ -0,0 +1,41 @@ +// +// Created by ricka on 2026-02-01. +// + +#include + +#include +#include "views/view.hpp" + +using namespace sdlk; + +static auto configure_global_style() -> void; + +auto main(const int argc, char* argv[]) -> int +{ + const auto sdlk_engine = app::make("Sdlk Engine", 1280, 720); + + configure_global_style(); + + sdlk_engine->add_view("home", home()); + sdlk_engine->add_view("project_editor", project_editor()); + return sdlk_engine->run("home", argc, argv); +} + +static auto configure_global_style() -> void +{ + auto& style = ImGui::GetStyle(); + + // scrollbar + style.GrabRounding = 8.0f; + style.ScrollbarSize = 10.0f; + style.ScrollbarRounding = 8.0f; + + // window + style.WindowRounding = 8.0f; + style.WindowPadding = ImVec2(10, 10); + + // button + style.FrameRounding = 8.0f; + style.FramePadding = ImVec2(10, 10); +} diff --git a/sources/engine/resources/images/examples/game_bg.png b/sources/engine/resources/images/examples/game_bg.png new file mode 100644 index 0000000..f910364 Binary files /dev/null and b/sources/engine/resources/images/examples/game_bg.png differ diff --git a/sources/engine/resources/images/home.png b/sources/engine/resources/images/home.png new file mode 100644 index 0000000..bf20de9 Binary files /dev/null and b/sources/engine/resources/images/home.png differ diff --git a/sources/engine/types/project.cpp b/sources/engine/types/project.cpp new file mode 100644 index 0000000..31ee1b5 --- /dev/null +++ b/sources/engine/types/project.cpp @@ -0,0 +1,48 @@ +// +// Created by ricka on 2026-02-06. +// + +#include "project.hpp" + +#include +#include +#include +#include +#include "../utils/json_reader.hpp" +#include +#include "../conf/sdlk_engine_conf.hpp" + +#define SDLK_PROJECT_FILE_CONF_NAME "sdlk_engine.json" +using json = nlohmann::json; + +namespace sdlk +{ + project::project(std::string path) + { + this->m_path = std::move(path); + const auto file_path = std::filesystem::path(this->m_path); + auto project_conf = json_reader::read(file_path / SDLK_PROJECT_FILE_CONF_NAME); + this->m_name = project_conf["name"]; + } + + auto project::open() const -> void + { + const auto engine = app::get(); + const auto project_editor_context = std::make_shared(); + project_editor_context->set("project_name", this->m_name); + + sdlk_engine_conf::load_or_init()->add_recent_project(this->m_path); + engine->switch_to("project_editor", project_editor_context); + std::cout << "Project opened: " << this->m_name << std::endl; + } + + auto project::get_name() -> std::string + { + return this->m_name; + } + + auto project::get_path() -> std::string + { + return this->m_path; + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/engine/types/project.hpp b/sources/engine/types/project.hpp new file mode 100644 index 0000000..a38209b --- /dev/null +++ b/sources/engine/types/project.hpp @@ -0,0 +1,25 @@ +// +// Created by ricka on 2026-02-06. +// + +#pragma once +#include + +namespace sdlk +{ + class project + { + protected: + std::string m_name{}; + std::string m_path{}; + + public: + explicit project(std::string path); + + auto open() const -> void; + [[nodiscard]] auto get_name() -> std::string; + [[nodiscard]] auto get_path() -> std::string; + + virtual ~project() = default; + }; +} // namespace sdlk diff --git a/sources/engine/utils/json_reader.hpp b/sources/engine/utils/json_reader.hpp new file mode 100644 index 0000000..378cfdf --- /dev/null +++ b/sources/engine/utils/json_reader.hpp @@ -0,0 +1,30 @@ +// +// Created by ricka on 2026-05-17. +// + +#pragma once + +#include +#include +#include + +using json = nlohmann::json; + +class json_reader { +public: + static auto read(const std::filesystem::path &path) -> json + { + std::ifstream file(path); + if (!file.is_open()) + { + throw std::runtime_error("Cannot open file: " + path.string()); + } + + json j; + file >> j; + + return j; + } + + json_reader() = delete; +}; \ No newline at end of file diff --git a/sources/engine/utils/os.cpp b/sources/engine/utils/os.cpp new file mode 100644 index 0000000..fd1b397 --- /dev/null +++ b/sources/engine/utils/os.cpp @@ -0,0 +1,18 @@ +// +// Created by ricka on 2026-05-17. +// + +#include "os.hpp" +#include + +auto os::get_home_dir() -> std::string +{ +#ifdef _WIN32 + const char* home = std::getenv("USERPROFILE"); +#else + const char* home = std::getenv("HOME"); +#endif + if(home) return std::string(home); + + throw std::runtime_error("Could not get home directory"); +} diff --git a/sources/engine/utils/os.hpp b/sources/engine/utils/os.hpp new file mode 100644 index 0000000..de39c94 --- /dev/null +++ b/sources/engine/utils/os.hpp @@ -0,0 +1,14 @@ +// +// Created by ricka on 2026-05-17. +// + +#pragma once + +#include + +class os +{ +public: + os() = delete; + [[nodiscard]] static auto get_home_dir() -> std::string; +}; diff --git a/sources/engine/views/home/components.hpp b/sources/engine/views/home/components.hpp new file mode 100644 index 0000000..0f955bf --- /dev/null +++ b/sources/engine/views/home/components.hpp @@ -0,0 +1,15 @@ +// +// Created by ricka on 2026-02-06. +// + +#pragma once + +#include + +#include "../../types/project.hpp" +# + +namespace sdlk +{ + auto draw_project_card(const std::shared_ptr& project) -> void; +} diff --git a/sources/engine/views/home/draw_project_card.cpp b/sources/engine/views/home/draw_project_card.cpp new file mode 100644 index 0000000..270512b --- /dev/null +++ b/sources/engine/views/home/draw_project_card.cpp @@ -0,0 +1,38 @@ +// +// Created by ricka on 2026-02-06. +// + +#include + +#include "components.hpp" + +namespace sdlk +{ + auto draw_project_card(const std::shared_ptr& project) -> void + { + ImGui::Dummy(ImVec2(0, 6)); + ImGui::BeginChild( + project->get_name().c_str(), + ImVec2(0, 68), + true, + ImGuiWindowFlags_None + ); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10)); + ImGui::TextUnformatted(project->get_name().c_str()); + ImGui::SameLine(); + + constexpr float buttonWidth = 80.0f; + const float avail = ImGui::GetContentRegionAvail().x; + + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + avail - buttonWidth); + if (ImGui::Button("Open", ImVec2(buttonWidth, 0))) + { + project->open(); + } + + ImGui::PopStyleVar(); + ImGui::EndChild(); + ImGui::Dummy(ImVec2(0, 6)); + } +} // namespace sdlk diff --git a/sources/engine/views/home/home.cpp b/sources/engine/views/home/home.cpp new file mode 100644 index 0000000..0c5d1e7 --- /dev/null +++ b/sources/engine/views/home/home.cpp @@ -0,0 +1,118 @@ +// +// Created by ricka on 2026-02-06. +// + +#include + +#include +#include +#include + +#include "components.hpp" +#include "../view.hpp" +#include "../../types/project.hpp" +#include "../../conf/sdlk_engine_conf.hpp" + +namespace sdlk +{ + static auto draw_home_content() -> void; + static auto open_existing_project() -> void; + static auto get_last_projects() -> std::vector>; + + auto home() -> std::shared_ptr + { + auto home_view = std::make_shared(); + home_view->add_child(imgui_renderable::make(&draw_home_content)); + return home_view; + } + + auto draw_home_content() -> void + { + static const auto projects = get_last_projects(); + static const std::shared_ptr logo = + gl_texture::from_file("./resources/images/home.png"); + + imgui_drawer_utils::centered_window( + [] + { + const ImGuiViewport* vp = ImGui::GetMainViewport(); + const float width = vp->Size.x * 0.6f; + const float height = width / logo->get_ratio() + 100 /* TODO */; + return ImVec2(width, height); + }, + [] + { + ImGui::Begin("Welcome to SDLK Game Creator", + nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + + const float content_width = ImGui::GetContentRegionAvail().x; + + imgui_drawer_utils::image(logo, ImVec2{ content_width, 300.0F }); + imgui_drawer_utils::spacing(2); + + ImGui::Button("Create a new Project", ImVec2(content_width, 0)); + + imgui_drawer_utils::spacing(); + + if (ImGui::Button("Open a Project", ImVec2(content_width, 0))) + { + open_existing_project(); + } + + imgui_drawer_utils::spacing(); + ImGui::Separator(); + imgui_drawer_utils::spacing(); + + ImGui::Text("Last Projects"); + imgui_drawer_utils::spacing(); + + constexpr float padding = 8.0f; + constexpr float card_size = 140.0f; + const float available_width = content_width; + int times_per_row = static_cast(available_width / (card_size + padding)); + + if (times_per_row < 1) + { + times_per_row = 1; + } + + for (int i = 0; i < projects.size(); ++i) + { + if (i != 0 && i % times_per_row != 0) + { + ImGui::SameLine(0, padding); + } + draw_project_card(projects[i]); + } + + ImGui::End(); + }); + } + + auto open_existing_project() -> void + { + if (const auto response = nfd_wrapper::instance()->open_folder_dialog(); + response.m_type == nfd_open_dialog_result::type::success) + { + auto path = response.m_path; + const auto to_open = std::make_shared(path.value()); + + to_open->open(); + } + } + + auto get_last_projects() -> std::vector> + { + std::vector> projects{}; + + projects.reserve(sdlk_engine_conf::MAX_RECENTS); + const auto last_project_paths = sdlk_engine_conf::load_or_init()->get_last_project_paths(); + for (const auto& path : last_project_paths) + { + projects.emplace_back(std::make_shared(path)); + } + return projects; + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/engine/views/project_editor/project_editor.cpp b/sources/engine/views/project_editor/project_editor.cpp new file mode 100644 index 0000000..410aa42 --- /dev/null +++ b/sources/engine/views/project_editor/project_editor.cpp @@ -0,0 +1,28 @@ +// +// Created by ricka on 2026-05-17. +// + +#include "../view.hpp" +#include +#include + +static auto draw_project_content(std::weak_ptr parent_view) -> std::function; + +auto sdlk::project_editor() -> std::shared_ptr +{ + auto project_view = std::make_shared(); + auto content_drawer = draw_project_content(project_view); + project_view->add_child(imgui_renderable::make(std::move(content_drawer))); + return project_view; +} + +auto draw_project_content(std::weak_ptr parent_view) -> std::function +{ + return [parent_view]() { + if (const auto parent = parent_view.lock()) + { + const auto project_name = parent->get_context("project_name").value_or("None"); + ImGui::TextUnformatted(project_name.c_str()); + } + }; +} \ No newline at end of file diff --git a/sources/engine/views/view.hpp b/sources/engine/views/view.hpp new file mode 100644 index 0000000..b78c6e1 --- /dev/null +++ b/sources/engine/views/view.hpp @@ -0,0 +1,14 @@ +// +// Created by ricka on 2026-02-06. +// + +#pragma once + +#include +#include + +namespace sdlk +{ + auto home() -> std::shared_ptr; + auto project_editor() -> std::shared_ptr; +} \ No newline at end of file diff --git a/sources/lib/CMakeLists.txt b/sources/lib/CMakeLists.txt new file mode 100644 index 0000000..7f8d55b --- /dev/null +++ b/sources/lib/CMakeLists.txt @@ -0,0 +1,130 @@ +set(IMGUI_DIR ${SDLK_SOURCE_DIR}/external/imgui) +set(IMGUI_SOURCES + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_tables.cpp + ${IMGUI_DIR}/imgui_widgets.cpp + # backends (SDL2 + OPENGL3) + ${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp + ${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp +) + +set(EXTRA_SOURCES extra/nfd_wrapper.cpp) +set(LIB_SOURCES + ${IMGUI_SOURCES} + ${EXTRA_SOURCES} + ../utils/file.cpp + core/glad/glad.cpp + core/gl/gl_program.cpp + core/gl/gl_shader.cpp + core/gl/gl_uniform.cpp + core/gl/gl_buffer.cpp + core/gl/gl_vertex_array.cpp + core/gl/gl_texture.cpp + core/events/event_listener.cpp + core/events/observer.cpp + core/components/renderable.cpp + core/components/component.cpp + core/components/multiple_view.cpp + core/components/2d/shape/polygon_shape.cpp + core/components/2d/shape/rectangle_shape.cpp + core/components/2d/fonts/free_type_handle_wrapper.cpp + core/components/2d/fonts/msdf_font.cpp + core/components/2d/shape/text_shape.cpp + core/app.cpp + core/converter.cpp + core/geometry.cpp + core/transform.cpp + core/camera.cpp + core/imgui/imgui_wrapper.cpp + core/imgui/imgui_renderable.cpp + core/imgui/imgui_drawer_utils.cpp +) + +# Create the library +if (BUILD_SHARED_LIBS) + add_library(${SDLK_LIB_NAME} SHARED ${LIB_SOURCES}) +else () + add_library(${SDLK_LIB_NAME} STATIC ${LIB_SOURCES}) +endif () + +file(COPY core/shaders DESTINATION ${CMAKE_BINARY_DIR}/bin/resources) + +# Set include directories for the library +target_include_directories( + ${SDLK_LIB_NAME} PRIVATE $) + +target_include_directories(${SDLK_LIB_NAME} PRIVATE $) + +# target_link +target_link_libraries(${SDLK_LIB_NAME} + PUBLIC + $ + $,SDL2::SDL2,SDL2::SDL2-static> +) + +target_link_libraries(${SDLK_LIB_NAME} PUBLIC glm::glm-header-only) + +target_link_libraries(${SDLK_LIB_NAME} PUBLIC $,SDL2_image::SDL2_image,SDL2_image::SDL2_image-static>) + +target_link_libraries(${SDLK_LIB_NAME} PRIVATE Freetype::Freetype) + +target_link_libraries(${SDLK_LIB_NAME} PUBLIC msdf-atlas-gen::msdf-atlas-gen) + +target_link_libraries(${SDLK_LIB_NAME} PRIVATE nfd) + +target_link_libraries(${SDLK_LIB_NAME} PRIVATE nlohmann_json::nlohmann_json) + +# set properties +set_target_properties( + ${SDLK_LIB_NAME} + PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + SOVERSION ${SDLK_LIB_VERSION_MAJOR} + VERSION ${SDLK_LIB_VERSION}) + +# lib install configuration +if (SDLK_LIB_INSTALL) + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Version.cmake + VERSION ${SDLK_VERSION} + COMPATIBILITY SameMajorVersion) + + include(GNUInstallDirs) + install(DIRECTORY ${SDLK_SOURCE_DIR}/include/${SDLK_LIB_NAME} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + install( + TARGETS ${SDLK_LIB_NAME} + EXPORT ${SDLK_LIB_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development) + + # Install the Config and ConfigVersion files + install( + EXPORT ${SDLK_LIB_NAME}Targets + FILE ${SDLK_LIB_NAME}Targets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDLK_LIB_NAME}) + + # Configure the Config file + configure_package_config_file( + ${SDLK_SOURCE_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake.in + ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDLK_LIB_NAME} + PATH_VARS SDLK_SOURCE_DIR + ) + + configure_file(${SDLK_SOURCE_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake.in + ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake @ONLY) + + # Install the Config, ConfigVersion, and custom Find module files + install(FILES ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Config.cmake + ${CMAKE_BINARY_DIR}/cmake/${SDLK_LIB_NAME}Version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDLK_LIB_NAME}) + +endif () diff --git a/sources/lib/core/app.cpp b/sources/lib/core/app.cpp new file mode 100644 index 0000000..6d9fbfd --- /dev/null +++ b/sources/lib/core/app.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "imgui/imgui_wrapper.hpp" + +namespace sdlk +{ + // TO handle ctrl + c or something else that can stop the application + static bool is_running = true; + static void signal_handler(int signal) + { + is_running = false; + } + + static std::shared_ptr global_app{nullptr}; + auto app::get() -> std::shared_ptr + { + return global_app; + } + + auto app::make(const std::string &window_title, const int &width, const int &height, const app_options &options, Uint32 sdl_init_flags) -> std::shared_ptr + { + if (global_app == nullptr) + { + global_app = std::shared_ptr( + new app(window_title, width, height, options, sdl_init_flags)); + } + + return global_app; + } + + app::app(const std::string &window_title, + const int &width, + const int &height, + const app_options &options, + const Uint32 sdl_init_flags) + : _options(options) + { + this->_frame_delay_ms = 1000 / this->_options.m_fps; + + if (SDL_Init(sdl_init_flags) != 0) + { + throw std::runtime_error("Cannot init sdl"); + } + + if ((IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) & (IMG_INIT_JPG | IMG_INIT_PNG)) == 0) + { + SDL_Quit(); + throw std::runtime_error("Cannot init sdl_image"); + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + constexpr auto dpi_scale = 1; + const auto window_width = static_cast(static_cast(width) * dpi_scale); + const auto window_height = static_cast(static_cast(height) * dpi_scale); + + this->p_window = SDL_CreateWindow(window_title.c_str(), + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + window_width, + window_height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); + + this->m_opengl_context = SDL_GL_CreateContext(this->p_window); + + if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) + { + SDL_DestroyWindow(this->p_window); + SDL_GL_DeleteContext(this->m_opengl_context); + IMG_Quit(); + SDL_Quit(); + throw std::runtime_error("Failed to initialize GLAD"); + } + + SDL_GL_MakeCurrent(p_window, m_opengl_context); + if (this->_options.m_vsync) + { + SDL_GL_SetSwapInterval(1); + } + + this->_imgui_wrapper = + std::make_shared(p_window, &m_opengl_context, "#version 330 core"); + this->m_view = std::make_shared(); + this->m_program = gl_program::from_files( + "resources/shaders/vertex.glsl", "resources/shaders/fragment.glsl"); + this->m_camera = std::make_shared(window_width, window_height); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glViewport(0, 0, window_width, window_height); + } + + auto app::handle_event(SDL_Event &event) const -> void + { + while (SDL_PollEvent(&event)) + { + imgui_wrapper::process_event(event); + + switch (event.type) + { + case SDL_QUIT: is_running = false; break; + default: + if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_CLOSE && + event.window.windowID == SDL_GetWindowID(p_window)) + { + is_running = false; + break; + } + + if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_RESIZED) + { + const int width = event.window.data1; + const int height = event.window.data2; + + this->m_camera->update(width, height); + glViewport(0, 0, width, height); + } + + event_listener::instance()->notify_event(event); + break; + } + } + } + + auto app::run(const std::string& default_view, int, char **) -> int + { + this->m_view->switch_to(default_view); + std::signal(SIGINT, signal_handler); + + const auto ndc_background_color = this->_options.m_background.ndc(); + glClearColor(ndc_background_color.r, + ndc_background_color.g, + ndc_background_color.b, + ndc_background_color.a); + + try + { + SDL_Event event; + this->_frame_start = SDL_GetTicks(); + while (is_running) + { + this->handle_event(event); + + glClear(GL_COLOR_BUFFER_BIT); + + imgui_wrapper::new_frame(); + + this->m_program->use(); + this->m_camera->load_uniforms(this->m_program); + this->m_view->render(this->m_program); + + imgui_wrapper::render(); + imgui_wrapper::draw(); + + SDL_GL_SwapWindow(this->p_window); + + this->limit_fps(); + } + } + catch (const std::runtime_error &e) + { + std::cerr << "[ ERROR ] : " << e.what() << "\n"; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + + auto app::limit_fps() -> void + { + auto frame_time = SDL_GetTicks() - this->_frame_start; + + if (frame_time < this->_frame_delay_ms) + { + SDL_Delay(this->_frame_delay_ms - frame_time); + } + + this->_frame_start = SDL_GetTicks(); + } + + auto app::add_view(const std::string &name, const std::shared_ptr &child) const -> void + { + this->m_view->add_view(name, child); + } + + auto app::switch_to(const std::string &name, const std::shared_ptr &context) const -> void + { + this->m_view->switch_to(name, context); + } + + app::~app() + { + if (this->p_window) + { + SDL_DestroyWindow(this->p_window); + } + + SDL_GL_DeleteContext(this->m_opengl_context); + + IMG_Quit(); + SDL_Quit(); + } +} // namespace sdlk diff --git a/sources/lib/core/camera.cpp b/sources/lib/core/camera.cpp new file mode 100644 index 0000000..f7c2075 --- /dev/null +++ b/sources/lib/core/camera.cpp @@ -0,0 +1,78 @@ +#include +#include +#include + +namespace sdlk +{ + camera::camera(const int width, const int height) + { + this->update(width, height); + } + + auto camera::get_projection() const -> const glm::mat4& + { + return this->m_projection; + } + + auto camera::get_view() const -> const glm::mat4& + { + return this->m_view; + } + + auto camera::translate(const glm::vec2& delta) -> void + { + m_position += delta; + this->update(_width, _height); + } + + auto camera::set_position(const glm::vec2& pos) -> void + { + m_position = pos; + this->update(_width, _height); + } + + auto camera::set_zoom(const float zoom) -> void + { + this->m_zoom = glm::max(zoom, 0.01f); // Avoid negative/zero zoom + this->update(_width, _height); + } + + auto camera::focus_on(const glm::vec2& target) -> void + { + this->set_position(target); + this->update(_width, _height); + } + + auto camera::update(const int width, const int height) -> void + { + this->_width = width; + this->_height = height; + this->recalculate(width, height); + } + + auto camera::recalculate(const int width, const int height) -> void + { + const float visible_width = static_cast(width) / m_zoom; + const float visible_height = static_cast(height) / m_zoom; + + this->m_projection = glm::ortho(0.0f, visible_width, visible_height, 0.0f, -1.0f, 1.0f); + this->m_view = glm::translate(glm::mat4(1.0f), glm::vec3(-m_position, 0.0f)); + } + + auto camera::get_zoom() const -> float + { + return this->m_zoom; + } + + auto camera::get_position() const -> glm::vec2 + { + return this->m_position; + } + + auto camera::load_uniforms(const std::shared_ptr& program) const -> void + { + const auto& uniform = program->get_uniform(); + uniform->set("u_view", this->m_view); + uniform->set("u_projection", this->m_projection); + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/components/2d/fonts/free_type_handle_wrapper.cpp b/sources/lib/core/components/2d/fonts/free_type_handle_wrapper.cpp new file mode 100644 index 0000000..b8a3516 --- /dev/null +++ b/sources/lib/core/components/2d/fonts/free_type_handle_wrapper.cpp @@ -0,0 +1,37 @@ +// +// Created by ricka on 2026-01-18. +// + +#include "free_type_handle_wrapper.hpp" + +#include +#include + +namespace sdlk +{ + static auto free_type_handle_wrapper_instance = std::make_shared(); + + free_type_handle_wrapper::free_type_handle_wrapper() + { + this->_ft_handle = msdfgen::initializeFreetype(); + if (!this->_ft_handle) + { + throw std::runtime_error("Free type handle initialization failed"); + } + } + + free_type_handle_wrapper::~free_type_handle_wrapper() + { + msdfgen::deinitializeFreetype(this->_ft_handle); + } + + auto free_type_handle_wrapper::instance() -> std::shared_ptr + { + return free_type_handle_wrapper_instance; + } + + auto free_type_handle_wrapper::raw() const -> msdfgen::FreetypeHandle* + { + return this->_ft_handle; + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/components/2d/fonts/free_type_handle_wrapper.hpp b/sources/lib/core/components/2d/fonts/free_type_handle_wrapper.hpp new file mode 100644 index 0000000..4d065f3 --- /dev/null +++ b/sources/lib/core/components/2d/fonts/free_type_handle_wrapper.hpp @@ -0,0 +1,25 @@ +// +// Created by ricka on 2026-01-18. +// +#pragma once + +#include + +#include + +namespace sdlk +{ + class free_type_handle_wrapper final + { + msdfgen::FreetypeHandle *_ft_handle{ nullptr }; + + public: + free_type_handle_wrapper(); + + [[nodiscard]] auto raw() const -> msdfgen::FreetypeHandle *; + + [[nodiscard]] static auto instance() -> std::shared_ptr; + + ~free_type_handle_wrapper(); + }; +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/components/2d/fonts/msdf_font.cpp b/sources/lib/core/components/2d/fonts/msdf_font.cpp new file mode 100644 index 0000000..f932f42 --- /dev/null +++ b/sources/lib/core/components/2d/fonts/msdf_font.cpp @@ -0,0 +1,211 @@ +// +// Created by ricka on 2026-01-18. +// + +#include +#include + +#include "free_type_handle_wrapper.hpp" + +namespace sdlk +{ + glyph_storage::~glyph_storage() + { + msdfgen::destroyFont(this->m_font_handle); + } + + auto glyph_storage::size() const -> size_t + { + return this->m_data.size(); + } + + auto glyph_storage::slice(const size_t start_index) -> glyph_geometry * + { + return &this->m_data[start_index]; + } + + auto glyph_storage::get(const char32_t &character) + -> std::optional> + { + if (this->is_loaded(character)) + { + return *this->m_glyphs[character]; + } + + return std::nullopt; + } + + auto glyph_storage::is_loaded(const char32_t &character) const -> bool + { + const auto &it = this->m_glyphs.find(character); + return it != this->m_glyphs.end(); + } + + auto glyph_storage::load(const Charset &charset) -> void + { + this->m_font_geometry.loadCharset(this->m_font_handle, 1.0, charset); + } + + auto glyph_storage::invalidate_glyphs_map() -> void + { + this->m_glyphs.clear(); + this->m_glyphs.reserve(this->m_data.size()); + + for (auto &glyph : this->m_data) + { + this->m_glyphs.emplace(glyph.getCodepoint(), &glyph); + } + } + + msdf_font::msdf_font(const std::string &font_path, msdf_font_conf &&conf) : m_conf(conf) + { + this->m_glyph_storage.m_font_handle = + msdfgen::loadFont(free_type_handle_wrapper::instance()->raw(), font_path.c_str()); + + if (!this->m_glyph_storage.m_font_handle) + { + throw std::runtime_error("Could not load font " + font_path); + } + + this->m_texture = std::make_shared(); + glGenTextures(1, &this->m_texture->get_id()); + + this->m_texture->bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + this->load(Charset::ASCII); + } + + auto msdf_font::load(const std::u32string &text) -> void + { + Charset charset{}; + for (const auto &c : text) + { + if (!this->is_loaded(c)) + { + charset.add(c); + } + } + + if (charset.empty()) + { + return; + } + + return this->load(charset); + } + + auto msdf_font::load(const char32_t &character) -> void + { + Charset charset{}; + charset.add(character); + return this->load(charset); + } + + auto msdf_font::load(const Charset &charset) -> void + { + const size_t old_size = this->m_glyph_storage.size(); + + this->m_glyph_storage.load(charset); + + this->add_to_atlas(old_size); + + this->m_glyph_storage.invalidate_glyphs_map(); + } + + auto msdf_font::is_loaded(const char32_t &c) const -> bool + { + return this->m_glyph_storage.is_loaded(c); + } + + auto msdf_font::get(const char32_t &c) + -> std::optional> + { + return this->m_glyph_storage.get(c); + } + + auto msdf_font::add_to_atlas(const size_t start_index) -> msdf_dynamic_atlas::ChangeFlags + { + const auto storage_size = this->m_glyph_storage.size(); + const auto new_storage_size = storage_size - start_index; + for (size_t i = start_index; i < storage_size; ++i) + { + this->m_glyph_storage.m_data[i].edgeColoring( + &msdfgen::edgeColoringInkTrap, m_conf.m_max_corner_angle, 0); + this->m_glyph_storage.m_data[i].wrapBox(m_conf.m_glyph_scale, + m_conf.m_pixel_range / m_conf.m_glyph_scale, + m_conf.m_miter_limit); + } + + const auto data = this->m_atlas.add( + this->m_glyph_storage.slice(start_index), static_cast(new_storage_size), false); + + this->update_texture(data); + + return data; + } + + auto msdf_font::get_bitmap() const -> bitmap_const_ref + { + return this->m_bitmap; + } + + auto msdf_font::update_texture(msdf_dynamic_atlas::ChangeFlags flags) -> void + { + this->m_texture->bind(); + this->m_bitmap = bitmap_const_ref{ this->m_atlas.atlasGenerator().atlasStorage() }; + + const bool need_full_upload = + !m_texture_initialized || flags & msdf_dynamic_atlas::ChangeFlag::RESIZED; + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (need_full_upload) + { + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGB8, + m_bitmap.width, + m_bitmap.height, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + m_bitmap.pixels); + m_texture_initialized = true; + } + else + { + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + m_bitmap.width, + m_bitmap.height, + GL_RGB, + GL_UNSIGNED_BYTE, + m_bitmap.pixels); + } + } + + auto msdf_font::get_texture() const -> std::shared_ptr + { + return this->m_texture; + } + + auto msdf_font::get_conf() const -> msdf_font_conf + { + return this->m_conf; + } + + auto msdf_font::get_metrics() const -> font_metrics + { + return this->m_glyph_storage.m_font_geometry.getMetrics(); + } + + auto msdf_font::make(const std::string &font_path) -> std::shared_ptr + { + return std::make_shared(font_path); + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/components/2d/shape/polygon_shape.cpp b/sources/lib/core/components/2d/shape/polygon_shape.cpp new file mode 100644 index 0000000..7254572 --- /dev/null +++ b/sources/lib/core/components/2d/shape/polygon_shape.cpp @@ -0,0 +1,204 @@ +// +// Created by ricka on 2025-12-31. +// + +#include +#include +#include +#include +#include + +namespace sdlk2d +{ + static auto map_points_to_polygon(const type::points& points) -> type::polygon; + static auto map_as_position_only(const type::polygon& polygon) -> std::vector; + static auto map_as_position_and_uv(const type::polygon& polygon) + -> std::vector>; + static auto map_as_position_and_color(const type::polygon& polygon) + -> std::vector>; + + polygon_shape::polygon_shape(const type::polygon& polygon) + { + this->m_style = shape_style(); + + const auto data = map_as_position_and_color(polygon); + + auto vao = std::make_shared(); + auto vbo = std::make_shared(GL_ARRAY_BUFFER); + + vao->bind(); + vbo->bind(); + + // Attrib position + color + vao->enable_attrib(0); // position + vao->enable_attrib(1); // color + + vao->attrib_pointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(data[0]), nullptr); // position + vao->attrib_pointer(1, + 4, + GL_FLOAT, + GL_FALSE, + sizeof(data[0]), + reinterpret_cast(3 * sizeof(float))); // color + + vbo->set_data(data.data(), GL_STATIC_DRAW, data.size(), sizeof(data[0])); + + vbo->unbind(); + vao->unbind(); + + this->m_geometry = std::make_shared(vao, vbo, nullptr); + } + + polygon_shape::polygon_shape(const type::points& points, const sdlk::color& uniform_color) + : polygon_shape(map_points_to_polygon(points), uniform_color) + { + } + + polygon_shape::polygon_shape(const type::polygon& polygon, const sdlk::color& color) + { + this->m_style = shape_style(color); + const auto data = map_as_position_only(polygon); + + auto vao = std::make_shared(); + auto vbo = std::make_shared(GL_ARRAY_BUFFER); + + vao->bind(); + vbo->bind(); + + // Attrib position + vao->enable_attrib(0); // position + vao->attrib_pointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(data[0]), nullptr); // position + vbo->set_data(data.data(), GL_STATIC_DRAW, data.size(), sizeof(data[0])); + + vbo->unbind(); + vao->unbind(); + + this->m_geometry = std::make_shared(vao, vbo, nullptr); + } + + polygon_shape::polygon_shape(const type::polygon& polygon, + const std::shared_ptr& texture) + { + this->m_style = shape_style(texture); + const auto data = map_as_position_and_uv(polygon); + + auto vao = std::make_shared(); + auto vbo = std::make_shared(GL_ARRAY_BUFFER); + + vao->bind(); + vbo->bind(); + + // Attrib position + UV + vao->enable_attrib(0); // position + vao->enable_attrib(2); // UV + + vao->attrib_pointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(data[0]), nullptr); // position + vao->attrib_pointer(2, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(data[0]), + reinterpret_cast(3 * sizeof(float))); // UV + + vbo->set_data(data.data(), GL_STATIC_DRAW, data.size(), sizeof(data[0])); // + + vbo->unbind(); + vao->unbind(); + + this->m_geometry = std::make_shared(vao, vbo, nullptr); + } + + auto polygon_shape::render(const std::shared_ptr& program) -> void + { + const auto& uniform = program->get_uniform(); + + const auto style_type = this->m_style.m_type; + uniform->set("u_text_rendering", false); + uniform->set("u_use_texture", style_type == shape_style::type::texture); + uniform->set("u_use_vertex_color", style_type == shape_style::type::vertex); + + switch (style_type) + { + case shape_style::type::texture: + this->m_style.m_texture->bind(); + uniform->set("u_texture", 0); + break; + case shape_style::type::uniform: + uniform->set("u_color", this->m_style.m_uniform_color.value().ndc()); + break; + default: break; + } + + renderable::render(program); + } + + static auto map_as_position_only(const type::polygon& polygon) -> std::vector + { + std::vector result; + result.reserve(polygon.size()); + + for (const auto& vertex : polygon) + { + result.emplace_back(vertex.m_position.x, vertex.m_position.y, 0.0f); + } + + return result; + } + + static auto map_as_position_and_uv(const type::polygon& polygon) + -> std::vector> + { + std::vector> result{}; + result.reserve(polygon.size()); + + for (const auto& vertex : polygon) + { + if (!vertex.m_uv.has_value()) + { + throw std::runtime_error("Vertex is missing UV for polygon_shape"); + } + + result.push_back( + { vertex.m_position.x, vertex.m_position.y, 0.0f, vertex.m_uv->x, vertex.m_uv->y }); + } + + return result; + } + + static auto map_as_position_and_color(const type::polygon& polygon) + -> std::vector> + { + std::vector> result{}; + result.reserve(polygon.size()); + + for (const auto& vertex : polygon) + { + if (!vertex.m_color.has_value()) + { + throw std::runtime_error("Vertex is missing Color for polygon_shape"); + } + + const auto ndc_color = vertex.m_color.value().ndc(); + result.push_back({ vertex.m_position.x, + vertex.m_position.y, + 0.0f, + ndc_color.r, + ndc_color.g, + ndc_color.b, + ndc_color.a }); + } + + return result; + } + + static auto map_points_to_polygon(const type::points& points) -> type::polygon + { + type::polygon polygon{}; + polygon.reserve(points.size()); + for (const auto& point : points) + { + polygon.emplace_back(point); + } + return polygon; + } +} // namespace sdlk2d diff --git a/sources/lib/core/components/2d/shape/rectangle_shape.cpp b/sources/lib/core/components/2d/shape/rectangle_shape.cpp new file mode 100644 index 0000000..ee33689 --- /dev/null +++ b/sources/lib/core/components/2d/shape/rectangle_shape.cpp @@ -0,0 +1,39 @@ +// +// Created by ricka on 2026-01-18. +// + +#include +#include + +namespace sdlk2d +{ + static auto to_points(const type::point &origin, const float &width, const float &height) + -> type::points; + + rectangle_shape::rectangle_shape(const type::point &origin, + const float &width, + const float &height, + const sdlk::color &color) + : polygon_shape(to_points(origin, width, height), color) + { + } + + static auto to_points(const type::point &origin, const float &width, const float &height) + -> type::points + { + const float x0 = origin.x; + const float y0 = origin.y; + const float x1 = origin.x + width; + const float y1 = origin.y + height; + + return { + { x0, y0 }, + { x1, y0 }, + { x1, y1 }, + + { x0, y0 }, + { x1, y1 }, + { x0, y1 }, + }; + } +} // namespace sdlk2d diff --git a/sources/lib/core/components/2d/shape/text_shape.cpp b/sources/lib/core/components/2d/shape/text_shape.cpp new file mode 100644 index 0000000..5deb630 --- /dev/null +++ b/sources/lib/core/components/2d/shape/text_shape.cpp @@ -0,0 +1,130 @@ +// +// Created by ricka on 2026-01-24. +// + +#include +#include +#include +#include +#include +#include +#include + +namespace sdlk2d +{ + text_shape::text_shape(std::u32string text, + const text_style &text_style, + const std::shared_ptr &font) + : m_style(text_style), + m_text(std::move(text)), + m_font(font) + { + auto vao = std::make_shared(); + auto vbo = std::make_shared(GL_ARRAY_BUFFER); + + vao->bind(); + vbo->bind(); + + vao->enable_attrib(0); // position + vao->enable_attrib(2); // uv + + vao->attrib_pointer(0, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(char_vertex), + reinterpret_cast(offsetof(char_vertex, m_pos))); + + vao->attrib_pointer(2, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(char_vertex), + reinterpret_cast(offsetof(char_vertex, m_uv))); + + this->m_geometry = std::make_shared(vao, vbo, nullptr); + + vbo->unbind(); + vao->unbind(); + + this->m_font->load(this->m_text); + this->update_vertices(); + } + + auto text_shape::render(const std::shared_ptr &program) -> void + { + this->m_geometry->get_vao()->bind(); + const auto uniform = program->get_uniform(); + + uniform->set("u_use_texture", true); + uniform->set("u_text_rendering", true); + uniform->set("u_use_vertex_color", false); + + uniform->set("u_px_range", this->m_font->get_conf().m_pixel_range); + uniform->set("u_color", this->m_style.m_fg_color.ndc()); + uniform->set("u_bg_color", this->m_style.m_bg_color.ndc()); + + this->m_font->get_texture()->bind(); + uniform->set("u_texture", 0); + + glDrawArrays(GL_TRIANGLES, 0, static_cast(m_vertices.size())); + } + + auto text_shape::update_vertices() -> void + { + this->m_geometry->get_vao()->bind(); + this->m_vertices.clear(); + + const auto font_metrics = this->m_font->get_metrics(); + const auto scale = this->m_style.m_size / static_cast(font_metrics.emSize); + glm::vec2 pen{ 0.0f, static_cast(font_metrics.ascenderY) * scale }; + + for (const char32_t &c : this->m_text) + { + const auto &opt_glyph = this->m_font->get(c); + if (!opt_glyph.has_value()) + continue; + + const auto glyph = opt_glyph.value().get(); + if (glyph.isWhitespace()) + { + pen.x += static_cast(glyph.getAdvance()) * scale; + continue; + } + + double pl, pb, pr, pt; + glyph.getQuadPlaneBounds(pl, pb, pr, pt); + + double ul, ub, ur, ut; + glyph.getQuadAtlasBounds(ul, ub, ur, ut); + + float x0 = pen.x + static_cast(pl) * scale; + float y0 = pen.y - static_cast(pt) * scale; + float x1 = pen.x + static_cast(pr) * scale; + float y1 = pen.y - static_cast(pb) * scale; + + const auto &atlas = this->m_font->get_bitmap(); + float u0 = static_cast(ul) / static_cast(atlas.width); + float v0 = static_cast(ut) / static_cast(atlas.height); + float u1 = static_cast(ur) / static_cast(atlas.width); + float v1 = static_cast(ub) / static_cast(atlas.height); + + m_vertices.push_back({ { x0, y0, 0 }, { u0, v0 } }); + m_vertices.push_back({ { x1, y1, 0 }, { u1, v1 } }); + m_vertices.push_back({ { x1, y0, 0 }, { u1, v0 } }); + + m_vertices.push_back({ { x0, y0, 0 }, { u0, v0 } }); + m_vertices.push_back({ { x0, y1, 0 }, { u0, v1 } }); + m_vertices.push_back({ { x1, y1, 0 }, { u1, v1 } }); + + pen.x += static_cast(glyph.getAdvance()) * scale; + } + + this->m_geometry->get_vbo()->bind(); + glBindBuffer(GL_ARRAY_BUFFER, this->m_geometry->get_vbo()->id()); + glBufferData(GL_ARRAY_BUFFER, + static_cast(m_vertices.size() * sizeof(char_vertex)), + m_vertices.data(), + GL_DYNAMIC_DRAW); + } +} // namespace sdlk2d diff --git a/sources/lib/core/components/component.cpp b/sources/lib/core/components/component.cpp new file mode 100644 index 0000000..c63dcc0 --- /dev/null +++ b/sources/lib/core/components/component.cpp @@ -0,0 +1,26 @@ +// +// Created by ricka on 2025-12-30. +// + +#include + +namespace sdlk +{ + component::component(std::vector> childs) + : m_childs(std::move(childs)) + { + } + + auto component::add_child(const std::shared_ptr &child) -> void + { + this->m_childs.push_back(child); + } + + auto component::render(const std::shared_ptr &program) -> void + { + for (const auto &child : this->m_childs) + { + child->render(program); + } + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/components/multiple_view.cpp b/sources/lib/core/components/multiple_view.cpp new file mode 100644 index 0000000..268736f --- /dev/null +++ b/sources/lib/core/components/multiple_view.cpp @@ -0,0 +1,49 @@ +// +// Created by ricka on 2025-12-30. +// + +#include +#include + +namespace sdlk +{ + multiple_view::multiple_view(const std::map>& childs) + : m_childs(childs) + { + } + + auto multiple_view::get_current() const -> std::shared_ptr + { + return m_current; + } + + auto multiple_view::switch_to(const std::string& name, const std::shared_ptr &ctx) -> void + { + if (this->m_childs.find(name) == this->m_childs.end()) + { + throw std::runtime_error("view::switch_to: no view named '" + name + "' found"); + } + + const auto &to_be_current = this->m_childs[name]; + if (ctx) + { + to_be_current->set_context(ctx); + } + + this->m_current = to_be_current; + } + + auto multiple_view::render(const std::shared_ptr& program) -> void + { + if (this->m_current) + { + this->m_current->render(program); + } + } + + auto multiple_view::add_view(const std::string& name, std::shared_ptr child) -> void + { + this->m_childs.insert(std::make_pair>( + name.data(), std::move(child))); + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/components/renderable.cpp b/sources/lib/core/components/renderable.cpp new file mode 100644 index 0000000..b73de37 --- /dev/null +++ b/sources/lib/core/components/renderable.cpp @@ -0,0 +1,31 @@ +// +// Created by ricka on 2025-12-30. +// + +#include +#include + +namespace sdlk +{ + auto renderable::render(const std::shared_ptr &program) -> void + { + if (this->m_geometry) + { + this->m_geometry->render(); + } + } + + renderable::renderable(const std::shared_ptr &geometry) : m_geometry(geometry) + { + } + + auto renderable::set_context(const std::shared_ptr &context) -> void + { + this->m_context = context; + } + + auto renderable::get_context() -> std::shared_ptr + { + return this->m_context; + } +} // namespace sdlk diff --git a/sources/lib/core/converter.cpp b/sources/lib/core/converter.cpp new file mode 100644 index 0000000..1ac2d7f --- /dev/null +++ b/sources/lib/core/converter.cpp @@ -0,0 +1,9 @@ +#include + +namespace sdlk::converter +{ + auto sdl_color_to_ndc(const SDL_Color &color) -> glm::vec4 + { + return { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; + } +} // namespace sdlk::converter diff --git a/sources/lib/core/events/event_listener.cpp b/sources/lib/core/events/event_listener.cpp new file mode 100644 index 0000000..0bae51c --- /dev/null +++ b/sources/lib/core/events/event_listener.cpp @@ -0,0 +1,51 @@ +#include + +#include + +namespace sdlk +{ + static auto event_listener_instance = std::make_shared(); + + auto event_listener::notify_event(const SDL_Event &event) -> void + { + const auto actual_type = get_event_type_value(event); + std::vector actions{}; + + for (const auto &[etype, actionList] : m_event_listeners) + { + if (etype == actual_type || etype == event_type::all) + { + actions.insert(actions.end(), actionList.begin(), actionList.end()); + } + } + + for (const auto &action : actions) + { + action.m_callback(event); + if (action.m_stop_propagation) + { + break; + } + }; + } + + auto event_listener::get_event_type_value(const SDL_Event &event) -> event_type + { + switch (event.type) + { + case SDL_KEYDOWN: return event_type::key_down; + case SDL_KEYUP: return event_type::key_up; + case SDL_WINDOWEVENT: return event_type::window_event; + case SDL_MOUSEMOTION: return event_type::mouse_motion; + case SDL_MOUSEBUTTONDOWN: return event_type::mouse_button_down; + case SDL_MOUSEBUTTONUP: return event_type::mouse_button_up; + case SDL_MOUSEWHEEL: return event_type::mouse_wheel; + default: return event_type::all; + } + } + + auto event_listener::instance() -> std::shared_ptr + { + return event_listener_instance; + } +} // namespace sdlk diff --git a/sources/lib/core/events/observer.cpp b/sources/lib/core/events/observer.cpp new file mode 100644 index 0000000..2ddd5b8 --- /dev/null +++ b/sources/lib/core/events/observer.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +namespace sdlk +{ + auto observer::add_event_listener(const event_type event_type, + event_callback callback, + const bool stop_propagation) -> void + { + event_listener::instance()->m_event_listeners[event_type].push_back( + { std::move(callback), stop_propagation }); + } +} // namespace sdlk diff --git a/sources/lib/core/geometry.cpp b/sources/lib/core/geometry.cpp new file mode 100644 index 0000000..61534a8 --- /dev/null +++ b/sources/lib/core/geometry.cpp @@ -0,0 +1,46 @@ +// +// Created by ricka on 2025-12-31. +// + +#include +#include + +namespace sdlk +{ + geometry::geometry(std::shared_ptr vao, + std::shared_ptr vbo, + std::shared_ptr ebo) + : m_vbo(std::move(vbo)), + m_ebo(std::move(ebo)), + m_vao(std::move(vao)) + { + this->m_is_indexed = this->m_ebo != nullptr; + } + + auto geometry::get_vbo() const -> std::shared_ptr + { + return this->m_vbo; + } + + auto geometry::get_vao() const -> std::shared_ptr + { + return this->m_vao; + } + + auto geometry::render() const -> void + { + this->m_vao->bind(); + + if (this->m_is_indexed) + { + glDrawElements(GL_TRIANGLES, + static_cast(this->m_ebo->get_count()), + GL_UNSIGNED_INT, + nullptr); + } + else + { + glDrawArrays(GL_TRIANGLES, 0, static_cast(this->m_vbo->get_count())); + } + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/gl/gl_buffer.cpp b/sources/lib/core/gl/gl_buffer.cpp new file mode 100644 index 0000000..1ed590c --- /dev/null +++ b/sources/lib/core/gl/gl_buffer.cpp @@ -0,0 +1,58 @@ +// +// Created by ricka on 2025-12-31. +// + +#include + +namespace sdlk +{ + gl_buffer::gl_buffer(const GLenum target) : m_target(target) + { + glGenBuffers(1, &m_id); + } + + gl_buffer::~gl_buffer() + { + if (m_id) + { + glDeleteBuffers(1, &m_id); + } + } + + auto gl_buffer::bind() const -> void + { + glBindBuffer(m_target, m_id); + } + + auto gl_buffer::unbind() const -> void + { + glBindBuffer(m_target, 0); + } + + auto gl_buffer::set_data(const void* data, + const GLenum usage, + const std::size_t count, + const std::size_t element_size) -> void + { + this->bind(); + this->m_count = count; + this->m_element_size = element_size; + glBufferData( + m_target, static_cast(this->m_count * this->m_element_size), data, usage); + } + + auto gl_buffer::id() -> GLuint& + { + return m_id; + } + + auto gl_buffer::get_count() const -> std::size_t + { + return this->m_count; + } + + auto gl_buffer::make(const GLenum target) -> std::shared_ptr + { + return std::make_shared(target); + } +} // namespace sdlk diff --git a/sources/lib/core/gl/gl_program.cpp b/sources/lib/core/gl/gl_program.cpp new file mode 100644 index 0000000..4bc297c --- /dev/null +++ b/sources/lib/core/gl/gl_program.cpp @@ -0,0 +1,59 @@ +// +// Created by ricka on 2025-12-30. +// + +#include +#include + +namespace sdlk +{ + gl_program::gl_program(const std::shared_ptr &vertex_shader, + const std::shared_ptr &fragment_shader) + { + this->m_id = glCreateProgram(); + this->m_uniform = gl_uniform::from(this); + + glAttachShader(this->m_id, vertex_shader->get_id()); + glAttachShader(this->m_id, fragment_shader->get_id()); + glLinkProgram(this->m_id); + + GLint success{}; + glGetProgramiv(this->m_id, GL_LINK_STATUS, &success); + if (!success) + { + char info_log[512]; + glGetProgramInfoLog(this->m_id, 512, nullptr, info_log); + throw std::runtime_error(std::string("Program linking failed: ") + info_log); + } + } + + auto gl_program::get_id() const -> GLuint + { + return this->m_id; + } + + auto gl_program::use() -> void + { + glUseProgram(this->m_id); + } + + auto gl_program::get_uniform() const -> std::shared_ptr + { + return this->m_uniform; + } + + auto gl_program::from_files(const std::string &vertex_path, const std::string &fragment_path) + -> std::shared_ptr + { + return std::make_shared(gl_shader::from_file(GL_VERTEX_SHADER, vertex_path), + gl_shader::from_file(GL_FRAGMENT_SHADER, fragment_path)); + } + + gl_program::~gl_program() + { + if (m_id) + { + glDeleteProgram(this->m_id); + } + } +} // namespace sdlk diff --git a/sources/lib/core/gl/gl_shader.cpp b/sources/lib/core/gl/gl_shader.cpp new file mode 100644 index 0000000..5001659 --- /dev/null +++ b/sources/lib/core/gl/gl_shader.cpp @@ -0,0 +1,48 @@ +// +// Created by ricka on 2025-12-30. +// + +#include +#include + +#include "../../../utils/file.hpp" + +namespace sdlk +{ + gl_shader::gl_shader(const GLenum type, const std::string& source) + { + const auto source_ctr = source.c_str(); + this->m_id = glCreateShader(type); + + glShaderSource(this->m_id, 1, &source_ctr, nullptr); + glCompileShader(this->m_id); + + GLint success{}; + glGetShaderiv(this->m_id, GL_COMPILE_STATUS, &success); + if (!success) + { + char info_log[512]; + glGetShaderInfoLog(this->m_id, 512, nullptr, info_log); + throw std::runtime_error(std::string("Shader compilation failed: ") + info_log); + } + } + + auto gl_shader::get_id() const -> GLuint + { + return this->m_id; + } + + auto gl_shader::from_file(const GLenum type, const std::string& file_path) + -> std::shared_ptr + { + return std::make_shared(type, file::read(file_path)); + } + + gl_shader::~gl_shader() + { + if (m_id) + { + glDeleteShader(this->m_id); + } + } +} // namespace sdlk \ No newline at end of file diff --git a/sources/core/texture.cpp b/sources/lib/core/gl/gl_texture.cpp similarity index 51% rename from sources/core/texture.cpp rename to sources/lib/core/gl/gl_texture.cpp index 804588e..4b35e28 100644 --- a/sources/core/texture.cpp +++ b/sources/lib/core/gl/gl_texture.cpp @@ -1,16 +1,22 @@ +// +// Created by ricka on 2026-01-18. +// + #include -#include +#include #include namespace sdlk { - - texture::texture(GLuint id, int width, int height) : m_id(id), m_width(width), m_height(height) + gl_texture::gl_texture(const GLuint id, const int width, const int height) + : m_id(id), + m_width(width), + m_height(height) { } - std::shared_ptr texture::from_file(const std::string& path) + std::shared_ptr gl_texture::from_file(const std::string& path) { SDL_Surface* surface = IMG_Load(path.c_str()); if (!surface) @@ -23,7 +29,7 @@ namespace sdlk return tex; } - std::shared_ptr texture::from_surface(SDL_Surface* surface) + std::shared_ptr gl_texture::from_surface(SDL_Surface* surface) { if (!surface) { @@ -34,7 +40,7 @@ namespace sdlk glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id); - GLint format = surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB; + const GLint format = surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB; glTexImage2D(GL_TEXTURE_2D, 0, @@ -49,30 +55,40 @@ namespace sdlk glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - return std::make_shared(id, surface->w, surface->h); + return std::make_shared(id, surface->w, surface->h); } - auto texture::bind(GLuint unit) const -> void + auto gl_texture::bind(const GLuint unit) const -> void { glActiveTexture(GL_TEXTURE0 + unit); glBindTexture(GL_TEXTURE_2D, this->m_id); } - auto texture::get_width() const -> int + auto gl_texture::get_width() const -> int { return this->m_width; } - auto texture::get_height() const -> int + auto gl_texture::get_height() const -> int { return this->m_height; } - texture::~texture() + auto gl_texture::get_id() -> GLuint& + { + return this->m_id; + } + + auto gl_texture::get_ratio() const -> float + { + return static_cast(this->m_width) / static_cast(this->m_height); + } + + gl_texture::~gl_texture() { if (this->m_id != 0) { glDeleteTextures(1, &this->m_id); } } -} // namespace sdlk +} // namespace sdlk2d diff --git a/sources/lib/core/gl/gl_uniform.cpp b/sources/lib/core/gl/gl_uniform.cpp new file mode 100644 index 0000000..b2d0434 --- /dev/null +++ b/sources/lib/core/gl/gl_uniform.cpp @@ -0,0 +1,75 @@ +// +// Created by ricka on 2025-12-30. +// + +#include +#include +#include +#include +#include + +namespace sdlk +{ + gl_uniform::gl_uniform(gl_program *program) : m_program(program) + {} + + auto gl_uniform::get_loc(const std::string &name) const -> GLint + { + const auto loc = glGetUniformLocation(this->m_program->get_id(), name.c_str()); + + if (loc == -1) + { + throw std::runtime_error("Cannot get the location of " + name); + } + + return loc; + } + + auto gl_uniform::from(gl_program *program) -> std::shared_ptr + { + return std::make_shared(program); + } + + // ---- scalar ---- + auto gl_uniform::set(const std::string &name, const int value) const -> void + { + glUniform1i(get_loc(name), value); + } + + auto gl_uniform::set(const std::string &name, const float value) const -> void + { + glUniform1f(get_loc(name), value); + } + + auto gl_uniform::set(const std::string &name, const bool value) const -> void + { + set(name, static_cast(value)); + } + + // ---- vectors ---- + auto gl_uniform::set(const std::string &name, const glm::vec2 &v) const -> void + { + glUniform2fv(get_loc(name), 1, glm::value_ptr(v)); + } + + auto gl_uniform::set(const std::string &name, const glm::vec3 &v) const -> void + { + glUniform3fv(get_loc(name), 1, glm::value_ptr(v)); + } + + auto gl_uniform::set(const std::string &name, const glm::vec4 &v) const -> void + { + glUniform4fv(get_loc(name), 1, glm::value_ptr(v)); + } + + // ---- matrices ---- + auto gl_uniform::set(const std::string &name, const glm::mat3 &m) const -> void + { + glUniformMatrix3fv(get_loc(name), 1, GL_FALSE, glm::value_ptr(m)); + } + + auto gl_uniform::set(const std::string &name, const glm::mat4 &m) const -> void + { + glUniformMatrix4fv(get_loc(name), 1, GL_FALSE, glm::value_ptr(m)); + } +} // namespace sdlk diff --git a/sources/lib/core/gl/gl_vertex_array.cpp b/sources/lib/core/gl/gl_vertex_array.cpp new file mode 100644 index 0000000..5c723f3 --- /dev/null +++ b/sources/lib/core/gl/gl_vertex_array.cpp @@ -0,0 +1,56 @@ +// +// Created by ricka on 2025-12-31. +// + +#include + +namespace sdlk +{ + gl_vertex_array::gl_vertex_array() + { + glGenVertexArrays(1, &m_id); + } + + gl_vertex_array::~gl_vertex_array() + { + if (m_id) + { + glDeleteVertexArrays(1, &m_id); + } + } + + auto gl_vertex_array::bind() const -> void + { + glBindVertexArray(m_id); + } + + auto gl_vertex_array::unbind() const -> void + { + glBindVertexArray(0); + } + + auto gl_vertex_array::enable_attrib(const GLuint index) -> void + { + glEnableVertexAttribArray(index); + } + + auto gl_vertex_array::attrib_pointer(const GLuint index, + const GLint size, + const GLenum type, + const GLboolean normalized, + const GLsizei stride, + const void* offset) -> void + { + glVertexAttribPointer(index, size, type, normalized, stride, offset); + } + + auto gl_vertex_array::id() -> GLuint& + { + return m_id; + } + + auto gl_vertex_array::make() -> std::shared_ptr + { + return std::make_shared(); + } +} // namespace sdlk diff --git a/sources/core/glad.cpp b/sources/lib/core/glad/glad.cpp similarity index 100% rename from sources/core/glad.cpp rename to sources/lib/core/glad/glad.cpp diff --git a/sources/lib/core/imgui/imgui_drawer_utils.cpp b/sources/lib/core/imgui/imgui_drawer_utils.cpp new file mode 100644 index 0000000..b835aef --- /dev/null +++ b/sources/lib/core/imgui/imgui_drawer_utils.cpp @@ -0,0 +1,36 @@ +// +// Created by ricka on 2026-02-06. +// + +#include +#include + +namespace sdlk::imgui_drawer_utils +{ + auto centered_window(const std::function& size_getter, + const std::function& renderable) -> void + { + const auto size = size_getter(); + const ImGuiViewport* vp = ImGui::GetMainViewport(); + const ImVec2 pos( + vp->Pos.x + (vp->Size.x - size.x) * 0.5f, vp->Pos.y + (vp->Size.y - size.y) * 0.5f); + + ImGui::SetNextWindowPos(pos, ImGuiCond_Always); + ImGui::SetNextWindowSize(size, ImGuiCond_Always); + + renderable(); + } + + auto image(const std::shared_ptr& texture, const ImVec2& size) -> void + { + ImGui::Image(static_cast(static_cast(texture->get_id())), size); + } + + auto spacing(const int iteration) -> void + { + for (int i = 0; i < iteration; ++i) + { + ImGui::Spacing(); + } + } +} // namespace sdlk::imgui_drawer_utils \ No newline at end of file diff --git a/sources/lib/core/imgui/imgui_renderable.cpp b/sources/lib/core/imgui/imgui_renderable.cpp new file mode 100644 index 0000000..e0aa136 --- /dev/null +++ b/sources/lib/core/imgui/imgui_renderable.cpp @@ -0,0 +1,24 @@ +// +// Created by ricka on 2026-02-04. +// + +#include + +namespace sdlk +{ + imgui_renderable::imgui_renderable(const std::function &render) : + _render(render) + { + } + + auto imgui_renderable::render(const std::shared_ptr& program) -> void + { + _render(); + } + + auto imgui_renderable::make(const std::function& render) + -> std::shared_ptr + { + return std::make_shared(render); + } +} // namespace sdlk diff --git a/sources/lib/core/imgui/imgui_wrapper.cpp b/sources/lib/core/imgui/imgui_wrapper.cpp new file mode 100644 index 0000000..0c3f182 --- /dev/null +++ b/sources/lib/core/imgui/imgui_wrapper.cpp @@ -0,0 +1,83 @@ +// +// Created by ricka on 2026-02-01. +// + +#include "imgui_wrapper.hpp" + +#include +#include +#include + +namespace sdlk +{ + imgui_wrapper::imgui_wrapper(SDL_Window *window, + SDL_GLContext *gl_context, + const std::string &glsl_version, + const imgui_wrapper_conf &conf) + : m_conf(conf) + { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + + if (m_conf.m_theme == imgui_theme::dark) + { + ImGui::StyleColorsDark(); + } + else + { + ImGui::StyleColorsLight(); + } + + auto &io = get_io(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + + const auto dpi = get_dpi(); + auto &style = ImGui::GetStyle(); + style.ScaleAllSizes(dpi); + style.FontScaleDpi = dpi; + + ImGui_ImplSDL2_InitForOpenGL(window, gl_context); + ImGui_ImplOpenGL3_Init(glsl_version.c_str()); + } + + auto imgui_wrapper::process_event(const SDL_Event &event) -> void + { + ImGui_ImplSDL2_ProcessEvent(&event); + } + + auto imgui_wrapper::draw() -> void + { + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } + + auto imgui_wrapper::render() -> void + { + ImGui::Render(); + } + + auto imgui_wrapper::new_frame() -> void + { + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + } + + auto imgui_wrapper::get_io() -> ImGuiIO & + { + return ImGui::GetIO(); + } + + auto imgui_wrapper::get_dpi() -> float + { + return ImGui_ImplSDL2_GetContentScaleForDisplay(0); + } + + imgui_wrapper::~imgui_wrapper() + { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + } +} // namespace sdlk diff --git a/sources/lib/core/imgui/imgui_wrapper.hpp b/sources/lib/core/imgui/imgui_wrapper.hpp new file mode 100644 index 0000000..f56dd1c --- /dev/null +++ b/sources/lib/core/imgui/imgui_wrapper.hpp @@ -0,0 +1,47 @@ +// +// Created by ricka on 2026-02-01. +// + +#pragma once + +#include +#include +#include + +#include + +namespace sdlk +{ + + enum class imgui_theme + { + dark, + light + }; + + struct imgui_wrapper_conf + { + imgui_theme m_theme = imgui_theme::dark; + }; + + class imgui_wrapper + { + imgui_wrapper_conf m_conf; + + public: + imgui_wrapper(SDL_Window *window, + SDL_GLContext *gl_context, + const std::string &glsl_version, + const imgui_wrapper_conf &conf = {}); + + static auto draw() -> void; + static auto render() -> void; + static auto new_frame() -> void; + static auto process_event(const SDL_Event &event) -> void; + + [[nodiscard]] static auto get_io() -> ImGuiIO &; + [[nodiscard]] static auto get_dpi() -> float; + + virtual ~imgui_wrapper(); + }; +} // namespace sdlk \ No newline at end of file diff --git a/sources/lib/core/shaders/fragment.glsl b/sources/lib/core/shaders/fragment.glsl new file mode 100644 index 0000000..06f3403 --- /dev/null +++ b/sources/lib/core/shaders/fragment.glsl @@ -0,0 +1,41 @@ +#version 330 core + +in vec4 out_color; +in vec2 text_coord; + +out vec4 frag_color; + +uniform vec4 u_bg_color; +uniform sampler2D u_texture; +uniform float u_px_range; +uniform bool u_use_texture; +uniform bool u_text_rendering; +uniform bool u_use_vertex_color; + +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +float screenPxRange() { + vec2 unitRange = vec2(u_px_range) / vec2(textureSize(u_texture, 0)); + vec2 screenTexSize = vec2(1.0) / fwidth(text_coord); + return max(0.5 * dot(unitRange, screenTexSize), 1.0); +} + +vec4 msdf() { + vec3 msd = texture(u_texture, text_coord).rgb; + float sd = median(msd.r, msd.g, msd.b); + float screenPxDistance = screenPxRange()*(sd - 0.5); + float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); + return mix(u_bg_color, out_color, opacity); +} + +void main(){ + if (u_text_rendering){ + frag_color = msdf(); + } else if (u_use_texture){ + frag_color = texture(u_texture, text_coord); + } else { + frag_color = out_color; + } +} diff --git a/sources/lib/core/shaders/vertex.glsl b/sources/lib/core/shaders/vertex.glsl new file mode 100644 index 0000000..6135cd9 --- /dev/null +++ b/sources/lib/core/shaders/vertex.glsl @@ -0,0 +1,23 @@ +#version 330 core + +layout (location = 0) in vec3 a_pos; +layout (location = 1) in vec4 a_color; +layout (location = 2) in vec2 a_uv; + +uniform mat4 u_view; +uniform mat4 u_projection; + +uniform bool u_use_texture; +uniform bool u_use_vertex_color; + +uniform vec4 u_color; + +out vec4 out_color; +out vec2 text_coord; + +void main() +{ + text_coord = u_use_texture ? a_uv : vec2(1.0); + out_color = u_use_vertex_color ? a_color : u_color; + gl_Position = u_projection * u_view * vec4(a_pos, 1.0); +} diff --git a/sources/lib/core/transform.cpp b/sources/lib/core/transform.cpp new file mode 100644 index 0000000..2c72d82 --- /dev/null +++ b/sources/lib/core/transform.cpp @@ -0,0 +1,87 @@ +// +// Created by ricka on 2025-12-31. +// + +#include +#include +#include + +namespace sdlk +{ + auto transform::compose() -> void + { + const glm::mat4 T = glm::translate(glm::mat4(1.0f), m_translation); + const glm::mat4 R = + glm::rotate(glm::mat4(1.0f), glm::radians(m_rotation_degrees), glm::vec3(0, 0, 1)); + const glm::mat4 S = glm::scale(glm::mat4(1.0f), glm::vec3(m_scale)); + + m_model = T * R * S; + } + + auto transform::set_translation(const glm::vec2 offset) -> void + { + this->m_translation = glm::vec3(offset, 0.0f); + this->compose(); + } + + auto transform::set_rotation(const float angle_degrees) -> void + { + this->m_rotation_degrees = angle_degrees; + this->compose(); + } + + auto transform::set_scale(const float scale) -> void + { + this->m_scale = scale; + this->compose(); + } + + auto transform::add_translation(const glm::vec2 offset) -> void + { + this->m_translation += glm::vec3(offset, 0.0f); + this->compose(); + } + + auto transform::add_rotation(const float angle_degrees) -> void + { + this->m_rotation_degrees += angle_degrees; + this->compose(); + } + + auto transform::add_scale(const float scale) -> void + { + this->m_scale += scale; + + if (m_scale < 0.01f) + { + this->m_scale = 0.01f; // prevent negative or zero scale + } + + this->compose(); + } + + auto transform::set_model(const glm::mat4& model) -> void + { + this->m_model = model; + } + + auto transform::get_scale() const -> float + { + return this->m_scale; + } + + auto transform::get_rotation() const -> float + { + return this->m_rotation_degrees; + } + + auto transform::get_translation() const -> glm::vec3 + { + return this->m_translation; + } + + auto transform::get_model() const -> glm::mat4 + { + return this->m_model; + } +} // namespace sdlk diff --git a/sources/lib/extra/nfd_wrapper.cpp b/sources/lib/extra/nfd_wrapper.cpp new file mode 100644 index 0000000..e3186c3 --- /dev/null +++ b/sources/lib/extra/nfd_wrapper.cpp @@ -0,0 +1,94 @@ +// +// Created by ricka on 2026-02-07. +// + +#include + +#include +#include + +namespace sdlk +{ + const auto INSTANCE = std::make_shared(); + + nfd_wrapper::nfd_wrapper() + { + NFD_Init(); + } + + nfd_wrapper::~nfd_wrapper() + { + NFD_Quit(); + } + + auto nfd_wrapper::instance() -> std::shared_ptr + { + return INSTANCE; + } + + auto nfd_wrapper::open_file_dialog(const std::vector& filters) + -> nfd_open_dialog_result + { + std::vector filters_value{}; + filters_value.reserve(filters.size()); + + for (const auto& [m_name, m_extensions] : filters) + { + filters_value.emplace_back( + nfdu8filteritem_t{ .name = m_name.c_str(), .spec = m_extensions.c_str() }); + } + + nfdu8char_t* output_path = nullptr; + nfdopendialogu8args_t args{}; + + args.filterList = filters_value.data(); + args.filterCount = filters_value.size(); + + switch (const nfdresult_t _result = NFD_OpenDialogU8_With(&output_path, &args)) + { + case NFD_OKAY: + { + std::string path_str(output_path); + NFD_FreePathU8(output_path); + return nfd_open_dialog_result(nfd_open_dialog_result::type::success, path_str); + } + case NFD_CANCEL: return nfd_open_dialog_result(nfd_open_dialog_result::type::canceled); + case NFD_ERROR: + default: + std::cerr << "NFD error: " << NFD_GetError() << std::endl; + return nfd_open_dialog_result(nfd_open_dialog_result::type::error); + } + } + + auto nfd_wrapper::open_folder_dialog() -> nfd_open_dialog_result + { + nfdu8char_t* out_path = nullptr; + constexpr nfdpickfolderu8args_t args{}; + + switch (const nfdresult_t result = NFD_PickFolderU8_With(&out_path, &args)) + { + case NFD_OKAY: + { + std::string path(out_path); + NFD_FreePathU8(out_path); + + return nfd_open_dialog_result( + nfd_open_dialog_result::type::success, + path + ); + } + + case NFD_CANCEL: + return nfd_open_dialog_result( + nfd_open_dialog_result::type::canceled + ); + + case NFD_ERROR: + default: + std::cerr << "NFD error: " << NFD_GetError() << std::endl; + return nfd_open_dialog_result( + nfd_open_dialog_result::type::error + ); + } + } +} // namespace sdlk diff --git a/sources/utils/file.cpp b/sources/utils/file.cpp new file mode 100644 index 0000000..1223e5a --- /dev/null +++ b/sources/utils/file.cpp @@ -0,0 +1,23 @@ +// +// Created by ricka on 2025-12-30. +// + +#include "file.hpp" + +#include + +namespace sdlk::file +{ + std::string read(const std::string &path) + { + std::ifstream file(path, std::ios::binary); + if (!file) + { + throw std::runtime_error("Cannot open file: " + path); + } + + std::ostringstream ss; + ss << file.rdbuf(); + return ss.str(); + } +} \ No newline at end of file diff --git a/sources/utils/file.hpp b/sources/utils/file.hpp new file mode 100644 index 0000000..2de267d --- /dev/null +++ b/sources/utils/file.hpp @@ -0,0 +1,13 @@ +// +// Created by ricka on 2025-12-30. +// + +#pragma once + +#include +#include + +namespace sdlk::file +{ + std::string read(const std::string &path); +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 701a0cc..6a5932f 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -6,7 +6,8 @@ "sdl2", "glm", "earcut-hpp", + "sdl2-image", "freetype", - "sdl2-image" + "nlohmann-json" ] } \ No newline at end of file