diff options
author | 3gg <3gg@shellblade.net> | 2025-07-19 13:23:29 -0700 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2025-07-19 13:23:29 -0700 |
commit | a19049d7a6bed9b236c5e714dde844925750e39d (patch) | |
tree | 0113934ffe8e94fc2e20c4248886a1b79ae64a55 | |
parent | 6e0913a3f77354eab2eeceba66db7d1750e22581 (diff) |
Implement camera pan
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | demos/isomap/isomap.c | 28 | ||||
-rw-r--r-- | include/isogfx/isogfx.h | 3 | ||||
-rw-r--r-- | src/isogfx.c | 24 |
4 files changed, 48 insertions, 8 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 498adc8..72429c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -17,6 +17,7 @@ target_include_directories(isogfx PUBLIC | |||
17 | 17 | ||
18 | target_link_libraries(isogfx PUBLIC | 18 | target_link_libraries(isogfx PUBLIC |
19 | filesystem | 19 | filesystem |
20 | math | ||
20 | memstack) | 21 | memstack) |
21 | 22 | ||
22 | target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic) | 23 | target_compile_options(isogfx PRIVATE -Wall -Wextra -Wpedantic) |
diff --git a/demos/isomap/isomap.c b/demos/isomap/isomap.c index efae7fd..a940535 100644 --- a/demos/isomap/isomap.c +++ b/demos/isomap/isomap.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <isogfx/isogfx.h> | 2 | #include <isogfx/isogfx.h> |
3 | 3 | ||
4 | #include <gfx/app.h> | 4 | #include <gfx/app.h> |
5 | #include <math/vec2.h> | ||
5 | 6 | ||
6 | #include <assert.h> | 7 | #include <assert.h> |
7 | #include <stdbool.h> | 8 | #include <stdbool.h> |
@@ -15,6 +16,8 @@ static const int MAX_FPS = 60; | |||
15 | static const int SCREEN_WIDTH = 704; | 16 | static const int SCREEN_WIDTH = 704; |
16 | static const int SCREEN_HEIGHT = 480; | 17 | static const int SCREEN_HEIGHT = 480; |
17 | 18 | ||
19 | static const R CAMERA_SPEED = 400; | ||
20 | |||
18 | #define MEMORY_SIZE (2 * 1024 * 1024) | 21 | #define MEMORY_SIZE (2 * 1024 * 1024) |
19 | uint8_t MEMORY[MEMORY_SIZE]; | 22 | uint8_t MEMORY[MEMORY_SIZE]; |
20 | 23 | ||
@@ -23,6 +26,7 @@ typedef struct GfxAppState { | |||
23 | IsoGfx* iso; | 26 | IsoGfx* iso; |
24 | int xpick; | 27 | int xpick; |
25 | int ypick; | 28 | int ypick; |
29 | vec2 camera; | ||
26 | SpriteSheet stag_sheet; | 30 | SpriteSheet stag_sheet; |
27 | Sprite stag; | 31 | Sprite stag; |
28 | } GfxAppState; | 32 | } GfxAppState; |
@@ -67,11 +71,33 @@ static void shutdown(GfxAppState* state) { | |||
67 | // | 71 | // |
68 | } | 72 | } |
69 | 73 | ||
74 | static vec2 get_camera_movement(R dt) { | ||
75 | vec2 offset = {0}; | ||
76 | if (gfx_app_is_key_pressed(KeyA)) { | ||
77 | offset.x -= 1; | ||
78 | } | ||
79 | if (gfx_app_is_key_pressed(KeyD)) { | ||
80 | offset.x += 1; | ||
81 | } | ||
82 | if (gfx_app_is_key_pressed(KeyW)) { | ||
83 | offset.y -= 1; | ||
84 | } | ||
85 | if (gfx_app_is_key_pressed(KeyS)) { | ||
86 | offset.y += 1; | ||
87 | } | ||
88 | if ((offset.x != 0) || (offset.y != 0)) { | ||
89 | offset = vec2_scale(vec2_normalize(offset), dt * CAMERA_SPEED); | ||
90 | } | ||
91 | return offset; | ||
92 | } | ||
93 | |||
70 | static void update(GfxAppState* state, double t, double dt) { | 94 | static void update(GfxAppState* state, double t, double dt) { |
71 | assert(state); | 95 | assert(state); |
72 | (void)dt; | 96 | |
97 | state->camera = vec2_add(state->camera, get_camera_movement((R)dt)); | ||
73 | 98 | ||
74 | IsoGfx* iso = state->iso; | 99 | IsoGfx* iso = state->iso; |
100 | isogfx_set_camera(iso, (int)state->camera.x, (int)state->camera.y); | ||
75 | isogfx_update(iso, t); | 101 | isogfx_update(iso, t); |
76 | } | 102 | } |
77 | 103 | ||
diff --git a/include/isogfx/isogfx.h b/include/isogfx/isogfx.h index 93c6d4e..e901231 100644 --- a/include/isogfx/isogfx.h +++ b/include/isogfx/isogfx.h | |||
@@ -102,6 +102,9 @@ void isogfx_set_sprite_animation(IsoGfx*, Sprite, int animation); | |||
102 | /// Currently this updates the sprite animations. | 102 | /// Currently this updates the sprite animations. |
103 | void isogfx_update(IsoGfx*, double t); | 103 | void isogfx_update(IsoGfx*, double t); |
104 | 104 | ||
105 | /// Set the camera. | ||
106 | void isogfx_set_camera(IsoGfx*, int x, int y); | ||
107 | |||
105 | /// Render the world. | 108 | /// Render the world. |
106 | void isogfx_render(IsoGfx*); | 109 | void isogfx_render(IsoGfx*); |
107 | 110 | ||
diff --git a/src/isogfx.c b/src/isogfx.c index 0aaeb9d..5d23ae4 100644 --- a/src/isogfx.c +++ b/src/isogfx.c | |||
@@ -55,6 +55,7 @@ typedef struct SpriteInstance { | |||
55 | typedef struct IsoGfx { | 55 | typedef struct IsoGfx { |
56 | Screen screen; | 56 | Screen screen; |
57 | CoordSystem iso_space; | 57 | CoordSystem iso_space; |
58 | ivec2 camera; | ||
58 | double last_animation_time; | 59 | double last_animation_time; |
59 | Tile next_tile; // For procedurally-generated tiles. | 60 | Tile next_tile; // For procedurally-generated tiles. |
60 | Tm_Map* map; | 61 | Tm_Map* map; |
@@ -76,6 +77,8 @@ static inline ivec2 ivec2_scale(ivec2 a, int s) { | |||
76 | return (ivec2){.x = a.x * s, .y = a.y * s}; | 77 | return (ivec2){.x = a.x * s, .y = a.y * s}; |
77 | } | 78 | } |
78 | 79 | ||
80 | static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } | ||
81 | |||
79 | static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) { | 82 | static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) { |
80 | return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2), | 83 | return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2), |
81 | .y = (iso.x + iso.y) * (t / 2)}; | 84 | .y = (iso.x + iso.y) * (t / 2)}; |
@@ -490,13 +493,13 @@ void isogfx_update(IsoGfx* iso, double t) { | |||
490 | /// Get the screen position of the top diamond-corner of the tile at world | 493 | /// Get the screen position of the top diamond-corner of the tile at world |
491 | /// (x,y). | 494 | /// (x,y). |
492 | static ivec2 GetTileScreenOrigin( | 495 | static ivec2 GetTileScreenOrigin( |
493 | const CoordSystem iso_space, int world_x, int world_y) { | 496 | const CoordSystem iso_space, ivec2 camera, int world_x, int world_y) { |
494 | const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x); | 497 | const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x); |
495 | const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y); | 498 | const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y); |
496 | const ivec2 screen_origin = | 499 | const ivec2 screen_origin = |
497 | ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset)); | 500 | ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset)); |
498 | 501 | const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera)); | |
499 | return screen_origin; | 502 | return origin_view_space; |
500 | } | 503 | } |
501 | 504 | ||
502 | static Pixel alpha_blend(Pixel src, Pixel dst) { | 505 | static Pixel alpha_blend(Pixel src, Pixel dst) { |
@@ -598,8 +601,9 @@ static void draw_world(IsoGfx* iso) { | |||
598 | // screen-centric approach would juggle multiple tiles throughout the scan. | 601 | // screen-centric approach would juggle multiple tiles throughout the scan. |
599 | for (int wy = 0; wy < iso->map->world_height; ++wy) { | 602 | for (int wy = 0; wy < iso->map->world_height; ++wy) { |
600 | for (int wx = 0; wx < iso->map->world_width; ++wx) { | 603 | for (int wx = 0; wx < iso->map->world_width; ++wx) { |
601 | const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); | 604 | const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); |
602 | const ivec2 screen_origin = GetTileScreenOrigin(iso->iso_space, wx, wy); | 605 | const ivec2 screen_origin = |
606 | GetTileScreenOrigin(iso->iso_space, iso->camera, wx, wy); | ||
603 | draw_tile(iso, screen_origin, tile); | 607 | draw_tile(iso, screen_origin, tile); |
604 | } | 608 | } |
605 | } | 609 | } |
@@ -631,11 +635,16 @@ static void draw_sprites(IsoGfx* iso) { | |||
631 | assert(sheet); | 635 | assert(sheet); |
632 | 636 | ||
633 | const ivec2 screen_origin = GetTileScreenOrigin( | 637 | const ivec2 screen_origin = GetTileScreenOrigin( |
634 | iso->iso_space, sprite->position.x, sprite->position.y); | 638 | iso->iso_space, iso->camera, sprite->position.x, sprite->position.y); |
635 | draw_sprite(iso, screen_origin, sprite, sheet); | 639 | draw_sprite(iso, screen_origin, sprite, sheet); |
636 | } | 640 | } |
637 | } | 641 | } |
638 | 642 | ||
643 | void isogfx_set_camera(IsoGfx* iso, int x, int y) { | ||
644 | assert(iso); | ||
645 | iso->camera = (ivec2){x, y}; | ||
646 | } | ||
647 | |||
639 | void isogfx_render(IsoGfx* iso) { | 648 | void isogfx_render(IsoGfx* iso) { |
640 | assert(iso); | 649 | assert(iso); |
641 | draw_world(iso); | 650 | draw_world(iso); |
@@ -649,7 +658,8 @@ void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { | |||
649 | assert(x < iso->map->world_width); | 658 | assert(x < iso->map->world_width); |
650 | assert(y < iso->map->world_height); | 659 | assert(y < iso->map->world_height); |
651 | 660 | ||
652 | const ivec2 screen_origin = GetTileScreenOrigin(iso->iso_space, x, y); | 661 | const ivec2 screen_origin = |
662 | GetTileScreenOrigin(iso->iso_space, iso->camera, x, y); | ||
653 | draw_tile(iso, screen_origin, tile); | 663 | draw_tile(iso, screen_origin, tile); |
654 | } | 664 | } |
655 | 665 | ||