Merge branch 'clipmidtex' into 'next'

Add clipmidtex property to lines and sides

See merge request STJr/SRB2!2434
This commit is contained in:
Lactozilla 2025-03-23 10:01:13 +00:00
commit 72b55fb1d6
9 changed files with 148 additions and 51 deletions

View file

@ -1,7 +1,7 @@
===============================================================================
Universal Doom Map Format Sonic Robo Blast 2 extensions v1.0 19.06.2024
Universal Doom Map Format Sonic Robo Blast 2 extensions v1.1 27.01.2025
Copyright (c) 2024 Sonic Team Junior
Copyright (c) 2025 Sonic Team Junior
uses Universal Doom Map Format Specification v1.1 as a template,
original document Copyright (c) 2009 James Haley.
Permission is granted to copy, distribute and/or modify this document
@ -80,6 +80,7 @@ Sonic Robo Blast 2 defines the following standardized fields:
noskew = <bool>; // Middle texture is not skewed.
midpeg = <bool>; // Middle texture is pegged.
midsolid = <bool>; // Middle texture is solid.
clipmidtex = <bool>; // Line's mid textures are clipped to floor and ceiling.
wrapmidtex = <bool>; // Line's mid textures are wrapped.
nonet = <bool>; // Special only takes effect in singleplayer games.
netonly = <bool>; // Special only takes effect in multiplayer games.
@ -146,6 +147,8 @@ Sonic Robo Blast 2 defines the following standardized fields:
light = <integer>; // Light level, relative to 'sector' light level. Default = 0.
lightabsolute = <bool>; // true = 'light' is an absolute value, ignoring 'sector' light level.
clipmidtex = <bool>; // Side's mid textures are clipped to floor and ceiling.
comment = <string>; // A comment. Implementors should attach no special
// semantic meaning to this field.
}
@ -308,6 +311,9 @@ Sonic Robo Blast 2 defines the following standardized fields:
Changelog
=======================================
1.1: 27.01.2025
Added clipmidtex property to lines and sides.
1.0: 19.02.2024
Initial version.

View file

@ -4564,6 +4564,7 @@ const char *const ML_LIST[] = {
"EFFECT6",
"BOUNCY",
"TFERLINE",
"CLIPMIDTEX",
NULL
};

View file

@ -104,13 +104,13 @@ typedef struct
//
// Solid, is an obstacle.
#define ML_IMPASSIBLE 1
#define ML_IMPASSIBLE 1<<0
// Blocks monsters only.
#define ML_BLOCKMONSTERS 2
#define ML_BLOCKMONSTERS 1<<1
// Backside will not be present at all if not two sided.
#define ML_TWOSIDED 4
#define ML_TWOSIDED 1<<2
// If a texture is pegged, the texture will have
// the end exposed to air held constant at the
@ -122,29 +122,31 @@ typedef struct
// top and bottom textures (use next to windows).
// upper texture unpegged
#define ML_DONTPEGTOP 8
#define ML_DONTPEGTOP 1<<3
// lower texture unpegged
#define ML_DONTPEGBOTTOM 16
#define ML_DONTPEGBOTTOM 1<<4
#define ML_SKEWTD 32
#define ML_SKEWTD 1<<5
// Don't let Knuckles climb on this line
#define ML_NOCLIMB 64
#define ML_NOCLIMB 1<<6
#define ML_NOSKEW 128
#define ML_MIDPEG 256
#define ML_MIDSOLID 512
#define ML_WRAPMIDTEX 1024
#define ML_NOSKEW 1<<7
#define ML_MIDPEG 1<<8
#define ML_MIDSOLID 1<<9
#define ML_WRAPMIDTEX 1<<10
#define ML_NETONLY 2048 // Apply effect only in netgames
#define ML_NONET 4096 // Apply effect only in single player games
#define ML_EFFECT6 8192
#define ML_NETONLY 1<<11 // Apply effect only in netgames
#define ML_NONET 1<<12 // Apply effect only in single player games
#define ML_EFFECT6 1<<13
// Bounce off walls!
#define ML_BOUNCY 16384
#define ML_BOUNCY 1<<14
#define ML_TFERLINE 32768
#define ML_TFERLINE 1<<15
#define ML_CLIPMIDTEX 1<<16
// Sector definition, from editing.
typedef struct

View file

