diff --git a/.clang-format b/.clang-format index 952c5ab..2a2c48e 100644 Binary files a/.clang-format and b/.clang-format differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b0322d..6105d88 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED true) option(AMY_GOD_DEV "Turn this on if you think you are god" OFF) # THis option should be disabled if you use STB_IMAGE in you main project set(AMY_BUILD_STB_IMAGE "Include STB Image code" CACHE BOOL 1) +set(AMY_WITH_MPG123 "Include MP3 Support" CACHE BOOL 1) #add_subdirectory(vendor/libpicasso) @@ -15,6 +16,7 @@ add_library(${PROJECT_NAME} STATIC source/app.cpp source/assets.cpp source/amethyst.cpp + source/color.cpp source/image.cpp source/renderer.cpp source/texture.cpp @@ -24,12 +26,23 @@ add_library(${PROJECT_NAME} STATIC source/iron/drawlist.cpp source/maths/mat.cpp ) -target_include_directories(${PROJECT_NAME} PUBLIC include) +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${DEVKITPRO}/portlibs/3ds/include +) +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/source/internal +) #target_link_libraries(${PROJECT_NAME} PUBLIC pica::pica) -target_link_libraries(${PROJECT_NAME} PUBLIC m z ctru citro3d) -target_compile_definitions(${PROJECT_NAME} PUBLIC AMY_3DS AMY_STB_IMAGE=${AMY_BUILD_STB_IMAGE}) +target_link_libraries(${PROJECT_NAME} PUBLIC m z ctru citro3d mpg123) +target_compile_definitions(${PROJECT_NAME} PUBLIC + AMY_3DS + AMY_STB_IMAGE=${AMY_BUILD_STB_IMAGE} + AMY_WITH_MPG123=${AMY_WITH_MPG123} +) target_compile_options(${PROJECT_NAME} PUBLIC -Wno-psabi) add_subdirectory(example) install(TARGETS ${PROJECT_NAME}) +install(DIRECTORY include DESTINATION .) \ No newline at end of file diff --git a/README.md b/README.md index df1ab8f..9f55d5f 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,29 @@ # amethyst -2d Renderer Library made for ReCraft3DS based on palladium. \ No newline at end of file +2d Renderer Library made for ReCraft3DS based on palladium. + +## Usage + +### Submodule: + +Add amethyst as submodule to your repository + +```bash +git submodule add https://github.com/tobid7/amethyst vendor/amethyst +``` + +Add it as submodule to your `CMakeLists.txt` + +```cmake +add_subdirectory(vendor/amethyst) +target_link_libraries(${PROJECT_NAME} PUBLIC amethyst) +``` + +### Installed + +Coming Soon... + +## Credits + +- [tobid7](https://github.com/tobid7) Lead Developer, Stealing a lot of code of [Palladium](https://github.com/tobid7/palladium) +- [nothings](https://github.com/nothings) [stb_image](https://github.com/nothings/stb) \ No newline at end of file diff --git a/example/source/main.cpp b/example/source/main.cpp index e864e72..b4c6b01 100755 --- a/example/source/main.cpp +++ b/example/source/main.cpp @@ -41,10 +41,11 @@ class Example : public Amy::App { Top->Clear(); Top->Use(); dl->DrawTex(Mgr->Get("icon")); - dl->DrawRectFilled(Amy::fvec2(50, 0), 48, 0x99999999); - dl->DrawCircleFilled(Amy::fvec2(200, 120), 50, 0xffffffff, 40); + dl->DrawRectFilled(Amy::fvec2(50, 0), 48, Amy::Color(255, 255, 255, 160)); + // Color only at runtime...yet (Palladium 0.6.0 dev is targeting this) + dl->DrawCircleFilled(Amy::fvec2(200, 120), 50, Amy::Color("#ffffff"), 40); dl->DrawSolid(); - dl->DrawRectFilled(0, 50, 0x4400ff00); + dl->DrawRectFilled(0, 50, Amy::Color(0.f, 1.f, 0.f, 1.f)); Iron::NewFrame(); Iron::DrawOn(Top); diff --git a/include/amethyst.hpp b/include/amethyst.hpp index 0252afd..15c60a2 100755 --- a/include/amethyst.hpp +++ b/include/amethyst.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/include/amethyst/color.hpp b/include/amethyst/color.hpp new file mode 100644 index 0000000..6ff8d0b --- /dev/null +++ b/include/amethyst/color.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include + +// We all know where the code is from ... RenderD7 -> Palladium/Amy + +namespace Amy { +class Color { + public: + /** + * Default Constructor (all variables are set to 0) + */ + constexpr Color() : r(0), g(0), b(0), a(0) {} + constexpr ~Color() {} + /** + * Constructor for 32Bit Color Input + * @param color 32Bit Color value + */ + constexpr Color(ui clr) { + a = (clr >> 24) & 0xff; + b = (clr >> 16) & 0xff; + g = (clr >> 8) & 0xff; + r = clr & 0xff; + } + /** + * Constructor for 8Bit Input + * @param r Red Value + * @param g Green Value + * @param b Blue Value + * @param a Optional Alpha Value (Defaults to 255) + */ + constexpr Color(int r, int g, int b, int a = 255) : r(r), g(g), b(b), a(a) {} + /** + * Constructor for float Input + * @param r Red Value + * @param g Green Value + * @param b Blue Value + * @param a Optional Alpha Value (Defaults to 1.0f) + * @note There is no Check if the number is between 0.0 and 1.0 + */ + constexpr Color(float r, float g, float b, float a = 1.f) + : r(static_cast(255.f * r)), + g(static_cast(255.f * g)), + b(static_cast(255.f * b)), + a(static_cast(255.f * a)) {} + /** + * Constructor for Hex Input + * @param hex Hex String in `#ffffff` or `#ffffffff` format + */ + Color(const std::string& hex) { Hex(hex); } + + /** + * Create Color Object by Hex String + * @param hex Hex String in `#ffffff` or `#ffffffff` format + * @return Color class itself + */ + Color& Hex(const std::string& hex); + /** + * Convert this Color Object to Hex string + * @param rgba [default false] sets if 8 or 6 digit color should be returned + * @return Color Hex String + */ + std::string Hex(bool rgba = false) const; + + /** + * Fade from Current to another Color + * @param color Color to fade to + * @param p Amount (supports -1.0 to 1.0 for use of sine) + * @return Class Reference + */ + constexpr Color& Fade(const Color& color, float p) { + a = static_cast((color.a - a) * ((p + 1.f) / 2)); + b = static_cast((color.b - b) * ((p + 1.f) / 2)); + g = static_cast((color.g - g) * ((p + 1.f) / 2)); + r = static_cast((color.r - r) * ((p + 1.f) / 2)); + return *this; + } + + /** + * Get 32Bit Color Value + * @return 32Bit Color Value (ABGR iirc) + */ + constexpr ui Get() const { return (a << 24) | (b << 16) | (g << 8) | r; } + /** + * Get The Luminance of the Color + * @return luminance (from 0.0 to 1.0) + */ + constexpr float Luminance() const { + // For Reference https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness + return (0.3 * (r / 255.f) + 0.59 * (g / 255.f) + 0.11 * (b / 255.f)); + } + /** + * Check if the Color is Light or Dark + * @return true if light + */ + constexpr bool IsLight() const { return (Luminance() >= 0.5); } + + /** + * Operator to cast Color to 32Bit Value + * @return 32Bit Color Value + */ + constexpr operator ui() const { return Get(); } + + /** Public Access Data section */ + uc r; + uc g; + uc b; + uc a; +}; +} // namespace Amy \ No newline at end of file diff --git a/include/amethyst/iron.hpp b/include/amethyst/iron.hpp index 875546d..33cd7a6 100644 --- a/include/amethyst/iron.hpp +++ b/include/amethyst/iron.hpp @@ -34,7 +34,8 @@ class Iron { public: Command() = default; ~Command() = default; - using ref = up; + AMY_UNIQUE(Command) + Command& Add(const u16& idx) { IndexBuf.push_back(VertexBuf.size() + idx); return *this; @@ -66,20 +67,26 @@ class Iron { Font() = default; ~Font() = default; + AMY_SHARED(Font) void LoadTTF(ksr path, int pxh = 32); void LoadTTF(kvr data, int pxh = 32); + /** + * Bitmap Font BTW + */ + void LoadBMF(ksr path); Codepoint& GetCodepoint(ui c); fvec2 GetTextBounds(ksr text, float scale); - void CmdTextEx(vec& cmds, const fvec2& pos, ui color, + void CmdTextEx(vec& cmds, const fvec2& pos, ui color, float scale, ksr text, ui flags = 0, const fvec2& box = 0); void pMakeAtlas(bool final, vec& pixels, int size, Texture* tex); int PxHeight; int PxFactor = 24; vec Textures; + std::map pCodeMap; }; class Drawlist { @@ -93,11 +100,11 @@ class Iron { Drawlist(Drawlist&&) noexcept = default; Drawlist& operator=(Drawlist&&) noexcept = default; - std::vector& Data() { return pData; } + std::vector& Data() { return pData; } void Merge(Drawlist* list); - Command::ref NewCommand(); - void Push(Command ::ref cmd); + Command::Ref NewCommand(); + void Push(Command ::Ref cmd); void Clear(); void DrawSolid(); @@ -147,14 +154,15 @@ class Iron { if (!ClipRects.empty()) ClipRects.pop(); } - operator std::vector&() { return pData; } + operator std::vector&() { return pData; } private: void clipCmd(Command* ptr); - std::vector pData; + std::vector pData; std::vector pPath; Texture* pTex = nullptr; std::stack ClipRects; + Font* pCurrentFont; int pLayer = 0; }; Iron() = default; @@ -164,7 +172,7 @@ class Iron { static void Exit(); static void NewFrame(); static void DrawOn(C3D::Screen* screen); - static void Draw(const std::vector& data); + static void Draw(const std::vector& data); static Texture* WhiteTex() { return m_solid; } /** Static renderer utility funcs */ diff --git a/include/amethyst/types.hpp b/include/amethyst/types.hpp index e53ed23..8179a65 100644 --- a/include/amethyst/types.hpp +++ b/include/amethyst/types.hpp @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -14,6 +14,20 @@ #include #include +#define AMY_SHARED(x) \ + using Ref = std::shared_ptr; \ + template \ + static Ref New(Args&&... args) { \ + return std::make_shared(std::forward(args)...); \ + } + +#define AMY_UNIQUE(x) \ + using Ref = std::unique_ptr; \ + template \ + static Ref New(Args&&... args) { \ + return std::make_unique(std::forward(args)...); \ + } + namespace Amy { using uc = unsigned char; using us = unsigned short; diff --git a/source/color.cpp b/source/color.cpp new file mode 100644 index 0000000..1cc5603 --- /dev/null +++ b/source/color.cpp @@ -0,0 +1,50 @@ +#include + +namespace Amy { +// The Solution of the biggest performance issue +// A Simple Lookup table +static const std::map HEX_DEC = { + {'0', 0}, {'1', 1}, {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5}, + {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'a', 10}, {'b', 11}, + {'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}, {'A', 10}, {'B', 11}, + {'C', 12}, {'D', 13}, {'E', 14}, {'F', 15}}; + +Color& Color::Hex(const std::string& hex) { + // zu dumm nen safetey check zu schreiben wadafuk + /**#ifndef AMY_GOD_DEV + /// Safetey check (not required if you just program well xd) + if (hex.length() != 7 || hex.length() != 9 || hex.length() != 6 || + hex.length() != 8 || std::find_if(hex.begin(), hex.end(), [](char c) { + return !std::isxdigit(c); + }) != hex.end()) { + return *this; + } + #endif*/ + int offset = ((hex.length() == 7 || hex.length() == 9) ? 1 : 0); + r = HEX_DEC.at(hex[offset]) * 16 + HEX_DEC.at(hex[offset + 1]); + offset += 2; + g = HEX_DEC.at(hex[offset]) * 16 + HEX_DEC.at(hex[offset + 1]); + offset += 2; + b = HEX_DEC.at(hex[offset]) * 16 + HEX_DEC.at(hex[offset + 1]); + offset += 2; + if (hex.length() == 9) { + a = HEX_DEC.at(hex[offset]) * 16 + HEX_DEC.at(hex[offset + 1]); + } else { + a = 255; + } + return *this; +} + +std::string Color::Hex(bool rgba) const { + /** Need to int cast (so it is used as num and not char...) */ + std::stringstream s; + s << "#"; + s << std::hex << std::setw(2) << std::setfill('0') << (int)r; + s << std::hex << std::setw(2) << std::setfill('0') << (int)g; + s << std::hex << std::setw(2) << std::setfill('0') << (int)b; + if (rgba) { + s << std::hex << std::setw(2) << std::setfill('0') << (int)a; + } + return s.str(); +} +} // namespace Amy \ No newline at end of file diff --git a/include/stb_image.h b/source/internal/stb_image.h old mode 100755 new mode 100644 similarity index 100% rename from include/stb_image.h rename to source/internal/stb_image.h diff --git a/source/iron/drawlist.cpp b/source/iron/drawlist.cpp index 65eb025..bb6537e 100644 --- a/source/iron/drawlist.cpp +++ b/source/iron/drawlist.cpp @@ -33,8 +33,8 @@ void Iron::Drawlist::Clear() { pLayer = 0; } -Iron::Command::ref Iron::Drawlist::NewCommand() { - auto ret = std::make_unique(); +Iron::Command::Ref Iron::Drawlist::NewCommand() { + auto ret = Command::New(); ret->Layer = pLayer; ret->Index = pData.size(); ret->Tex = pTex; @@ -48,7 +48,7 @@ void Iron::Drawlist::clipCmd(Command* ptr) { } } -void Iron::Drawlist::Push(Command::ref cmd) { pData.push_back(std::move(cmd)); } +void Iron::Drawlist::Push(Command::Ref cmd) { pData.push_back(std::move(cmd)); } void Iron::Drawlist::DrawSolid() { pTex = Iron::WhiteTex(); } diff --git a/source/iron/font.cpp b/source/iron/font.cpp new file mode 100644 index 0000000..7e23944 --- /dev/null +++ b/source/iron/font.cpp @@ -0,0 +1,24 @@ +#include + +namespace Amy { +void Iron::Font::LoadBMF(ksr path) {} + +void Iron::Font::LoadTTF(ksr path, int size) {} + +void Iron::Font::pMakeAtlas(bool final, vec& font_tex, int texszs, + Texture* tex) { + tex->Load(font_tex, texszs, texszs); + Textures.push_back(tex); +} + +Iron::Font::Codepoint& Iron::Font::GetCodepoint(ui cp) { + // Check if codepoijt exist or return a static invalid one + auto res = pCodeMap.find(cp); + if (res == pCodeMap.end()) { + static Codepoint invalid; + invalid.Valid = false; + return invalid; + } + return res->second; +} +} // namespace Amy \ No newline at end of file diff --git a/source/iron/iron.cpp b/source/iron/iron.cpp index 4c12f30..21945a4 100644 --- a/source/iron/iron.cpp +++ b/source/iron/iron.cpp @@ -83,7 +83,7 @@ void Iron::DrawOn(C3D::Screen* screen) { m_shader->SetMat4(uLocProj, m_mtx); } -void Iron::Draw(const std::vector& data) { +void Iron::Draw(const std::vector& data) { // disable depthtest cause we have no z buffer C3D::DepthTest(false); pFragConfig();