diff --git a/src/lua_hud.h b/src/lua_hud.h index 20a6e6549..0c632b1fa 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -49,6 +49,12 @@ enum hud { extern boolean hud_running; +extern boolean hud_interpolate; // interpolation enabled +extern UINT8 hud_interptag; // current tag +extern UINT32 hud_interpcounter; // number of HUD hooks ran +extern boolean hud_interpstring; // string mode +extern boolean hud_interplatch; // string mode was toggled + boolean LUA_HudEnabled(enum hud option); void LUA_SetHudHook(int hook, huddrawlist_h list); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 6a61b2782..65c076146 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -34,6 +34,13 @@ #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); boolean hud_running = false; + +boolean hud_interpolate = false; +UINT8 hud_interptag = 0; +UINT32 hud_interpcounter = 0; +boolean hud_interpstring = false; +boolean hud_interplatch = false; + static UINT8 hud_enabled[(hud_MAX/8)+1]; static UINT8 camnum = 1; @@ -1210,6 +1217,38 @@ static int libd_getDeltaTime(lua_State *L) return 1; } +static int libd_interpolate(lua_State *L) +{ + HUDONLY + + // do type checking even if interpolation is disabled + boolean newinterpolate; + UINT8 newtag; + + if (lua_isnumber(L, 1)) + { + newinterpolate = true; + newtag = luaL_checkinteger(L, 1); + } + else + { + newinterpolate = luaL_checkboolean(L, 1); + newtag = 0; + } + + hud_interpolate = newinterpolate; + hud_interptag = newtag; + + return 0; +} + +static int libd_interpLatch(lua_State *L) +{ + HUDONLY + hud_interpstring = hud_interplatch = luaL_checkboolean(L, 1); + return 0; +} + static luaL_Reg lib_draw[] = { // cache {"patchExists", libd_patchExists}, @@ -1250,6 +1289,9 @@ static luaL_Reg lib_draw[] = { {"drawOnMinimap", libd_drawOnMinimap}, {"userTransFlag", libd_getusertransflag}, {"getDeltaTime", libd_getDeltaTime}, + // interpolation + {"interpolate", libd_interpolate}, + {"interpLatch", libd_interpLatch}, {NULL, NULL} }; @@ -1342,6 +1384,10 @@ boolean LUA_HudEnabled(enum hud option) void LUA_SetHudHook(int hook, huddrawlist_h list) { lua_getref(gL, lib_draw_ref); + + // Update interpolation + hud_interpolate = hud_interpstring = hud_interplatch = false; + hud_interpcounter++; lua_pushlightuserdata(gL, list); lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c index 7e5dbdc64..f25e477e6 100644 --- a/src/lua_hudlib_drawlist.c +++ b/src/lua_hudlib_drawlist.c @@ -12,11 +12,18 @@ #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, @@ -34,40 +41,50 @@ enum drawitem_e { // 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; - fixed_t w; - fixed_t h; - INT32 c; - fixed_t scale; - fixed_t hscale; - fixed_t vscale; - patch_t *patch; - INT32 flags; - UINT16 basecolor; - UINT16 outlinecolor; + 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 + }; + union { + fixed_t vscale; // drawStretched + fixed_t h; // drawFill + INT32 digits; // drawPaddedNum + UINT8 strength; // fadeScreen + }; UINT8 *colormap; - UINT8 *basecolormap; - UINT8 *outlinecolormap; - fixed_t sx; - fixed_t sy; - INT32 num; - INT32 digits; - size_t stroffset; // offset into strbuf to get str - UINT16 color; - UINT8 strength; - INT32 align; - INT32 timer; - INT32 threshold; - boolean bossmode; + + union { // for title cards + boolean bossmode; + boolean p4; + INT32 timer; + INT32 threshold; + }; + // 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_capacity; size_t items_len; + drawitem_t *olditems; + size_t olditems_len; + size_t capacity; char *strbuf; size_t strbuf_capacity; size_t strbuf_len; @@ -92,8 +109,10 @@ huddrawlist_h LUA_HUD_CreateDrawList(void) drawlist = (huddrawlist_h) Z_Calloc(sizeof(struct huddrawlist_s), PU_STATIC, NULL); drawlist->items = NULL; - drawlist->items_capacity = 0; 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; @@ -103,14 +122,14 @@ huddrawlist_h LUA_HUD_CreateDrawList(void) 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 - - // this memset probably isn't necessary - if (list->items) - { - memset(list->items, 0, sizeof(drawitem_t) * list->items_capacity); - } + list->olditems_len = list->items_len; list->items_len = 0; @@ -129,6 +148,10 @@ void LUA_HUD_DestroyDrawList(huddrawlist_h list) { Z_Free(list->items); } + if (list->olditems) + { + Z_Free(list->olditems); + } if (list->strbuf) { Z_Free(list->strbuf); @@ -147,11 +170,11 @@ boolean LUA_HUD_IsDrawListValid(huddrawlist_h list) static size_t AllocateDrawItem(huddrawlist_h list) { if (!list) I_Error("can't allocate draw item: invalid list"); - if (list->items_capacity <= list->items_len + 1) + if (list->capacity <= list->items_len) { - if (list->items_capacity == 0) list->items_capacity = 128; - else list->items_capacity *= 2; - list->items = (drawitem_t *) Z_Realloc(list->items, sizeof(struct drawitem_s) * list->items_capacity, PU_STATIC, NULL); + 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++; @@ -181,6 +204,136 @@ static size_t CopyString(huddrawlist_h list, const char* str) } } +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, @@ -192,9 +345,10 @@ void LUA_HUD_AddDraw( { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; + item->id = GetItemId(); item->type = DI_Draw; - item->x = x; - item->y = y; + item->x = x << FRACBITS; + item->y = y << FRACBITS; item->patch = patch; item->flags = flags; item->colormap = colormap; @@ -212,6 +366,7 @@ void LUA_HUD_AddDrawScaled( { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; + item->id = GetItemId(); item->type = DI_DrawScaled; item->x = x; item->y = y; @@ -234,6 +389,7 @@ void LUA_HUD_AddDrawStretched( { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; + item->id = GetItemId(); item->type = DI_DrawStretched; item->x = x; item->y = y; @@ -254,11 +410,13 @@ void LUA_HUD_AddDrawNum( { 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( @@ -272,12 +430,14 @@ void LUA_HUD_AddDrawPaddedNum( { 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( @@ -291,12 +451,14 @@ void LUA_HUD_AddDrawPingNum( { 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( @@ -310,12 +472,14 @@ void LUA_HUD_AddDrawFill( { 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( @@ -329,12 +493,14 @@ void LUA_HUD_AddDrawString( { 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( @@ -363,6 +529,7 @@ void LUA_HUD_AddDrawTitleCardString( { size_t i = AllocateDrawItem(list); drawitem_t *item = &list->items[i]; + item->id = GetItemId(); item->type = DI_DrawTitleCardString; item->x = x; item->y = y; @@ -383,16 +550,23 @@ void LUA_HUD_AddDrawKartString( { 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_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; @@ -401,63 +575,112 @@ void LUA_HUD_DrawList(huddrawlist_h list) 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(item->x<y<flags, item->patch, item->colormap); + V_DrawFixedPatch(LERPS(x), LERPS(y), FRACUNIT, item->flags, item->patch, item->colormap); break; case DI_DrawScaled: - V_DrawFixedPatch(item->x, item->y, item->scale, item->flags, item->patch, item->colormap); + V_DrawFixedPatch(LERPS(x), LERPS(y), LERP(scale), item->flags, item->patch, item->colormap); break; case DI_DrawStretched: - V_DrawStretchyFixedPatch(item->x, item->y, item->hscale, item->vscale, item->flags, item->patch, item->colormap); + V_DrawStretchyFixedPatch(LERPS(x), LERPS(y), LERP(hscale), LERP(vscale), item->flags, item->patch, item->colormap); break; case DI_DrawNum: - V_DrawTallNum(item->x, item->y, item->flags, item->num); + V_DrawTallNum(LERPS(x), LERPS(y), item->flags, item->num); break; case DI_DrawPaddedNum: - V_DrawPaddedTallNum(item->x, item->y, item->flags, item->num, item->digits); + V_DrawPaddedTallNum(LERPS(x), LERPS(y), item->flags, item->num, item->digits); break; case DI_DrawPingNum: - V_DrawPingNum(item->x, item->y, item->flags, item->num, item->colormap); + V_DrawPingNum(LERPS(x), LERPS(y), item->flags, item->num, item->colormap); break; case DI_DrawFill: - V_DrawFill(item->x, item->y, item->w, item->h, item->c); + 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(item->x, item->y, item->flags, itemstr); + V_DrawString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_center: - V_DrawCenteredString(item->x, item->y, item->flags, itemstr); + V_DrawCenteredString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_right: - V_DrawRightAlignedString(item->x, item->y, item->flags, itemstr); + V_DrawRightAlignedString(LERPS(x), LERPS(y), item->flags, itemstr); break; // hu_font, 0.5x scale case align_small: - V_DrawSmallString(item->x, item->y, item->flags, itemstr); + V_DrawSmallString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_smallcenter: - V_DrawCenteredSmallString(item->x, item->y, item->flags, itemstr); + V_DrawCenteredSmallString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_smallright: - V_DrawRightAlignedSmallString(item->x, item->y, item->flags, itemstr); + V_DrawRightAlignedSmallString(LERPS(x), LERPS(y), item->flags, itemstr); break; // tny_font case align_thin: - V_DrawThinString(item->x, item->y, item->flags, itemstr); + V_DrawThinString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_thincenter: - V_DrawCenteredThinString(item->x, item->y, item->flags, itemstr); + V_DrawCenteredThinString(LERPS(x), LERPS(y), item->flags, itemstr); break; case align_thinright: - V_DrawRightAlignedThinString(item->x, item->y, item->flags, itemstr); + V_DrawRightAlignedThinString(LERPS(x), LERPS(y), item->flags, itemstr); break; } break; @@ -465,10 +688,10 @@ void LUA_HUD_DrawList(huddrawlist_h list) V_DrawFadeScreen(item->color, item->strength); break; case DI_DrawTitleCardString: - V_DrawTitleCardString(item->x, item->y, itemstr, item->flags, item->bossmode, item->timer, item->threshold); + V_DrawTitleCardString(LERPS(x), LERPS(y), itemstr, item->flags, item->bossmode, item->timer, item->threshold); break; case DI_DrawKartString: - V_DrawKartString(item->x, item->y, item->flags, itemstr); + V_DrawKartString(LERPS(x), LERPS(y), item->flags, itemstr); break; default: I_Error("can't draw draw list item: invalid draw list item type"); diff --git a/src/st_stuff.c b/src/st_stuff.c index 0f547f404..07aeded4e 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -703,7 +703,6 @@ static void ST_overlayDrawer(UINT8 playernum) if (renderisnewtic) { - LUA_HUD_ClearDrawList(luahuddrawlist_game[playernum]); LUA_HookHUD(luahuddrawlist_game[playernum], HUD_HOOK(game)); } LUA_HUD_DrawList(luahuddrawlist_game[playernum]); @@ -882,6 +881,11 @@ void ST_Drawer(void) if (st_overlay) { UINT8 i; + if (renderisnewtic) + { + for (i = 0; i <= r_splitscreen; i++) + LUA_HUD_ClearDrawList(luahuddrawlist_game[i]); + } // No deadview! for (i = 0; i <= r_splitscreen; i++) { @@ -892,6 +896,9 @@ void ST_Drawer(void) ST_overlayDrawer(i); } + for (i = 0; i <= r_splitscreen; i++) + LUA_HUD_DrawList(luahuddrawlist_game[i]); + // draw Midnight Channel's overlay ontop if (mapheaderinfo[gamemap-1]->typeoflevel & TOL_TV) // Very specific Midnight Channel stuff. ST_MayonakaStatic();