@ -1072,7 +1072,7 @@ static void HWR_RenderMidtexture(INT32 gl_midtexture, float cliplow, float cliph
}
// The cut-off values of a linedef can always be constant, since every line has an absoulute front and or back sector
if (gl_curline->polyseg)
if (gl_curline->polyseg && ((gl_linedef->flags & ML_CLIPMIDTEX) || (gl_sidedef->flags & SIDEFLAG_CLIP_MIDTEX)) == 0)
{
lowcut = polybottom;
highcut = polytop;

View file

@ -213,6 +213,7 @@ enum side_e {
side_sector,
side_special,
side_repeatcnt,
side_clipmidtex,
side_light,
side_light_top,
side_light_mid,
@ -249,6 +250,7 @@ static const char *const side_opt[] = {
"sector",
"special",
"repeatcnt",
"clipmidtex",
"light",
"light_top",
"light_mid",
@ -1327,6 +1329,9 @@ static int side_get(lua_State *L)
case side_repeatcnt:
lua_pushinteger(L, side->repeatcnt);
return 1;
case side_clipmidtex:
lua_pushinteger(L, side->flags & SIDEFLAG_CLIP_MIDTEX);
return 1;
case side_light:
lua_pushinteger(L, side->light);
return 1;
@ -1453,6 +1458,12 @@ static int side_set(lua_State *L)
case side_repeatcnt:
side->repeatcnt = luaL_checkinteger(L, 3);
break;
case side_clipmidtex:
if (luaL_checkboolean(L, 3))
side->flags |= SIDEFLAG_CLIP_MIDTEX;
else
side->flags &= ~SIDEFLAG_CLIP_MIDTEX;
break;
case side_light:
side->light = luaL_checkinteger(L, 3);
break;

View file

@ -1637,6 +1637,8 @@ static UINT32 GetSideDiff(const side_t *si, const side_t *spawnsi)
diff |= LD_SDBOTSCALEY;
if (si->repeatcnt != spawnsi->repeatcnt)
diff |= LD_SDREPEATCNT;
if (si->flags != spawnsi->flags)
diff |= LD_SDFLAGS;
if (si->light != spawnsi->light)
diff |= LD_SDLIGHT;
if (si->light_top != spawnsi->light_top)
@ -1696,6 +1698,8 @@ static void ArchiveSide(save_t *save_p, const side_t *si, UINT32 diff)
P_WriteFixed(save_p, si->scaley_bottom);
if (diff & LD_SDREPEATCNT)
P_WriteINT16(save_p, si->repeatcnt);
if (diff & LD_SDFLAGS)
P_WriteUINT16(save_p, si->flags);
if (diff & LD_SDLIGHT)
P_WriteINT16(save_p, si->light);
if (diff & LD_SDTOPLIGHT)
@ -1769,7 +1773,7 @@ static void ArchiveLines(save_t *save_p)
if (diff & LD_DIFF2)
P_WriteUINT8(save_p, diff2);
if (diff & LD_FLAG)
P_WriteINT16(save_p, li->flags);
P_WriteUINT32(save_p, li->flags);
if (diff & LD_SPECIAL)
P_WriteINT16(save_p, li->special);
if (diff & LD_CLLCOUNT)
@ -1852,6 +1856,8 @@ static void UnArchiveSide(save_t *save_p, side_t *si)
si->scaley_bottom = P_ReadFixed(save_p);
if (diff & LD_SDREPEATCNT)
si->repeatcnt = P_ReadINT16(save_p);
if (diff & LD_SDFLAGS)
si->flags = P_ReadUINT16(save_p);
if (diff & LD_SDLIGHT)
si->light = P_ReadINT16(save_p);
if (diff & LD_SDTOPLIGHT)
@ -1893,7 +1899,7 @@ static void UnArchiveLines(save_t *save_p)
li = &lines[i];
if (diff & LD_FLAG)
li->flags = P_ReadINT16(save_p);
li->flags = P_ReadUINT32(save_p);
if (diff & LD_SPECIAL)
li->special = P_ReadINT16(save_p);
if (diff & LD_CLLCOUNT)

View file

@ -1367,6 +1367,7 @@ static void P_LoadSidedefs(UINT8 *data)
sd->scalex_top = sd->scalex_mid = sd->scalex_bottom = FRACUNIT;
sd->scaley_top = sd->scaley_mid = sd->scaley_bottom = FRACUNIT;
sd->flags = 0;
sd->light = sd->light_top = sd->light_mid = sd->light_bottom = 0;
sd->lightabsolute = sd->lightabsolute_top = sd->lightabsolute_mid = sd->lightabsolute_bottom = false;
@ -1980,6 +1981,8 @@ static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char
P_SetSidedefSector(i, atol(val));
else if (fastcmp(param, "repeatcnt"))
sides[i].repeatcnt = atol(val);
else if (fastcmp(param, "clipmidtex") && fastcmp("true", val))
sides[i].flags |= SIDEFLAG_CLIP_MIDTEX;
else if (fastcmp(param, "light"))
sides[i].light = atol(val);
else if (fastcmp(param, "light_top"))
@ -2078,10 +2081,10 @@ static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char
lines[i].flags |= ML_MIDPEG;
else if (fastcmp(param, "midsolid") && fastcmp("true", val))
lines[i].flags |= ML_MIDSOLID;
else if (fastcmp(param, "clipmidtex") && fastcmp("true", val))
lines[i].flags |= ML_CLIPMIDTEX;
else if (fastcmp(param, "wrapmidtex") && fastcmp("true", val))
lines[i].flags |= ML_WRAPMIDTEX;
/*else if (fastcmp(param, "effect6") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT6;*/
else if (fastcmp(param, "nonet") && fastcmp("true", val))
lines[i].flags |= ML_NONET;
else if (fastcmp(param, "netonly") && fastcmp("true", val))
@ -2668,6 +2671,8 @@ static void P_WriteTextmap(void)
fprintf(f, "midpeg = true;\n");
if (wlines[i].flags & ML_MIDSOLID)
fprintf(f, "midsolid = true;\n");
if (wlines[i].flags & ML_CLIPMIDTEX)
fprintf(f, "clipmidtex = true;\n");
if (wlines[i].flags & ML_WRAPMIDTEX)
fprintf(f, "wrapmidtex = true;\n");
if (wlines[i].flags & ML_NONET)
@ -2723,6 +2728,8 @@ static void P_WriteTextmap(void)
fprintf(f, "texturemiddle = \"%.*s\";\n", 8, textures[wsides[i].midtexture]->name);
if (wsides[i].repeatcnt != 0)
fprintf(f, "repeatcnt = %d;\n", wsides[i].repeatcnt);
if (wsides[i].flags & SIDEFLAG_CLIP_MIDTEX)
fprintf(f, "clipmidtex = true;\n");
if (wsides[i].light != 0)
fprintf(f, "light = %d;\n", wsides[i].light);
if (wsides[i].light_top != 0)
@ -3141,6 +3148,7 @@ static void P_LoadTextmap(void)
sd->bottomtexture = R_TextureNumForName("-");
sd->sector = NULL;
sd->repeatcnt = 0;
sd->flags = 0;
sd->light = sd->light_top = sd->light_mid = sd->light_bottom = 0;
sd->lightabsolute = sd->lightabsolute_top = sd->lightabsolute_mid = sd->lightabsolute_bottom = false;

View file

@ -588,7 +588,7 @@ typedef struct line_s
angle_t angle; // Precalculated angle between dx and dy
// Animation related.
INT16 flags;
UINT32 flags;
INT16 special;
taglist_t tags;
INT32 args[NUMLINEARGS];
@ -620,6 +620,12 @@ typedef struct line_s
struct pslope_s *midtexslope;
} line_t;
// Don't make available to Lua or I will find where you live
typedef enum
{
SIDEFLAG_CLIP_MIDTEX = 1 << 0, // Like the line counterpart, but only for this side.
} sideflags_t;
typedef struct
{
// add this to the calculated texture column
@ -628,13 +634,16 @@ typedef struct
// add this to the calculated texture top
fixed_t rowoffset;
// per-texture offsets for UDMF
// per-texture offsets
fixed_t offsetx_top, offsetx_mid, offsetx_bottom;
fixed_t offsety_top, offsety_mid, offsety_bottom;
fixed_t scalex_top, scalex_mid, scalex_bottom;
fixed_t scaley_top, scaley_mid, scaley_bottom;
// Rendering-related flags
UINT16 flags;
// per-wall lighting for UDMF
// TODO: implement per-texture lighting
INT16 light, light_top, light_mid, light_bottom;

View file

@ -232,6 +232,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
fixed_t wall_scaley;
fixed_t scalestep;
fixed_t scale1;
fixed_t texture_top, texture_bottom, texture_height;
boolean clipmidtex;
// Calculate light table.
// Use different light tables
@ -403,12 +405,17 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
else
back = backsector;
clipmidtex = (ldef->flags & ML_CLIPMIDTEX) || (sidedef->flags & SIDEFLAG_CLIP_MIDTEX);
texture_height = textureheight[texnum];
if (sidedef->repeatcnt)
repeats = 1 + sidedef->repeatcnt;
else if (ldef->flags & ML_WRAPMIDTEX)
{
fixed_t high, low;
height = FixedDiv(texture_height, wall_scaley);
if (front->ceilingheight > back->ceilingheight)
high = back->ceilingheight;
else
@ -419,8 +426,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
else
low = back->floorheight;
repeats = (high - low)/textureheight[texnum];
if ((high-low)%textureheight[texnum])
repeats = (high - low)/height;
if ((high-low)%height)
repeats++; // tile an extra time to fill the gap -- Monster Iestyn
}
else
@ -428,6 +435,33 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
for (times = 0; times < repeats; times++)
{
fixed_t left_top = 0, left_bottom = 0;
fixed_t right_top = 0, right_bottom = 0;
fixed_t top_step = 0, bottom_step = 0;
// Get left and right ends of wall for clipping it
if (clipmidtex)
{
// For this to work correctly with polyobjects, it needs to use its own back sector, rather than the seg's front sector
sector_t *sec_front = curline->polyseg ?
curline->polyseg->lines[0]->backsector :
frontsector;
// calculate both left ends
left_top = P_GetSectorCeilingZAt(sec_front, ds->leftpos.x, ds->leftpos.y) - viewz;
left_bottom = P_GetSectorFloorZAt(sec_front, ds->leftpos.x, ds->leftpos.y) - viewz;
// calculate right ends now
right_top = P_GetSectorCeilingZAt(sec_front, ds->rightpos.x, ds->rightpos.y) - viewz;
right_bottom = P_GetSectorFloorZAt(sec_front, ds->rightpos.x, ds->rightpos.y) - viewz;
top_step = (right_top - left_top) / range;
bottom_step = (right_bottom - left_bottom) / range;
left_top += top_step * (x1 - ds->x1);
left_bottom += bottom_step * (x1 - ds->x1);
}
if (times > 0)
{
rw_scalestep = scalestep;
@ -441,17 +475,17 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
}
}
dc_texheight = textureheight[texnum]>>FRACBITS;
dc_texheight = texture_height>>FRACBITS;
// draw the columns
for (dc_x = x1; dc_x <= x2; dc_x++)
{
dc_texturemid = ds->maskedtextureheight[dc_x];
if (curline->linedef->flags & ML_MIDPEG)
dc_texturemid += (textureheight[texnum])*times + textureheight[texnum];
if (ldef->flags & ML_MIDPEG)
dc_texturemid += texture_height*times + texture_height;
else
dc_texturemid -= (textureheight[texnum])*times;
dc_texturemid -= texture_height*times;
// Check for overflows first
overflow_test = (INT64)centeryfrac - (((INT64)dc_texturemid*spryscale)>>FRACBITS);
@ -468,29 +502,53 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
}
}
spryscale += rw_scalestep;
if (clipmidtex)
{
left_top += top_step;
left_bottom += bottom_step;
}
continue;
}
// calculate lighting
texture_top = dc_texturemid;
texture_bottom = texture_top - texture_height;
// If the texture is meant to be clipped
if (clipmidtex)
{
texture_top = min(FixedMul(left_top, wall_scaley), texture_top);
texture_bottom = max(FixedMul(left_bottom, wall_scaley), texture_bottom);
left_top += top_step;
left_bottom += bottom_step;
}
// NB: sprtopscreen needs to start where dc_texturemid does, so that R_DrawMaskedColumn works correctly.
// windowtop however is set so that the column gets clipped properly.
sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
realbot = centeryfrac - FixedMul(texture_bottom, spryscale);
// set wall bounds if necessary
if (dc_numlights || clipmidtex)
{
windowtop = centeryfrac - FixedMul(texture_top, spryscale);
windowbottom = realbot;
}
dc_iscale = FixedMul(ds->invscale[dc_x], wall_scaley);
col = R_GetColumn(texnum, maskedtexturecol[dc_x] >> FRACBITS);
// draw light list if there is one
if (dc_numlights)
{
lighttable_t **xwalllights;
sprtopscreen = windowtop = (centeryfrac - FixedMul(dc_texturemid, spryscale));
realbot = FixedMul(textureheight[texnum], spryscale) + sprtopscreen;
dc_iscale = FixedMul(ds->invscale[dc_x], wall_scaley);
windowbottom = realbot;
// draw the texture
col = R_GetColumn(texnum, maskedtexturecol[dc_x] >> FRACBITS);
for (i = 0; i < dc_numlights; i++)
{
lighttable_t **xwalllights;
rlight = &dc_lightlist[i];
if ((rlight->flags & FOF_NOSHADE))
if (rlight->flags & FOF_NOSHADE)
continue;
if (rlight->lightnum < 0)
@ -500,7 +558,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
else
xwalllights = scalelight[rlight->lightnum];
pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
pindex = FixedMul(FixedMul(spryscale, wall_scaley), LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
if (pindex >= MAXLIGHTSCALE)
pindex = MAXLIGHTSCALE - 1;
@ -545,7 +603,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
}
// calculate lighting
pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
pindex = FixedMul(FixedMul(spryscale, wall_scaley), LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
if (pindex >= MAXLIGHTSCALE)
pindex = MAXLIGHTSCALE - 1;
@ -555,11 +613,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
if (frontsector->extra_colormap)
dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
dc_iscale = FixedMul(ds->invscale[dc_x], wall_scaley);
// draw the texture
col = R_GetColumn(texnum, maskedtexturecol[dc_x] >> FRACBITS);
colfunc_2s(col, lengthcol);
spryscale += rw_scalestep;