Compare commits

..
Author SHA1 Message Date
alufolie91 3f68b0f8c1 OpenGL: prefer GL_RGBA for water screen copy
as per recommended https://wikis.khronos.org/opengl/Common_Mistakes
2026-10-01 19:30:35 +02:00
alufolie91 938c95c80d Merge branch 'Saturn-Next' into waterrefract 2026-08-23 03:30:14 +02:00
alufolie91 dfc7b5482e Merge branch 'Saturn-Next' into waterrefract 2026-07-20 16:45:18 +02:00
alufolie91 d258c920de Merge remote-tracking branch 'origin/Saturn-Next' into waterrefract 2026-07-20 11:47:31 +02:00
alufolie91 b3eac23977 dont draw refraction planes in wireframe mode
idk but this prob just fucks shit up
2026-07-17 22:43:31 +02:00
alufolie91 a114dd840b Merge branch 'Saturn-Next' into waterrefract 2026-07-17 22:43:28 +02:00
alufolie91 87fa339c8d tweak ripple and refraction shaders
this much much more closely resembles the software effect
i manually tweaked this until it looked very similar
its still not quite perfect probably, but in many cases its a close match
2026-07-17 08:39:26 +02:00
alufolie91 afb5b717fe smol change to GL_DrawWaterPolygon
this doesent need anything, its basically just a blank polygon, so can save some work and also pass less stuff to this
2026-07-16 20:20:45 +02:00
alufolie91 1a7dc425d2 remove horizonline handling for refraction planes
forgot those wont work with fofs anyways
still need to look into that sometime
but thisll save some perf
2026-07-16 18:31:45 +02:00
alufolie91 038e8f0864 clean up the refraction shader a bit
most stuff wasnt needed, forgot to remove it earlier
2026-07-16 18:18:36 +02:00
alufolie91 8ac506bf35 change how water refraction is handled
split it into its own pass
we will draw "refraction planes" before drawing anything translucent
this way we wont cause any draw order issues or anything like that
this is also a bit faster and more sane than the other approach
only caveat: translucent stuff behind water planes is unaffected by refraction
but better than it was before, and sure better than trying to copy the screen per plane or shit like that lol
like this, this should be mostly feasible to actually use, the skybox water stuff kinda was a non issue since i noticed even just normal water does not draw over the map geometry (must have misremembered that part since software does do that), but still might be worth looking into at some point
2026-07-16 17:57:55 +02:00
alufolie91 6cf2561dd3 dedupe GL_DrawWaterPolygon 2026-07-16 17:57:52 +02:00
alufolie91 3e459d4a21 WIP: OpenGL water refraction
tries to replicate the effect from software renderer
this is quite a mess, but finally feel like pushing this since i finally figured out the depth testing stuff
this still has some issues:
-map geometry in front of skybox water will just draw over it completely (not super sure how to fix this other than deferring water rendering after rendering both skybox and normal scene)
-translucent geometry is not included in refraction (and may draw in front of it?), software also suffers from this in certain cases, i just hope its not that visible

this really needs a cleanup at some point
and due to how this is setup for floor and ceiling planes this also costs quite a lot of gpu time (atleast on the cpu side this costs barely anything)
2026-07-16 17:57:46 +02:00
37 changed files with 601 additions and 249 deletions

No files matched your search

