Lua colorlib: extracolormap support

This commit is contained in:
Lactozilla 2023-08-04 00:31:51 -03:00
parent 0ce41f3e1d
commit 4ea2887d7f
13 changed files with 457 additions and 60 deletions

View file

@ -105,6 +105,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
lua_blockmaplib.c
lua_hudlib.c
lua_hudlib_drawlist.c
lua_colorlib.c
lua_inputlib.c
)

View file

@ -213,6 +213,7 @@ static const struct {
{META_HUDINFO, "hudinfo_t"},
{META_PATCH, "patch_t"},
{META_COLORMAP, "colormap"},
{META_EXTRACOLORMAP,"extracolormap_t"},
{META_CAMERA, "camera_t"},
{META_ACTION, "action"},
@ -1928,6 +1929,26 @@ static int lib_pCeilingzAtPos(lua_State *L)
return 1;
}
static int lib_pGetSectorColormapAt(lua_State *L)
{
sector_t *sector = NULL;
if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
fixed_t z = luaL_checkfixed(L, 4);
INLEVEL
if (!sector)
return LUA_ErrInvalid(L, "sector_t");
extracolormap_t *exc;
if (sector)
exc = P_GetColormapFromSectorAt(sector, x, y, z);
else
exc = P_GetSectorColormapAt(x, y, z);
LUA_PushUserdata(L, exc, META_EXTRACOLORMAP);
return 1;
}
static int lib_pDoSpring(lua_State *L)
{
mobj_t *spring = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@ -4151,6 +4172,7 @@ static luaL_Reg lib[] = {
{"P_RadiusAttack",lib_pRadiusAttack},
{"P_FloorzAtPos",lib_pFloorzAtPos},
{"P_CeilingzAtPos",lib_pCeilingzAtPos},
{"P_GetSectorColormapAt",lib_pGetSectorColormapAt},
{"P_DoSpring",lib_pDoSpring},
{"P_TryCameraMove", lib_pTryCameraMove},
{"P_TeleportCameraMove", lib_pTeleportCameraMove},

335
src/lua_colorlib.c Normal file
View file

@ -0,0 +1,335 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2021-2022 by "Lactozilla".
// Copyright (C) 2014-2023 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_colorlib.c
/// \brief color and colormap libraries for Lua scripting
#include "doomdef.h"
#include "fastcmp.h"
#include "r_data.h"
#include "lua_script.h"
#include "lua_libs.h"
static UINT8 GetRGBAColorsFromTable(lua_State *L, UINT32 index, UINT8 *rgba, boolean useAlpha)
{
UINT8 num = 0;
lua_pushnil(L);
while (lua_next(L, index)) {
lua_Integer i = 0;
const char *field = NULL;
if (lua_isnumber(L, -2))
i = lua_tointeger(L, -2);
else
field = luaL_checkstring(L, -2);
#define CHECKFIELD(p, c) (i == p || (field && fastcmp(field, c)))
#define RGBASET(p, c) { \
INT32 color = luaL_checkinteger(L, -1); \
if (color < 0 || color > 255) \
luaL_error(L, c " channel %d out of range (0 - 255)", color); \
rgba[p] = (UINT8)color; \
num++; \
}
if (CHECKFIELD(1, "r")) {
RGBASET(0, "red color");
} else if (CHECKFIELD(2, "g")) {
RGBASET(1, "green color");
} else if (CHECKFIELD(3, "b")) {
RGBASET(2, "blue color");
} else if (useAlpha && CHECKFIELD(4, "a")) {
RGBASET(3, "alpha");
}
#undef CHECKFIELD
#undef RGBASET
lua_pop(L, 1);
}
return num;
}
#define IS_HEX_CHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))
#define ARE_HEX_CHARS(str, i) IS_HEX_CHAR(str[i]) && IS_HEX_CHAR(str[i + 1])
static UINT32 hex2int(char x)
{
if (x >= '0' && x <= '9')
return x - '0';
else if (x >= 'a' && x <= 'f')
return x - 'a' + 10;
else if (x >= 'A' && x <= 'F')
return x - 'A' + 10;
return 0;
}
static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc)
{
const char *hex = str;
if (hex[0] == '#')
hex++;
else if (!IS_HEX_CHAR(hex[0]))
return 0;
size_t len = strlen(hex);
if (len == 3)
{
// Shorthand like #09C
for (unsigned i = 0; i < 3; i++)
{
if (!IS_HEX_CHAR(hex[i]))
return 0;
UINT32 hx = hex2int(hex[i]);
*rgba++ = (hx * 16) + hx;
}
return 3;
}
else if (len == 6 || len == 8)
{
if (numc != 4)
len = 6;
// A triplet like #0099CC
for (unsigned i = 0; i < len; i += 2)
{
if (!ARE_HEX_CHARS(hex, i))
return false;
*rgba++ = (hex2int(hex[i]) * 16) + hex2int(hex[i + 1]);
}
return len;
}
return 0;
}
/////////////////////////
// extracolormap userdata
/////////////////////////
enum extracolormap_e {
extracolormap_r = 0,
extracolormap_g,
extracolormap_b,
extracolormap_a,
extracolormap_rgba,
extracolormap_fade_r,
extracolormap_fade_g,
extracolormap_fade_b,
extracolormap_fade_a,
extracolormap_fade_rgba,
extracolormap_fade_start,
extracolormap_fade_end,
extracolormap_colormap
};
static const char *const extracolormap_opt[] = {
"r",
"g",
"b",
"a",
"rgba",
"fade_r",
"fade_g",
"fade_b",
"fade_a",
"fade_rgba",
"fade_start",
"fade_end",
"colormap",
NULL};
#define ALPHA_SCALE_FACTOR 102 // (255 / 25) * 10
#define SCALE_ALPHA_UP(alpha) (((alpha) * ALPHA_SCALE_FACTOR) / 10)
#define SCALE_ALPHA_DOWN(alpha) (((alpha) * 10) / ALPHA_SCALE_FACTOR)
static int extracolormap_get(lua_State *L)
{
extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt);
switch (field)
{
case extracolormap_r:
lua_pushinteger(L, R_GetRgbaR(exc->rgba));
break;
case extracolormap_g:
lua_pushinteger(L, R_GetRgbaG(exc->rgba));
break;
case extracolormap_b:
lua_pushinteger(L, R_GetRgbaB(exc->rgba));
break;
case extracolormap_a:
lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->rgba)));
break;
case extracolormap_rgba:
lua_pushinteger(L, R_GetRgbaR(exc->rgba));
lua_pushinteger(L, R_GetRgbaG(exc->rgba));
lua_pushinteger(L, R_GetRgbaB(exc->rgba));
lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->rgba)));
return 4;
case extracolormap_fade_r:
lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
break;
case extracolormap_fade_g:
lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
break;
case extracolormap_fade_b:
lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
break;
case extracolormap_fade_a:
lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->fadergba)));
break;
case extracolormap_fade_rgba:
lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->fadergba)));
return 4;
case extracolormap_fade_start:
lua_pushinteger(L, exc->fadestart);
break;
case extracolormap_fade_end:
lua_pushinteger(L, exc->fadeend);
break;
case extracolormap_colormap:
LUA_PushUserdata(L, exc->colormap, META_COLORMAP);
break;
}
return 1;
}
static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba)
{
rgba[3] = SCALE_ALPHA_UP(rgba[3]);
if (lua_type(L, 3) == LUA_TSTRING)
{
const char *str = lua_tostring(L, 3);
UINT8 parsed = ParseHTMLColor(str, rgba, 4);
if (!parsed)
luaL_error(L, "Malformed HTML color '%s'", str);
}
else
GetRGBAColorsFromTable(L, 3, rgba, true);
rgba[3] = SCALE_ALPHA_DOWN(rgba[3]);
}
static int extracolormap_set(lua_State *L)
{
extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt);
UINT8 r = R_GetRgbaR(exc->rgba);
UINT8 g = R_GetRgbaG(exc->rgba);
UINT8 b = R_GetRgbaB(exc->rgba);
UINT8 a = R_GetRgbaA(exc->rgba);
UINT8 fr = R_GetRgbaR(exc->fadergba);
UINT8 fg = R_GetRgbaG(exc->fadergba);
UINT8 fb = R_GetRgbaB(exc->fadergba);
UINT8 fa = R_GetRgbaA(exc->fadergba);
UINT8 rgba[4];
INT32 old_rgba = exc->rgba, old_fade_rgba = exc->fadergba; // It's not unsigned?
UINT8 old_fade_start = exc->fadestart, old_fade_end = exc->fadeend;
#define val luaL_checkinteger(L, 3)
switch(field)
{
case extracolormap_r:
exc->rgba = R_PutRgbaRGBA(val, g, b, a);
break;
case extracolormap_g:
exc->rgba = R_PutRgbaRGBA(r, val, b, a);
break;
case extracolormap_b:
exc->rgba = R_PutRgbaRGBA(r, g, val, a);
break;
case extracolormap_a:
exc->rgba = R_PutRgbaRGBA(r, g, b, SCALE_ALPHA_DOWN(val));
break;
case extracolormap_rgba:
rgba[0] = r;
rgba[1] = g;
rgba[2] = b;
rgba[3] = a;
GetExtraColormapRGBA(L, rgba);
exc->rgba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
break;
case extracolormap_fade_r:
exc->fadergba = R_PutRgbaRGBA(val, fg, fb, fa);
break;
case extracolormap_fade_g:
exc->fadergba = R_PutRgbaRGBA(fr, val, fb, fa);
break;
case extracolormap_fade_b:
exc->fadergba = R_PutRgbaRGBA(fr, fg, val, fa);
break;
case extracolormap_fade_a:
exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, SCALE_ALPHA_DOWN(val));
break;
case extracolormap_fade_rgba:
rgba[0] = fr;
rgba[1] = fg;
rgba[2] = fb;
rgba[3] = fa;
GetExtraColormapRGBA(L, rgba);
exc->fadergba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
break;
case extracolormap_fade_start:
if (val > 31)
return luaL_error(L, "fade start %d out of range (0 - 31)", val);
exc->fadestart = val;
break;
case extracolormap_fade_end:
if (val > 31)
return luaL_error(L, "fade end %d out of range (0 - 31)", val);
exc->fadeend = val;
break;
case extracolormap_colormap:
return luaL_error(L, LUA_QL("extracolormap_t") " field " LUA_QS " should not be set directly.", extracolormap_opt[field]);
}
#undef val
if (exc->rgba != old_rgba
|| exc->fadergba != old_fade_rgba
|| exc->fadestart != old_fade_start
|| exc->fadeend != old_fade_end)
R_GenerateLightTable(exc, true);
return 0;
}
int LUA_ColorLib(lua_State *L)
{
luaL_newmetatable(L, META_EXTRACOLORMAP);
lua_pushcfunction(L, extracolormap_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, extracolormap_set);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
return 0;
}

