From 6ff16e3db4afffdb9b2e916e85d48206d7b42f41 Mon Sep 17 00:00:00 2001 From: Daanish Date: Wed, 20 May 2026 20:56:25 +0100 Subject: [PATCH] Added proper hit test with rotation and pivoting --- include/app/app.h | 54 +++++++++++------ include/binding/definitions.h | 109 ++++++++++++++++++++++++++++++++-- include/binding/ruby.h | 1 + include/utils.h | 9 ++- include/window/window.h | 7 ++- samples/button.rb | 6 +- src/window/window.cc | 30 ++++++++-- 7 files changed, 181 insertions(+), 35 deletions(-) diff --git a/include/app/app.h b/include/app/app.h index b59a027..69180f0 100644 --- a/include/app/app.h +++ b/include/app/app.h @@ -15,6 +15,7 @@ struct Element { Type type; Vec2 position = {0, 0}; float rotation = 0; + Vec2 pivot = {0, 0}; Vec2 scale = {1, 1}; float alpha = 1.0f; float z = 0; @@ -24,7 +25,7 @@ struct Element { Element(Type type) : type(type) {} virtual ~Element() = default; virtual void render(Window &window, uint64_t abs_time) = 0; - virtual Vec2 size(Window *window) = 0; + virtual Vec2 size() = 0; void click() { on_click.call(); @@ -43,11 +44,11 @@ struct EImage : Element { animation_pool.release(animation_id); } - Vec2 size(Window *window) override { + Vec2 size() override { Animation &animation = *animation_pool[animation_id]; if (animation.frames.empty()) return {0, 0}; - return window->get_image_size(animation.frames[0]); + return window.get_image_size(animation.frames[0]); } void render(Window &window, uint64_t abs_time) override { @@ -55,7 +56,7 @@ struct EImage : Element { if (animation.frames.empty()) return; int frame_index = (int)((abs_time * animation.frame_rate) / 1e9) % animation.frames.size(); - window.draw(animation.frames[frame_index], position, z, rotation, scale, alpha); + window.draw(animation.frames[frame_index], position, z, rotation, pivot, scale, alpha); } }; @@ -86,10 +87,10 @@ struct EText : Element { last_key = UINT16_MAX; } - Vec2 size(Window *window) override { + Vec2 size() override { if (text_id == UINT64_MAX) return {0, 0}; - return window->get_text_size(text_id); + return window.get_text_size(text_id); }; void render(Window &window, uint64_t) override { @@ -106,7 +107,7 @@ struct EText : Element { last_key = key; last_color = color; } - window.write(text_id, position, z, rotation, scale, alpha); + window.write(text_id, position, z, rotation, pivot, scale, alpha); } private: @@ -119,14 +120,14 @@ private: struct ERect : Element { Vec2 shape = {0, 0}; Color color = {255, 255, 255}; - Vec2 size(Window *) override { + Vec2 size() override { return shape; }; ERect(Vec2 shape, Color color) : Element(Type::RECT), shape(shape), color(color) {} void render(Window &window, uint64_t) override { - window.draw_rect(Rect::from_size(position, shape), color, z, rotation, scale, alpha); + window.draw_rect(Rect::from(position, shape), color, z, rotation, pivot, scale, alpha); } }; @@ -177,16 +178,19 @@ struct Scene { element_pool[id]->click(); } -#warning "This does not take rotation into account \ -reference and use reverse-transform on point then AABB check instead" + void render(Window &window, uint64_t abs_time) { + for (const uint64_t &e : element_ids) + element_pool[e]->render(window, abs_time); + } + std::vector elements_at(Vec2 position) { std::vector result; for (auto it = element_ids.rbegin(); it != element_ids.rend(); ++it) { Element *e = element_pool[*it]; - Vec2 size = e->size(&window); - Rect rect = Rect::from_size(e->position, size * e->scale); - if (rect.contains(position)) { + Vec2 size = e->size(); + Rect rect = Rect::from(size); + if (rect.contains(world_to_local(position, e))) { result.push_back(*it); if (e->click_through) continue; @@ -198,9 +202,25 @@ reference and use reverse-transform on point then AABB check instead" return result; } - void render(Window &window, uint64_t abs_time) { - for (const uint64_t &e : element_ids) - element_pool[e]->render(window, abs_time); + Vec2 world_to_local(Vec2 p, Element *e) { + Vec2 size = e->size(); + Vec2 pivot = e->pivot * size * e->scale; + p = p - e->position; + + float rad = -e->rotation * (M_PI / 180.0f); + float c = cos(rad); + float s = sin(rad); + + p = p - pivot; + p = { + p.x * c - p.y * s, + p.x * s + p.y * c + }; + + p = p + pivot; + p = p / e->scale; + + return p; } }; diff --git a/include/binding/definitions.h b/include/binding/definitions.h index b7ea837..a78ce1e 100644 --- a/include/binding/definitions.h +++ b/include/binding/definitions.h @@ -317,7 +317,9 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } - float x = 0, y = 0, z = 0, rotation = 0, scale_x = 1, scale_y = 1, alpha = 1; + float x = 0, y = 0, z = 0, rotation = 0; + float scale_x = 1, scale_y = 1, alpha = 1; + float pivot_x = 0.5, pivot_y = 0.5; bool click_through = false; if (!mrb_nil_p(kwargs)) { mrb_value pos_hash = HASH_GET(kwargs, position); @@ -332,6 +334,12 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { HASH_FLOAT(scale_hash, y, scale_y); } + mrb_value pivot_hash = HASH_GET(kwargs, pivot); + if (!mrb_nil_p(pivot_hash)) { + HASH_FLOAT(pivot_hash, x, pivot_x); + HASH_FLOAT(pivot_hash, y, pivot_y); + } + HASH_FLOAT(kwargs, z, z); HASH_FLOAT(kwargs, rotation, rotation); HASH_FLOAT(kwargs, alpha, alpha); @@ -351,6 +359,7 @@ static mrb_value ElementText_init(mrb_state *mrb, mrb_value self) { element->position = {x, y}; element->scale = {scale_x, scale_y}; element->rotation = rotation; + element->pivot = {pivot_x, pivot_y}; element->alpha = alpha; element->z = z; element->click_through = click_through; @@ -533,6 +542,28 @@ static mrb_value element_text_rotation_set(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } +static mrb_value element_text_pivot_get(mrb_state *mrb, mrb_value self) { + uint64_t id = get_id_ElementText(mrb, self); + app::EText *element = (app::EText *)app::element_pool[id]; + mrb_value pivot_hash = mrb_hash_new(mrb); + HASH_SET(pivot_hash, x, mrb_float_value(mrb, element->pivot.x)); + HASH_SET(pivot_hash, y, mrb_float_value(mrb, element->pivot.y)); + return pivot_hash; +} + +static mrb_value element_text_pivot_set(mrb_state *mrb, mrb_value self) { + mrb_value pivot_hash; + mrb_get_args(mrb, "H", &pivot_hash); + + uint64_t id = get_id_ElementText(mrb, self); + app::EText *element = (app::EText *)app::element_pool[id]; + + HASH_FLOAT(pivot_hash, x, element->pivot.x); + HASH_FLOAT(pivot_hash, y, element->pivot.y); + + return mrb_nil_value(); +} + static mrb_value element_text_scale_get(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementText(mrb, self); app::EText *element = (app::EText *)app::element_pool[id]; @@ -609,13 +640,13 @@ static mrb_value element_text_click_through_set(mrb_state *mrb, mrb_value self) static mrb_value element_text_width(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementText(mrb, self); app::EText *element = (app::EText *)app::element_pool[id]; - return mrb_float_value(mrb, element->size(&window).x); + return mrb_float_value(mrb, element->size().x); } static mrb_value element_text_height(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementText(mrb, self); app::EText *element = (app::EText *)app::element_pool[id]; - return mrb_float_value(mrb, element->size(&window).y); + return mrb_float_value(mrb, element->size().y); } static void ElementRect_free(mrb_state *mrb, void *ptr) { @@ -643,7 +674,8 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { mrb_value kwargs; mrb_get_args(mrb, "ffi|H", &w, &h, &color_i, &kwargs); - float x = 0, y = 0, z = 0, rotation = 0, scale_x = 1, scale_y = 1, alpha = 1; + float x = 0, y = 0, z = 0, rotation = 0; + float scale_x = 1, scale_y = 1, alpha = 1, pivot_x = 0.5, pivot_y = 0.5; bool click_through = false; if (!mrb_nil_p(kwargs)) { mrb_value pos_hash = HASH_GET(kwargs, position); @@ -658,6 +690,12 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { HASH_FLOAT(scale_hash, y, scale_y); } + mrb_value pivot_hash = HASH_GET(kwargs, pivot); + if (!mrb_nil_p(pivot_hash)) { + HASH_FLOAT(pivot_hash, x, pivot_x); + HASH_FLOAT(pivot_hash, y, pivot_y); + } + HASH_FLOAT(kwargs, z, z); HASH_FLOAT(kwargs, rotation, rotation); HASH_FLOAT(kwargs, alpha, alpha); @@ -679,6 +717,7 @@ static mrb_value ElementRect_init(mrb_state *mrb, mrb_value self) { element->position = {x, y}; element->scale = {scale_x, scale_y}; element->rotation = rotation; + element->pivot = {pivot_x, pivot_y}; element->alpha = alpha; element->z = z; element->click_through = click_through; @@ -829,6 +868,28 @@ static mrb_value element_rect_rotation_set(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } +static mrb_value element_rect_pivot_get(mrb_state *mrb, mrb_value self) { + uint64_t id = get_id_ElementRect(mrb, self); + app::ERect *element = (app::ERect *)app::element_pool[id]; + mrb_value pivot_hash = mrb_hash_new(mrb); + HASH_SET(pivot_hash, x, mrb_float_value(mrb, element->pivot.x)); + HASH_SET(pivot_hash, y, mrb_float_value(mrb, element->pivot.y)); + return pivot_hash; +} + +static mrb_value element_rect_pivot_set(mrb_state *mrb, mrb_value self) { + mrb_value pivot_hash; + mrb_get_args(mrb, "H", &pivot_hash); + + uint64_t id = get_id_ElementRect(mrb, self); + app::ERect *element = (app::ERect *)app::element_pool[id]; + + HASH_FLOAT(pivot_hash, x, element->pivot.x); + HASH_FLOAT(pivot_hash, y, element->pivot.y); + + return mrb_nil_value(); +} + static mrb_value element_rect_scale_get(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementRect(mrb, self); app::ERect *element = (app::ERect *)app::element_pool[id]; @@ -938,7 +999,8 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } - float x = 0, y = 0, z = 0, rotation = 0, scale_x = 1, scale_y = 1, alpha = 1; + float x = 0, y = 0, z = 0, rotation = 0; + float scale_x = 1, scale_y = 1, alpha = 1, pivot_x = 0.5, pivot_y = 0.5; bool click_through = false; if (!mrb_nil_p(kwargs)) { mrb_value pos_hash = HASH_GET(kwargs, position); @@ -953,6 +1015,12 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { HASH_FLOAT(scale_hash, y, scale_y); } + mrb_value pivot_hash = HASH_GET(kwargs, pivot); + if (!mrb_nil_p(pivot_hash)) { + HASH_FLOAT(pivot_hash, x, pivot_x); + HASH_FLOAT(pivot_hash, y, pivot_y); + } + HASH_FLOAT(kwargs, z, z); HASH_FLOAT(kwargs, rotation, rotation); HASH_FLOAT(kwargs, alpha, alpha); @@ -965,6 +1033,7 @@ static mrb_value ElementImage_init(mrb_state *mrb, mrb_value self) { element->position = {x, y}; element->scale = {scale_x, scale_y}; element->rotation = rotation; + element->pivot = {pivot_x, pivot_y}; element->alpha = alpha; element->z = z; element->click_through = click_through; @@ -1098,6 +1167,28 @@ static mrb_value element_image_rotation_set(mrb_state *mrb, mrb_value self) { return mrb_nil_value(); } +static mrb_value element_image_pivot_get(mrb_state *mrb, mrb_value self) { + uint64_t id = get_id_ElementImage(mrb, self); + app::EImage *element = (app::EImage *)app::element_pool[id]; + mrb_value pivot_hash = mrb_hash_new(mrb); + HASH_SET(pivot_hash, x, mrb_float_value(mrb, element->pivot.x)); + HASH_SET(pivot_hash, y, mrb_float_value(mrb, element->pivot.y)); + return pivot_hash; +} + +static mrb_value element_image_pivot_set(mrb_state *mrb, mrb_value self) { + mrb_value pivot_hash; + mrb_get_args(mrb, "H", &pivot_hash); + + uint64_t id = get_id_ElementImage(mrb, self); + app::EImage *element = (app::EImage *)app::element_pool[id]; + + HASH_FLOAT(pivot_hash, x, element->pivot.x); + HASH_FLOAT(pivot_hash, y, element->pivot.y); + + return mrb_nil_value(); +} + static mrb_value element_image_scale_get(mrb_state *mrb, mrb_value self) { uint64_t id = get_id_ElementImage(mrb, self); app::EImage *element = (app::EImage *)app::element_pool[id]; @@ -1572,14 +1663,18 @@ inline void setup() { ADD_METHOD(ElementText, "color", element_text_color_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "font=", element_text_font_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementText, "font", element_text_font_get, MRB_ARGS_NONE()); +#warning "Make text params a proper class instead of using a hash for text params" ADD_METHOD(ElementText, "params", element_text_params, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "params!", element_text_params_load, MRB_ARGS_NONE()); +#warning "Make vector a proper class instead of using a hash for vectors" ADD_METHOD(ElementText, "position=", element_text_position_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementText, "position", element_text_position_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "scale=", element_text_scale_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementText, "scale", element_text_scale_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "rotation=", element_text_rotation_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementText, "rotation", element_text_rotation_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementText, "pivot=", element_text_pivot_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementText, "pivot", element_text_pivot_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "alpha=", element_text_alpha_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementText, "alpha", element_text_alpha_get, MRB_ARGS_NONE()); ADD_METHOD(ElementText, "z=", element_text_z_set, MRB_ARGS_REQ(1)); @@ -1608,6 +1703,8 @@ inline void setup() { ADD_METHOD(ElementRect, "scale", element_rect_scale_get, MRB_ARGS_NONE()); ADD_METHOD(ElementRect, "rotation=", element_rect_rotation_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementRect, "rotation", element_rect_rotation_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementRect, "pivot=", element_rect_pivot_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementRect, "pivot", element_rect_pivot_get, MRB_ARGS_NONE()); ADD_METHOD(ElementRect, "alpha=", element_rect_alpha_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementRect, "alpha", element_rect_alpha_get, MRB_ARGS_NONE()); ADD_METHOD(ElementRect, "z=", element_rect_z_set, MRB_ARGS_REQ(1)); @@ -1630,6 +1727,8 @@ inline void setup() { ADD_METHOD(ElementImage, "scale", element_image_scale_get, MRB_ARGS_NONE()); ADD_METHOD(ElementImage, "rotation=", element_image_rotation_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementImage, "rotation", element_image_rotation_get, MRB_ARGS_NONE()); + ADD_METHOD(ElementImage, "pivot=", element_image_pivot_set, MRB_ARGS_REQ(1)); + ADD_METHOD(ElementImage, "pivot", element_image_pivot_get, MRB_ARGS_NONE()); ADD_METHOD(ElementImage, "alpha=", element_image_alpha_set, MRB_ARGS_REQ(1)); ADD_METHOD(ElementImage, "alpha", element_image_alpha_get, MRB_ARGS_NONE()); ADD_METHOD(ElementImage, "z=", element_image_z_set, MRB_ARGS_REQ(1)); diff --git a/include/binding/ruby.h b/include/binding/ruby.h index a4ff745..b6a5c9d 100644 --- a/include/binding/ruby.h +++ b/include/binding/ruby.h @@ -103,6 +103,7 @@ DEF_SYM(italic) DEF_SYM(underline) DEF_SYM(position) DEF_SYM(rotation) +DEF_SYM(pivot) DEF_SYM(scale) DEF_SYM(alpha) DEF_SYM(fps) diff --git a/include/utils.h b/include/utils.h index 9552bab..3d0a0d3 100644 --- a/include/utils.h +++ b/include/utils.h @@ -80,6 +80,7 @@ struct Line { Vec2 start; Vec2 end; + Line() = default; Line(Vec2 start, Vec2 end) : start(start), end(end) {} bool intersects(const Line &other) const { @@ -98,7 +99,7 @@ struct Line { struct Rect { Line diagonal; // from top-left to bottom-right - Rect(Vec2 a, Vec2 b) : diagonal(a, b) { + Rect(Vec2 a, Vec2 b) { diagonal.start = { MIN(a.x, b.x), MIN(a.y, b.y) @@ -109,7 +110,11 @@ struct Rect { }; } - static Rect from_size(Vec2 position, Vec2 size) { + static Rect from(Vec2 size) { + return Rect({0, 0}, size); + } + + static Rect from(Vec2 position, Vec2 size) { return Rect(position, position + size); } diff --git a/include/window/window.h b/include/window/window.h index e8bd776..2b1889c 100644 --- a/include/window/window.h +++ b/include/window/window.h @@ -320,11 +320,11 @@ public: uint64_t create_text(uint64_t font_id, const char *text, uint32_t length, Color color); Vec2 get_text_size(uint64_t text_id); - bool write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 scale, float alpha); + bool write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); - bool draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 scale, float alpha); + bool draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); - bool draw_rect(Rect rect, Color color, float z, float angle, Vec2 scale, float alpha); + bool draw_rect(Rect rect, Color color, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); void render(); // the rest of the calls only build the instruction list, this actually renders everything added to the screen (following z order) @@ -366,6 +366,7 @@ private: float z; float angle; + Vec2 pivot; Vec2 scale; float alpha; diff --git a/samples/button.rb b/samples/button.rb index 54dd67f..cba77a6 100644 --- a/samples/button.rb +++ b/samples/button.rb @@ -14,10 +14,12 @@ App.localize(:en, :btn_text, "Click %{name}!") text = ElementText.new :btn_text, default_font, 0xFFFF00, {name: "Me"}, position: {x: 50, y: 50}, z: 1, click_through: true, alpha: 1 -rect = ElementRect.new 100, 25, 0xFF0000, position: {x: 50, y: 50}, z: 0, rotation: 60, scale: { x: 1.5, y: 1 }, alpha: 0.4 +rect = ElementRect.new 100, 25, 0xFF0000, position: {x: 50, y: 50}, z: 0, rotation: 60, pivot: {x: 0.5, y: 0.5}, scale: { x: 1, y: 1 }, alpha: 0.4 image_element = ElementImage.new bg_animation, position: {x: 0, y: 0}, z: -1, click_through: true, alpha: 1 +pp text, rect, image_element + rect.on_click do rect.color = rect.color == 0xFF0000 ? 0x00FF00 : @@ -47,7 +49,7 @@ main_scene.on_update do |delta_time| pp App.text_input if App.text_input != "" - next unless App.pressed?(:press) + next unless App.down?(:press) pp "#{App.pressed?(:press)}, #{App.down?(:press)}, #{App.released?(:press)}" diff --git a/src/window/window.cc b/src/window/window.cc index 45226d1..2360077 100644 --- a/src/window/window.cc +++ b/src/window/window.cc @@ -155,7 +155,7 @@ Vec2 Window::get_text_size(uint64_t text_id) { return text_pool[text_id]->size; } -bool Window::write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 scale, float alpha) { +bool Window::write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha) { if (!text_pool.is_valid(text_id)) { fprintf(stderr, "(Write) Invalid text ID: %llu\n", (unsigned long long)text_id); return false; @@ -165,6 +165,7 @@ bool Window::write(uint64_t text_id, Vec2 position, float z, float angle, {.type = RenderInstruction::DRAW_TEXT, .z = z, .angle = angle, + .pivot = pivot, .scale = scale, .alpha = alpha, .draw_text = { @@ -176,7 +177,7 @@ bool Window::write(uint64_t text_id, Vec2 position, float z, float angle, return true; }; -bool Window::draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 scale, float alpha) { +bool Window::draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha) { if (!image_pool.is_valid(image_id)) { fprintf(stderr, "Invalid image ID: %llu\n", (unsigned long long)image_id); return false; @@ -186,6 +187,7 @@ bool Window::draw(uint64_t image_id, Vec2 position, float z, float angle, {.type = RenderInstruction::DRAW_IMAGE, .z = z, .angle = angle, + .pivot = pivot, .scale = scale, .alpha = alpha, .draw_image = { @@ -197,11 +199,12 @@ bool Window::draw(uint64_t image_id, Vec2 position, float z, float angle, return true; }; -bool Window::draw_rect(Rect rect, Color color, float z, float angle, Vec2 scale, float alpha) { +bool Window::draw_rect(Rect rect, Color color, float z, float angle, Vec2 pivot, Vec2 scale, float alpha) { render_instructions.push_back( {.type = RenderInstruction::DRAW_RECT, .z = z, .angle = angle, + .pivot = pivot, .scale = scale, .alpha = alpha, .draw_rect = { @@ -279,13 +282,18 @@ void Window::render() { SDL_SetTextureAlphaModFloat(texture, instruction.alpha); + SDL_FPoint pivot_point = { + instruction.pivot.x * scale_x, + instruction.pivot.y * scale_y + }; + SDL_RenderTextureRotated( renderer, texture, nullptr, &dst_rect, instruction.angle, - nullptr, + &pivot_point, flip ); } break; @@ -311,13 +319,18 @@ void Window::render() { SDL_SetTextureAlphaModFloat(image->texture, instruction.alpha); + SDL_FPoint pivot_point = { + instruction.pivot.x * scale_x, + instruction.pivot.y * scale_y + }; + SDL_RenderTextureRotated( renderer, image->texture, nullptr, &dst_rect, instruction.angle, - nullptr, + &pivot_point, flip ); } break; @@ -338,13 +351,18 @@ void Window::render() { SDL_SetTextureAlphaModFloat(white_tex, instruction.alpha); + SDL_FPoint pivot_point = { + instruction.pivot.x * dst_rect.w, + instruction.pivot.y * dst_rect.h + }; + SDL_RenderTextureRotated( renderer, white_tex, nullptr, &dst_rect, instruction.angle, - nullptr, + &pivot_point, SDL_FLIP_NONE ); } break;