-7
View File
@@ -257,9 +257,6 @@ set(SRB2_CONFIG_SATURNJOIN ON CACHE BOOL
set(SRB2_CONFIG_SCREENFBO ON CACHE BOOL
"Enable hardware renderer (OpenGL) FBO downsampling support")
set(SRB2_CONFIG_PHOBOS ON CACHE BOOL
" Compile the Phobos client. Which contains things not compatible with vanilla, such as raised skinlimits, 32 player support, bugfixes, etc.")
if(${SRB2_CONFIG_DEDICATED})
set(SRB2_CONFIG_HAVE_GME NO)
set(SRB2_CONFIG_HAVE_OPENMPT NO)
@@ -586,10 +583,6 @@ if(${SRB2_CONFIG_SATURNJOIN})
add_definitions(-DDOSATURNJOIN)
endif()
if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_SCREENFBO})
add_definitions(-DPHOBOS_BUILD)
endif()
# Targets
# Compatibility flag with later versions of GCC
-4
View File
@@ -65,10 +65,6 @@
# DEBUGMODE=1 - Enable various debugging capabilities.
# Also disables optimizations.
#
# PHOBOS_BUILD=1 - Compile the Phobos client.
# Contains things not compatible with vanilla.
# such as raised skinlimits, 32 player support, bugfixes, etc.
#
# Development flags:
#
# VALGRIND=1 - Enable Valgrind memory debugging support.
-1
View File
@@ -9,7 +9,6 @@ passthru_opts+=\
HAVE_DISCORDRPC DEVELOP\
NOHOLEPUNCH\
HAVE_IPV6\
PHOBOS_BUILD\
# build with debugging information
ifdef DEBUGMODE
-9
View File
@@ -206,15 +206,6 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr)
p += n; \
} while (0)
#ifdef PHOBOS_BUILD
#define WRITESKIN(p,b) WRITEUINT16(p,b)
#define READSKIN(p) READUINT16(p)
#else
#define WRITESKIN(p,b) WRITEUINT8(p,b)
#define READSKIN(p) READUINT8(p)
#endif
#ifdef __cplusplus
} // extern "C"
#endif
+3 -18
View File
@@ -1125,7 +1125,7 @@ static cl_mode_t cl_mode = CL_SEARCHING;
static UINT16 cl_lastcheckedfilecount = 0; // used for full file list
// Player name send/load
#ifndef PHOBOS_BUILD
static void CV_SavePlayerNames(UINT8 **p)
{
INT32 i = 0;
@@ -1161,7 +1161,6 @@ static void CV_LoadPlayerNames(UINT8 **p)
memcpy(player_names[i], tmp_name, MAXPLAYERNAME+1);
}
}
#endif
static void CL_DrawAddonTypes(void)
{
@@ -1773,7 +1772,7 @@ static void SV_SendPlayerInfo(INT32 node)
// doesent really matter what we put here lel
netbuffer->u.playerinfo[0].score = LONG(42069);
netbuffer->u.playerinfo[0].timeinserver = SHORT(42069);
netbuffer->u.playerinfo[0].skin = (skinnum_t)1; // no clue wtf skin 1 is, tails maybe?
netbuffer->u.playerinfo[0].skin = (UINT8)1; // no clue wtf skin 1 is, tails maybe?
netbuffer->u.playerinfo[0].data = 0;
@@ -1818,7 +1817,7 @@ static void SV_SendPlayerInfo(INT32 node)
netbuffer->u.playerinfo[i].score = LONG(players[i].score);
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
netbuffer->u.playerinfo[i].skin = (skinnum_t)players[i].skin;
netbuffer->u.playerinfo[i].skin = (UINT8)players[i].skin;
// Extra data
// Kart has extra skincolors, so we can't use this
@@ -1928,10 +1927,8 @@ static void SV_SendMapIcon(INT32 node)
*/
static boolean SV_SendServerConfig(INT32 node)
{
#ifndef PHOBOS_BUILD
INT32 i;
UINT8 *p, *op;
#endif
boolean waspacketsent;
doomdata_t *netbuffer = DOOMCOM_DATA(doomcom);
@@ -1949,7 +1946,6 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->u.servercfg.gametype = (UINT8)gametype;
netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
#ifndef PHOBOS_BUILD
// we fill these structs with FFs so that any players not in game get sent as 0xFFFF
// which is nice and easy for us to detect
memset(netbuffer->u.servercfg.playerskins, 0xFF, sizeof(netbuffer->u.servercfg.playerskins));
@@ -1965,7 +1961,6 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin;
netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor;
}
#endif
netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value));
netbuffer->u.servercfg.allownewplayer = cv_allownewplayer.value;
@@ -1973,16 +1968,12 @@ static boolean SV_SendServerConfig(INT32 node)
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
#ifdef PHOBOS_BUILD
const size_t len = sizeof(serverconfig_pak);
#else
op = p = netbuffer->u.servercfg.varlengthinputs;
CV_SavePlayerNames(&p);
CV_SaveNetVars(&p, false);
const size_t len = sizeof (serverconfig_pak) + (size_t)(p - op);
#endif
#ifdef DEBUGFILE
if (debugfile)
@@ -5423,10 +5414,8 @@ static void PT_ServerRefuse(SINT8 node)
// Positive response of client join request
static void PT_ServerCFG(SINT8 node)
{
#ifndef PHOBOS_BUILD
INT32 j;
UINT8 *scp;
#endif
if (server && serverrunning && node != servernode)
{ // but wait I thought I'm the server?
@@ -5449,10 +5438,8 @@ static void PT_ServerCFG(SINT8 node)
if ((gametype = netbuffer->u.servercfg.gametype) >= NUMGAMETYPES)
I_Error("Bad gametype %d in cliserv!", gametype);
modifiedgame = netbuffer->u.servercfg.modifiedgame;
#ifndef PHOBOS_BUILD
for (j = 0; j < MAXPLAYERS; j++)
adminplayers[j] = netbuffer->u.servercfg.adminplayers[j];
#endif
memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
}
@@ -5478,7 +5465,6 @@ static void PT_ServerCFG(SINT8 node)
SendSaturnInfo(node);
#endif
#ifndef PHOBOS_BUILD
memset(playeringame, 0, sizeof(playeringame));
for (j = 0; j < MAXPLAYERS; j++)
@@ -5495,7 +5481,6 @@ static void PT_ServerCFG(SINT8 node)
scp = netbuffer->u.servercfg.varlengthinputs;
CV_LoadPlayerNames(&scp);
scp += CV_LoadNetVars(scp);
#endif
/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
/// Shouldn't them be downloaded even at intermission time?
+2 -8
View File
@@ -339,17 +339,13 @@ typedef struct
UINT8 clientnode;
UINT8 gamestate;
#ifndef PHOBOS_BUILD
// 0xFF == not in game; else player skin num
UINT8 playerskins[MAXPLAYERS];
UINT8 playercolor[MAXPLAYERS];
#endif
UINT8 gametype;
UINT8 modifiedgame;
#ifndef PHOBOS_BUILD
SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed
#endif
char server_context[8]; // Unique context id, generated at server startup.
@@ -358,9 +354,7 @@ typedef struct
boolean allownewplayer;
boolean discordinvites;
#ifndef PHOBOS_BUILD
UINT8 varlengthinputs[0]; // Playernames and netvars
#endif
} ATTRPACK serverconfig_pak;
typedef struct {
@@ -450,7 +444,7 @@ typedef struct
char name[MAXPLAYERNAME+1];
UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH
UINT8 team;
skinnum_t skin;
UINT8 skin;
UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused.
UINT32 score;
UINT16 timeinserver; // In seconds.
@@ -460,7 +454,7 @@ typedef struct
typedef struct
{
char name[MAXPLAYERNAME+1];
skinnum_t skin;
UINT8 skin;
UINT8 color;
UINT32 pflags;
UINT32 score;
+3 -13
View File
@@ -435,18 +435,10 @@ consvar_t cv_pointlimit = {"pointlimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, point
static CV_PossibleValue_t timelimit_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
consvar_t cv_timelimit = {"timelimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t,
TimeLimit_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef PHOBOS_BUILD
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}};
#else
static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}};
#endif
consvar_t cv_numlaps = {"numlaps", "3", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t,
NumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef PHOBOS_BUILD
static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, "Map default"}, {0, NULL}};
#else
static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}};
#endif
consvar_t cv_basenumlaps = {"basenumlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_forceskin = {"forceskin", "Off", CV_NETVAR|CV_CALL|CV_CHEAT, Forceskin_cons_t, ForceSkin_OnChange, 0, NULL, NULL, 0, 0, NULL};
@@ -1689,8 +1681,7 @@ static void SendNameAndColor(UINT8 splitplayer)
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, playername->zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)playercolor->value);
WRITESKIN(p, (skinnum_t)playerskin->value);
WRITEUINT8(p, (UINT8)playerskin->value);
SendNetXCmdForPlayer(splitplayer, XD_NAMEANDCOLOR, buf, p - buf);
}
@@ -1698,8 +1689,7 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
{
player_t *player;
char name[MAXPLAYERNAME+1] = {};
UINT8 color;
skinnum_t skin;
UINT8 color, skin;
#ifdef PARANOIA
if (playernum < 0 || playernum > MAXPLAYERS)
@@ -1721,7 +1711,7 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
READSTRINGN(*cp, name, MAXPLAYERNAME);
color = READUINT8(*cp);
skin = READSKIN(*cp);
skin = READUINT8(*cp);
// set name
if (player_name_changes[playernum] < MAXNAMECHANGES)
+14 -24
View File
@@ -130,17 +130,8 @@ extern char logfilename[1024];
#else
#define VERSION 1 // Game version
#define SUBVERSION 6 // more precise version number
#ifdef PHOBOS_BUILD
#define CLIENTNAME "Phobos"
#define CLIENTNAMEW L"Phobos"
#else
#define CLIENTNAME "Saturn"
#define CLIENTNAMEW L"Saturn"
#endif
#define VERSIONSTRING CLIENTNAME " v10"
#define VERSIONSTRINGW CLIENTNAMEW L" v10"
#define VERSIONSTRING "Saturn v10"
#define VERSIONSTRINGW L"Saturn v10"
#define SATURN_TESTING // comment out for saturn release builds!
@@ -611,19 +602,11 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
// None of these that are disabled in the normal build are guaranteed to work perfectly
// Compile them at your own risk!
#ifdef PHOBOS_BUILD
// we want 32 players yesss
#undef MAXPLAYERS
#define MAXPLAYERS 32
// raise skinlimit to 1024
#undef MAXSKINS
#define MAXSKINS 1024
// overwrite some of those
// when not compiling in vanilla compat mode
#define SATURNJOIN
#define SATURNPAK
// undefine to enable fixes and features that are not vanilla compatible
#define COMPAT_VANILLA
// if we build with 32 player (or more or less!) support we dont need to care about vanilla compat
#if MAXPLAYERS != 16
#undef COMPAT_VANILLA
#endif
//-- SATURN __
@@ -690,6 +673,13 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
#undef UPDATE_ALERT
#endif
// overwrite some of those
// when not compiling in vanilla compat mode
#ifndef COMPAT_VANILLA
#define SATURNJOIN
#define SATURNPAK
#endif
#ifdef __cplusplus
} // extern "C"
#endif
-6
View File
@@ -42,12 +42,6 @@ extern "C" {
#define INT64 int64_t
#define UINT64 uint64_t
#ifdef PHOBOS_BUILD
typedef uint16_t skinnum_t;
#else
typedef uint8_t skinnum_t;
#endif
#ifdef __APPLE_CC__
#define DEBUG_LOG
#define NOIPX
+1 -3
View File
@@ -2335,7 +2335,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
while (READUINT8(extrainfo_p) == DW_STANDING) // Assume standings are always first in the extrainfo
{
INT32 i;
char temp[17];
char temp[16];
pdemo->standings[count].ranking = READUINT8(extrainfo_p);
@@ -2345,7 +2345,6 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
// Skin
memcpy(temp, extrainfo_p, 16);
temp[16] = '\0';
extrainfo_p += 16;
pdemo->standings[count].skin = UINT8_MAX;
@@ -2360,7 +2359,6 @@ void G_LoadDemoInfo(menudemo_t *pdemo)
// Color
memcpy(temp, extrainfo_p, 16);
temp[16] = '\0';
extrainfo_p += 16;
for (i = 0; i < MAXSKINCOLORS; i++)
+1 -2
View File
@@ -82,8 +82,7 @@ typedef struct menudemo_s {
struct {
UINT8 ranking;
char name[17];
skinnum_t skin;
UINT8 color;
UINT8 skin, color;
UINT32 timeorscore;
} standings[MAXPLAYERS];
} menudemo_t;
+1 -1
View File
@@ -63,7 +63,7 @@ gameaction_t gameaction = 0;
gamestate_t gamestate = GS_NULL;
boolean botingame = false;
skinnum_t botskin = 0;
UINT8 botskin = 0;
UINT8 botcolor = 0;
// SRB2kart
+1 -2
View File
@@ -55,7 +55,6 @@ extern gamestate_t gamestate;
extern gameaction_t gameaction;
extern boolean botingame;
extern skinnum_t botskin;
extern UINT8 botcolor;
extern UINT8 botskin, botcolor;
#endif //__G_STATE__
+1
View File
@@ -191,6 +191,7 @@ enum
SHADER_SPRITE,
SHADER_MODEL,
SHADER_WATER,
SHADER_WATERREFRACT,
SHADER_FOG,
SHADER_SKY,
SHADER_PALETTE_POSTPROCESS,
+1
View File
@@ -39,6 +39,7 @@ void GL_EnableStencilTest(void);
void GL_DisableStencilTest(void);
void GL_DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags);
void GL_DrawWaterPolygon(FOutVector *pOutVerts, FUINT iNumPts);
void GL_DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, unsigned int *IndexArray);
void GL_Draw2DLine(F2DCoord *v1, F2DCoord *v2, RGBA_t Color);
+275 -22
View File
@@ -103,6 +103,8 @@ static constexpr INT32 bsp_culling_distances[] = {
// Performance stats
ps_metric_t ps_hw_nodesorttime = {};
ps_metric_t ps_hw_nodedrawtime = {};
ps_metric_t ps_hw_waterdrawtime = {};
ps_metric_t ps_hw_spritesorttime = {};
ps_metric_t ps_hw_spritedrawtime = {};
@@ -712,7 +714,9 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
if (PolyFlags & PF_Fog)
shader = SHADER_FOG;
else if (cv_ripplewater.value && (PolyFlags & PF_Ripple))
{
shader = SHADER_WATER;
}
else
shader = SHADER_FLOOR;
@@ -807,6 +811,164 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
#undef SETUP3DVERT
}
// cut down version of HWR_RenderPlane
// which renders only the refracted scene in place of planes
// FIXME: we probably could just use stencil stuff for this
// but i hate messing with that
static void HWR_RenderRefractionPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, sector_t *FOFsector)
{
FOutVector *v3d;
polyvertex_t *pv;
pslope_t *slope = NULL;
size_t nrPlaneVerts;
INT32 i;
float height; // constant y for all points on the convex flat polygon
static FOutVector *planeVerts = NULL;
static UINT16 numAllocedPlaneVerts = 0;
poly_t *planepoly = xsub->planepoly;
// no convex poly were generated for this subsector
if (!planepoly)
return;
nrPlaneVerts = planepoly->numpts;
if (nrPlaneVerts < 3) // not even a triangle ?
return;
const sector_t *sec = FOFsector;
// Get the slope pointer to simplify future code
if (!isceiling && sec->f_slope)
slope = sec->f_slope;
else if (isceiling && sec->c_slope)
slope = sec->c_slope;
height = FixedToFloat(fixedheight);
// Allocate plane-vertex buffer if we need to
if (!planeVerts || nrPlaneVerts > numAllocedPlaneVerts)
{
numAllocedPlaneVerts = (UINT16)nrPlaneVerts;
Z_Free(planeVerts);
Z_Malloc(numAllocedPlaneVerts * sizeof (FOutVector), PU_LEVEL, &planeVerts);
}
pv = planepoly->pts;
#define SETUP3DVERT(vert, vx, vy) {\
vert->s = 0.0f;\
vert->t = 0.0f;\
\
if (slope)\
{\
fixedheight = P_GetSlopeZAt(slope, FloatToFixed((vx)), FloatToFixed((vy)));\
height = FixedToFloat(fixedheight);\
}\
\
vert->x = (vx);\
vert->y = height;\
vert->z = (vy);\
\
}
for (i = 0, v3d = planeVerts; i < (INT32)nrPlaneVerts; i++, v3d++, pv++)
SETUP3DVERT(v3d, pv->x, pv->y);
GL_DrawWaterPolygon(planeVerts, nrPlaneVerts);
//FIXME: horizonlines with fofs dont work in gl for some reason!
(void)subsector;
/*if (gl_maphashorizonlines && subsector && cv_glhorizonlines.value)
{
// Horizon lines
FOutVector horizonpts[6];
float dist, vx, vy;
float x1, y1, xd, yd;
UINT8 numplanes, j;
vertex_t v; // For determining the closest distance from the line to the camera, to split render planes for minimum distortion;
static constexpr float renderdist = 27000.0f; // How far out to properly render the plane
static constexpr float farrenderdist = 32768.0f; // From here, raise plane to horizon level to fill in the line with some texture distortion
const seg_t *line = &segs[subsector->firstline];
for (i = 0; i < subsector->numlines; i++, line++)
{
if (line->polyseg)
continue;
line_t* ld = line->linedef;
// this check sucks and is a hotspot lel
if (LIKELY(ld->special != HORIZONSPECIAL))
continue;
if (R_PointOnSegSide(viewx, viewy, line) != 0)
continue;
P_ClosestPointOnLine(viewx, viewy, ld, &v);
dist = FixedToFloat(R_PointToDist(v.x, v.y));
x1 = (line->fv1.x);
y1 = (line->fv1.y);
xd = (line->fv2.x) - x1;
yd = (line->fv2.y) - y1;
// Based on the seg length and the distance from the line, split horizon into multiple poly sets to reduce distortion
dist = sqrtf((xd*xd) + (yd*yd)) / dist / 16.0f;
if (dist > 100.0f)
numplanes = 100;
else
numplanes = (UINT8)dist + 1;
for (j = 0; j < numplanes; j++)
{
// Left side
vx = x1 + xd * j / numplanes;
vy = y1 + yd * j / numplanes;
SETUP3DVERT((&horizonpts[1]), vx, vy);
dist = sqrtf(powf(vx - gl_viewx, 2) + powf(vy - gl_viewy, 2));
vx = (vx - gl_viewx) * renderdist / dist + gl_viewx;
vy = (vy - gl_viewy) * renderdist / dist + gl_viewy;
SETUP3DVERT((&horizonpts[0]), vx, vy);
// Right side
vx = x1 + xd * (j+1) / numplanes;
vy = y1 + yd * (j+1) / numplanes;
SETUP3DVERT((&horizonpts[2]), vx, vy);
dist = sqrtf(powf(vx - gl_viewx, 2) + powf(vy - gl_viewy, 2));
vx = (vx - gl_viewx) * renderdist / dist + gl_viewx;
vy = (vy - gl_viewy) * renderdist / dist + gl_viewy;
SETUP3DVERT((&horizonpts[3]), vx, vy);
// Horizon fills
vx = (horizonpts[0].x - gl_viewx) * farrenderdist / renderdist + gl_viewx;
vy = (horizonpts[0].z - gl_viewy) * farrenderdist / renderdist + gl_viewy;
SETUP3DVERT((&horizonpts[5]), vx, vy);
horizonpts[5].y = gl_viewz;
vx = (horizonpts[3].x - gl_viewx) * farrenderdist / renderdist + gl_viewx;
vy = (horizonpts[3].z - gl_viewy) * farrenderdist / renderdist + gl_viewy;
SETUP3DVERT((&horizonpts[4]), vx, vy);
horizonpts[4].y = gl_viewz;
// Draw
GL_DrawWaterPolygon(&Surf, horizonpts, 6, PF_NoTexture|PF_Ripple);
}
}
}
*/
#undef SETUP3DVERT
}
#ifdef WALLSPLATS
static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf)
{
@@ -2662,13 +2824,11 @@ static void HWR_AddPolyObjectPlanes(void)
polyobjsector = po_ptrs[i]->lines[0]->backsector; // the in-level polyobject sector
if (polyobjsector->floorheight <= gl_frontsector->ceilingheight &&
polyobjsector->floorheight >= gl_frontsector->floorheight &&
(viewz < polyobjsector->floorheight))
if (polyobjsector->floorheight <= gl_frontsector->ceilingheight
&& polyobjsector->floorheight >= gl_frontsector->floorheight
&& (viewz < polyobjsector->floorheight))
{
light = R_GetPlaneLight(gl_frontsector, polyobjsector->floorheight, true);
const INT16 llevel = (light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel);
extracolormap_t *colmap = (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap);
if (po_ptrs[i]->translucency > 0)
{
@@ -2676,24 +2836,22 @@ static void HWR_AddPolyObjectPlanes(void)
FBITFIELD blendmode;
blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf);
HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->floorpic].lumpnum, po_ptrs[i], false, polyobjsector->floorheight,
llevel, Surf.PolyColor.s.alpha, polyobjsector, blendmode, colmap);
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), Surf.PolyColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap));
}
else
{
HWR_GetFlat(levelflats[polyobjsector->floorpic].lumpnum, R_NoEncore(polyobjsector, false));
HWR_RenderPolyObjectPlane(po_ptrs[i], false, polyobjsector->floorheight, PF_Occlude,
llevel, levelflats[polyobjsector->floorpic].lumpnum,
polyobjsector, 255, colmap);
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->floorpic].lumpnum,
polyobjsector, 255, (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap));
}
}
if (polyobjsector->ceilingheight >= gl_frontsector->floorheight &&
polyobjsector->ceilingheight <= gl_frontsector->ceilingheight &&
(viewz > polyobjsector->ceilingheight))
if (polyobjsector->ceilingheight >= gl_frontsector->floorheight
&& polyobjsector->ceilingheight <= gl_frontsector->ceilingheight
&& (viewz > polyobjsector->ceilingheight))
{
light = R_GetPlaneLight(gl_frontsector, polyobjsector->ceilingheight, true);
const INT16 llevel = (light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel);
extracolormap_t *colmap = (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap);
if (po_ptrs[i]->translucency > 0)
{
@@ -2701,14 +2859,14 @@ static void HWR_AddPolyObjectPlanes(void)
FBITFIELD blendmode;
blendmode = HWR_TranstableToAlpha(po_ptrs[i]->translucency, &Surf);
HWR_AddTransparentPolyobjectFloor(levelflats[polyobjsector->ceilingpic].lumpnum, po_ptrs[i], true, polyobjsector->ceilingheight,
llevel, Surf.PolyColor.s.alpha, polyobjsector, blendmode, colmap);
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), Surf.PolyColor.s.alpha, polyobjsector, blendmode, (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap));
}
else
{
HWR_GetFlat(levelflats[polyobjsector->ceilingpic].lumpnum, R_NoEncore(polyobjsector, true));
HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude,
llevel, levelflats[polyobjsector->ceilingpic].lumpnum,
polyobjsector, 255, colmap);
(light == -1 ? gl_frontsector->lightlevel : *gl_frontsector->lightlist[light].lightlevel), levelflats[polyobjsector->ceilingpic].lumpnum,
polyobjsector, 255, (light == -1 ? gl_frontsector->extra_colormap : gl_frontsector->lightlist[light].extra_colormap));
}
}
}
@@ -3721,7 +3879,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr, const boolean papersprite)
Surf.PolyColor.s.alpha = alpha;
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false);
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false); // sprite shader
top = bot;
endtop = endbot;
@@ -3748,7 +3906,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr, const boolean papersprite)
Surf.PolyColor.s.alpha = alpha;
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false);
HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false); // sprite shader
}
// -----------------+
@@ -4229,7 +4387,9 @@ static void HWR_RenderDrawNodes(void)
// Reversed order
for (i = 0; i < numdrawnodes; i++)
{
sortindex[i] = numdrawnodes - i - 1;
}
// The order is correct apart from planes in the same subsector.
// So scan the list and sort out these cases.
@@ -4305,7 +4465,6 @@ static void HWR_RenderDrawNodes(void)
HWR_RenderPolyObjectPlane(polyplane->polysector, polyplane->isceiling, polyplane->fixedheight, polyplane->blend, polyplane->lightlevel,
polyplane->lumpnum, polyplane->FOFSector, polyplane->alpha, polyplane->planecolormap);
}
break;
case DRAWNODE_WALL:
@@ -4329,6 +4488,89 @@ static void HWR_RenderDrawNodes(void)
drawnodes.clear(); // clear so our size is 0 again!
}
static void HWR_RenderRefractionWater(void)
{
// this basically just draws a refracted screen copy in the place of water planes
// best approach i can think of to handle translucent things being drawn behind water planes
// this means other translucent shit will appear "correctly" and does not cause any sorting issues
// caveat: anything translucent behind water planes will be unaffected by the refraction effect
// there just is no better way to do this unless we wanna trash performance to hell by doing a screen copy per water plane
// it "might" be possible to only partially copy the screen like the software renderer
// but that will be rather tricky to implement since we dont really have "screen bounds" we can use for this
// plus i also immensely suck at maths
static std::vector<gl_drawnode_t> waterdrawnodes;
const size_t numdrawnodes = drawnodes.size();
// no drawnodes...
if (!numdrawnodes)
return;
waterdrawnodes.reserve(DRAWNODES_INIT_SIZE/2); // surely there will be less water than other stuff, right?
// go through our drawnodes to pull out the water
// HWR_RenderDrawNodes will skip those
// we do this before since trying to capture
// anything translucent in the refraction is utter arse pain
for (size_t w = 0; w < numdrawnodes; w++)
{
gl_drawnode_t *drawnode = &drawnodes[w];
// gotta be a plane duh
if (drawnode->type != DRAWNODE_PLANE)
continue;
planeinfo_t *plane = &drawnode->u.plane;
// uh dont think we actually need this lol
// but better be safe for now
if (plane->alpha >= 255)
continue;
// this only applies to translucent water with ripple effect!
if (!(plane->blend & PF_Ripple))
continue;
waterdrawnodes.push_back(drawnodes[w]);
}
const size_t numwaterdrawnodes = waterdrawnodes.size();
// no water....
if (!numwaterdrawnodes)
return;
// this is probably a bit wasteful
// since we only copy the screen once
// but you might be able to see both planes not overlap if you look from a wonky angle
auto wotercompare = [](const void *p1, const void *p2)
{
const gl_drawnode_t *n1 = (const gl_drawnode_t *)p1;
const gl_drawnode_t *n2 = (const gl_drawnode_t *)p2;
return abs(n2->u.plane.fixedheight - viewz) -
abs(n1->u.plane.fixedheight - viewz);
};
qs22j(waterdrawnodes.data(), waterdrawnodes.size(), sizeof(gl_drawnode_t), wotercompare);
GL_SetTransform(&atransform);
GL_CopyMainFramebufferTexture();
GL_CopyMainFramebufferDepth();
// now finally render the refraction planes
for (auto &drawnode : waterdrawnodes)
{
planeinfo_t *plane = &drawnode.u.plane;
gl_frontsector = NULL;
HWR_RenderRefractionPlane(NULL, plane->xsub, plane->isceiling, plane->fixedheight, plane->FOFSector);
}
waterdrawnodes.clear();
}
// --------------------------------------------------------------------------
// Draw all vissprites
// --------------------------------------------------------------------------
@@ -5605,9 +5847,20 @@ static void HWR_RenderViewpoint(gl_portal_t *rootportal, int stencil_level, bool
HWR_DrawSprites<DrawSpritesType::kSprites>();
PS_STOP_TIMING(ps_hw_spritedrawtime);
ps_numdrawnodes.value.i = 0;
ps_hw_nodesorttime.value.p = 0;
ps_hw_nodedrawtime.value.p = 0;
ps_numdrawnodes.value.i = 0;
ps_hw_nodesorttime.value.p = 0;
ps_hw_nodedrawtime.value.p = 0;
ps_hw_waterdrawtime.value.p = 0;
// thisll be a good mess making this toggable
if (cv_ripplewater.value && HWR_UseShader() && !wireframe) // dont need this without shaders do we
{
// should we not do this during portal render?
// wonder how bad the perf will be....
PS_START_TIMING(ps_hw_waterdrawtime);
HWR_RenderRefractionWater();
PS_STOP_TIMING(ps_hw_waterdrawtime);
}
HWR_RenderDrawNodes();
+1
View File
@@ -68,6 +68,7 @@ enum
// Performance stats
extern ps_metric_t ps_hw_nodesorttime;
extern ps_metric_t ps_hw_nodedrawtime;
extern ps_metric_t ps_hw_waterdrawtime;
extern ps_metric_t ps_hw_spritesorttime;
extern ps_metric_t ps_hw_spritedrawtime;
+3
View File
@@ -41,6 +41,9 @@ static struct {
// Water shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_WATER_FRAGMENT_SHADER},
// Water refraction shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_WATERREFRACT_FRAGMENT_SHADER},
// Fog shader
{GLSL_DEFAULT_VERTEX_SHADER, GLSL_FOG_FRAGMENT_SHADER},
+38 -8
View File
@@ -243,23 +243,30 @@
// Water surface shader
//
// Mostly guesstimated, rather than the rest being built off Software science.
// Still needs to distort things underneath/around the water...
// Still needs to distort things underneath/around the water... // huehueuehuehue
//
// readjusted to match software much more closely
#define GLSL_WATER_CONST \
"const float freq = 0.07;\n" \
"const float amp = 0.07;\n" \
"const float amp_t = 0.025;\n" \
"const float speed = 4.0;\n" \
"const float pi = 3.14159;\n" \
"const float falltresh = 0.007;\n" \
#define GLSL_WATER_TEXEL \
"float water_z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \
"float a = -pi * (water_z * freq) + (leveltime * speed);\n" \
"float sdistort = sin(a) * amp;\n" \
"float cdistort = cos(a) * amp;\n" \
"vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n"
"float falloff = 1.0 / (1.0 + water_z * falltresh);\n" \
"float distort = sin(a) * amp * falloff;\n" \
"vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - distort, gl_TexCoord[0].t));\n" \
#define GLSL_WATER_FRAGMENT_SHADER \
"#version 120\n" \
GLSL_FLOOR_FUDGES \
"const float freq = 0.025;\n" \
"const float amp = 0.025;\n" \
"const float speed = 2.0;\n" \
"const float pi = 3.14159;\n" \
GLSL_WATER_CONST \
"#ifdef SRB2_PALETTE_RENDERING\n" \
"uniform sampler2D tex;\n" \
"uniform sampler3D palette_lookup_tex;\n" \
@@ -294,6 +301,29 @@
"}\n" \
"#endif\0"
// water refraction stuff
// this is probably complete shit but i dont care
// got the idea for depth testing from https://lettier.github.io/3d-game-shaders-for-beginners/screen-space-refraction.html
#define GLSL_WATERREFRACT_FRAGMENT_SHADER \
"#version 120\n" \
GLSL_WATER_CONST \
"uniform sampler2D scene_tex;\n" \
"uniform sampler2D scene_depth_tex;\n" \
"uniform vec2 scr_resolution;\n" \
"uniform float leveltime;\n" \
"void main(void) {\n" \
"float water_z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \
"float a = -pi * (water_z * freq) + (leveltime * speed);\n" \
"float falloff = 1.0 / (1.0 + water_z * falltresh);\n" \
"float distort = sin(a) * amp * falloff;\n" \
"vec2 uv = (gl_FragCoord.xy / scr_resolution) + vec2(distort, distort * 2.0) * 0.04;\n" \
"float scenedepth = texture2D(scene_depth_tex, uv).r;\n" \
"if (scenedepth < gl_FragCoord.z) {\n" \
"discard;\n" \
"}\n" \
"gl_FragColor = texture2D(scene_tex, uv);\n" \
"}\0"
//
// Fog block shader
//
+91
View File
@@ -173,6 +173,15 @@ boolean supportstencil = false;
// can know when the textures aren't there, as textures are always considered resident in their virtual memory
static GLuint screenTextures[NUMSCREENTEXTURES] = {};
// for water refraction
enum
{
SCENE_TEX,
SCENE_DEPTHTEX,
NUMSCENETEXTURES
};
static GLuint sceneTextures[NUMSCENETEXTURES] = {};
#define byte2float(byte) (GLfloat)(byte / 255.0f)
// -----------------+
@@ -723,6 +732,9 @@ typedef enum
gluniform_palette_lookup_tex, // 3d texture containing the rgb->index lookup table
gluniform_lighttable_tex, // 2d texture containing a light table
gluniform_scene_tex,
gluniform_scene_depth_tex,
// misc.
gluniform_leveltime,
@@ -2386,6 +2398,9 @@ static boolean GL_Shader_CompileProgram(gl_shader_t *shader, GLint i)
// misc.
shader->uniforms[gluniform_leveltime] = GETUNI("leveltime");
shader->uniforms[gluniform_scene_tex] = GETUNI("scene_tex");
shader->uniforms[gluniform_scene_depth_tex] = GETUNI("scene_depth_tex");
#undef GETUNI
// set permanent uniform values
@@ -2400,6 +2415,9 @@ static boolean GL_Shader_CompileProgram(gl_shader_t *shader, GLint i)
UNIFORM_1(shader->uniforms[gluniform_palette_lookup_tex], 1, pglUniform1i);
UNIFORM_1(shader->uniforms[gluniform_lighttable_tex], 2, pglUniform1i);
UNIFORM_1(shader->uniforms[gluniform_scene_tex], 4, pglUniform1i);
UNIFORM_1(shader->uniforms[gluniform_scene_depth_tex], 5, pglUniform1i);
// restore gl shader state
pglUseProgram(gl_shaderstate.program);
#undef UNIFORM_1
@@ -2480,6 +2498,24 @@ void GL_DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, F
Clamp2D(GL_TEXTURE_WRAP_T);
}
void GL_DrawWaterPolygon(FOutVector *pOutVerts, FUINT iNumPts)
{
GL_SetShader(HWR_GetShaderFromTarget(SHADER_WATERREFRACT));
pglActiveTexture(GL_TEXTURE4);
pglBindTexture(GL_TEXTURE_2D, sceneTextures[SCENE_TEX]);
pglActiveTexture(GL_TEXTURE0);
pglActiveTexture(GL_TEXTURE5);
pglBindTexture(GL_TEXTURE_2D, sceneTextures[SCENE_DEPTHTEX]);
pglActiveTexture(GL_TEXTURE0);
GL_PreparePolygon(NULL, PF_NoTexture|PF_Ripple);
pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x);
pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts);
}
void GL_DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, unsigned int *IndexArray)
{
GL_PreparePolygon(pSurf, PolyFlags);
@@ -3384,6 +3420,57 @@ void GL_Framebuffer_Disable(void)
}
#endif
// faster than fbo blitting ig...
// just copies the whole framebuffer as a texture
void GL_CopyMainFramebufferTexture(void)
{
if (!sceneTextures[SCENE_TEX])
{
pglGenTextures(1, &sceneTextures[SCENE_TEX]);
pglBindTexture(GL_TEXTURE_2D, sceneTextures[SCENE_TEX]);
pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Clamp2D(GL_TEXTURE_WRAP_S);
Clamp2D(GL_TEXTURE_WRAP_T);
pglBindTexture(GL_TEXTURE_2D, 0);
}
// now copy the whole scene
pglBindTexture(GL_TEXTURE_2D, sceneTextures[SCENE_TEX]);
pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screen_width, screen_height);
// not sure why, but this is needed
// otherwise bad things happen
tex_downloaded = sceneTextures[SCENE_TEX];
pglBindTexture(GL_TEXTURE_2D, 0);
}
void GL_CopyMainFramebufferDepth(void)
{
if (!sceneTextures[SCENE_DEPTHTEX])
{
pglGenTextures(1, &sceneTextures[SCENE_DEPTHTEX]);
pglBindTexture(GL_TEXTURE_2D, sceneTextures[SCENE_DEPTHTEX]);
pglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, screen_width, screen_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Clamp2D(GL_TEXTURE_WRAP_S);
Clamp2D(GL_TEXTURE_WRAP_T);
pglBindTexture(GL_TEXTURE_2D, 0);
}
// and copy the depth from the scene
// we need this for depth testing within the shader
// so we can discard stuff in front of the water
// this aint reflections after all!
pglBindTexture(GL_TEXTURE_2D, sceneTextures[SCENE_DEPTHTEX]);
pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, screen_width, screen_height);
pglBindTexture(GL_TEXTURE_2D, 0);
}
static const float defaultscreenVerts[12] =
{
-1.0f, -1.0f, 1.0f,
@@ -3505,6 +3592,10 @@ void GL_FlushScreenTextures(void)
{
pglDeleteTextures(NUMSCREENTEXTURES, screenTextures);
memset(screenTextures, 0, sizeof(screenTextures));
// gotta wipe the scene textures for the water refraction stuff aswell
pglDeleteTextures(NUMSCENETEXTURES, sceneTextures);
memset(sceneTextures, 0, sizeof(sceneTextures));
}
void GL_DrawScreenTexture(int tex, FSurfaceInfo *surf, FBITFIELD polyflags)
+3
View File
@@ -104,6 +104,9 @@ void GL_Framebuffer_Disable(void);
extern int supportFBO;
#endif
void GL_CopyMainFramebufferTexture(void);
void GL_CopyMainFramebufferDepth(void);
// ==========================================================================
// GLOBAL
// ==========================================================================
+3 -3
View File
@@ -452,10 +452,10 @@ void A_SpawnFreshCopy(void *actor);
extern boolean actionsoverridden[NUMACTIONS];
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#ifdef PHOBOS_BUILD
#define NUMMOBJFREESLOTS 4096 // should be sufficient
#else
#ifdef COMPAT_VANILLA
#define NUMMOBJFREESLOTS 1024
#else
#define NUMMOBJFREESLOTS 4096 // should be sufficient
#endif
#define NUMSPRITEFREESLOTS NUMMOBJFREESLOTS
#define NUMSTATEFREESLOTS (NUMMOBJFREESLOTS*8)
+10 -19
View File
@@ -62,6 +62,14 @@ static lua_CFunction liblist[] = {
NULL
};
// Lua asks for memory using this.
static void *LUA_Alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
(void)ud;
(void)osize;
return Z_Realloc(ptr, nsize, PU_LUA, NULL);
}
// Panic function Lua calls when there's an unprotected error.
// This function cannot return. Lua would kill the application anyway if it did.
FUNCNORETURN static int LUA_Panic(lua_State *L)
@@ -189,7 +197,7 @@ void LUA_ClearState(void)
CONS_Printf(M_GetText("Pardon me while I initialize the Lua scripting interface...\n"));
// allocate state
L = luaL_newstate();
L = lua_newstate(LUA_Alloc, NULL);
lua_atpanic(L, LUA_Panic);
// open base libraries
@@ -365,7 +373,7 @@ void LUA_DumpFile(const char *filename)
static void createMathLibState(void)
{
mL = luaL_newstate();
mL = lua_newstate(LUA_Alloc, NULL);
lua_atpanic(mL, LUA_Panic);
// open only enum lib
@@ -615,10 +623,6 @@ void LUA_InvalidateLevel(void)
LUA_InvalidateUserdata(&slope->o);
LUA_InvalidateUserdata(&slope->d);
}
// We invalidated a lot of userdata objects, a lot of them probably ended up unreachable so they can be
// GC'd right away
lua_gc(gL, LUA_GCCOLLECT, 0);
}
void LUA_InvalidateMapthings(void)
@@ -1457,16 +1461,3 @@ int Lua_CreateFieldTable(lua_State *L, const char *const lst[])
return luaL_ref(L, LUA_REGISTRYINDEX);
}
size_t LUA_MemoryUsage(void)
{
size_t usage = 0;
if (gL)
usage += lua_gc(gL, LUA_GCCOUNT, 0);
if (mL)
usage += lua_gc(mL, LUA_GCCOUNT, 0);
return usage;
}
-3
View File
@@ -82,9 +82,6 @@ int Lua_optoption(lua_State *L, int narg, int def, int list_ref);
int Lua_CreateFieldTable(lua_State *L, const char *const lst[]);
void LUA_HookNetArchive(lua_CFunction archFunc, savebuffer_t *save);
// Returns lua memory usage in kbytes
size_t LUA_MemoryUsage(void);
// Console wrapper
void COM_Lua_f(void);
+15 -15
View File
@@ -6451,7 +6451,7 @@ static void M_ChooseTimeAttack(INT32 choice)
else
G_RecordDemo(nameofdemo);
G_DeferedInitNew(false, mapname, (skinnum_t)(cv_chooseskin.value-1), 0, false);
G_DeferedInitNew(false, mapname, (UINT8)(cv_chooseskin.value-1), 0, false);
}
static void M_HandleStaffReplay(INT32 choice)
@@ -7677,7 +7677,7 @@ static void M_DrawMPMainMenu(void)
#define iconwidth 32
#define spacingwidth 32
#define incrwidth (iconwidth + spacingwidth)
UINT16 i = 0, pskin, pcol;
UINT8 i = 0, pskin, pcol;
// player arrangement width, but there's also a chance i'm a furry, shhhhhh
const INT32 paw = iconwidth + 3*incrwidth;
INT32 trans = 0;
@@ -7930,16 +7930,16 @@ static void M_DrawSetupMultiPlayerMenu(void)
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *patch;
UINT16 frame;
UINT16 speed;
UINT16 weight;
UINT16 i;
UINT16 s, w;
UINT8 frame;
UINT8 speed;
UINT8 weight;
UINT8 i;
UINT8 s, w;
INT32 skinnum = 0;
INT32 statx, staty;
UINT32 speenframe;
INT32 sltw, actw, hetw;
skinnum_t skintodisplay;
UINT8 skintodisplay;
INT32 nameboxaddy = 0;
const UINT8 *flashcol = V_GetStringColormap(highlightflags);
@@ -7996,7 +7996,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
break;
case SKINMENUTYPE_2D:
skintodisplay = (skinnum_t)setupm_fakeskin;
skintodisplay = setupm_fakeskin;
if (setupm_skinlockedselect) //show the skin we are trying to select
skintodisplay = skinstats[setupm_skinxpos][setupm_skinypos][setupm_skinselect];
else if (skinstatscount[setupm_skinxpos][setupm_skinypos] && itemOn == 1)
@@ -8376,7 +8376,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
colwidth = colwidth - 10;
INT32 j = -colwidth;
INT32 col = setupm_fakecolor - colwidth;
INT16 col = setupm_fakecolor - colwidth;
INT32 x = mx;
INT32 cw = indexwidth;
UINT8 ch;
@@ -8404,7 +8404,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
{
const INT32 icons = 4;
INT32 k = -icons;
INT32 col = setupm_fakeskin - icons;
INT16 col = setupm_fakeskin - icons;
INT32 x = BASEVIDWIDTH/2 - ((icons+1)*24) - 4;
fixed_t scale = FRACUNIT/2;
INT32 offx = 8, offy = 8;
@@ -8467,7 +8467,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
switch (cv_skinselectmenu.value)
{
case SKINMENUTYPE_2D:
skintodisplay = (skinnum_t)setupm_fakeskin;
skintodisplay = (UINT8)setupm_fakeskin;
if (setupm_skinlockedselect) // show the skin we are trying to select
skintodisplay = skinstats[setupm_skinxpos][setupm_skinypos][setupm_skinselect];
else if (skinstatscount[setupm_skinxpos][setupm_skinypos] && itemOn == 1)
@@ -8475,10 +8475,10 @@ static void M_DrawSetupMultiPlayerMenu(void)
break;
case SKINMENUTYPE_EXTENDED:
case SKINMENUTYPE_GRID:
skintodisplay = ((itemOn == 1 && (setupm_skinselect < numskins)) ? skinsorted[setupm_skinselect] : (skinnum_t)setupm_fakeskin);
skintodisplay = ((itemOn == 1 && (setupm_skinselect < numskins)) ? skinsorted[setupm_skinselect] : (UINT8)setupm_fakeskin);
break;
default:
skintodisplay = (skinnum_t)setupm_fakeskin;
skintodisplay = (UINT8)setupm_fakeskin;
break;
}
@@ -10060,7 +10060,7 @@ static void M_DrawColorMenu(void)
for (max = currentMenu->numitems; max > 0; max--)
{
if (currentMenu->menuitems[max-1].status != IT_DISABLED && currentMenu->menuitems[max-1].alphaKey*2 + tempcentery <= (currentMenu->y + 2*scrollareaheight))
if (currentMenu->menuitems[max].status != IT_DISABLED && currentMenu->menuitems[max-1].alphaKey*2 + tempcentery <= (currentMenu->y + 2*scrollareaheight))
break;
}
+2 -2
View File
@@ -230,8 +230,8 @@ typedef struct
char levelname[32];
UINT8 actnum;
UINT8 skincolor;
skinnum_t skinnum;
skinnum_t botskin;
UINT8 skinnum;
UINT8 botskin;
UINT8 botcolor;
UINT8 numemeralds;
INT32 lives;
+2
View File
@@ -98,6 +98,7 @@ perfstatrow_t rendertime_rows[] = {
{" sprdraw", " Sprite render: ", &ps_hw_spritedrawtime, PS_TIME|PS_LEVEL|PS_HW},
{" nodesrt", " Drwnode sort: ", &ps_hw_nodesorttime, PS_TIME|PS_LEVEL|PS_HW},
{" nodedrw", " Drwnode render:", &ps_hw_nodedrawtime, PS_TIME|PS_LEVEL|PS_HW},
{" wtrdrw", " Water render: ", &ps_hw_waterdrawtime, PS_TIME|PS_LEVEL|PS_HW},
{" other ", " Other: ", &ps_otherrendertime, PS_TIME|PS_LEVEL|PS_HW},
#endif
@@ -536,6 +537,7 @@ static void PS_UpdateFrameStats(void)
ps_skyboxtime.value.p +
ps_hw_nodesorttime.value.p +
ps_hw_nodedrawtime.value.p +
ps_hw_waterdrawtime.value.p +
ps_hw_spritesorttime.value.p +
ps_hw_spritedrawtime.value.p;
+2 -2
View File
@@ -238,8 +238,8 @@ void M_TextInputClear(textinput_t *input)
void M_TextInputSetString(textinput_t *input, const char *c)
{
strncpy(input->buffer, c, input->buffer_size-1);
input->buffer[input->buffer_size-1] = '\0';
memset(input->buffer, 0, input->buffer_size);
strncpy(input->buffer, c, input->buffer_size);
input->cursor = input->select = input->length = strlen(input->buffer);
}
+3 -3
View File
@@ -295,10 +295,10 @@ FUNCINLINE static ATTRINLINE boolean P_MobjWasRemoved(const mobj_t *mobj)
// so can only check if its not NULL in vanilla compat mode
FUNCINLINE static ATTRINLINE boolean P_MobjWasRemovedCompat(const mobj_t *mobj)
{
#ifdef PHOBOS_BUILD
return P_MobjWasRemoved(mobj);
#ifdef COMPAT_VANILLA
return (!mobj);
#else
return !mobj;
return P_MobjWasRemoved(mobj);
#endif
}
+11 -31
View File
@@ -69,7 +69,7 @@ FUNCINLINE static ATTRINLINE void P_ArchivePlayer(savebuffer_t *save)
pllives = 3; // has less than that.
WRITEUINT8(save->p, player->skincolor);
WRITESKIN(save->p, player->skin);
WRITEUINT8(save->p, player->skin);
WRITEUINT32(save->p, player->score);
WRITEINT32(save->p, pllives);
@@ -77,7 +77,7 @@ FUNCINLINE static ATTRINLINE void P_ArchivePlayer(savebuffer_t *save)
if (botskin)
{
WRITESKIN(save->p, botskin);
WRITEUINT8(save->p, botskin);
WRITEUINT8(save->p, botcolor);
}
}
@@ -88,7 +88,7 @@ FUNCINLINE static ATTRINLINE void P_ArchivePlayer(savebuffer_t *save)
FUNCINLINE static ATTRINLINE void P_UnArchivePlayer(savebuffer_t *save)
{
savedata.skincolor = READUINT8(save->p);
savedata.skin = READSKIN(save->p);
savedata.skin = READUINT8(save->p);
savedata.score = READINT32(save->p);
savedata.lives = READINT32(save->p);
@@ -96,11 +96,9 @@ FUNCINLINE static ATTRINLINE void P_UnArchivePlayer(savebuffer_t *save)
if (savedata.botcolor)
{
savedata.botskin = READSKIN(save->p);
savedata.botskin = READUINT8(save->p);
if (savedata.botskin-1 >= numskins)
savedata.botskin = 0;
savedata.botcolor = READUINT8(save->p);
}
else
@@ -119,9 +117,7 @@ static void P_NetArchivePlayers(savebuffer_t *save, boolean resending)
for (i = 0; i < MAXPLAYERS; i++)
{
#ifndef PHOBOS_BUILD
if (resending)
#endif
WRITESINT8(save->p, (SINT8)adminplayers[i]);
if (!playeringame[i])
@@ -131,9 +127,7 @@ static void P_NetArchivePlayers(savebuffer_t *save, boolean resending)
// no longer send ticcmds
#ifndef PHOBOS_BUILD
if (resending)
#endif
WRITESTRINGN(save->p, player_names[i], MAXPLAYERNAME);
const player_t *player = &players[i];
@@ -162,16 +156,11 @@ static void P_NetArchivePlayers(savebuffer_t *save, boolean resending)
WRITEUINT16(save->p, player->flashpal);
WRITEUINT16(save->p, player->flashcount);
#ifdef PHOBOS_BUILD
WRITEUINT8(save->p, player->skincolor);
WRITEINT32(save->p, player->skin);
#else
if (resending)
{
WRITEUINT8(save->p, player->skincolor);
WRITEINT32(save->p, player->skin);
}
#endif
WRITEUINT32(save->p, player->score);
WRITEFIXED(save->p, player->dashspeed);
@@ -320,9 +309,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save, boolean reloading)
for (i = 0; i < MAXPLAYERS; i++)
{
#ifndef PHOBOS_BUILD
if (reloading)
#endif
adminplayers[i] = (INT32)READSINT8(save->p);
// Do NOT memset player struct to 0
@@ -333,9 +320,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save, boolean reloading)
// NOTE: sending tics should (hopefully) no longer be necessary
#ifndef PHOBOS_BUILD
if (reloading)
#endif
READSTRINGN(save->p, player_names[i], MAXPLAYERNAME);
player_t *player = &players[i];
@@ -364,16 +349,11 @@ static void P_NetUnArchivePlayers(savebuffer_t *save, boolean reloading)
player->flashpal = READUINT16(save->p);
player->flashcount = READUINT16(save->p);
#ifdef PHOBOS_BUILD
player->skincolor = READUINT8(save->p);
player->skin = READINT32(save->p);
#else
if (reloading)
{
player->skincolor = READUINT8(save->p);
player->skin = READINT32(save->p);
}
#endif
player->score = READUINT32(save->p);
player->dashspeed = READFIXED(save->p); // dashing speed
@@ -1128,10 +1108,10 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
diff2 = 0;
// not the default but the most probable
#ifdef PHOBOS_BUILD
if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz !=0)
#else
#ifdef COMPAT_VANILLA
if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0)
#else
if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz !=0)
#endif
diff |= MD_MOM;
if (mobj->radius != mobj->info->radius)
@@ -1260,7 +1240,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
WRITEFIXED(save->p, mobj->momx);
WRITEFIXED(save->p, mobj->momy);
WRITEFIXED(save->p, mobj->momz);
#ifdef PHOBOS_BUILD
#ifndef COMPAT_VANILLA
WRITEFIXED(save->p, mobj->pmomz);
#endif
}
@@ -1324,7 +1304,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
if (diff2 & MD2_CVMEM)
WRITEINT32(save->p, mobj->cvmem);
if (diff2 & MD2_SKIN)
WRITESKIN(save->p, (skinnum_t)((skin_t *)mobj->skin - skins));
WRITEUINT8(save->p, (UINT8)((skin_t *)mobj->skin - skins));
if (diff2 & MD2_COLOR)
WRITEUINT8(save->p, mobj->color);
if (diff2 & MD2_EXTVAL1)
@@ -2082,7 +2062,7 @@ static void LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
mobj->momx = READFIXED(save->p);
mobj->momy = READFIXED(save->p);
mobj->momz = READFIXED(save->p);
#ifdef PHOBOS_BUILD
#ifndef COMPAT_VANILLA
mobj->pmomz = READFIXED(save->p);
#endif
} // otherwise they're zero, and the memset took care of it
@@ -2185,7 +2165,7 @@ static void LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
if (diff2 & MD2_CVMEM)
mobj->cvmem = READINT32(save->p);
if (diff2 & MD2_SKIN)
mobj->skin = &skins[READSKIN(save->p)];
mobj->skin = &skins[READUINT8(save->p)];
if (diff2 & MD2_COLOR)
mobj->color = READUINT8(save->p);
if (diff2 & MD2_EXTVAL1)
+2 -2
View File
@@ -24,8 +24,8 @@
typedef struct
{
UINT8 skincolor;
skinnum_t skin;
skinnum_t botskin;
UINT8 skin;
UINT8 botskin;
UINT8 botcolor;
INT32 score;
INT32 lives;
+4 -4
View File
@@ -2074,14 +2074,14 @@ static void P_LoadReject(UINT8 *data, size_t rejectsize)
if (rejectsize < neededsize)
{
CorruptMapWarning(va("P_LoadReject: REJECT is %s byte%s too small. REJECT might be invalid!\n", sizeu1(neededsize - rejectsize), (neededsize - rejectsize) == 1 ? "" : "s"));
#ifdef PHOBOS_BUILD
rejectmatrix = NULL;
return;
#else
#ifdef COMPAT_VANILLA
// we can pad this and somewhat prevent desyncs
// probs not cool if the reject is from a completely different map
// but whatever
allocsize = neededsize;
#else
rejectmatrix = NULL;
return;
#endif
}
+1 -1
View File
@@ -4604,7 +4604,7 @@ void P_UpdateSpecials(void)
// POINT LIMIT
P_CheckPointLimit();
#ifndef PHOBOS_BUILD
#ifdef COMPAT_VANILLA
// Dynamic slopeness
// we run this here only when not joined midrace
// for vanilla compat reasons :chaosleep:
+1 -1
View File
@@ -651,7 +651,7 @@ void P_Ticker(boolean run)
if (run)
{
// Dynamic slopeness
#ifndef PHOBOS_BUILD
#ifdef COMPAT_VANILLA
if (midgamejoin) // only run here if we joined midgame to fix some desynchs
#endif
P_RunDynamicSlopes();
+6 -6
View File
@@ -42,8 +42,8 @@ INT32 numallskins = 0;
INT32 numlocalskins = 0;
skin_t skins[MAXSKINS];
skinnum_t skinstats[9][9][MAXSKINS];
skinnum_t skinstatscount[9][9] = {
UINT8 skinstats[9][9][MAXSKINS];
UINT8 skinstatscount[9][9] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
@@ -55,7 +55,7 @@ skinnum_t skinstatscount[9][9] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0}
};
skinnum_t skinsorted[MAXSKINS] = {};
UINT8 skinsorted[MAXSKINS] = {};
skin_t localskins[MAXLOCALSKINS] = {};
skin_t allskins[MAXSKINS+MAXLOCALSKINS] = {};
@@ -398,8 +398,8 @@ static UINT16 W_CheckForSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
static int skinSortFunc(const void *a, const void *b) // tbh i have no clue what the naming conventions for local functions are
{
int diff = 0;
const skinnum_t val_a = *(const skinnum_t *)a;
const skinnum_t val_b = *(const skinnum_t *)b;
const UINT8 val_a = *(const UINT8 *)a;
const UINT8 val_b = *(const UINT8 *)b;
const skin_t *in1 = &skins[val_a];
const skin_t *in2 = &skins[val_b];
@@ -474,7 +474,7 @@ static int skinSortFunc(const void *a, const void *b) // tbh i have no clue what
void sortSkinGrid(void)
{
//CONS_Printf("Sorting skin list (%d)...\n", cv_skinselectgridsort.value);
qs22j(skinsorted, numskins, sizeof(skinnum_t), skinSortFunc);
qs22j(skinsorted, numskins, sizeof(UINT8), skinSortFunc);
}
//
+3 -3
View File
@@ -71,9 +71,9 @@ extern INT32 numskins;
extern INT32 numlocalskins;
extern INT32 numallskins;
extern skin_t skins[MAXSKINS];
extern skinnum_t skinstats[9][9][MAXSKINS];
extern skinnum_t skinstatscount[9][9];
extern skinnum_t skinsorted[MAXSKINS];
extern UINT8 skinstats[9][9][MAXSKINS];
extern UINT8 skinstatscount[9][9];
extern UINT8 skinsorted[MAXSKINS];
extern skin_t localskins[MAXLOCALSKINS];
extern skin_t allskins[MAXSKINS+MAXLOCALSKINS];
+97 -26
View File
@@ -44,6 +44,7 @@
#ifdef HAVE_VALGRIND
#include "valgrind.h"
static boolean Z_calloc = false;
#include "memcheck.h"
#endif
#define ZONEID 0xa441d13d
@@ -179,7 +180,7 @@ void Z_Free(void *ptr)
if (block->user != NULL)
*block->user = NULL;
#ifdef HAVE_VALGRIND
#ifdef VALGRIND_DESTROY_MEMPOOL
VALGRIND_DESTROY_MEMPOOL(block);
#endif
block->prev->next = block->next;
@@ -245,6 +246,10 @@ void *Z_Malloc(size_t size, INT32 tag, void *user)
ptr = MEMORY(block);
I_Assert((intptr_t)ptr % alignof (max_align_t) == 0);
#ifdef HAVE_VALGRIND
Z_calloc = false;
#endif
block->next = head.next;
block->prev = &head;
head.next = block;
@@ -258,9 +263,8 @@ void *Z_Malloc(size_t size, INT32 tag, void *user)
#endif
block->size = size;
#ifdef HAVE_VALGRIND
#ifdef VALGRIND_CREATE_MEMPOOL
VALGRIND_CREATE_MEMPOOL(block, size, Z_calloc);
Z_calloc = false;
#endif
#ifdef PARANOIA
@@ -297,8 +301,7 @@ void *Z_Calloc2(size_t size, INT32 tag, void *user, const char *file, INT32 line
void *Z_Calloc(size_t size, INT32 tag, void *user)
#endif
{
#ifdef HAVE_VALGRIND
#ifdef VALGRIND_MEMPOOL_ALLOC
Z_calloc = true;
#endif
#ifdef ZDEBUG
@@ -510,16 +513,6 @@ void Z_CheckHeap(INT32 tag)
(void)tag;
#endif
#ifdef ZDEBUG
#define HeapError(msg) \
I_Error("Z_CheckHeap : %s:%d, block %u (owned by %s:%d) %s", \
file, line, blocknumon, block->ownerfile, block->ownerline, msg)
#else
#define HeapError(msg) \
I_Error("Z_CheckHeap : block %u (owned by %s:%d) %s", \
blocknumon, block->ownerfile, block->ownerline, msg)
#endif
for (block = head.next; block != &head; block = block->next)
{
blocknumon++;
@@ -528,30 +521,108 @@ void Z_CheckHeap(INT32 tag)
CONS_Debug(DBG_MEMORY, "block %u owned by %s:%d\n",
blocknumon, block->ownerfile, block->ownerline);
#endif
#ifdef HAVE_VALGRIND
if (RUNNING_ON_VALGRIND && !VALGRIND_MEMPOOL_EXISTS(block))
#ifdef VALGRIND_MEMPOOL_EXISTS
if (!VALGRIND_MEMPOOL_EXISTS(block))
{
HeapError(" should not exist");
I_Error("Z_CheckHeap :"
#ifdef ZDEBUG
" %s %d"
#endif
" block %u"
#ifdef ZDEBUG
" (owned by %s:%d)"
#endif
" should not exist"
#ifdef ZDEBUG
, file, line
#endif
, blocknumon
#ifdef ZDEBUG
, block->ownerfile, block->ownerline
#endif
);
}
#endif
if (block->user != NULL && *(block->user) != given)
{
HeapError(" doesn't have a proper user");
I_Error("Z_CheckHeap :"
#ifdef ZDEBUG
" %s %d"
#endif
" block %u"
#ifdef ZDEBUG
" (owned by %s:%d)"
#endif
" doesn't have a proper user"
#ifdef ZDEBUG
, file, line
#endif
, blocknumon
#ifdef ZDEBUG
, block->ownerfile, block->ownerline
#endif
);
}
if (block->next->prev != block)
{
HeapError(" lacks proper backlink");
I_Error("Z_CheckHeap :"
#ifdef ZDEBUG
" %s %d"
#endif
" block %u"
#ifdef ZDEBUG
" (owned by %s:%d)"
#endif
" lacks proper backlink"
#ifdef ZDEBUG
, file, line
#endif
, blocknumon
#ifdef ZDEBUG
, block->ownerfile, block->ownerline
#endif
);
}
if (block->prev->next != block)
{
HeapError(" lacks proper forward link");
I_Error("Z_CheckHeap :"
#ifdef ZDEBUG
" %s %d"
#endif
" block %u"
#ifdef ZDEBUG
" (owned by %s:%d)"
#endif
" lacks proper forward link"
#ifdef ZDEBUG
, file, line
#endif
, blocknumon
#ifdef ZDEBUG
, block->ownerfile, block->ownerline
#endif
);
}
#ifdef PARANOIA
if (block->id != ZONEID)
{
HeapError(" has the wrong ID");
I_Error("Z_CheckHeap :"
#ifdef ZDEBUG
" %s %d"
#endif
" block %u"
#ifdef ZDEBUG
" (owned by %s:%d)"
#endif
" have the wrong ID"
#ifdef ZDEBUG
, file, line
#endif
, blocknumon
#ifdef ZDEBUG
, block->ownerfile, block->ownerline
#endif
);
}
#endif
}
@@ -663,8 +734,8 @@ static void Command_Memfree_f(void)
Z_CheckHeap(-1);
CONS_Printf("\x82%s", M_GetText("Memory Info\n"));
CONS_Printf(M_GetText("Total heap used : %7s KB\n"), sizeu1((Z_TotalUsage()>>10) + LUA_MemoryUsage()));
CONS_Printf(M_GetText("Lua : %7s KB\n"), sizeu1(LUA_MemoryUsage())); // No longer uses z_zone memory but for consistency keep it here
CONS_Printf(M_GetText("Total heap used : %7s KB\n"), sizeu1(Z_TotalUsage()>>10));
CONS_Printf(M_GetText("Lua : %7s KB\n"), sizeu1(Z_TagUsage(PU_LUA)>>10));
CONS_Printf(M_GetText("Static : %7s KB\n"), sizeu1(Z_TagUsage(PU_STATIC)>>10));
CONS_Printf(M_GetText("Static (sound) : %7s KB\n"), sizeu1(Z_TagUsage(PU_SOUND)>>10));
CONS_Printf(M_GetText("Static (music) : %7s KB\n"), sizeu1(Z_TagUsage(PU_MUSIC)>>10));