View file

@ -84,6 +84,7 @@ extern boolean mousegrabbedbylua;
#define META_HUDINFO "HUDINFO_T*"
#define META_PATCH "PATCH_T*"
#define META_COLORMAP "COLORMAP"
#define META_EXTRACOLORMAP "EXTRACOLORMAP_T"
#define META_CAMERA "CAMERA_T*"
#define META_ACTION "ACTIONF_T*"
@ -111,4 +112,5 @@ int LUA_TagLib(lua_State *L);
int LUA_PolyObjLib(lua_State *L);
int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
int LUA_ColorLib(lua_State *L);
int LUA_InputLib(lua_State *L);

View file

@ -57,6 +57,7 @@ enum sector_e {
sector_ffloors,
sector_fslope,
sector_cslope,
sector_extracolormap,
sector_flags,
sector_specialflags,
sector_damagetype,
@ -95,6 +96,7 @@ static const char *const sector_opt[] = {
"ffloors",
"f_slope",
"c_slope",
"extracolormap",
"flags",
"specialflags",
"damagetype",
@ -751,6 +753,9 @@ static int sector_get(lua_State *L)
case sector_cslope: // c_slope
LUA_PushUserdata(L, sector->c_slope, META_SLOPE);
return 1;
case sector_extracolormap: // extra_colormap
LUA_PushUserdata(L, sector->extra_colormap, META_EXTRACOLORMAP);
return 1;
case sector_flags: // flags
lua_pushinteger(L, sector->flags);
return 1;

View file

@ -58,6 +58,7 @@ static lua_CFunction liblist[] = {
LUA_PolyObjLib, // polyobj_t
LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff
LUA_ColorLib, // general color functions
LUA_InputLib, // inputs
NULL
};

View file

@ -2805,3 +2805,17 @@ boolean M_IsStringEmpty(const char *s)
return true;
}
// Rounds off floating numbers and checks for 0 - 255 bounds
int M_RoundUp(double number)
{
if (number > 255.0l)
return 255;
if (number < 0.0l)
return 0;
if ((int)number <= (int)(number - 0.5f))
return (int)number + 1;
return (int)number;
}

View file

@ -112,6 +112,9 @@ boolean M_IsStringEmpty(const char *s);
// counting bits, for weapon ammo code, usually
FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
// Rounds off floating numbers and checks for 0 - 255 bounds
int M_RoundUp(double number);
#include "w_wad.h"
extern char configfile[MAX_WADPATH];

View file

@ -438,6 +438,10 @@ boolean PIT_PushableMoved(mobj_t *thing);
boolean P_DoSpring(mobj_t *spring, mobj_t *object);
INT32 P_GetSectorLightAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z);
extracolormap_t *P_GetColormapFromSectorAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z);
extracolormap_t *P_GetSectorColormapAt(fixed_t x, fixed_t y, fixed_t z);
//
// P_SETUP
//

