From af7d6dee00ac03b27969c12459e8126909bbe4a0 Mon Sep 17 00:00:00 2001 From: Luka Jankovic Date: Wed, 13 Aug 2025 23:48:14 +0200 Subject: [PATCH] replace context offset with Camera2D --- CMakeLists.txt | 1 + kubo_camera.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ kubo_camera.h | 30 ++++++++++++++++++++++++++++++ kubo_context.c | 26 -------------------------- kubo_context.h | 5 ----- kubo_input.c | 26 +++++++++++++++++++++----- kubo_input.h | 11 ++++++++--- kubo_wall.c | 8 ++------ kubo_wall.h | 3 +-- kubo_window.c | 42 ++++++++++++++++++++---------------------- kubo_window.h | 3 ++- 11 files changed, 132 insertions(+), 70 deletions(-) create mode 100644 kubo_camera.c create mode 100644 kubo_camera.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a7726b7..2884e6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ set(SOURCES kubo_window.c kubo_context.c kubo_command.c + kubo_camera.c main.c ) diff --git a/kubo_camera.c b/kubo_camera.c new file mode 100644 index 0000000..f14fbbd --- /dev/null +++ b/kubo_camera.c @@ -0,0 +1,47 @@ +/* + * Copyright Luka Jankovic 2025 + * + * This file is part of Kubo. + * + * Kubo is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Kubo is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Kubo. If not, see . + */ + +#include "kubo_camera.h" + +void kubo_camera_zoom(Camera2D *camera) { + float wheel = GetMouseWheelMove(); + if (wheel != 0) { + Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), *camera); + camera->offset = GetMousePosition(); + camera->target = mouseWorldPos; + + float scale = 0.2f * wheel; + camera->zoom = Clamp(expf(logf(camera->zoom) + scale), 0.125f, 64.0f); + } +} + +void kubo_camera_pan(Camera2D *camera) { + Vector2 delta = GetMouseDelta(); + delta = Vector2Scale(delta, -1.0f / camera->zoom); + kubo_camera_shift(camera, delta); +} + +void kubo_camera_shift(Camera2D *camera, Vector2 delta) { + camera->target = Vector2Add(camera->target, delta); +} + +void kubo_camera_reset(Camera2D *camera) { + camera->target = (Vector2){.x = 0, .y = 0}; + camera->offset = (Vector2){.x = 0, .y = 0}; + camera->zoom = 1.0f; +} diff --git a/kubo_camera.h b/kubo_camera.h new file mode 100644 index 0000000..2dc2b9c --- /dev/null +++ b/kubo_camera.h @@ -0,0 +1,30 @@ +/* + * Copyright Luka Jankovic 2025 + * + * This file is part of Kubo. + * + * Kubo is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Kubo is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * Kubo. If not, see . + */ + +#ifndef KUBO_CAMERA_H +#define KUBO_CAMERA_H + +#include +#include + +void kubo_camera_zoom(Camera2D *camera); +void kubo_camera_pan(Camera2D *camera); +void kubo_camera_shift(Camera2D *camera, Vector2 delta); +void kubo_camera_reset(Camera2D *camera); + +#endif diff --git a/kubo_context.c b/kubo_context.c index d7aa2fb..d8f032a 100644 --- a/kubo_context.c +++ b/kubo_context.c @@ -38,9 +38,6 @@ void kubo_context_init(struct kubo_context *context) { context->exit_pending = false; context->state = kubo_context_states[KUBO_CONTEXT_NORMAL]; - - context->offset_x = 0; - context->offset_y = 0; } void kubo_context_cleanup(struct kubo_context *context) { @@ -105,9 +102,6 @@ void kubo_context_delete_wall(struct kubo_context *context) { void kubo_context_input_up(struct kubo_context *context) { switch (context->state.id) { - case KUBO_CONTEXT_NORMAL: - context->offset_y -= KUBO_CONTEXT_OFFSET_JMP; - break; case KUBO_CONTEXT_WALL_SELECT: select_next_wall(context); break; @@ -118,9 +112,6 @@ void kubo_context_input_up(struct kubo_context *context) { void kubo_context_input_down(struct kubo_context *context) { switch (context->state.id) { - case KUBO_CONTEXT_NORMAL: - context->offset_y += KUBO_CONTEXT_OFFSET_JMP; - break; case KUBO_CONTEXT_WALL_SELECT: select_prev_wall(context); break; @@ -131,9 +122,6 @@ void kubo_context_input_down(struct kubo_context *context) { void kubo_context_input_left(struct kubo_context *context) { switch (context->state.id) { - case KUBO_CONTEXT_NORMAL: - context->offset_x -= KUBO_CONTEXT_OFFSET_JMP; - break; case KUBO_CONTEXT_WALL_SELECT: select_prev_wall(context); break; @@ -144,9 +132,6 @@ void kubo_context_input_left(struct kubo_context *context) { void kubo_context_input_right(struct kubo_context *context) { switch (context->state.id) { - case KUBO_CONTEXT_NORMAL: - context->offset_x += KUBO_CONTEXT_OFFSET_JMP; - break; case KUBO_CONTEXT_WALL_SELECT: select_next_wall(context); break; @@ -155,17 +140,6 @@ void kubo_context_input_right(struct kubo_context *context) { } } -void kubo_context_reset_offset(struct kubo_context *context) { - switch (context->state.id) { - case KUBO_CONTEXT_NORMAL: - context->offset_x = 0; - context->offset_y = 0; - break; - default: - break; - } -} - static void select_next_wall(struct kubo_context *context) { if (!context->walls.count) { return; diff --git a/kubo_context.h b/kubo_context.h index 684885b..6f1ebc8 100644 --- a/kubo_context.h +++ b/kubo_context.h @@ -51,9 +51,6 @@ struct kubo_context { struct kubo_wall_arr walls; struct kubo_context_state_data state; - int offset_x; - int offset_y; - // KUBO_CONTEXT_WALL_SELECT size_t wall_select_index; }; @@ -74,6 +71,4 @@ void kubo_context_input_down(struct kubo_context *context); void kubo_context_input_left(struct kubo_context *context); void kubo_context_input_right(struct kubo_context *context); -void kubo_context_reset_offset(struct kubo_context *context); - #endif diff --git a/kubo_input.c b/kubo_input.c index ef913f5..811b951 100644 --- a/kubo_input.c +++ b/kubo_input.c @@ -18,22 +18,22 @@ #include "kubo_input.h" -static void key_input(struct kubo_context *context); +static void key_input(struct kubo_context *context, Camera2D *camera); static void char_input(struct kubo_context *context); static void handle_cmd_input(struct kubo_context *context); -void kubo_input_handle(struct kubo_context *context) { +void kubo_input_handle(struct kubo_context *context, Camera2D *camera) { char_input(context); if (context->state.id == KUBO_CONTEXT_COMMAND) { handle_cmd_input(context); } - key_input(context); + key_input(context, camera); } -static void key_input(struct kubo_context *context) { +static void key_input(struct kubo_context *context, Camera2D *camera) { int key_code = GetKeyPressed(); switch (key_code) { @@ -52,21 +52,37 @@ static void key_input(struct kubo_context *context) { case KEY_RIGHT: case KEY_L: + if (context->state.id == KUBO_CONTEXT_NORMAL) { + kubo_camera_shift(camera, + (Vector2){.x = KUBO_INPUT_CAMERA_SHIFT, .y = 0}); + } kubo_context_input_right(context); break; case KEY_UP: case KEY_K: + if (context->state.id == KUBO_CONTEXT_NORMAL) { + kubo_camera_shift(camera, + (Vector2){.x = 0, .y = KUBO_INPUT_CAMERA_SHIFT}); + } kubo_context_input_up(context); break; case KEY_LEFT: case KEY_H: + if (context->state.id == KUBO_CONTEXT_NORMAL) { + kubo_camera_shift(camera, + (Vector2){.x = -KUBO_INPUT_CAMERA_SHIFT, .y = 0}); + } kubo_context_input_left(context); break; case KEY_DOWN: case KEY_J: + if (context->state.id == KUBO_CONTEXT_NORMAL) { + kubo_camera_shift(camera, + (Vector2){.x = 0, .y = -KUBO_INPUT_CAMERA_SHIFT}); + } kubo_context_input_down(context); break; @@ -75,7 +91,7 @@ static void key_input(struct kubo_context *context) { break; case KEY_SPACE: - kubo_context_reset_offset(context); + kubo_camera_reset(camera); break; default: diff --git a/kubo_input.h b/kubo_input.h index 0ec3c06..96d4cb1 100644 --- a/kubo_input.h +++ b/kubo_input.h @@ -19,9 +19,14 @@ #ifndef KUBO_INPUT_H #define KUBO_INPUT_H -#include "kubo_context.h" -#include "kubo_command.h" +#include -void kubo_input_handle(struct kubo_context *context); +#include "kubo_camera.h" +#include "kubo_command.h" +#include "kubo_context.h" + +#define KUBO_INPUT_CAMERA_SHIFT 50 + +void kubo_input_handle(struct kubo_context *context, Camera2D *camera); #endif diff --git a/kubo_wall.c b/kubo_wall.c index 53e9d1d..5bb89c3 100644 --- a/kubo_wall.c +++ b/kubo_wall.c @@ -40,8 +40,7 @@ Vector2 kubo_wall_get_vertex(struct kubo_wall *wall, size_t index) { return kubo_vector2_arr_get(&wall->vertices, index); } -void kubo_wall_render(struct kubo_wall *wall, bool select, int offset_x, - int offset_y) { +void kubo_wall_render(struct kubo_wall *wall, bool select, Camera2D *camera) { Color wall_color = (select && wall->state == KUBO_WALL_SELECTED) ? BLUE : BLACK; @@ -50,15 +49,12 @@ void kubo_wall_render(struct kubo_wall *wall, bool select, int offset_x, for (size_t i = 0; i < wall->vertices.count; i++) { points[i] = kubo_vector2_arr_get(&wall->vertices, i); - - points[i].x += offset_x; - points[i].y += offset_y; } DrawSplineLinear(points, wall->vertices.count, 10.f, wall_color); if (wall->state == KUBO_WALL_DRAWING) { - Vector2 mouse = GetMousePosition(); + Vector2 mouse = GetScreenToWorld2D(GetMousePosition(), *camera); Vector2 mouse_points[] = {points[wall->vertices.count - 1], mouse}; DrawSplineLinear(mouse_points, 2, 10.f, wall_color); diff --git a/kubo_wall.h b/kubo_wall.h index 2373428..5a18268 100644 --- a/kubo_wall.h +++ b/kubo_wall.h @@ -47,7 +47,6 @@ void kubo_wall_cleanup(struct kubo_wall *wall); bool kubo_wall_add_vertex(struct kubo_wall *wall, Vector2 vec); Vector2 kubo_wall_get_vertex(struct kubo_wall *wall, size_t index); -void kubo_wall_render(struct kubo_wall *wall, bool select, int offset_x, - int offset_y); +void kubo_wall_render(struct kubo_wall *wall, bool select, Camera2D *camera); #endif diff --git a/kubo_window.c b/kubo_window.c index a655b44..903292f 100644 --- a/kubo_window.c +++ b/kubo_window.c @@ -18,12 +18,12 @@ #include "kubo_window.h" +Camera2D camera = {0}; + static void window_render(struct kubo_context *context); static void window_left_mouse(struct kubo_context *context); static void window_right_mouse(struct kubo_context *context); -static void pan(struct kubo_context *context); - static void new_wall_click(struct kubo_context *context); static void new_wall_end(struct kubo_context *context); @@ -33,6 +33,8 @@ void kubo_window_init() { EnableEventWaiting(); SetExitKey(0); SetTargetFPS(KUBO_TARGET_FPS); + + camera.zoom = 1.0f; } void kubo_window_cleanup() { CloseWindow(); } @@ -43,18 +45,26 @@ bool kubo_window_should_close(struct kubo_context *context) { void kubo_window_tick(struct kubo_context *context) { window_render(context); - pan(context); + + kubo_camera_zoom(&camera); + if ((context->state.id == KUBO_CONTEXT_NORMAL && + IsMouseButtonDown(MOUSE_BUTTON_LEFT)) || + IsMouseButtonDown(MOUSE_BUTTON_MIDDLE)) { + kubo_camera_pan(&camera); + } + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { window_left_mouse(context); } if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { window_right_mouse(context); } - kubo_input_handle(context); + kubo_input_handle(context, &camera); } static void window_render(struct kubo_context *context) { BeginDrawing(); + BeginMode2D(camera); ClearBackground(WHITE); for (size_t i = 0; i < context->walls.count; i++) { @@ -62,9 +72,11 @@ static void window_render(struct kubo_context *context) { assert(wall != NULL); kubo_wall_render(wall, context->state.id == KUBO_CONTEXT_WALL_SELECT, - context->offset_x, context->offset_y); + &camera); } + EndMode2D(); + kubo_bar_render(context); EndDrawing(); @@ -94,15 +106,6 @@ static void window_right_mouse(struct kubo_context *context) { } } -static void pan(struct kubo_context *context) { - if (context->state.id == KUBO_CONTEXT_NORMAL && - IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - Vector2 delta = GetMouseDelta(); - context->offset_x += delta.x; - context->offset_y += delta.y; - } -} - static void new_wall_click(struct kubo_context *context) { struct kubo_wall *current_wall = kubo_context_get_pending_wall(context); @@ -110,14 +113,9 @@ static void new_wall_click(struct kubo_context *context) { return; } - int bar_limit = GetScreenHeight() - KUBO_BAR_HEIGHT; - Vector2 new_pos = GetMousePosition(); - new_pos.x -= context->offset_x; - new_pos.y -= context->offset_y; - if (new_pos.y < bar_limit) { - kubo_wall_add_vertex(current_wall, new_pos); - current_wall->state = KUBO_WALL_DRAWING; - } + Vector2 new_pos = GetScreenToWorld2D(GetMousePosition(), camera); + kubo_wall_add_vertex(current_wall, new_pos); + current_wall->state = KUBO_WALL_DRAWING; } static void new_wall_end(struct kubo_context *context) { diff --git a/kubo_window.h b/kubo_window.h index 8300246..2f9867b 100644 --- a/kubo_window.h +++ b/kubo_window.h @@ -21,9 +21,10 @@ #include +#include "kubo_bar.h" +#include "kubo_camera.h" #include "kubo_context.h" #include "kubo_input.h" -#include "kubo_bar.h" #define KUBO_WINDOW_WIDTH 1000 #define KUBO_WINDOW_HEIGHT 800