Merge pull request 'Correctly render sprites' (#5) from fix-render into main
Reviewed-on: #5pull/6/head
commit
1748db8412
|
@ -1,3 +1,6 @@
|
||||||
|
# ide files
|
||||||
|
.nvimrc
|
||||||
|
|
||||||
# build dirs
|
# build dirs
|
||||||
bin/
|
bin/
|
||||||
release/
|
release/
|
||||||
|
@ -8,3 +11,4 @@ compile_commands.json
|
||||||
|
|
||||||
# clangd cache
|
# clangd cache
|
||||||
.cache/
|
.cache/
|
||||||
|
.helix
|
||||||
|
|
|
@ -80,10 +80,10 @@ static
|
||||||
void _exec_sprite_cmd(const drawcmd_t* cmd) {
|
void _exec_sprite_cmd(const drawcmd_t* cmd) {
|
||||||
const sprite_t* sprite = &cmd->sprite;
|
const sprite_t* sprite = &cmd->sprite;
|
||||||
SDL_FRect untransformed = {sprite->x, sprite->y, sprite->sx, sprite->sy};
|
SDL_FRect untransformed = {sprite->x, sprite->y, sprite->sx, sprite->sy};
|
||||||
|
untransformed.x -= sprite->origin.x;
|
||||||
|
untransformed.y -= sprite->origin.y;
|
||||||
SDL_FRect destrect = get_dest_with_size(untransformed, cmd->ui);
|
SDL_FRect destrect = get_dest_with_size(untransformed, cmd->ui);
|
||||||
SDL_FPoint origin = {destrect.w * sprite->origin.x, destrect.h * sprite->origin.y};
|
SDL_FPoint origin = {destrect.w * sprite->origin.x, destrect.h * sprite->origin.y};
|
||||||
destrect.x -= origin.x;
|
|
||||||
destrect.y -= origin.y;
|
|
||||||
SDL_RenderCopyExF(g_context.renderer, sprite->texture,
|
SDL_RenderCopyExF(g_context.renderer, sprite->texture,
|
||||||
&sprite->uv, &destrect, sprite->rot,
|
&sprite->uv, &destrect, sprite->rot,
|
||||||
&origin, sprite->flip);
|
&origin, sprite->flip);
|
||||||
|
@ -443,7 +443,7 @@ sprite_t make_sprite(const char* file, float x, float y) {
|
||||||
sprite_t sprite=(sprite_t){
|
sprite_t sprite=(sprite_t){
|
||||||
.texture=get_texture(file),
|
.texture=get_texture(file),
|
||||||
.x=x,.y=y,
|
.x=x,.y=y,
|
||||||
.origin=(SDL_FPoint){.x=0,.y=0},
|
.origin=(SDL_FPoint){.x=0.0,.y=0.0},
|
||||||
.sx=1.0,.sy=1.0,
|
.sx=1.0,.sy=1.0,
|
||||||
.rot=0,
|
.rot=0,
|
||||||
.depth=RLAYER_SPRITES,
|
.depth=RLAYER_SPRITES,
|
||||||
|
@ -451,7 +451,6 @@ sprite_t make_sprite(const char* file, float x, float y) {
|
||||||
.flip=SDL_FLIP_NONE,
|
.flip=SDL_FLIP_NONE,
|
||||||
};
|
};
|
||||||
SDL_QueryTexture(sprite.texture, NULL, NULL, &sprite.uv.w, &sprite.uv.h);
|
SDL_QueryTexture(sprite.texture, NULL, NULL, &sprite.uv.w, &sprite.uv.h);
|
||||||
sprite.origin.x = -(float)sprite.uv.h/2.f; sprite.origin.y = -(float)sprite.uv.h/2.f;
|
|
||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ object_t* make_object() {
|
||||||
object_t* o = _find_free_object();
|
object_t* o = _find_free_object();
|
||||||
o->active = 1;
|
o->active = 1;
|
||||||
o->enabled = 1;
|
o->enabled = 1;
|
||||||
|
o->collider = collider_default();
|
||||||
o->evt_draw = &object_draw_sprite;
|
o->evt_draw = &object_draw_sprite;
|
||||||
o->evt_update = NULL;
|
o->evt_update = NULL;
|
||||||
memset(&o->sprite, 0, sizeof(sprite_t));
|
memset(&o->sprite, 0, sizeof(sprite_t));
|
||||||
|
@ -39,6 +40,12 @@ object_t* instantiate_object(const object_t *original) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collider_t collider_default() {
|
||||||
|
return (collider_t) {
|
||||||
|
.type=COLLIDERTYPE_NONE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void update_objects() {
|
void update_objects() {
|
||||||
for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) {
|
for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) {
|
||||||
if(g_objects[i].active == 1
|
if(g_objects[i].active == 1
|
||||||
|
@ -58,3 +65,142 @@ void draw_objects() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
int _rect_overlap(float aminx, float aminy, float amaxx, float amaxy, float bminx, float bminy, float bmaxx, float bmaxy) {
|
||||||
|
return
|
||||||
|
(
|
||||||
|
(aminx < bmaxx && aminx > bmaxx)
|
||||||
|
||
|
||||||
|
(amaxx > bminx && amaxx < bmaxx)
|
||||||
|
) && (
|
||||||
|
(aminy < bmaxy && aminy > bmaxy)
|
||||||
|
||
|
||||||
|
(amaxy > bminy && amaxy < bmaxy)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
short _collision_aabb_aabb(const object_t* a, const object_t* b) {
|
||||||
|
const float aminx = a->collider.aabb.x + a->sprite.x, aminy = a->collider.aabb.y + a->sprite.x;
|
||||||
|
const float amaxx = aminx + a->collider.aabb.w, amaxy = aminy + a->collider.aabb.h;
|
||||||
|
const float bminx = b->collider.aabb.x, bminy = b->collider.aabb.y;
|
||||||
|
const float bmaxx = b->collider.aabb.x + b->collider.aabb.w, bmaxy = b->collider.aabb.y + b->collider.aabb.h;
|
||||||
|
|
||||||
|
return _rect_overlap(aminx, aminy, amaxx, amaxy, bminx, bminy, bmaxx, bmaxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
short _collision_circle_circle(const object_t* a, const object_t* b) {
|
||||||
|
const float ax = a->sprite.x + a->collider.circle.x, ay = a->sprite.y + a->collider.circle.y,
|
||||||
|
bx = b->sprite.x + b->collider.circle.x, by = b->sprite.y + b->collider.circle.y;
|
||||||
|
const float dx = fabsf(ax-bx), dy = fabsf(ay-by);
|
||||||
|
const float sqrdist = dx*dx+dy*dy;
|
||||||
|
const float mindist = a->collider.circle.radius + b->collider.circle.radius;
|
||||||
|
const float mindistsqr = mindist*mindist;
|
||||||
|
return sqrdist < mindistsqr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
float fclampf(float x, float min_, float max_) {
|
||||||
|
return fminf(max_, fmaxf(min_, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
short _collision_circle_aabb(const object_t* circle, const object_t* aabb) {
|
||||||
|
// generate a point on the edge of the rectangle that is closest to the circle
|
||||||
|
const float bbminx = aabb->collider.aabb.x + aabb->sprite.x, bbmaxx = bbminx + aabb->collider.aabb.w,
|
||||||
|
bbminy = aabb->collider.aabb.y + aabb->sprite.x, bbmaxy = bbminy + aabb->collider.aabb.h;
|
||||||
|
const float cx = circle->sprite.x + circle->collider.circle.x,
|
||||||
|
cy = circle->sprite.y + circle->collider.circle.y;
|
||||||
|
const float x = fclampf(cx, bbminx, bbmaxx),
|
||||||
|
y = fclampf(cy, bbminy, bbmaxy);
|
||||||
|
const float dx = fabsf(cx - x), dy = fabsf(cy - y);
|
||||||
|
|
||||||
|
// calculate the square distance from the centre of the circle to the edge of the aabb
|
||||||
|
const float distsqr = dx*dx+dy*dy;
|
||||||
|
const float rsqr = circle->collider.circle.radius*circle->collider.circle.radius;
|
||||||
|
|
||||||
|
// return if the square distance is larger than the square of the radius
|
||||||
|
return distsqr < rsqr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
short _collision_check(const object_t* a, const object_t* b) {
|
||||||
|
if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_AABB) {
|
||||||
|
return _collision_aabb_aabb(a, b);
|
||||||
|
} else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_CIRCLE) {
|
||||||
|
return _collision_circle_circle(a, b);
|
||||||
|
} else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_AABB) {
|
||||||
|
return _collision_circle_aabb(a, b);
|
||||||
|
} else if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_CIRCLE) {
|
||||||
|
return _collision_circle_aabb(b, a);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
short _can_collide(const object_t* object) {
|
||||||
|
return object->active && object->enabled && object->collider.evt_collision != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_collision() {
|
||||||
|
for(int outer = 0; outer < WORLD_NUM_OBJECTS; ++outer) {
|
||||||
|
object_t* oobject = g_objects + outer;
|
||||||
|
if(!_can_collide(oobject)) continue;
|
||||||
|
for(int inner = 0; inner < WORLD_NUM_OBJECTS; ++inner) {
|
||||||
|
object_t* iobject = g_objects + inner;
|
||||||
|
if(!_can_collide(oobject)) continue;
|
||||||
|
|
||||||
|
if(outer != inner && _collision_check(iobject, oobject)) {
|
||||||
|
oobject->collider.evt_collision(oobject, iobject);
|
||||||
|
iobject->collider.evt_collision(iobject, oobject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_t* interpolate_move(object_t* object, float target_x, float target_y, float max_step_size) {
|
||||||
|
// calculate step delta
|
||||||
|
float dx = target_x - object->sprite.x, dy = target_y - object->sprite.y;
|
||||||
|
// calculate direction x,y
|
||||||
|
float m = sqrtf(dx*dx + dy*dy);
|
||||||
|
dx /= m; dy /= m;
|
||||||
|
dx *= max_step_size; dy *= max_step_size;
|
||||||
|
|
||||||
|
// ensure this object would ever collide
|
||||||
|
// if it wouldn't collide anyway, just set position
|
||||||
|
if(_can_collide(object)) {
|
||||||
|
object->sprite.x = target_x;
|
||||||
|
object->sprite.y = target_y;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1. check collision with every other object
|
||||||
|
* 2. move towards target
|
||||||
|
*/
|
||||||
|
while(object->sprite.x != target_x || object->sprite.y != target_y) {
|
||||||
|
for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) {
|
||||||
|
object_t* other = g_objects + i;
|
||||||
|
if(!_can_collide(other)) continue;
|
||||||
|
if(object != other && _collision_check(other, object)) {
|
||||||
|
other->collider.evt_collision(other, object);
|
||||||
|
object->collider.evt_collision(object, other);
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move towards target, snap to target if distance is too low
|
||||||
|
float distx = fabsf(object->sprite.x - target_x), disty = fabsf(object->sprite.y - target_y);
|
||||||
|
if(distx < fabsf(dx) && disty < fabsf(dy)) {
|
||||||
|
object->sprite.x += dx; object->sprite.y += dy;
|
||||||
|
} else {
|
||||||
|
object->sprite.x = target_x;
|
||||||
|
object->sprite.y = target_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no collision, return nothing
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -10,11 +10,33 @@ typedef struct object_t object_t;
|
||||||
|
|
||||||
typedef void(*tick_fn)(struct object_t*);
|
typedef void(*tick_fn)(struct object_t*);
|
||||||
typedef void(*draw_fn)(struct object_t*);
|
typedef void(*draw_fn)(struct object_t*);
|
||||||
|
typedef void(*collided_fn)(struct object_t*, struct object_t*);
|
||||||
|
|
||||||
|
typedef enum collider_type_t {
|
||||||
|
COLLIDERTYPE_MIN,
|
||||||
|
COLLIDERTYPE_NONE,
|
||||||
|
COLLIDERTYPE_CIRCLE,
|
||||||
|
COLLIDERTYPE_AABB,
|
||||||
|
COLLIDERTYPE_MAX,
|
||||||
|
} collider_type_t;
|
||||||
|
|
||||||
|
typedef struct collider_t {
|
||||||
|
collider_type_t type;
|
||||||
|
collided_fn evt_collision;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
float x, y;
|
||||||
|
float radius;
|
||||||
|
} circle;
|
||||||
|
SDL_FRect aabb;
|
||||||
|
};
|
||||||
|
} collider_t;
|
||||||
|
|
||||||
struct object_t {
|
struct object_t {
|
||||||
sprite_t sprite;
|
sprite_t sprite;
|
||||||
int active; // 1 if this object is in use and should not be overriden
|
int active; // 1 if this object is in use and should not be overriden.
|
||||||
int enabled; // 1 if this object's events should be triggered
|
int enabled; // 1 if this object's events should be triggered.
|
||||||
|
collider_t collider; // the collider to use for this object's physics interaction.
|
||||||
|
|
||||||
uintptr_t timer; // free to use for whatever
|
uintptr_t timer; // free to use for whatever
|
||||||
|
|
||||||
|
@ -26,12 +48,19 @@ extern object_t g_objects[WORLD_NUM_OBJECTS];
|
||||||
|
|
||||||
void world_clear();
|
void world_clear();
|
||||||
|
|
||||||
|
|
||||||
object_t* make_object();
|
object_t* make_object();
|
||||||
object_t* instantiate_object(const object_t* original);
|
object_t* instantiate_object(const object_t* original);
|
||||||
|
|
||||||
|
collider_t collider_default();
|
||||||
|
|
||||||
void object_draw_sprite(object_t* object);
|
void object_draw_sprite(object_t* object);
|
||||||
|
|
||||||
void update_objects();
|
void update_objects();
|
||||||
void draw_objects();
|
void draw_objects();
|
||||||
|
|
||||||
|
void update_collision();
|
||||||
|
|
||||||
|
object_t* interpolate_move(object_t* object, float target_x, float target_y, float max_step_size);
|
||||||
|
|
||||||
#endif /* _world_h */
|
#endif /* _world_h */
|
||||||
|
|
Loading…
Reference in New Issue