View file

@ -5067,3 +5067,35 @@ fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
return ceilingz;
}
INT32 P_GetSectorLightAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z)
{
if (!sector->numlights)
return -1;
INT32 light = sector->numlights - 1;
// R_GetPlaneLight won't work on sloped lights!
for (INT32 lightnum = 1; lightnum < sector->numlights; lightnum++) {
fixed_t h = P_GetLightZAt(&sector->lightlist[lightnum], x, y);
if (h <= z) {
light = lightnum - 1;
break;
}
}
return light;
}
extracolormap_t *P_GetColormapFromSectorAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z)
{
if (sector->numlights)
return *sector->lightlist[P_GetSectorLightAt(sector, x, y, z)].extra_colormap;
else
return sector->extra_colormap;
}
extracolormap_t *P_GetSectorColormapAt(fixed_t x, fixed_t y, fixed_t z)
{
return P_GetColormapFromSectorAt(R_PointInSubsector(x, y)->sector, x, y, z);
}

View file

@ -692,9 +692,26 @@ extracolormap_t *R_ColormapForName(char *name)
//
static double deltas[256][3], map[256][3];
static int RoundUp(double number);
static colorlookup_t lighttable_lut;
static UINT8 LightTableNearest(UINT8 r, UINT8 g, UINT8 b)
{
return NearestColor(r, g, b);
}
static UINT8 LightTableNearest_LUT(UINT8 r, UINT8 g, UINT8 b)
{
return GetColorLUT(&lighttable_lut, r, g, b);
}
lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
{
extra_colormap->colormap = Z_MallocAlign((256 * 34) + 10, PU_LEVEL, NULL, 8);
R_GenerateLightTable(extra_colormap, false);
return extra_colormap->colormap;
}
void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup)
{
double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb;
double maskamt = 0, othermask = 0;
@ -711,7 +728,6 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
UINT8 fadestart = extra_colormap->fadestart,
fadedist = extra_colormap->fadeend - extra_colormap->fadestart;
lighttable_t *lighttable = NULL;
size_t i;
/////////////////////
@ -753,6 +769,16 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
int p;
char *colormap_p;
UINT8 (*NearestColorFunc)(UINT8, UINT8, UINT8);
if (uselookup)
{
InitColorLUT(&lighttable_lut, pMasterPalette, false);
NearestColorFunc = LightTableNearest_LUT;
}
else
NearestColorFunc = LightTableNearest;
// Initialise the map and delta arrays
// map[i] stores an RGB color (as double) for index i,
// which is then converted to SRB2's palette later
@ -783,8 +809,7 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
// Now allocate memory for the actual colormap array itself!
// aligned on 8 bit for asm code
colormap_p = Z_MallocAlign((256 * 34) + 10, PU_LEVEL, NULL, 8);
lighttable = (UINT8 *)colormap_p;
colormap_p = (char *)extra_colormap->colormap;
// Calculate the palette index for each palette index, for each light level
// (as well as the two unused colormap lines we inherited from Doom)
@ -792,9 +817,9 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
{
for (i = 0; i < 256; i++)
{
*colormap_p = NearestColor((UINT8)RoundUp(map[i][0]),
(UINT8)RoundUp(map[i][1]),
(UINT8)RoundUp(map[i][2]));
*colormap_p = NearestColorFunc((UINT8)M_RoundUp(map[i][0]),
(UINT8)M_RoundUp(map[i][1]),
(UINT8)M_RoundUp(map[i][2]));
colormap_p++;
if ((UINT32)p < fadestart)
@ -818,8 +843,6 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
}
}
}
return lighttable;
}
extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
@ -1133,20 +1156,6 @@ UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette)
return (UINT8)bestcolor;
}
// Rounds off floating numbers and checks for 0 - 255 bounds
static int RoundUp(double number)
{
if (number > 255.0l)
return 255;
if (number < 0.0l)
return 0;
if ((int)number <= (int)(number - 0.5f))
return (int)number + 1;
return (int)number;
}
#ifdef EXTRACOLORMAPLUMPS
const char *R_NameForColormap(extracolormap_t *extra_colormap)
{

View file

@ -92,6 +92,7 @@ typedef enum
TMCF_OVERRIDE = 1<<13,
} textmapcolormapflags_t;
void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup);
lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap);
extracolormap_t * R_CreateColormapFromLinedef(char *p1, char *p2, char *p3);
extracolormap_t* R_CreateColormap(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags);

