diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2018-05-24 01:06:33 -0700 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2018-05-24 01:10:12 -0700 |
commit | 21c0f72b53a594f41c464c17afbc5f500fc91f25 (patch) | |
tree | 622fcb05572a9a03abbbf8605b627c3c69bfc91b | |
parent | 635e2e8f2b0007a52121c0240d445d0fcf82a231 (diff) |
libstage: rewrite aabb_to_rect() for less error
There was too much fractional math happening at once resulting in
artifacts like nodes sharing the same position jiggling relative to
eachother as they moved. Instead compute the various values discretely,
flooring their values.
This also integrates the node origin, which previously was ignored.
A clamp has also been added to prevent zero-size nodes in rect w or h when
their AABB dimensions are non-zero.
-rw-r--r-- | src/stage.c | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/src/stage.c b/src/stage.c index 35d6835..c52e5b1 100644 --- a/src/stage.c +++ b/src/stage.c @@ -369,18 +369,46 @@ void stage_get_position(stage_t *stage, v2f_t *res_position) *res_position = stage->position; } - -static void aabb_to_rect(const aabb_t *aabb, const v2f_t *pos, const SDL_Rect *stage_rect, SDL_Rect *res_rect) +/* translate an aabb having origin origin at pos position into a dest rect witin stage_rect */ +static void aabb_to_rect(const v2f_t *stage_pos, const aabb_t *aabb, const v2f_t *aabb_origin, const v2f_t *aabb_pos, const SDL_Rect *stage_rect, SDL_Rect *res_rect) { - float half_w = ((float)stage_rect->w) * .5f, half_h = ((float)stage_rect->h) * .5f; /* FIXME silly to recompute this repeatedly... */ + float aabb_w = (aabb->max.x - aabb->min.x); + float aabb_h = (aabb->max.y - aabb->min.y); + + /* res dimensions are simply aabb dimensions scaled by stage dimensions */ + res_rect->w = floorf(aabb_w * .5f * (float)stage_rect->w); + res_rect->h = floorf(aabb_h * .5f * (float)stage_rect->h); + + /* center in stage_rect */ + res_rect->x = stage_rect->x + floorf((float)stage_rect->w * .5f); + res_rect->y = stage_rect->y + floorf((float)stage_rect->h * .5f); + + /* apply stage_pos (scaled relative to stage_rect) */ + res_rect->x += floorf(stage_pos->x * .5f * (float)stage_rect->w); + res_rect->y += -floorf(stage_pos->y * .5f * (float)stage_rect->h); - res_rect->x = stage_rect->x; - res_rect->y = stage_rect->y; - res_rect->x += ((float)aabb->min.x + pos->x) * half_w + half_w; - res_rect->y += stage_rect->h - (((float)aabb->max.y + pos->y) * half_h + half_h); + /* apply aabb_pos (scaled relative to stage_rect) */ + res_rect->x += floorf(aabb_pos->x * .5f * (float)stage_rect->w); + res_rect->y += -floorf(aabb_pos->y * .5f * (float)stage_rect->h); + + /* apply aabb_origin (scaled relative to scaled aabb_w) (this probably needs to be inverted) */ + res_rect->x += floorf(aabb_origin->x * .5f * (float)res_rect->w); + res_rect->y += -floorf(aabb_origin->y * .5f * (float)res_rect->h); + + /* apply aabb (scaled relative to stage_rect) */ + res_rect->x += floorf(aabb->min.x * .5f * (float)stage_rect->w); + res_rect->y += -floorf(aabb->max.y * .5f * (float)stage_rect->h); + + /* Prevent producing 0-dimensioned non-zero rects even if it's technicaly incorrect, + * what's likely going on is the window has been resized very small. Rather + * than allowing things to become completely invisible in such circumstances, + * give them at least a single pixel. + */ + if (res_rect->w == 0 && aabb_w > 0.f) + res_rect->w = 1; - res_rect->w = (aabb->max.x - aabb->min.x) * half_w; - res_rect->h = (aabb->max.y - aabb->min.y) * half_h; + if (res_rect->h == 0 && aabb_h > 0.f) + res_rect->h = 1; } @@ -395,7 +423,7 @@ static void render_nodes(const stage_t *stage, const list_head_t *head, SDL_Rend continue; /* scale the node's aabb stage coordinates to destination renderer coordinates */ - aabb_to_rect(&node->aabb, &node->position, dest_rect, &node_rect); + aabb_to_rect(&stage->position, &node->aabb, &node->origin, &node->position, dest_rect, &node_rect); /* if we have a cached texture, see if the dimensions changed */ if (node->cached.texture && @@ -503,7 +531,7 @@ stage_node_t * stage_node_lookup_cartesian(const stage_t *stage, int x, int y) if (!node->active) continue; - aabb_to_rect(&node->aabb, &node->position, &rect, &node_rect); + aabb_to_rect(&stage->position, &node->aabb, &node->origin, &node->position, &rect, &node_rect); if (x >= node_rect.x && x < node_rect.x + node_rect.w && y >= node_rect.y && y < node_rect.y + node_rect.h) return node; |