diff --git a/assets/basic.png b/assets/basic.png new file mode 100644 index 0000000..1548bce Binary files /dev/null and b/assets/basic.png differ diff --git a/2d.hpp b/include/2d.h similarity index 80% rename from 2d.hpp rename to include/2d.h index 71eeba5..9f7681c 100644 --- a/2d.hpp +++ b/include/2d.h @@ -5,19 +5,25 @@ #include #include +namespace arras { + using Float = float; using Vec2 = std::complex; const Float eps = 1e-6; -Float cross(const Vec2& a, const Vec2& b) { +inline Float cross(const Vec2& a, const Vec2& b) { return a.real() * b.imag() - a.imag() * b.real(); } -Float dot(const Vec2& a, const Vec2& b) { +inline Float dot(const Vec2& a, const Vec2& b) { return a.real() * b.real() + a.imag() * b.imag(); } +inline Float asAngle(const Vec2& d) { + return std::atan2(d.imag(), d.real()); +} + struct Circle { Vec2 o; Float r; @@ -39,8 +45,10 @@ struct Polygon { Polygon(const std::array& v): vertex(v) {} Polygon(std::initializer_list&& v) { int i = 0; - for (auto &&x : v) { - if (i >= n) break; + for (auto&& x : v) { + if (i >= n) { + break; + } vertex[i] = x; i += 1; } @@ -64,12 +72,12 @@ struct Polygon { } }; -bool intersect(const Circle& a, const Circle& b) { +inline bool intersect(const Circle& a, const Circle& b) { return abs(a.o - b.o) <= a.r + b.r; } template -bool intersect(const Polygon& a, const Polygon& b) { +inline bool intersect(const Polygon& a, const Polygon& b) { for (int i = 0; i < n; i++) { if (b.contain(a[i])) { return true; @@ -98,7 +106,7 @@ bool intersect(const Polygon& a, const Polygon& b) { } template -bool intersect(const Polygon& a, const Circle& b) { +inline bool intersect(const Polygon& a, const Circle& b) { if (a.contain(b.o)) { return true; } @@ -121,8 +129,9 @@ bool intersect(const Polygon& a, const Circle& b) { } template -bool intersect(const Circle& b, const Polygon& a) { +inline bool intersect(const Circle& b, const Polygon& a) { return intersect(a, b); } +} #endif diff --git a/include/render.h b/include/render.h new file mode 100644 index 0000000..1e009cd --- /dev/null +++ b/include/render.h @@ -0,0 +1,98 @@ +#ifndef HEADER_ARRAS_RENDER_H +#define HEADER_ARRAS_RENDER_H + +extern "C" { +#include +#include +} + +#include "2d.h" +#include +#include +#include +#include + +namespace arras { + +struct Color { + uint8_t r, g, b, a; + + Color(); + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +}; + +std::shared_ptr makeRect(int x, int y, int w, int h); +std::shared_ptr makeFRect(const arras::Vec2& pos, const arras::Vec2& size); + +class RenderSurface { +public: + RenderSurface(RenderSurface&& rs); + RenderSurface(const RenderSurface& rs); + explicit RenderSurface(SDL_RWops* src); + RenderSurface(int width, int height); + + ~RenderSurface(); + + SDL_Surface* nativeHandle() const noexcept; + + int width() const noexcept; + int height() const noexcept; + +private: + SDL_Surface* s; +}; + +class RenderWindow; + +class RenderTexture { + RenderTexture(const RenderTexture&) = delete; + RenderTexture& operator=(const RenderTexture&) = delete; + +public: + RenderTexture(RenderTexture&& rt); + RenderTexture(const RenderWindow& rd, SDL_RWops* src); + RenderTexture(const RenderWindow& rd, const RenderSurface& rs); + + ~RenderTexture(); + + SDL_Texture* nativeHandle() const noexcept; + + int width() const noexcept; + int height() const noexcept; + + std::pair calcRenderSize(int w, int h) const noexcept; + +private: + SDL_Texture* t; + int w, h; +}; + +class RenderWindow { + RenderWindow(const RenderWindow&) = delete; + RenderWindow& operator=(const RenderWindow&) = delete; + +public: + RenderWindow(RenderWindow&& rd); + RenderWindow(int w, int h); + + ~RenderWindow(); + + SDL_Renderer* nativeHandle() const noexcept; + + void renderTextureAt(const RenderTexture& t, const Vec2& pos) const; + void renderTextureAt(const RenderTexture& t, const Vec2& pos, const Vec2& direction) const; + + void present() const noexcept; + void clear(const Color& c = {}) const; + + int width() const noexcept; + int height() const noexcept; + +private: + int w, h; + SDL_Window* window; + SDL_Renderer* renderer; +}; +} // namespace arras + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..d643d1f --- /dev/null +++ b/main.cpp @@ -0,0 +1,32 @@ +#include "2d.h" +#include "render.h" +#include +#include +#include + +int main() { + arras::RenderWindow w(2360, 1240); + arras::RenderTexture t(w, SDL_RWFromFile("assets/basic.png", "rb")); + std::printf("%d %d\n", t.width(), t.height()); + + const float PI = acos(-1); + arras::Vec2 d{1, 0}, v{std::cos(PI / 300), std::sin(PI / 300)}; + SDL_Event evt; + while (true) { + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) { + break; + } + } + + w.clear(); + w.renderTextureAt(t, {200, 200}, d); + w.present(); + d *= v; + // pos += v; + // if (pos.real() > 1000 || pos.real() < 200) { + // v *= -1; + // } + std::this_thread::sleep_for(std::chrono::milliseconds(16)); + } +} diff --git a/objects.hpp b/objects.hpp new file mode 100644 index 0000000..342cd51 --- /dev/null +++ b/objects.hpp @@ -0,0 +1,32 @@ +#ifndef HEADER_OBJECTS_HPP +#define HEADER_OBJECTS_HPP + +#include "2d.h" +#include + +namespace arras { + +struct Transform { + Vec2 position; +}; + +struct Velocity { + Vec2 velocity; +}; + +struct EngineAcceleration { + Vec2 ideal_velocity; + Float acceleration; +}; + +inline entt::entity makeTank(entt::registry &r, Vec2 position) { + const auto e = r.create(); + r.emplace(e, Transform{position}); + r.emplace(e, Velocity{{0, 0}}); + r.emplace(e, EngineAcceleration{{0, 0}, 1}); + return e; +} + +} + +#endif \ No newline at end of file diff --git a/src/render/misc.cpp b/src/render/misc.cpp new file mode 100644 index 0000000..5ca083b --- /dev/null +++ b/src/render/misc.cpp @@ -0,0 +1,42 @@ +#include "render.h" + +namespace { + +class SDLLoader { +private: + SDLLoader() { + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); + IMG_Init(IMG_INIT_PNG); + } + + ~SDLLoader() { + IMG_Quit(); + SDL_Quit(); + } + + static const SDLLoader _; +}; + +} // namespace + +arras::Color::Color(): r(255), g(255), b(255), a(255) {} +arras::Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a): r(r), g(g), b(b), a(a) {} + +std::shared_ptr arras::makeRect(int x, int y, int w, int h) { + auto res = std::make_shared(); + res->x = x; + res->y = y; + res->w = w; + res->h = h; + return res; +} + +std::shared_ptr arras::makeFRect(const arras::Vec2& pos, const arras::Vec2& size) { + auto res = std::make_shared(); + res->x = pos.real(); + res->y = pos.imag(); + res->w = size.real(); + res->h = size.imag(); + return res; +} diff --git a/src/render/surface.cpp b/src/render/surface.cpp new file mode 100644 index 0000000..70146ad --- /dev/null +++ b/src/render/surface.cpp @@ -0,0 +1,53 @@ +#include "render.h" + +using namespace arras; + +RenderSurface::RenderSurface(RenderSurface&& rs): s(rs.s) { + rs.s = nullptr; +} + +RenderSurface::RenderSurface(const RenderSurface& rs) { + s = SDL_CreateRGBSurfaceWithFormat(0, rs.width(), rs.height(), 32, SDL_PIXELFORMAT_RGBA8888); + if (!s) { + throw std::runtime_error( + fmt::format("SDL_CreateRGBSurfaceWithFormat() failed: {}", SDL_GetError())); + } + if (SDL_BlitSurface(rs.nativeHandle(), + nullptr, + s, + makeRect(0, 0, rs.width(), rs.height()).get()) + != 0) { + throw std::runtime_error(fmt::format("SDL_BlitSurface() failed: {}", SDL_GetError())); + } +} + +RenderSurface::RenderSurface(SDL_RWops* src) { + s = IMG_Load_RW(src, 1); + if (!s) { + throw std::runtime_error(fmt::format("IMG_Load_RW() failed: {}", IMG_GetError())); + } +} + +RenderSurface::RenderSurface(int width, int height) { + s = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGBA8888); + if (!s) { + throw std::runtime_error( + fmt::format("SDL_CreateRGBSurfaceWithFormat() failed: {}", SDL_GetError())); + } +} + +RenderSurface ::~RenderSurface() { + SDL_FreeSurface(s); +} + +SDL_Surface* RenderSurface::nativeHandle() const noexcept { + return s; +} + +int RenderSurface::width() const noexcept { + return s->w; +} + +int RenderSurface::height() const noexcept { + return s->h; +} \ No newline at end of file diff --git a/src/render/texture.cpp b/src/render/texture.cpp new file mode 100644 index 0000000..5b86e16 --- /dev/null +++ b/src/render/texture.cpp @@ -0,0 +1,63 @@ + +#include "render.h" + +using namespace arras; + +RenderTexture::RenderTexture(RenderTexture&& rt): t(rt.t) { + rt.t = nullptr; +} + +RenderTexture::RenderTexture(const RenderWindow& rd, SDL_RWops* src) { + t = IMG_LoadTexture_RW(rd.nativeHandle(), src, 1); + if (!t) { + throw std::runtime_error(fmt::format("IMG_LoadTexture_RW() failed: {}", IMG_GetError())); + } + if (SDL_QueryTexture(t, nullptr, nullptr, &w, &h) != 0) { + throw std::runtime_error(fmt::format("SDL_QueryTexture() failed: {}", SDL_GetError())); + } +} + +RenderTexture::RenderTexture(const RenderWindow& rd, const RenderSurface& rs) { + t = SDL_CreateTextureFromSurface(rd.nativeHandle(), rs.nativeHandle()); + if (!t) { + throw std::runtime_error( + fmt::format("SDL_CreateRGBSurfaceWithFormat() failed: {}", SDL_GetError())); + } + if (SDL_QueryTexture(t, nullptr, nullptr, &w, &h)) { + throw std::runtime_error(fmt::format("SDL_QueryTexture() failed: {}", SDL_GetError())); + } +} + +RenderTexture::~RenderTexture() { + SDL_DestroyTexture(t); +} + +SDL_Texture* RenderTexture::nativeHandle() const noexcept { + return t; +} + +int RenderTexture::width() const noexcept { + return w; +} + +int RenderTexture::height() const noexcept { + return h; +} + +std::pair RenderTexture::calcRenderSize(int w, int h) const noexcept { + int rw, rh; + if (w == 0 && h == 0) { + rw = width(); + rh = height(); + } else if (w == 0) { + rw = (h * width() / height()); + rh = h; + } else if (h == 0) { + rw = w; + rh = (w * height() / width()); + } else { + rw = w; + rh = h; + } + return {rw, rh}; +} diff --git a/src/render/window.cpp b/src/render/window.cpp new file mode 100644 index 0000000..90b44a3 --- /dev/null +++ b/src/render/window.cpp @@ -0,0 +1,76 @@ +#ifndef HEADER_ARRAS_RENDER_WINDOW_HPP +#define HEADER_ARRAS_RENDER_WINDOW_HPP + +#include "render.h" +#define Pi 3.14159265358979323846 + +using namespace arras; + +RenderWindow::RenderWindow(RenderWindow&& rd) + : w(rd.w), h(rd.h), window(rd.window), renderer(rd.renderer) { + rd.renderer = nullptr; + rd.window = nullptr; +} + +RenderWindow::RenderWindow(int w, int h): w(w), h(h) { + if (SDL_CreateWindowAndRenderer(w, h, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI, &window, &renderer) != 0) { + throw std::runtime_error( + fmt::format("SDL_CreateWindowAndRenderer() failed: {}", SDL_GetError())); + } +} + +RenderWindow::~RenderWindow() { + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); +} + +SDL_Renderer* RenderWindow::nativeHandle() const noexcept { + return renderer; +} + +void RenderWindow::renderTextureAt(const RenderTexture& t, const Vec2& pos) const { + if (SDL_RenderCopyF(renderer, + t.nativeHandle(), + NULL, + makeFRect(pos, {t.width(), t.height()}).get())) { + throw std::runtime_error(fmt::format("SDL_RenderCopy() failed: {}", SDL_GetError())); + } +} + +void RenderWindow::renderTextureAt(const RenderTexture& t, + const Vec2& pos, + const Vec2& direction) const { + if (SDL_RenderCopyExF(renderer, + t.nativeHandle(), + NULL, + makeFRect(pos, {t.width(), t.height()}).get(), + -asAngle(direction) * 180 / Pi, + nullptr, + SDL_FLIP_NONE)) { + throw std::runtime_error(fmt::format("SDL_RenderCopy() failed: {}", SDL_GetError())); + } +} + +void RenderWindow::present() const noexcept { + SDL_RenderPresent(renderer); +} + +void RenderWindow::clear(const Color& c) const { + if (SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a)) { + throw std::runtime_error( + fmt::format("SDL_SetRenderDrawColor() failed: {}", SDL_GetError())); + } + if (SDL_RenderClear(renderer)) { + throw std::runtime_error(fmt::format("SDL_RenderClear() failed: {}", SDL_GetError())); + } +} + +int RenderWindow::width() const noexcept { + return w; +} + +int RenderWindow::height() const noexcept { + return h; +} + +#endif diff --git a/test/cases/2d.cpp b/test/cases/2d.cpp index 0be2e94..c8b5ed8 100644 --- a/test/cases/2d.cpp +++ b/test/cases/2d.cpp @@ -1,4 +1,4 @@ -#include "2d.hpp" +#include "2d.h" #include "test/u.hpp" #include #include @@ -6,6 +6,7 @@ namespace { AddTestCase _(1, "2d.hpp", [](TestCase& t) { + using namespace arras; t.expectEq([] { return cross({1, 2}, {4, 1}); }, -7); t.expectEq([] { return cross({.5, 2.5}, {4, 1.5}); }, -9.25); diff --git a/test/cases/objects.cpp b/test/cases/objects.cpp new file mode 100644 index 0000000..9c8a420 --- /dev/null +++ b/test/cases/objects.cpp @@ -0,0 +1,10 @@ +#include "objects.hpp" +#include "test/u.hpp" + +namespace { + +AddTestCase _(1, "objects.hpp", [](TestCase& t) { + +}); + +} diff --git a/xmake.lua b/xmake.lua index b23e643..d5d6fe6 100644 --- a/xmake.lua +++ b/xmake.lua @@ -4,13 +4,31 @@ set_version("0.0a0") set_languages("c++17") set_targetdir(".") -target("test") +add_requires("entt 3.11", "libsdl 2.26", "libsdl_image 2.6", "fmt 10") +add_includedirs("include", ".") + + +target("main") + set_default(true) set_kind("binary") + add_files("main.cpp") + add_files("src/**/*.cpp") + + add_packages("entt", "libsdl", "libsdl_image", "fmt") set_warnings("allextra") + +target("test") + set_default(false) + set_kind("binary") + set_prefixname("test-") + add_files("test/main.cpp") add_files("test/cases/*.cpp") - add_includedirs(".") + add_files("src/**/*.cpp") + + add_packages("entt", "fmt") + + set_warnings("allextra") set_optimize("none") set_symbols("debug") add_defines("DEBUG") - set_prefixname("test-") \ No newline at end of file