Demonstrate on-the-fly flip for sprites.feature/data-structs
@@ -67,9 +67,7 @@ add_executable( | |||
src/packages/game/input/IZ_keyboard.h | |||
src/packages/config/IZ_config.c | |||
src/packages/config/IZ_config.h | |||
src/packages/game/geometry/IZ_point2d.c | |||
src/packages/game/geometry/IZ_point2d.h | |||
src/packages/game/geometry/IZ_vector2d.c | |||
src/packages/game/geometry/IZ_vector2d.c | |||
src/packages/game/geometry/IZ_vector2d.h | |||
src/packages/game/geometry/IZ_rect.c | |||
src/packages/game/geometry/IZ_rect.h | |||
@@ -115,9 +113,7 @@ add_executable( | |||
src/packages/test/IZ_mock.h | |||
src/packages/test/IZ_test.h | |||
src/packages/game/geometry/IZ_point2d.h | |||
src/packages/game/geometry/IZ_point2d.c | |||
src/packages/game/geometry/IZ_rect.h | |||
src/packages/game/geometry/IZ_rect.h | |||
src/packages/game/geometry/IZ_rect.c | |||
src/packages/game/geometry/IZ_vector2d.h | |||
src/packages/game/geometry/IZ_vector2d.c | |||
@@ -127,6 +127,18 @@ IZ_AppResult IZ_AppRun(struct IZ_App* app, u8 argc, const char* argv[]) { | |||
.priority = IZ_VIDEO_SPRITE_PRIORITY_MEDIUM, | |||
}, &app->video_state.active_sprites[sprite_slot_index]); | |||
app->video_state.active_sprites[sprite_slot_index].sprite.scale_factor = 0.25f; | |||
app->video_state.active_sprites[sprite_slot_index].sprite.position = (IZ_Vector2D) { 100.f, 100.f }; | |||
IZ_AssetResolveDir("weapon-specialist", asset_dir); | |||
sprite_slot_index = IZ_VideoGetNextFreeSpriteSlot(&app->video_state); | |||
IZ_VideoLoadSprite(&app->video_state, (IZ_VideoLoadSpriteParams) { | |||
.dir = asset_dir, | |||
.filename = "sprite.svg", | |||
.priority = IZ_VIDEO_SPRITE_PRIORITY_MEDIUM, | |||
}, &app->video_state.active_sprites[sprite_slot_index]); | |||
app->video_state.active_sprites[sprite_slot_index].sprite.scale_factor = 0.25f; | |||
app->video_state.active_sprites[sprite_slot_index].sprite.position = (IZ_Vector2D) { 50.f, 50.f }; | |||
app->video_state.active_sprites[sprite_slot_index].sprite.flip_x = true; | |||
while (true) { | |||
app->ticks = SDL_GetTicks64(); | |||
@@ -123,16 +123,23 @@ void IZ_VideoUpdate(IZ_VideoState* video_state) { | |||
IZ_Sprite* sprite = &video_state->active_sprites[sprite_index].sprite; | |||
f32 draw_width = sprite->original_width * sprite->scale_factor; | |||
f32 draw_height = sprite->original_height * sprite->scale_factor; | |||
u8 flip_flag = 0; | |||
if (sprite->flip_x) { | |||
flip_flag |= SDL_FLIP_HORIZONTAL; | |||
} | |||
if (sprite->flip_y) { | |||
flip_flag |= SDL_FLIP_VERTICAL; | |||
} | |||
SDL_RenderCopyExF(video_state->renderer, sprite->texture, NULL, &(SDL_FRect) { | |||
// TODO honor each sprite's location in the world for calculation in the screen. | |||
.x = 100, | |||
.y = 100, | |||
.x = sprite->position.x, | |||
.y = sprite->position.y, | |||
.w = draw_width, | |||
.h = draw_height, | |||
}, sprite->rotate_degrees++, &(SDL_FPoint) { | |||
.x = draw_width / 2, | |||
.y = draw_height / 2, | |||
}, SDL_FLIP_NONE); | |||
}, flip_flag); | |||
// our goal is to render the svg files and apply custom transforms to some SVG groups if ever. | |||
// TODO perhaps we can parse the SVG for easier transforms? | |||
@@ -1,10 +1,10 @@ | |||
#ifndef IZ_ENTITY_H | |||
#define IZ_ENTITY_H | |||
#include "../geometry/IZ_point2d.h" | |||
#include "../geometry/IZ_vector2d.h" | |||
typedef struct { | |||
IZ_Point2D pos; | |||
IZ_Vector2D pos; | |||
// TODO object appearance (sprite, sprites contain bounding boxes, collisions contain bounding boxes) | |||
} IZ_Entity; | |||
@@ -1,8 +0,0 @@ | |||
#include "IZ_point2d.h" | |||
IZ_Point2D IZ_Point2DTranslate(IZ_Point2D point, f32 translate_x, f32 translate_y) { | |||
return (IZ_Point2D) { | |||
.x = point.x + translate_x, | |||
.y = point.y + translate_y, | |||
}; | |||
} |
@@ -1,14 +0,0 @@ | |||
#ifndef IZ_POINT2D_H | |||
#define IZ_POINT2D_H | |||
#include "../../common/IZ_common.h" | |||
// TODO: unify with vector2d | |||
typedef struct { | |||
f32 x; | |||
f32 y; | |||
} IZ_Point2D; | |||
IZ_Point2D IZ_Point2DTranslate(IZ_Point2D, f32, f32); | |||
#endif |
@@ -9,7 +9,7 @@ IZ_Bounds IZ_RectGetBounds(IZ_Rect rect) { | |||
}; | |||
} | |||
bool IZ_RectBoundsContainPoint(IZ_Bounds bounds, IZ_Point2D point) { | |||
bool IZ_RectBoundsContainPoint(IZ_Bounds bounds, IZ_Vector2D point) { | |||
return ( | |||
bounds.left <= point.x | |||
&& bounds.top <= point.y | |||
@@ -2,7 +2,7 @@ | |||
#define IZ_RECT_H | |||
#include <stdbool.h> | |||
#include "IZ_point2d.h" | |||
#include "IZ_vector2d.h" | |||
typedef struct { | |||
f32 left; | |||
@@ -13,14 +13,14 @@ typedef struct { | |||
typedef struct { | |||
// top left | |||
IZ_Point2D pos; | |||
IZ_Vector2D pos; | |||
f32 width; | |||
f32 height; | |||
} IZ_Rect; | |||
IZ_Bounds IZ_RectGetBounds(IZ_Rect); | |||
bool IZ_RectBoundsContainPoint(IZ_Bounds, IZ_Point2D); | |||
bool IZ_RectBoundsContainPoint(IZ_Bounds, IZ_Vector2D); | |||
bool IZ_RectBoundsCollide(IZ_Bounds, IZ_Bounds); | |||
@@ -2,21 +2,21 @@ | |||
IZ_Vector2D IZ_Vector2DAdd(IZ_Vector2D addend, IZ_Vector2D augend) { | |||
return (IZ_Vector2D) { | |||
.right = addend.right + augend.right, | |||
.up = addend.up + augend.up, | |||
.x = addend.x + augend.x, | |||
.y = addend.y + augend.y, | |||
}; | |||
} | |||
IZ_Vector2D IZ_Vector2DMultiply(IZ_Vector2D multiplicand, IZ_Vector2D multiplier) { | |||
return (IZ_Vector2D) { | |||
.right = multiplicand.right * multiplier.right, | |||
.up = multiplicand.up * multiplier.up, | |||
.x = multiplicand.x * multiplier.x, | |||
.y = multiplicand.y * multiplier.y, | |||
}; | |||
} | |||
IZ_Vector2D IZ_Vector2DScale(IZ_Vector2D vector, f32 scalar) { | |||
return (IZ_Vector2D) { | |||
.right = vector.right * scalar, | |||
.up = vector.up * scalar, | |||
.x = vector.x * scalar, | |||
.y = vector.y * scalar, | |||
}; | |||
} |
@@ -1,11 +1,11 @@ | |||
#ifndef IZ_VECTOR2D_H | |||
#define IZ_VECTOR2D_H | |||
#include "IZ_point2d.h" | |||
#include "../../common/IZ_common.h" | |||
typedef struct { | |||
f32 right; | |||
f32 up; | |||
f32 x; | |||
f32 y; | |||
} IZ_Vector2D; | |||
IZ_Vector2D IZ_Vector2DAdd(IZ_Vector2D, IZ_Vector2D); | |||
@@ -1,101 +1,78 @@ | |||
#include "../../test/IZ_test.h" | |||
#include "IZ_point2d.h" | |||
#include "IZ_vector2d.h" | |||
#include "IZ_rect.h" | |||
spec("geometry") { | |||
describe("point2d") { | |||
describe("Translate") { | |||
it("translates coordinates of a point") { | |||
static IZ_Point2D input = { | |||
.x = 420.f, | |||
.y = 1337.f, | |||
}; | |||
static IZ_Point2D expected = { | |||
.x = 426.f, | |||
.y = 1346.f, | |||
}; | |||
static IZ_Point2D actual; | |||
actual = IZ_Point2DTranslate(input, 6.f, 9.f); | |||
check(expected.x == actual.x, "X values do not match."); | |||
check(expected.y == actual.y, "Y values do not match."); | |||
} | |||
} | |||
} | |||
describe("vector2d") { | |||
describe("Add") { | |||
it("adds two vectors") { | |||
static IZ_Vector2D addend = { | |||
.right = 420.f, | |||
.up = 1337.f, | |||
.x = 420.f, | |||
.y = 1337.f, | |||
}; | |||
static IZ_Vector2D augend = { | |||
.right = 6.f, | |||
.up = 9.f, | |||
.x = 6.f, | |||
.y = 9.f, | |||
}; | |||
static IZ_Vector2D expected_sum = { | |||
.right = 426.f, | |||
.up = 1346.f, | |||
.x = 426.f, | |||
.y = 1346.f, | |||
}; | |||
static IZ_Vector2D actual_sum; | |||
actual_sum = IZ_Vector2DAdd(addend, augend); | |||
check(expected_sum.right == actual_sum.right, "Right values do not match."); | |||
check(expected_sum.up == actual_sum.up, "Up values do not match."); | |||
check(expected_sum.x == actual_sum.x, "Right values do not match."); | |||
check(expected_sum.y == actual_sum.y, "Up values do not match."); | |||
} | |||
} | |||
describe("Multiply") { | |||
it("multiplies two vectors") { | |||
static IZ_Vector2D multiplicand = { | |||
.right = 6.f, | |||
.up = 9.f, | |||
.x = 6.f, | |||
.y = 9.f, | |||
}; | |||
static IZ_Vector2D multiplier = { | |||
.right = 3.f, | |||
.up = 2.f, | |||
.x = 3.f, | |||
.y = 2.f, | |||
}; | |||
static IZ_Vector2D expected_product = { | |||
.right = 18.f, | |||
.up = 18.f, | |||
.x = 18.f, | |||
.y = 18.f, | |||
}; | |||
static IZ_Vector2D actual_product; | |||
actual_product = IZ_Vector2DMultiply(multiplicand, multiplier); | |||
check(expected_product.right == actual_product.right, "Right values do not match."); | |||
check(expected_product.up == actual_product.up, "Up values do not match."); | |||
check(expected_product.x == actual_product.x, "Right values do not match."); | |||
check(expected_product.y == actual_product.y, "Up values do not match."); | |||
} | |||
} | |||
describe("Scale") { | |||
it("scales a vector") { | |||
static IZ_Vector2D v = { | |||
.right = 420.f, | |||
.up = 69.f, | |||
.x = 420.f, | |||
.y = 69.f, | |||
}; | |||
static f32 s = 2.f; | |||
static IZ_Vector2D expected = { | |||
.right = 840.f, | |||
.up = 138.f, | |||
.x = 840.f, | |||
.y = 138.f, | |||
}; | |||
static IZ_Vector2D actual; | |||
actual = IZ_Vector2DScale(v, s); | |||
check(expected.right == actual.right, "Right values do not match."); | |||
check(expected.up == actual.up, "Up values do not match."); | |||
check(expected.x == actual.x, "Right values do not match."); | |||
check(expected.y == actual.y, "Up values do not match."); | |||
} | |||
} | |||
} | |||
@@ -138,7 +115,7 @@ spec("geometry") { | |||
.bottom = 300, | |||
}; | |||
static IZ_Point2D p = { | |||
static IZ_Vector2D p = { | |||
.x = 75, | |||
.y = 150, | |||
}; | |||
@@ -154,12 +131,12 @@ spec("geometry") { | |||
.bottom = 300, | |||
}; | |||
static IZ_Point2D p1 = { | |||
static IZ_Vector2D p1 = { | |||
.x = 50, | |||
.y = 100, | |||
}; | |||
static IZ_Point2D p2 = { | |||
static IZ_Vector2D p2 = { | |||
.x = 200, | |||
.y = 300, | |||
}; | |||
@@ -176,7 +153,7 @@ spec("geometry") { | |||
.bottom = 300, | |||
}; | |||
static IZ_Point2D p = { | |||
static IZ_Vector2D p = { | |||
.x = 0, | |||
.y = 0, | |||
}; | |||
@@ -92,6 +92,14 @@ IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, void* user_data, con | |||
} | |||
void IZ_VideoTeardown(IZ_VideoState* state) { | |||
for (u16 i = 0; i < MAX_ACTIVE_SPRITES; i += 1) { | |||
if (state->active_sprites[i].sprite.texture) { | |||
SDL_DestroyTexture(state->active_sprites[i].sprite.texture); | |||
state->active_sprites[i].sprite.texture = NULL; | |||
state->active_sprites[i].requested_at = 0; | |||
} | |||
} | |||
SDL_DestroyWindow(state->window); | |||
} | |||
@@ -100,8 +108,14 @@ u16 IZ_VideoGetNextFreeSpriteSlot(IZ_VideoState* state) { | |||
// 1. Run through all sprites in the active sprites array | |||
// 2. Check each sprite's priority and requested_at (for eviction policy) | |||
// 3. Return that new slot. (prefer returning empty slots) | |||
// 4. Return the max value for u16 if there's not slot left) | |||
return 0; | |||
// 4. Return MAX_ACTIVE_SPRITES if there's no slot left) | |||
for (u16 i = 0; i < MAX_ACTIVE_SPRITES; i += 1) { | |||
if (!state->active_sprites[i].sprite.texture) { | |||
return i; | |||
} | |||
} | |||
return MAX_ACTIVE_SPRITES; | |||
} | |||
void IZ_VideoLoadSprite(IZ_VideoState* state, IZ_VideoLoadSpriteParams params, IZ_SpriteSlot* out) { | |||
@@ -111,7 +125,7 @@ void IZ_VideoLoadSprite(IZ_VideoState* state, IZ_VideoLoadSpriteParams params, I | |||
u32 sprite_length_bytes = ini_getl(params.dir, params.filename, 0, "assets.ini"); | |||
u8* sprite = malloc(sprite_length_bytes + 1); | |||
fread(sprite, 1, sprite_length_bytes, f); | |||
SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_ADD); | |||
SDL_SetRenderDrawBlendMode(state->renderer, SDL_BLENDMODE_BLEND); | |||
SDL_Surface* test_surface = IMG_LoadSVG_RW(SDL_RWFromConstMem(sprite, sprite_length_bytes)); | |||
free(sprite); | |||
if (test_surface) { | |||
@@ -120,15 +134,9 @@ void IZ_VideoLoadSprite(IZ_VideoState* state, IZ_VideoLoadSpriteParams params, I | |||
out->sprite.original_height = test_surface->h; | |||
out->sprite.scale_factor = 1; | |||
out->sprite.rotate_degrees = 0; | |||
out->sprite.position = (IZ_Vector2D) { 0, 0 }; | |||
out->sprite.flip_x = false; | |||
out->sprite.flip_y = false; | |||
SDL_FreeSurface(test_surface); | |||
} | |||
} | |||
void IZ_VideoTeardownTexture(IZ_Sprite* sprite) { | |||
if (!sprite->texture) { | |||
return; | |||
} | |||
SDL_DestroyTexture(sprite->texture); | |||
sprite->original_width = 0; | |||
sprite->original_height = 0; | |||
} |
@@ -9,6 +9,7 @@ | |||
#include "../../../net/IZ_net_client.h" | |||
#include "../../../config/IZ_config.h" | |||
#include "../../../common/IZ_common.h" | |||
#include "../../geometry/IZ_vector2d.h" | |||
#include "../../input/IZ_input.h" | |||
#define MAX_ACTIVE_SPRITES 512u | |||
@@ -32,10 +33,13 @@ typedef enum { | |||
typedef struct { | |||
SDL_Texture* texture; | |||
IZ_Vector2D position; | |||
f32 original_width; | |||
f32 original_height; | |||
f32 scale_factor; | |||
f32 rotate_degrees; | |||
bool flip_x; | |||
bool flip_y; | |||
} IZ_Sprite; | |||
typedef struct { | |||
@@ -86,8 +90,6 @@ u16 IZ_VideoGetNextFreeSpriteSlot(IZ_VideoState*); | |||
void IZ_VideoLoadSprite(IZ_VideoState*, IZ_VideoLoadSpriteParams, IZ_SpriteSlot*); | |||
void IZ_VideoTeardownTexture(IZ_Sprite*); | |||
void IZ_VideoTeardown(IZ_VideoState*); | |||
#endif |