View file

@ -1306,8 +1306,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
patch_t *patch;
fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2;
INT32 heightsec, phs;
INT32 light = 0;
fixed_t scalemul; UINT8 trans;
fixed_t scalemul;
UINT8 trans;
fixed_t floordiff;
fixed_t groundz;
pslope_t *groundslope;
@ -1425,27 +1425,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
if (thing->renderflags & RF_NOCOLORMAPS)
shadow->extra_colormap = NULL;
else
{
if (thing->subsector->sector->numlights)
{
INT32 lightnum;
light = thing->subsector->sector->numlights - 1;
// R_GetPlaneLight won't work on sloped lights!
for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) {
fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interp.x, interp.y);
if (h <= shadow->gzt) {
light = lightnum - 1;
break;
}
}
}
if (thing->subsector->sector->numlights)
shadow->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
else
shadow->extra_colormap = thing->subsector->sector->extra_colormap;
}
shadow->extra_colormap = P_GetColormapFromSectorAt(thing->subsector->sector, interp.x, interp.y, shadow->gzt);
shadow->transmap = R_GetTranslucencyTable(trans + 1);
shadow->colormap = scalelight[0][0]; // full dark!
@ -2132,21 +2112,9 @@ static void R_ProjectSprite(mobj_t *thing)
if (thing->subsector->sector->numlights)
{
INT32 lightnum;
fixed_t top = (splat) ? gz : gzt;
light = thing->subsector->sector->numlights - 1;
// R_GetPlaneLight won't work on sloped lights!
for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) {
fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interp.x, interp.y);
if (h <= top) {
light = lightnum - 1;
break;
}
}
//light = R_GetPlaneLight(thing->subsector->sector, gzt, false);
lightnum = (*thing->subsector->sector->lightlist[light].lightlevel >> LIGHTSEGSHIFT);
light = P_GetSectorLightAt(thing->subsector->sector, interp.x, interp.y, splat ? gz : gzt);
INT32 lightnum = (*thing->subsector->sector->lightlist[light].lightlevel >> LIGHTSEGSHIFT);
if (lightnum < 0)
spritelights = scalelight[0];
else if (lightnum >= LIGHTLEVELS)