// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2014-2016 by John "JTE" Muniz. // Copyright (C) 2014-2022 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file lua_hudlib_drawlist.c /// \brief a data structure for managing cached drawlists for the Lua hud lib #include "lua_hudlib_drawlist.h" #include "lua_hud.h" #include "blua/lstate.h" // shhhhhh #include "lua_libs.h" #include #include "v_video.h" #include "z_zone.h" #include "r_main.h" #include "r_fps.h" enum drawitem_e { DI_Draw = 0, DI_DrawScaled, DI_DrawStretched, DI_DrawNum, DI_DrawPaddedNum, DI_DrawPingNum, DI_DrawFill, DI_DrawString, DI_FadeScreen, DI_DrawTitleCardString, DI_DrawTitleCardStringBoss, DI_DrawKartString, DI_SetClipRect, DI_ClearClipRect, DI_MAX, }; // A single draw item with all possible arguments needed for a draw call. typedef struct drawitem_s { UINT64 id; enum drawitem_e type; fixed_t x; fixed_t y; union { INT32 flags; INT32 c; }; union { fixed_t scale; // drawScaled fixed_t hscale; // drawStretched fixed_t w; // drawFill INT32 align; // drawString INT32 num; // drawNum, drawPaddedNum, drawPingNum UINT16 color; // fadeScreen INT32 timer; // drawTitleCardString }; union { fixed_t vscale; // drawStretched fixed_t h; // drawFill INT32 digits; // drawPaddedNum UINT8 strength; // fadeScreen INT32 threshold; // drawTitleCardString }; UINT8 *colormap; // pointers (and size_t's) last, for potentially better packing union { patch_t *patch; size_t stroffset; // offset into strbuf to get str }; } drawitem_t; // The internal structure of a drawlist. struct huddrawlist_s { drawitem_t *items; size_t items_len; drawitem_t *olditems; size_t olditems_len; size_t capacity; char *strbuf; size_t strbuf_capacity; size_t strbuf_len; }; // alignment types for v.drawString enum align { align_left = 0, align_center, align_right, align_small, align_smallcenter, align_smallright, align_thin, align_thincenter, align_thinright }; huddrawlist_h LUA_HUD_CreateDrawList(void) { huddrawlist_h drawlist; drawlist = (huddrawlist_h) Z_Calloc(sizeof(struct huddrawlist_s), PU_STATIC, NULL); drawlist->items = NULL; drawlist->items_len = 0; drawlist->olditems = NULL; drawlist->olditems_len = 0; drawlist->capacity = 0; drawlist->strbuf = NULL; drawlist->strbuf_capacity = 0; drawlist->strbuf_len = 0; return drawlist; } void LUA_HUD_ClearDrawList(huddrawlist_h list) { // swap the old and new lists void *tmp = list->olditems; list->olditems = list->items; list->items = tmp; // rather than deallocate, we'll just save the existing allocation and empty // it out for reuse list->olditems_len = list->items_len; list->items_len = 0; if (list->strbuf) { list->strbuf[0] = 0; } list->strbuf_len = 0; } void LUA_HUD_DestroyDrawList(huddrawlist_h list) { if (list == NULL) return; if (list->items) { Z_Free(list->items); } if (list->olditems) { Z_Free(list->olditems); } if (list->strbuf) { Z_Free(list->strbuf); } Z_Free(list); } boolean LUA_HUD_IsDrawListValid(huddrawlist_h list) { if (!list) return false; // that's all we can really do to check the validity of the handle right now return true; } static size_t AllocateDrawItem(huddrawlist_h list) { if (!list) I_Error("can't allocate draw item: invalid list"); if (list->capacity <= list->items_len) { list->capacity = list->capacity == 0 ? 128 : list->capacity * 2; list->items = Z_Realloc(list->items, sizeof(struct drawitem_s) * list->capacity, PU_STATIC, NULL); list->olditems = Z_Realloc(list->olditems, sizeof(struct drawitem_s) * list->capacity, PU_STATIC, NULL); } return list->items_len++; } // copy string to list's internal string buffer // lua can deallocate the string before we get to use it, so it's important to // keep our own copy static size_t CopyString(huddrawlist_h list, const char* str) { size_t lenstr; if (!list) I_Error("can't allocate string; invalid list"); lenstr = strlen(str); if (list->strbuf_capacity <= list->strbuf_len + lenstr + 1) { if (list->strbuf_capacity == 0) list->strbuf_capacity = 256; else list->strbuf_capacity *= 2; list->strbuf = (char*) Z_Realloc(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL); } { size_t old_len = list->strbuf_len; strncpy(&list->strbuf[old_len], str, lenstr + 1); list->strbuf_len += lenstr + 1; return old_len; } } /* static void CalcStringCoords(drawitem_t *item, const char *string) { if (!(item->flags & V_NOSCALESTART)) { INT32 dupx = vid.dupx; INT32 dupy = vid.dupy; item->flags |= V_NOSCALESTART; if (item->flags & V_SCALEPATCHMASK) switch ((item->flags & V_SCALEPATCHMASK) >> V_SCALEPATCHSHIFT) { case 1: // V_NOSCALEPATCH dupx = dupy = 1; break; case 2: // V_SMALLSCALEPATCH dupx = vid.smalldupx; dupy = vid.smalldupy; break; case 3: // V_MEDSCALEPATCH dupx = vid.meddupx; dupy = vid.meddupy; break; default: break; } // only use one dup, to avoid stretching (har har) dupx = dupy = (dupx < dupy ? dupx : dupy); INT32 x = item->x * dupx; INT32 y = item->y * dupy; if (item->flags & V_SPLITSCREEN) y += (BASEVIDHEIGHT * (dupy - 1))/2; if ((item->flags & V_SPLITSCREEN) && r_splitscreen > 1) x += (BASEVIDWIDTH * (dupx - 1))/2; if (vid.width != BASEVIDWIDTH * dupx) { // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, // so center this imaginary screen if ((item->flags & (V_SPLITSCREEN|V_SNAPTOLEFT)) == (V_SPLITSCREEN|V_SNAPTOLEFT)) x += (vid.width/2 - (BASEVIDWIDTH/2 * dupx)); else if (item->flags & V_SNAPTORIGHT) x += (vid.width - (BASEVIDWIDTH * dupx)); else if (!(item->flags & V_SNAPTOLEFT)) x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; } if (vid.height != BASEVIDHEIGHT * dupy) { // same thing here if ((item->flags & (V_SPLITSCREEN|V_SNAPTOTOP)) == (V_SPLITSCREEN|V_SNAPTOTOP)) y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)); else if (item->flags & V_SNAPTOBOTTOM) y += (vid.height - (BASEVIDHEIGHT * dupy)); else if (!(item->flags & V_SNAPTOTOP)) y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; } item->x = x; item->y = y; } } static void CalcFillCoords(drawitem_t *item) { if (!(item->flags & V_NOSCALESTART)) { INT32 dupx = vid.dupx; INT32 dupy = vid.dupy; INT32 x = item->x * dupx; INT32 y = item->y * dupy; INT32 c = item->c; item->flags |= V_NOSCALESTART; item->w *= dupx; item->h *= dupy; // Center it if necessary if (vid.width != BASEVIDWIDTH * dupx) { // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, // so center this imaginary screen if (c & V_SNAPTORIGHT) x += (vid.width - (BASEVIDWIDTH * dupx)); else if (!(c & V_SNAPTOLEFT)) x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; } if (vid.height != BASEVIDHEIGHT * dupy) { // same thing here if (c & V_SNAPTOBOTTOM) y += (vid.height - (BASEVIDHEIGHT * dupy)); else if (!(c & V_SNAPTOTOP)) y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; } if (c & V_SPLITSCREEN) y += (BASEVIDHEIGHT * dupy)/2; if ((c & V_SPLITSCREEN) && r_splitscreen > 1) x += (BASEVIDWIDTH * dupx)/2; item->x = x; item->y = y; } } */ #define INTERP_LATCH 1 #define INTERP_STRING 2 #define INTERP_FLAGMASK 0x3 static UINT64 GetItemId(void) { if (!hud_interpolate) return 0; // leave bits 0 and 1 free for the string mode UINT64 id = ((uintptr_t)gL->savedpc << 32) | (hud_interpcounter << 10) | (hud_interptag << 2); if (hud_interplatch) { id |= INTERP_LATCH; hud_interplatch = false; } if (hud_interpstring) id |= INTERP_STRING; return id; } void LUA_HUD_AddDraw( huddrawlist_h list, INT32 x, INT32 y, patch_t *patch, INT32 flags, UINT8 *colormap ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_Draw; item->x = x << FRACBITS; item->y = y << FRACBITS; item->patch = patch; item->flags = flags; item->colormap = colormap; } void LUA_HUD_AddDrawScaled( huddrawlist_h list, fixed_t x, fixed_t y, fixed_t scale, patch_t *patch, INT32 flags, UINT8 *colormap ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawScaled; item->x = x; item->y = y; item->scale = scale; item->patch = patch; item->flags = flags; item->colormap = colormap; } void LUA_HUD_AddDrawStretched( huddrawlist_h list, fixed_t x, fixed_t y, fixed_t hscale, fixed_t vscale, patch_t *patch, INT32 flags, UINT8 *colormap ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawStretched; item->x = x; item->y = y; item->hscale = hscale; item->vscale = vscale; item->patch = patch; item->flags = flags; item->colormap = colormap; } void LUA_HUD_AddDrawNum( huddrawlist_h list, INT32 x, INT32 y, INT32 num, INT32 flags ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawNum; item->x = x; item->y = y; item->num = num; item->flags = flags; // CalcStringCoords(item, NULL); } void LUA_HUD_AddDrawPaddedNum( huddrawlist_h list, INT32 x, INT32 y, INT32 num, INT32 digits, INT32 flags ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawPaddedNum; item->x = x; item->y = y; item->num = num; item->digits = digits; item->flags = flags; // CalcStringCoords(item, NULL); } void LUA_HUD_AddDrawPingNum( huddrawlist_h list, INT32 x, INT32 y, INT32 flags, INT32 num, UINT8 *colormap ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawPingNum; item->x = x; item->y = y; item->flags = flags; item->num = num; item->colormap = colormap; // CalcStringCoords(item, NULL); } void LUA_HUD_AddDrawFill( huddrawlist_h list, INT32 x, INT32 y, INT32 w, INT32 h, INT32 c ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawFill; item->x = x; item->y = y; item->w = w; item->h = h; item->c = c; // CalcFillCoords(item); } void LUA_HUD_AddDrawString( huddrawlist_h list, fixed_t x, fixed_t y, const char *str, INT32 flags, INT32 align ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawString; item->x = x; item->y = y; item->stroffset = CopyString(list, str); item->flags = flags; item->align = align; // CalcStringCoords(item, str); } void LUA_HUD_AddFadeScreen( huddrawlist_h list, UINT16 color, UINT8 strength ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; // nothing to interpolate here item->id = 0; item->type = DI_FadeScreen; item->color = color; item->strength = strength; } void LUA_HUD_AddDrawTitleCardString( huddrawlist_h list, INT32 x, INT32 y, INT32 flags, const char *str, boolean bossmode, INT32 timer, INT32 threshold ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = bossmode ? DI_DrawTitleCardStringBoss : DI_DrawTitleCardString; item->x = x; item->y = y; item->flags = flags; item->stroffset = CopyString(list, str); item->timer = timer; item->threshold = threshold; } void LUA_HUD_AddDrawKartString( huddrawlist_h list, fixed_t x, fixed_t y, const char *str, INT32 flags ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_DrawKartString; item->x = x; item->y = y; item->stroffset = CopyString(list, str); item->flags = flags; // CalcStringCoords(item, str); } void LUA_HUD_AddSetClipRect( huddrawlist_h list, fixed_t x, fixed_t y, fixed_t w, fixed_t h, INT32 flags ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = GetItemId(); item->type = DI_SetClipRect; item->x = x; item->y = y; item->w = w; item->h = h; item->flags = flags; } void LUA_HUD_AddClearClipRect( huddrawlist_h list ) { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; item->id = 0; item->type = DI_ClearClipRect; } void LUA_HUD_DrawList(huddrawlist_h list) { size_t i; size_t j = 0; fixed_t frac = R_UsingFrameInterpolation() ? rendertimefrac : FRACUNIT; fixed_t lerpx = 0, lerpy = 0; drawitem_t *latchitem = NULL; drawitem_t *oldlatchitem = NULL; if (!list) I_Error("HUD drawlist invalid"); if (list->items_len <= 0) return; if (!list->items) I_Error("HUD drawlist->items invalid"); for (i = 0; i < list->items_len; i++) { drawitem_t *item = &list->items[i]; drawitem_t *olditem = NULL; const char *itemstr = &list->strbuf[item->stroffset]; if (item->id != 0) { // find the old one too // this is kinda cursed... we need to check every item // but stop when the first-checked item is reached again size_t stop = j; do { if (j == list->olditems_len) { j = 0; continue; } drawitem_t *old = &list->olditems[j++]; if ((old->id & ~INTERP_FLAGMASK) == (item->id & ~INTERP_FLAGMASK)) { // gotcha! olditem = old; if (item->id & INTERP_LATCH) { lerpx = FixedMul(frac, item->x - olditem->x); lerpy = FixedMul(frac, item->y - olditem->y); latchitem = item; oldlatchitem = olditem; } else if (!(item->id & INTERP_STRING)) { lerpx = FixedMul(frac, item->x - olditem->x); lerpy = FixedMul(frac, item->y - olditem->y); latchitem = NULL; } break; } } while (j != stop); } else { lerpx = lerpy = 0; latchitem = NULL; } #define LERP(it) (olditem ? olditem->it + FixedMul(frac, item->it - olditem->it) : item->it) // half of this could be done when the coordinates are latched... zzzzzz #define LERPS(it) (latchitem ? ((latchitem->it + (item->it - latchitem->it)) - (latchitem->it - oldlatchitem->it)) + lerp##it : (olditem ? olditem->it + lerp##it : item->it)) //define LERPS(it) (olditem ? (latchitem ? (latchitem->it + (item->it - latchitem->it)) - (latchitem->it - oldlatchitem->it) : olditem->it) + lerp##it : item->it) switch (item->type) { case DI_Draw: V_DrawFixedPatch(LERPS(x), LERPS(y), FRACUNIT, item->flags, item->patch, item->colormap); break; case DI_DrawScaled: V_DrawFixedPatch(LERPS(x), LERPS(y), LERP(scale), item->flags, item->patch, item->colormap); break; case DI_DrawStretched: V_DrawStretchyFixedPatch(LERPS(x), LERPS(y), LERP(hscale), LERP(vscale), item->flags, item->patch, item->colormap); break; case DI_DrawNum: V_DrawTallNum(LERPS(x), LERPS(y), item->flags, item->num); break; case DI_DrawPaddedNum: V_DrawPaddedTallNum(LERPS(x), LERPS(y), item->flags, item->num, item->digits); break; case DI_DrawPingNum: V_DrawPingNum(LERPS(x), LERPS(y), item->flags, item->num, item->colormap); break; case DI_DrawFill: V_DrawFill(LERPS(x), LERPS(y), LERP(w), LERP(h), item->c); break; case DI_DrawString: switch(item->align) { // hu_font case align_left: V_DrawString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_center: V_DrawCenteredString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_right: V_DrawRightAlignedString(LERPS(x), LERPS(y), item->flags, itemstr); break; // hu_font, 0.5x scale case align_small: V_DrawSmallString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_smallcenter: V_DrawCenteredSmallString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_smallright: V_DrawRightAlignedSmallString(LERPS(x), LERPS(y), item->flags, itemstr); break; // tny_font case align_thin: V_DrawThinString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_thincenter: V_DrawCenteredThinString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_thinright: V_DrawRightAlignedThinString(LERPS(x), LERPS(y), item->flags, itemstr); break; } break; case DI_FadeScreen: V_DrawFadeScreen(item->color, item->strength); break; case DI_DrawTitleCardString: V_DrawTitleCardString(LERPS(x), LERPS(y), itemstr, item->flags, false, item->timer, item->threshold); break; case DI_DrawTitleCardStringBoss: V_DrawTitleCardString(LERPS(x), LERPS(y), itemstr, item->flags, true, item->timer, item->threshold); break; case DI_DrawKartString: V_DrawKartString(LERPS(x), LERPS(y), item->flags, itemstr); break; case DI_SetClipRect: V_SetClipRect(LERPS(x), LERPS(y), LERP(w), LERP(h), item->flags); break; case DI_ClearClipRect: V_ClearClipRect(); break; default: I_Error("can't draw draw list item: invalid draw list item type"); continue; } } }