diff --git a/example/source/main.cpp b/example/source/main.cpp index 07f4afc..2152bbd 100755 --- a/example/source/main.cpp +++ b/example/source/main.cpp @@ -46,10 +46,17 @@ class example : public amy::app { public: example() { amy::ctru::init(); + consoleInit(GFX_BOTTOM, NULL); amy::c3d::init(); m_top = amy::c3d::createScreen(GFX_TOP, GFX_LEFT); amy::iron::init(); - }; + dl = new amy::iron::drawlist(); + dl->drawSolid(); + // throw std::runtime_error(std::format( + // "solid tex: {:#08x}\nsize: {}\nptr: {:#08x}", + // (amy::ui)amy::iron::whiteTex(), amy::iron::whiteTex()->size(), + // (amy::ui)amy::iron::whiteTex()->ptr())); + }; ~example() { amy::c3d::deleteScreen(m_top); amy::c3d::deinit(); @@ -59,13 +66,17 @@ class example : public amy::app { amy::c3d::startFrame(); m_top->startDraw(); m_top->clear(); + dl->drawRectFilled(0, 50, 0xff00ff00); amy::iron::newFrame(); amy::iron::drawOn(m_top); + amy::iron::draw(dl->data()); + dl->clear(); amy::c3d::endFrame(); } private: amy::c3d::screen* m_top = nullptr; + amy::iron::drawlist* dl = nullptr; }; int main() { diff --git a/include/amethyst/c3d.hpp b/include/amethyst/c3d.hpp index 39dcf95..65b8438 100755 --- a/include/amethyst/c3d.hpp +++ b/include/amethyst/c3d.hpp @@ -83,7 +83,8 @@ class c3d { static void deleteScreen(screen* screen); static void drawArrays(int start, int count, GPU_Primitive_t prim = GPU_TRIANGLES); - static void drawElements(int count, const void* idx_ptr, int type = GPU_SHORT, + static void drawElements(int count, const void* idx_ptr, + int type = C3D_UNSIGNED_SHORT, GPU_Primitive_t prim = GPU_TRIANGLES); static void depthTest(bool on, GPU_TESTFUNC func = GPU_GREATER, GPU_WRITEMASK mask = GPU_WRITE_ALL); diff --git a/include/amethyst/iron.hpp b/include/amethyst/iron.hpp index 0b04a40..d36db2a 100644 --- a/include/amethyst/iron.hpp +++ b/include/amethyst/iron.hpp @@ -1,10 +1,10 @@ #pragma once -#include #include #include #include #include +#include #include #include @@ -12,18 +12,23 @@ namespace amy { class iron { public: struct vertex { - vertex(float x, float y, float u, float v, u32 clr) { + vertex(float x, float y, float u, float v, ui clr) { pos.x = x; pos.y = y; uv.x = x; uv.y = y; color = clr; } + vertex(const fvec2& pos, const fvec2& uv, ui clr) { + this->pos = pos; + this->uv = uv; + this->color = clr; + } vertex() {} amy::fvec2 pos; amy::fvec2 uv; - u32 color = 0; + ui color = 0; }; class command { public: @@ -57,6 +62,8 @@ class iron { drawlist(drawlist&&) noexcept = default; drawlist& operator=(drawlist&&) noexcept = default; + std::vector& data() { return m_data; } + void merge(drawlist* list); command::ref newCommand(); void push(command ::ref cmd); @@ -65,9 +72,56 @@ class iron { void drawSolid(); void drawTex(texture* tex) { m_tex = tex; } + /** Draw Api */ + void drawRect(const fvec2& pos, const fvec2& size, ui color, + int thickness = 1); + void drawRectFilled(const fvec2& pos, const fvec2& size, ui color); + void drawTriangle(const fvec2& a, const fvec2& b, const fvec2& c, ui color, + int thickness = 1); + void drawTriangleFilled(const fvec2& a, const fvec2& b, const fvec2& c, + ui color); + void drawCircle(const fvec2& center, float radius, ui color, int segments, + int thickness = 1); + void drawCircleFilled(const fvec2& center, float radius, ui color, + int segments); + void drawText(const fvec2& pos, const std::string& text, ui color); + void drawTextEx(const fvec2& pos, const std::string& text, ui color, + ui flags, const fvec2& box = 0); + void drawLine(const fvec2& a, const fvec2& b, ui color, int thickness = 1); + void drawPolyLine(const std::vector& points, ui color, ui flags = 0, + int thickness = 1); + void drawConvexPolyFilled(const std::vector& points, ui color); + + /** Path api */ + void pathAdd(const fvec2& pos) { m_path.push_back(std::move(pos)); } + void pathClear() { m_path.clear(); } + void pathReserve(size_t count) { m_path.reserve(m_path.size() + count); } + void pathStroke(ui color, int thickness = 1, ui flags = 0) { + drawPolyLine(m_path, color, flags, thickness); + pathClear(); + } + void pathFill(ui color) { drawConvexPolyFilled(m_path, color); } + void pathArcToN(const fvec2& c, float radius, float a_min, float a_max, + int segments); + void pathFastArcToN(const fvec2& c, float radius, float a_min, float a_max, + int segments); + void pathRect(const fvec2& a, const fvec2& b, float rounding = 0.f); + void pathRectEx(const fvec2& a, const fvec2& b, float rounfing, ui flags); + + void pushClipRect(const fvec4& clip) { clipRects.push(clip); } + void popClipRect() { + if (!clipRects.empty()) clipRects.pop(); + } + + operator std::vector&() { return m_data; } + private: + void clipCmd(command* ptr); std::vector m_data; - texture* m_tex; + std::vector m_path; + texture* m_tex = nullptr; + std::stack clipRects; + int m_layer = 0; }; iron() = default; ~iron() = default; @@ -76,10 +130,28 @@ class iron { static void newFrame(); static void drawOn(c3d::screen* screen); static void draw(const std::vector& data); + static texture* whiteTex() { return m_solid; } + + /** Static renderer utility funcs */ + + static void rotateCorner(fvec2& pos, float s, float c); + static rect primRect(const fvec2& pos, const fvec2& size, float angle = 0.f); + static rect primLine(const fvec2& a, const fvec2& b, int thickness = 1); + static void cmdQuad(command* cmd, const rect& q, const rect& uv, ui color); + static void cmdTriangle(command* cmd, const fvec2& a, const fvec2& b, + const fvec2& c, ui clr); + static void cmdConvexPolyFilled(command* cmd, + const std::vector& points, ui clr, + texture* tex); + static bool inBox(const fvec2& pos, const fvec2& size, const fvec4& area); + static bool inBox(const fvec2& pos, const fvec4& area); + static bool inBox(const fvec2& a, const fvec2& b, const fvec2& c, + const fvec4& area); private: static void setupShader(); static void fragConfig(); + static void initSolidTex(); static std::vector> m_vbuf; static std::vector> m_ibuf; @@ -87,5 +159,6 @@ class iron { static c3d::shader* m_shader; static mat4 m_mtx; static int m_idx, m_vtx; + static texture* m_solid; }; } // namespace amy \ No newline at end of file diff --git a/include/amethyst/rect.hpp b/include/amethyst/rect.hpp new file mode 100644 index 0000000..ba4e395 --- /dev/null +++ b/include/amethyst/rect.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include + +namespace amy { +class rect { + public: + rect() : m_top(0), m_bot(0) {} + ~rect() = default; + rect(const fvec4& t, const fvec4& b) : m_top(t), m_bot(b) {} + rect(const fvec2& tl, const fvec2& tr, const fvec2& bl, const fvec2& br) + : m_top(tl, tr), m_bot(bl, br) {} + rect(const fvec4& duv) + : m_top(duv.x, duv.y, duv.z, duv.y), m_bot(duv.x, duv.w, duv.z, duv.w) {} + + fvec2 topLeft() const { return fvec2(m_top.x, m_top.y); } + fvec2 topRight() const { return fvec2(m_top.z, m_top.w); } + fvec2 botLeft() const { return fvec2(m_bot.x, m_bot.y); } + fvec2 botRight() const { return fvec2(m_bot.z, m_bot.w); } + + fvec4 top() const { return m_top; } + fvec4& top() { return m_top; } + fvec4 bot() const { return m_bot; } + fvec4& bot() { return m_bot; } + + rect& topLeft(const fvec2& v) { + m_top.x = v.x; + m_top.y = v.y; + return *this; + } + + rect& topRight(const fvec2& v) { + m_top.z = v.x; + m_top.w = v.y; + return *this; + } + + rect& botLeft(const fvec2& v) { + m_bot.x = v.x; + m_bot.y = v.y; + return *this; + } + + rect& botRight(const fvec2& v) { + m_bot.z = v.x; + m_bot.w = v.y; + return *this; + } + + void swapVec2XY() { + m_top.SwapXY(); + m_top.SwapZW(); + m_bot.SwapXY(); + m_bot.SwapZW(); + } + + private: + fvec4 m_top; + fvec4 m_bot; +}; +} // namespace amy \ No newline at end of file diff --git a/include/amethyst/texture.hpp b/include/amethyst/texture.hpp index 3b8bd7f..dc62695 100755 --- a/include/amethyst/texture.hpp +++ b/include/amethyst/texture.hpp @@ -4,6 +4,7 @@ #include #include +#include #include namespace amy { @@ -13,6 +14,8 @@ class texture : public asset { texture(cstr& path); ~texture(); void load(cstr& path); + void load(const std::vector& pixels, int w, int h, int bpp = 4, + image::format fmt = image::RGBA); void unload(); int w() const { return m_size.x; } @@ -21,6 +24,7 @@ class texture : public asset { int& h() { return m_size.y; } ivec2 size() const { return m_size; } ivec2& size() { return m_size; } + rect& uv() { return m_uv; } C3D_Tex* ptr() { return m_loaded ? &m_tex : nullptr; } @@ -29,6 +33,7 @@ class texture : public asset { private: C3D_Tex m_tex; ivec2 m_size; + rect m_uv; bool m_loaded = false; }; } // namespace amy \ No newline at end of file diff --git a/include/amethyst/types.hpp b/include/amethyst/types.hpp index 01fda51..76b38ea 100644 --- a/include/amethyst/types.hpp +++ b/include/amethyst/types.hpp @@ -1,10 +1,13 @@ #pragma once +#include #include #include #include #include #include +#include +#include #include #include #include diff --git a/source/iron/drawlist.cpp b/source/iron/drawlist.cpp index c884bb4..097976f 100644 --- a/source/iron/drawlist.cpp +++ b/source/iron/drawlist.cpp @@ -1,5 +1,271 @@ #include +/** Setup for everything (oder so) */ +enum LiPathRectFlags_ : amy::ui { + LiPathRectFlags_None = 0, + LiPathRectFlags_KeepTopLeft = (1 << 0), + LiPathRectFlags_KeepTopRight = (1 << 1), + LiPathRectFlags_KeepBotRight = (1 << 2), + LiPathRectFlags_KeepBotLeft = (1 << 3), + LiPathRectFlags_KeepTop = (1 << 0) | (1 << 1), + LiPathRectFlags_KeepBot = (1 << 2) | (1 << 3), + LiPathRectFlags_KeepLeft = (1 << 0) | (1 << 3), + LiPathRectFlags_KeepRight = (1 << 1) | (1 << 2), +}; + namespace amy { -void iron::drawlist::merge(iron::drawlist* list) {} +constexpr auto __pi = std::numbers::pi; + +void iron::drawlist::merge(iron::drawlist* list) { + for (size_t i = 0; i < list->m_data.size(); i++) { + m_data.push_back(std::move(list->m_data[i])); + } + list->clear(); +} + +void iron::drawlist::clear() { m_data.clear(); } + +iron::command::ref iron::drawlist::newCommand() { + auto ret = std::make_unique(); + ret->layer = m_layer; + ret->index = m_data.size(); + ret->tex = m_tex; + return ret; +} + +void iron::drawlist::clipCmd(command* ptr) { + if (!clipRects.empty()) { + ptr->scissorOn = true; + ptr->scissorRect = ivec4(clipRects.top()); + } +} + +void iron::drawlist::push(command::ref cmd) { + m_data.push_back(std::move(cmd)); +} + +void iron::drawlist::drawSolid() { m_tex = iron::whiteTex(); } + +void iron::drawlist::pathArcToN(const fvec2& c, float radius, float a_min, + float a_max, int segments) { + // pathAdd(c) + pathReserve(segments + 1); + for (int i = 0; i < segments; i++) { + float a = a_min + ((float)i / (float)segments) * (a_max - a_min); + pathAdd(fvec2(c.x + std::cos(a) * radius, c.y + std::sin(a) * radius)); + } +} + +void iron::drawlist::pathFastArcToN(const fvec2& c, float r, float amin, + float amax, int s) { + /** + * Funcion with less division overhead + * Usefull for stuff where a lot of calculations are required + */ + float d = (amax - amin) / s; + pathReserve(s + 1); + for (int i = 0; i <= s; i++) { + float a = amin + i * d; + pathAdd(fvec2(c.x + std::cos(a) * r, c.y + std::sin(a) * r)); + } +} + +void iron::drawlist::pathRect(const fvec2& a, const fvec2& b, float rounding) { + if (rounding == 0.f) { + pathAdd(a); + pathAdd(fvec2(b.x, a.y)); + pathAdd(b); + pathAdd(fvec2(a.x, b.y)); + } else { + float r = std::min({rounding, (b.x - a.x) * 0.5f, (b.y - a.y) * 0.5f}); + /** Calculate Optimal segment count automatically */ + float corner = __pi * 0.5f; + int segments = std::max(3, int(std::ceil(corner / (6.0f * __pi / 180.0f)))); + + /** + * To Correctly render filled shapes with Paths API + * The Commands need to be setup clockwise + */ + /** Top Left */ + pathAdd(fvec2(a.x + r, a.y)); + pathFastArcToN(fvec2(b.x - r, a.y + r), r, -__pi / 2.0f, 0.0f, segments); + /** Top Right */ + pathAdd(fvec2(b.x, b.y - r)); + pathFastArcToN(fvec2(b.x - r, b.y - r), r, 0.0f, __pi / 2.0f, segments); + /** Bottom Right */ + pathAdd(fvec2(a.x + r, b.y)); + pathFastArcToN(fvec2(a.x + r, b.y - r), r, __pi / 2.0f, __pi, segments); + /** Bottom Left */ + pathAdd(fvec2(a.x, a.y + r)); + pathFastArcToN(fvec2(a.x + r, a.y + r), r, __pi, 3.0f * __pi / 2.0f, + segments); + } +} + +void iron::drawlist::pathRectEx(const fvec2& a, const fvec2& b, float rounding, + ui flags) { + if (rounding == 0.f) { + pathAdd(a); + pathAdd(fvec2(b.x, a.y)); + pathAdd(b); + pathAdd(fvec2(a.x, b.y)); + } else { + float r = std::min({rounding, (b.x - a.x) * 0.5f, (b.y - a.y) * 0.5f}); + /** Calculate Optimal segment count automatically */ + float corner = __pi * 0.5f; + int segments = std::max(3, int(std::ceil(corner / (6.0f * __pi / 180.0f)))); + + /** + * To Correctly render filled shapes with Paths API + * The Commands need to be setup clockwise + */ + /** Top Left */ + if (flags & LiPathRectFlags_KeepTopLeft) { + pathAdd(a); + } else { + pathAdd(fvec2(a.x + r, a.y)); + pathFastArcToN(fvec2(b.x - r, a.y + r), r, -__pi / 2.0f, 0.0f, segments); + } + + /** Top Right */ + if (flags & LiPathRectFlags_KeepTopRight) { + pathAdd(fvec2(b.x, a.y)); + } else { + pathAdd(fvec2(b.x, b.y - r)); + pathFastArcToN(fvec2(b.x - r, b.y - r), r, 0.0f, __pi / 2.0f, segments); + } + /** Bottom Right */ + if (flags & LiPathRectFlags_KeepBotRight) { + pathAdd(b); + } else { + pathAdd(fvec2(a.x + r, b.y)); + pathFastArcToN(fvec2(a.x + r, b.y - r), r, __pi / 2.0f, __pi, segments); + } + /** Bottom Left */ + if (flags & LiPathRectFlags_KeepBotLeft) { + pathAdd(fvec2(a.x, b.y)); + } else { + pathAdd(fvec2(a.x, a.y + r)); + pathFastArcToN(fvec2(a.x + r, a.y + r), r, __pi, 3.0f * __pi / 2.0f, + segments); + } + } +} + +void iron::drawlist::drawRect(const fvec2& pos, const fvec2& size, ui color, + int thickness) { + pathRect(pos, pos + size); + pathStroke(color, thickness, 1); +} + +void iron::drawlist::drawRectFilled(const fvec2& pos, const fvec2& size, + ui color) { + pathRect(pos, pos + size); + pathFill(color); +} + +void iron::drawlist::drawTriangle(const fvec2& a, const fvec2& b, + const fvec2& c, ui color, int thickness) { + pathAdd(a); + pathAdd(b); + pathAdd(c); + pathStroke(color, thickness, 1); +} + +void iron::drawlist::drawTriangleFilled(const fvec2& a, const fvec2& b, + const fvec2& c, ui color) { + pathAdd(a); + pathAdd(b); + pathAdd(c); + pathFill(color); +} + +void iron::drawlist::drawCircle(const fvec2& center, float rad, ui color, + int segments, int thickness) { + if (segments <= 0) { + // Auto Segment + } else { + float am = (__pi * 2.0f) * ((float)segments) / (float)segments; + pathArcToN(center, rad, 0.f, am, segments); + } + drawSolid(); // Only Solid Color Supported + pathStroke(color, thickness, 1); +} + +void iron::drawlist::drawCircleFilled(const fvec2& center, float rad, ui color, + int segments) { + if (segments <= 0) { + // Auto Segment + } else { + float am = (__pi * 2.0f) * ((float)segments) / (float)segments; + pathArcToN(center, rad, 0.f, am, segments); + } + pathFill(color); +} + +void iron::drawlist::drawPolyLine(const std::vector& points, ui clr, + ui flags, int thickness) { + if (points.size() < 2) { + return; + } + drawSolid(); + auto cmd = newCommand(); + bool close = (flags & (1 << 0)); + int num_points = close ? (int)points.size() : (int)points.size() - 1; + if (flags & (1 << 1)) { + // TODO: Find a way to draw less garbage looking lines + } else { + // Non antialiased lines look awful when rendering with thickness != 1 + for (int i = 0; i < num_points; i++) { + int j = (i + 1) == (int)points.size() ? 0 : (i + 1); + auto line = primLine(points[i], points[j], thickness); + cmdQuad(cmd.get(), line, fvec4(0.f, 1.f, 1.f, 0.f), clr); + } + } + push(std::move(cmd)); +} + +void iron::drawlist::drawConvexPolyFilled(const std::vector& points, + ui clr) { + if (points.size() < 3) { + return; // Need at least three points + } + auto cmd = newCommand(); + cmdConvexPolyFilled(cmd.get(), points, clr, m_tex); + push(std::move(cmd)); +} + +void iron::drawlist::drawText(const fvec2& pos, const std::string& text, + ui color) { + /*if (!pCurrentFont) { + return; + } + std::vector cmds; + pCurrentFont->CmdTextEx(cmds, pos, color, pFontScale, text); + for (size_t i = 0; i < cmds.size(); i++) { + cmds[i]->Index = pDrawList.size(); + cmds[i]->Layer = Layer; + AddCommand(std::move(cmds[i])); + }*/ +} + +void iron::drawlist::drawTextEx(const fvec2& p, const std::string& text, + ui color, ui flags, const fvec2& box) { + /*if (!pCurrentFont) { + return; + } + std::vector cmds; + pCurrentFont->CmdTextEx(cmds, p, color, pFontScale, text, flags, box); + for (size_t i = 0; i < cmds.size(); i++) { + cmds[i]->Index = pDrawList.size(); + cmds[i]->Layer = Layer; + AddCommand(std::move(cmds[i])); + }*/ +} + +void iron::drawlist::drawLine(const fvec2& a, const fvec2& b, ui color, int t) { + pathAdd(a); + pathAdd(b); + pathStroke(color, t); +} } // namespace amy \ No newline at end of file diff --git a/source/iron/iron.cpp b/source/iron/iron.cpp index ed3656d..77e6263 100644 --- a/source/iron/iron.cpp +++ b/source/iron/iron.cpp @@ -1,4 +1,17 @@ #include +#include + +#define catch2() \ + ; \ + ({ \ + std::cout << std::format("{}:{}", __FILE__, __LINE__) << std::endl; \ + while (aptMainLoop()) { \ + hidScanInput(); \ + if (hidKeysDown() & KEY_A) { \ + break; \ + } \ + } \ + }) namespace amy { const char* __ironshader__ = R"(; LI7 Shader @@ -35,17 +48,26 @@ const char* __ironshader__ = R"(; LI7 Shader mov out_color, r1 end .end)"; + +// clang-format off +unsigned char li_shader[] = { + 0x44, 0x56, 0x4c, 0x42, 0x1, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x50, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0x1, 0xf0, 0x7, 0x4e, 0x2, 0x8, 0x2, 0x8, 0x3, 0x18, 0x2, 0x8, 0x4, 0x28, 0x2, 0x8, 0x5, 0x38, 0x2, 0x8, 0x6, 0x10, 0x40, 0x4c, 0x7, 0xf1, 0x27, 0x22, 0x8, 0x10, 0x21, 0x4c, 0x0, 0x0, 0x0, 0x88, 0xac, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa1, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x61, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xd5, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x45, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x1, 0x1, 0x37, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0xf, 0x0, 0x0, 0x0, 0x3, 0x0, 0x2, 0x0, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x13, 0x0, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x0, + }; +// clang-format on +size_t li_shader_size = 0x124; std::vector> iron::m_vbuf; std::vector> iron::m_ibuf; int iron::uLocProj = 0; c3d::shader* iron::m_shader = nullptr; mat4 iron::m_mtx; int iron::m_idx = 0, iron::m_vtx = 0; +texture* iron::m_solid = nullptr; void iron::init() { setupShader(); m_vbuf.resize(4 * 4096); m_ibuf.resize(6 * 4096); + initSolidTex(); } void iron::newFrame() { @@ -53,11 +75,16 @@ void iron::newFrame() { m_vtx = 0; } +C3D_Mtx m; + void iron::drawOn(c3d::screen* screen) { m_shader->use(); + Mtx_Identity(&m); + Mtx_OrthoTilt(&m, 0.f, (float)screen->width(), (float)screen->height(), 0.f, + 1.f, -1.f, false); m_mtx = mat4::ortho(0.f, (float)screen->width(), (float)screen->height(), 0.f, 1.f, -1.f); - m_shader->setMat4(uLocProj, m_mtx); + m_shader->setMat4(uLocProj, &m); } void iron::draw(const std::vector& data) { @@ -74,10 +101,12 @@ void iron::draw(const std::vector& data) { auto scissorOn = data[i]->scissorOn; auto scissor = data[i]->scissorRect; auto start = i; + // Loop until a statgechange and copy all data into vertex/index buf while (i < data.size() && scissorOn == data[i]->scissorOn && scissor == data[i]->scissorRect && tex == data[i]->tex) { auto c = data[i].get(); + for (int j = 0; j < c->indexBuf.size(); j++) { m_ibuf[m_idx++] = m_vtx + c->indexBuf[i]; } @@ -97,7 +126,8 @@ void iron::draw(const std::vector& data) { void iron::setupShader() { m_shader = new c3d::shader(); - m_shader->compile(__ironshader__); + m_shader->load("romfs:/shaders/lithium.shbin"); + // m_shader->compile(__ironshader__); m_shader->input(GPU_FLOAT, 2); // pos m_shader->input(GPU_FLOAT, 2); // uv m_shader->input(GPU_UNSIGNED_BYTE, 4); // color @@ -109,4 +139,126 @@ void iron::fragConfig() { c3d::frag::src(C3D_Both, GPU_TEXTURE0); c3d::frag::func(C3D_Both, GPU_MODULATE); } + +void iron::initSolidTex() { + // i know there is a lot of memory wasted :( + std::vector pixels(16 * 16 * 4, 0xff); + m_solid = new texture(); + m_solid->load(pixels, 16, 16); +} + +bool iron::inBox(const fvec2& pos, const fvec2& size, const fvec4& area) { + return (pos.x + size.x >= area.x && pos.y + size.y >= area.y && + pos.x <= area.z && pos.y <= area.w); +} + +bool iron::inBox(const fvec2& pos, const fvec4& area) { + return (pos.x > area.x && pos.x < area.x + area.z && pos.y > area.y && + pos.y < area.y + area.w); +} + +bool iron::inBox(const fvec2& a, const fvec2& b, const fvec2& c, + const fvec4& area) { + return ((a.x < area.z && b.x < area.z && c.x < area.z) || + (a.y < area.w && b.y < area.w && c.y < area.w) || + (a.x > 0 && b.x > 0 && c.x > 0) || (a.y > 0 && b.y > 0 && c.y > 0)); +} + +void iron::rotateCorner(fvec2& pos, float s, float c) { + float x = pos.x * c - pos.y * s; + float y = pos.y * c - pos.x * s; + pos = fvec2(x, y); +} + +rect iron::primRect(const fvec2& pos, const fvec2& size, float angle) { + fvec2 c = size * 0.5f; // Center + fvec2 corner[4] = { + fvec2(-c.x, -c.y), + fvec2(-c.x + size.x, -c.y), + fvec2(-c.x, -c.y + size.y), + fvec2(-c.x + size.x, -c.y + size.y), + }; + + // Only rotate if required + if (angle != 0.f) { + float s = std::sin(angle); + float co = std::cos(angle); + for (int i = 0; i < 4; i++) { + rotateCorner(corner[i], s, co); + } + } + + // Return Result + return rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c, + corner[3] + pos + c); +} + +rect iron::primLine(const fvec2& a, const fvec2& b, int thickness) { + // Using the vec maths api makes the code as short as it is + fvec2 dir = a - b; + float len = dir.Len(); + fvec2 unit_dir = dir / len; + fvec2 perpendicular(-unit_dir.y, unit_dir.x); + fvec2 off = perpendicular * ((float)thickness * 0.5f); + + return rect(a + off, b + off, a - off, b - off); +} + +void iron::cmdQuad(command* cmd, const rect& q, const rect& uv, ui color) { + cmd->add(0).add(1).add(2); + cmd->add(0).add(2).add(3); + cmd->add(vertex(q.botRight(), uv.botRight(), color)); + cmd->add(vertex(q.topRight(), uv.topRight(), color)); + cmd->add(vertex(q.topLeft(), uv.topLeft(), color)); + cmd->add(vertex(q.botLeft(), uv.botLeft(), color)); +} + +void iron::cmdTriangle(command* cmd, const fvec2& a, const fvec2& b, + const fvec2& c, ui color) { + cmd->add(2).add(1).add(0); // reverse cause otherwise invisible + cmd->add(vertex(a, fvec2(0, 1), color)); + cmd->add(vertex(b, fvec2(1, 1), color)); + cmd->add(vertex(c, fvec2(1, 0), color)); +} + +void iron::cmdConvexPolyFilled(command* cmd, const std::vector& points, + ui color, texture* tex) { + if (points.size() < 3 || tex == nullptr) { +#ifdef AMY_GOD_DEV + return; +#else + throw std::runtime_error("[amy] iron: trying to render convex poly with " + + std::to_string(points.size()) + + " points and texture " + std::to_string((ui)tex)); +#endif + } + // Support for Custom Textures (UV calculation) + float minX = points[0].x, minY = points[0].y; + float maxX = minX, maxY = minY; + // Check for the max and min Positions + for (const auto& it : points) { + if (it.x < minX) minX = it.x; + if (it.y < minY) minY = it.y; + if (it.x > maxX) maxX = it.x; + if (it.y > maxY) maxY = it.y; + } + // Get Short defines for UV + // (Bottom Right is not required) + auto uv_tl = tex->uv().topLeft(); + auto uv_tr = tex->uv().topRight(); + auto uv_bl = tex->uv().botLeft(); + + // Render + for (int i = 2; i < (int)points.size(); i++) { + cmd->add(0).add(i).add(i - 1); + } + for (int i = 0; i < (int)points.size(); i++) { + // Calculate U and V coords + float u = + uv_tl.x + ((points[i].x - minX) / (maxX - minX)) * (uv_tr.x - uv_tl.x); + float v = + uv_tl.y + ((points[i].y - minY) / (maxY - minY)) * (uv_bl.y - uv_tl.y); + cmd->add(vertex(points[i], fvec2(u, v), color)); + } +} } // namespace amy \ No newline at end of file diff --git a/source/texture.cpp b/source/texture.cpp index b071007..f9ac270 100755 --- a/source/texture.cpp +++ b/source/texture.cpp @@ -44,35 +44,42 @@ void texture::unload() { } void texture::load(cstr& path) { - unload(); image img(path); if (img.width() > 1024 || img.height() > 1024) { throw std::runtime_error("Max Texture Size is 1024x1024!"); } + load(img.getBuffer(), img.width(), img.height(), img.bpp(), img.fmt()); +} - int bpp = img.bpp(); - m_size.x = img.width(); +void texture::load(const std::vector& pixels, int w, int h, int bpp, + image::format fmt) { + if (w > 1024 || h > 1024) { + throw std::runtime_error("Max Texture Size is 1024x1024!"); + } + unload(); + + m_size.x = w; if (utils::isSingleBitNum(m_size.x)) { m_size.x = utils::nextPow2(m_size.x); } - m_size.y = img.height(); + m_size.y = h; if (utils::isSingleBitNum(m_size.y)) { m_size.y = utils::nextPow2(m_size.y); } auto filter = GPU_NEAREST; - auto format = image2TexFmt(img.fmt()); + auto format = image2TexFmt(fmt); C3D_TexInit(&m_tex, (u16)m_size.x, (u16)m_size.y, format); C3D_TexSetFilter(&m_tex, filter, filter); // Using std::fill_n instead cause i hate this error lines // under the memset func in my editor std::fill_n((unsigned char*)m_tex.data, m_tex.size, 0); - for (int x = 0; x < img.width(); x++) { - for (int y = 0; y < img.height(); y++) { + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { int dst_pos = tile3dsTex(x, y, m_size.x) * bpp; - int src_pos = (y * img.width() + x) * bpp; + int src_pos = (y * w + x) * bpp; /// Best idea i had for (int i = 0; i < bpp; i++) { - ((u8*)m_tex.data)[dst_pos + bpp - 1 - i] = img[src_pos + i]; + ((u8*)m_tex.data)[dst_pos + bpp - 1 - i] = pixels[src_pos + i]; } } }