diff --git a/src/doomdata.h b/src/doomdata.h index 276e03297..b60338cc3 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -103,48 +103,51 @@ typedef struct // LineDef attributes. // -// Solid, is an obstacle. -#define ML_IMPASSIBLE 1 +enum +{ + // Solid, is an obstacle. + ML_IMPASSIBLE = 1<<0, -// Blocks monsters only. -#define ML_BLOCKMONSTERS 2 + // Blocks monsters only. + ML_BLOCKMONSTERS = 1<<1, -// Backside will not be present at all if not two sided. -#define ML_TWOSIDED 4 + // Backside will not be present at all if not two sided. + ML_TWOSIDED = 1<<2, -// If a texture is pegged, the texture will have -// the end exposed to air held constant at the -// top or bottom of the texture (stairs or pulled -// down things) and will move with a height change -// of one of the neighbor sectors. -// Unpegged textures allways have the first row of -// the texture at the top pixel of the line for both -// top and bottom textures (use next to windows). + // If a texture is pegged, the texture will have + // the end exposed to air held constant at the + // top or bottom of the texture (stairs or pulled + // down things) and will move with a height change + // of one of the neighbor sectors. + // Unpegged textures allways have the first row of + // the texture at the top pixel of the line for both + // top and bottom textures (use next to windows). -// upper texture unpegged -#define ML_DONTPEGTOP 8 + // upper texture unpegged + ML_DONTPEGTOP = 1<<3, -// lower texture unpegged -#define ML_DONTPEGBOTTOM 16 + // lower texture unpegged + ML_DONTPEGBOTTOM = 1<<4, -#define ML_SKEWTD 32 + ML_SKEWTD = 1<<5, -// Don't let Knuckles climb on this line -#define ML_NOCLIMB 64 + // Don't let Knuckles climb on this line + ML_NOCLIMB = 1<<6, -#define ML_NOSKEW 128 -#define ML_MIDPEG 256 -#define ML_MIDSOLID 512 -#define ML_WRAPMIDTEX 1024 + ML_NOSKEW = 1<<7, + ML_MIDPEG = 1<<8, + ML_MIDSOLID = 1<<9, + 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 + ML_NETONLY = 1<<11, // Apply effect only in netgames + ML_NONET = 1<<12, // Apply effect only in single player games + ML_EFFECT6 = 1<<13, -// Bounce off walls! -#define ML_BOUNCY 16384 + // Bounce off walls! + ML_BOUNCY = 1<<14, -#define ML_TFERLINE 32768 + ML_TFERLINE = 1<<15 +}; // Sector definition, from editing. typedef struct diff --git a/src/p_setup.c b/src/p_setup.c index d2e4f50f7..e978cbbb4 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1366,6 +1366,15 @@ 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; + + for (unsigned j = 0; j < NUM_WALL_OVERLAYS; j++) + { + sd->overlays[j].texture = R_TextureNumForName("-"); + sd->overlays[j].offsetx = sd->overlays[j].offsety = 0; + sd->overlays[j].scalex = sd->overlays[j].scaley = FRACUNIT; + } + P_SetSidedefSector(i, (UINT16)SHORT(msd->sector)); // Special info stored in texture fields! @@ -1896,6 +1905,24 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char } } +static void ParseTextmapSidedefOverlay(unsigned which, UINT32 i, const char *param, const char *val) +{ + if (fastcmp(param, "texture")) + sides[i].overlays[which].texture = R_TextureNumForName(val); + else if (fastcmp(param, "offsetx")) + sides[i].overlays[which].offsetx = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "offsety")) + sides[i].overlays[which].offsety = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scalex")) + sides[i].overlays[which].scalex = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scaley")) + sides[i].overlays[which].scaley = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "noskew") && fastcmp("true", val)) + sides[i].flags |= GET_SIDEFLAG_EDGENOSKEW(which); + else if (fastcmp(param, "wrap") && fastcmp("true", val)) + sides[i].flags |= GET_SIDEFLAG_EDGEWRAP(which); +} + static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char *val) { if (fastcmp(param, "offsetx")) @@ -1936,6 +1963,19 @@ 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); + // Oh God there's a total of 28 fields to read. Uhhh + // Okay, so ParseTextmapSidedefOverlay handles the parsing of all the parameters edges have. + else if (fastncmp(param, "edge_", 5) && strlen(param) > 5) + { + if (fastncmp(param, "edge_top_upper_", 15) && strlen(param) > 15) + ParseTextmapSidedefOverlay(EDGE_TEXTURE_TOP_UPPER, i, param + 15, val); + else if (fastncmp(param, "edge_top_lower_", 15) && strlen(param) > 15) + ParseTextmapSidedefOverlay(EDGE_TEXTURE_TOP_LOWER, i, param + 15, val); + else if (fastncmp(param, "edge_bottom_upper_", 18) && strlen(param) > 18) + ParseTextmapSidedefOverlay(EDGE_TEXTURE_BOTTOM_UPPER, i, param + 18, val); + else if (fastncmp(param, "edge_bottom_lower_", 18) && strlen(param) > 18) + ParseTextmapSidedefOverlay(EDGE_TEXTURE_BOTTOM_LOWER, i, param + 18, val); + } } static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char *val) @@ -2020,8 +2060,6 @@ static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char lines[i].flags |= ML_MIDSOLID; 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)) @@ -2190,6 +2228,24 @@ typedef struct mapthing_t *angleanchor; } sectorspecialthings_t; +static void WriteTextmapEdgeTexture(const char *prefix, unsigned i, side_t *side, FILE *f) +{ + if (side->overlays[i].texture > 0 && side->overlays[i].texture < numtextures) + fprintf(f, "%s""texture = \"%.*s\";\n", prefix, 8, textures[side->overlays[i].texture]->name); + if (side->overlays[i].offsetx != 0) + fprintf(f, "%s""offsetx = %f;\n", prefix, FIXED_TO_FLOAT(side->overlays[i].offsetx)); + if (side->overlays[i].offsety != 0) + fprintf(f, "%s""offsety = %f;\n", prefix, FIXED_TO_FLOAT(side->overlays[i].offsety)); + if (side->overlays[i].scalex != FRACUNIT) + fprintf(f, "%s""scalex = %f;\n", prefix, FIXED_TO_FLOAT(side->overlays[i].scalex)); + if (side->overlays[i].scaley != FRACUNIT) + fprintf(f, "%s""scaley = %f;\n", prefix, FIXED_TO_FLOAT(side->overlays[i].scaley)); + if (side->flags & GET_SIDEFLAG_EDGENOSKEW(i)) + fprintf(f, "%s""noskew = true;\n", prefix); + if (side->flags & GET_SIDEFLAG_EDGEWRAP(i)) + fprintf(f, "%s""wrap = true;\n", prefix); +} + static void P_WriteTextmap(void) { size_t i, j; @@ -2654,6 +2710,10 @@ 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); + WriteTextmapEdgeTexture("edge_top_upper_", EDGE_TEXTURE_TOP_UPPER, &wsides[i], f); + WriteTextmapEdgeTexture("edge_top_lower_", EDGE_TEXTURE_TOP_LOWER, &wsides[i], f); + WriteTextmapEdgeTexture("edge_bottom_upper_", EDGE_TEXTURE_BOTTOM_UPPER, &wsides[i], f); + WriteTextmapEdgeTexture("edge_bottom_lower_", EDGE_TEXTURE_BOTTOM_LOWER, &wsides[i], f); fprintf(f, "}\n"); fprintf(f, "\n"); } @@ -3041,9 +3101,16 @@ static void P_LoadTextmap(void) sd->offsety_top = sd->offsety_mid = sd->offsety_bottom = 0; sd->scalex_top = sd->scalex_mid = sd->scalex_bottom = FRACUNIT; sd->scaley_top = sd->scaley_mid = sd->scaley_bottom = FRACUNIT; + sd->flags = 0; sd->toptexture = R_TextureNumForName("-"); sd->midtexture = R_TextureNumForName("-"); sd->bottomtexture = R_TextureNumForName("-"); + for (unsigned j = 0; j < NUM_WALL_OVERLAYS; j++) + { + sd->overlays[j].texture = R_TextureNumForName("-"); + sd->overlays[j].offsetx = sd->overlays[j].offsety = 0; + sd->overlays[j].scalex = sd->overlays[j].scaley = FRACUNIT; + } sd->sector = NULL; sd->repeatcnt = 0; diff --git a/src/r_defs.h b/src/r_defs.h index 16c660b01..3427d7143 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -555,6 +555,21 @@ typedef enum #define NO_SIDEDEF 0xFFFFFFFF +enum +{ + EDGE_TEXTURE_TOP_UPPER, + EDGE_TEXTURE_TOP_LOWER, + EDGE_TEXTURE_BOTTOM_UPPER, + EDGE_TEXTURE_BOTTOM_LOWER, + + NUM_WALL_OVERLAYS +}; + +#define IS_TOP_EDGE_TEXTURE(i) ((i) == EDGE_TEXTURE_TOP_UPPER || (i) == EDGE_TEXTURE_TOP_LOWER) +#define IS_BOTTOM_EDGE_TEXTURE(i) (!IS_TOP_EDGE_TEXTURE(i)) +#define IS_UPPER_EDGE_TEXTURE(i) ((i) == EDGE_TEXTURE_TOP_UPPER || (i) == EDGE_TEXTURE_BOTTOM_UPPER) +#define IS_LOWER_EDGE_TEXTURE(i) (!IS_UPPER_EDGE_TEXTURE(i)) + typedef struct line_s { // Vertices, from v1 to v2. @@ -595,6 +610,23 @@ typedef struct line_s UINT32 secportal; // transferred sector portal } line_t; +// Don't make available to Lua or I will find where you live +enum +{ + SIDEFLAG_EDGENOSKEW1 = 1<<7, + SIDEFLAG_EDGENOSKEW2 = 1<<8, + SIDEFLAG_EDGENOSKEW3 = 1<<9, + SIDEFLAG_EDGENOSKEW4 = 1<<10, + + SIDEFLAG_EDGEWRAP1 = 1<<11, + SIDEFLAG_EDGEWRAP2 = 1<<12, + SIDEFLAG_EDGEWRAP3 = 1<<13, + SIDEFLAG_EDGEWRAP4 = 1<<14 +}; + +#define GET_SIDEFLAG_EDGENOSKEW(which) (SIDEFLAG_EDGENOSKEW1<<(which)) +#define GET_SIDEFLAG_EDGEWRAP(which) (SIDEFLAG_EDGEWRAP1<<(which)) + typedef struct { // add this to the calculated texture column @@ -610,10 +642,19 @@ typedef struct fixed_t scalex_top, scalex_mid, scalex_bottom; fixed_t scaley_top, scaley_mid, scaley_bottom; + UINT16 flags; + // Texture indices. // We do not maintain names here. INT32 toptexture, bottomtexture, midtexture; + // Upper and lower overlays for top and bottom textures + struct { + INT32 texture; + fixed_t offsetx, offsety; + fixed_t scalex, scaley; + } overlays[NUM_WALL_OVERLAYS]; + // Linedef the sidedef belongs to line_t *line; @@ -808,6 +849,7 @@ typedef struct drawseg_s INT16 *sprtopclip; INT16 *sprbottomclip; fixed_t *maskedtexturecol; + fixed_t *maskedtextureheight; // For handling sloped midtextures fixed_t *invscale; struct visplane_s *ffloorplanes[MAXFFLOORS]; @@ -819,8 +861,6 @@ typedef struct drawseg_s UINT8 portalpass; // if > 0 and <= portalrender, do not affect sprite clipping - fixed_t maskedtextureheight[MAXVIDWIDTH]; // For handling sloped midtextures - vertex_t leftpos, rightpos; // Used for rendering FOF walls with slopes } drawseg_t; diff --git a/src/r_segs.c b/src/r_segs.c index bd4869bdc..face74ce2 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -38,6 +38,9 @@ static boolean maskedtexture; static INT32 toptexture, bottomtexture, midtexture; static INT32 numthicksides, numbackffloors; +static boolean hasoverlaytexture; +static INT32 overlaytexture[NUM_WALL_OVERLAYS]; + angle_t rw_normalangle; // angle to line origin angle_t rw_angle1; @@ -61,6 +64,14 @@ static fixed_t rw_toptexturescalex, rw_toptexturescaley; static fixed_t rw_bottomtexturescalex, rw_bottomtexturescaley; static fixed_t rw_invmidtexturescalex, rw_invtoptexturescalex, rw_invbottomtexturescalex; +static struct +{ + fixed_t mid, slide; + fixed_t back, backslide; + fixed_t scalex, scaley, invscalex; + fixed_t offsetx, offsety; +} rw_overlay[NUM_WALL_OVERLAYS]; + // Lactozilla: 3D floor clipping static boolean rw_floormarked = false; static boolean rw_ceilingmarked = false; @@ -79,6 +90,13 @@ static fixed_t *maskedtextureheight = NULL; static fixed_t *thicksidecol = NULL; static fixed_t *invscale = NULL; +static boolean texcoltables; + +// TODO: Allocate dynamically +static fixed_t overlaytextureheight[NUM_WALL_OVERLAYS][MAXVIDWIDTH]; +static fixed_t overlaytexturecol[NUM_WALL_OVERLAYS][MAXVIDWIDTH]; +static INT16 overlayopening[2][MAXVIDWIDTH]; + //SoM: 3/23/2000: Use boom opening limit removal static size_t numopenings; static INT16 *openings, *lastopening; @@ -144,13 +162,93 @@ transnum_t R_GetLinedefTransTable(fixed_t alpha) return (20*(FRACUNIT - alpha - 1) + FRACUNIT) >> (FRACBITS+1); } +// Setup lighting based on the presence/lack-of 3D floors. +static void R_PrepareMaskedSegLightlist(drawseg_t *ds, INT32 range) +{ + lightlist_t *light; + r_lightlist_t *rlight; + INT32 lightnum; + + dc_numlights = 0; + + if (frontsector->numlights) + { + dc_numlights = frontsector->numlights; + if (dc_numlights >= dc_maxlights) + { + dc_maxlights = dc_numlights; + dc_lightlist = Z_Realloc(dc_lightlist, sizeof (*dc_lightlist) * dc_maxlights, PU_STATIC, NULL); + } + + for (INT32 i = 0; i < dc_numlights; i++) + { + fixed_t leftheight, rightheight; + light = &frontsector->lightlist[i]; + rlight = &dc_lightlist[i]; + leftheight = P_GetLightZAt(light, ds-> leftpos.x, ds-> leftpos.y); + rightheight = P_GetLightZAt(light, ds->rightpos.x, ds->rightpos.y); + + leftheight -= viewz; + rightheight -= viewz; + + rlight->height = (centeryfrac) - FixedMul(leftheight , ds->scale1); + rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2); + rlight->heightstep = (rlight->heightstep-rlight->height)/(range); + //if (x1 > ds->x1) + //rlight->height -= (x1 - ds->x1)*rlight->heightstep; + rlight->startheight = rlight->height; // keep starting value here to reset for each repeat + rlight->lightlevel = *light->lightlevel; + rlight->extra_colormap = *light->extra_colormap; + rlight->flags = light->flags; + + if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) + || (rlight->flags & FOF_FOG) + || (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG))) + lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT); + else + lightnum = LIGHTLEVELS - 1; + + if (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG)) + ; + else if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + rlight->lightnum = lightnum; + } + } + else + { + if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) + || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); + else + lightnum = LIGHTLEVELS - 1; + + if (colfunc == colfuncs[COLDRAWFUNC_FOG] + || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) + ; + else if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + } +} + void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) { size_t pindex; column_t *col; - INT32 lightnum, texnum, i; + INT32 texnum, i; fixed_t height, realbot; - lightlist_t *light; r_lightlist_t *rlight; void (*colfunc_2s)(column_t *); line_t *ldef; @@ -236,77 +334,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) } // Setup lighting based on the presence/lack-of 3D floors. - dc_numlights = 0; - if (frontsector->numlights) - { - dc_numlights = frontsector->numlights; - if (dc_numlights >= dc_maxlights) - { - dc_maxlights = dc_numlights; - dc_lightlist = Z_Realloc(dc_lightlist, sizeof (*dc_lightlist) * dc_maxlights, PU_STATIC, NULL); - } - - for (i = 0; i < dc_numlights; i++) - { - fixed_t leftheight, rightheight; - light = &frontsector->lightlist[i]; - rlight = &dc_lightlist[i]; - leftheight = P_GetLightZAt(light, ds-> leftpos.x, ds-> leftpos.y); - rightheight = P_GetLightZAt(light, ds->rightpos.x, ds->rightpos.y); - - leftheight -= viewz; - rightheight -= viewz; - - rlight->height = (centeryfrac) - FixedMul(leftheight , ds->scale1); - rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2); - rlight->heightstep = (rlight->heightstep-rlight->height)/(range); - //if (x1 > ds->x1) - //rlight->height -= (x1 - ds->x1)*rlight->heightstep; - rlight->startheight = rlight->height; // keep starting value here to reset for each repeat - rlight->lightlevel = *light->lightlevel; - rlight->extra_colormap = *light->extra_colormap; - rlight->flags = light->flags; - - if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) - || (rlight->flags & FOF_FOG) - || (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG))) - lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT); - else - lightnum = LIGHTLEVELS - 1; - - if (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG)) - ; - else if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; - - rlight->lightnum = lightnum; - } - } - else - { - if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) - || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) - lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); - else - lightnum = LIGHTLEVELS - 1; - - if (colfunc == colfuncs[COLDRAWFUNC_FOG] - || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) - ; - else if (curline->v1->y == curline->v2->y) - lightnum--; - else if (curline->v1->x == curline->v2->x) - lightnum++; - - if (lightnum < 0) - walllights = scalelight[0]; - else if (lightnum >= LIGHTLEVELS) - walllights = scalelight[LIGHTLEVELS - 1]; - else - walllights = scalelight[lightnum]; - } + R_PrepareMaskedSegLightlist(ds, range); maskedtexturecol = ds->maskedtexturecol; @@ -489,6 +517,268 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) colfunc = colfuncs[BASEDRAWFUNC]; } +// Renders a texture right on top of the wall texture +static void R_RenderExtraTexture(drawseg_t *ds, unsigned which, INT32 x1, INT32 x2) +{ + size_t pindex; + column_t *col; + INT32 i; + fixed_t height, realbot; + r_lightlist_t *rlight; + void (*colfunc_2s)(column_t *); + INT32 times, repeats; + INT64 overflow_test; + INT32 range; + + fixed_t wall_scaley; + fixed_t scalestep; + fixed_t scale1; + + fixed_t *extraheight = overlaytextureheight[which]; + fixed_t *texturecol = overlaytexturecol[which]; + INT32 texnum = overlaytexture[which]; + + UINT8 vertflip = textures[texnum]->flip & 2; + + // Calculate light table. + // Use different light tables + // for horizontal / vertical / diagonal. Diagonal? + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + windowbottom = windowtop = sprbotscreen = INT32_MAX; + + wall_scaley = rw_overlay[which].scaley; + if (wall_scaley < 0) + { + wall_scaley = -wall_scaley; + vertflip = !vertflip; + } + + scalestep = FixedDiv(ds->scalestep, wall_scaley); + scale1 = FixedDiv(ds->scale1, wall_scaley); + + range = max(ds->x2-ds->x1, 1); + rw_scalestep = scalestep; + spryscale = scale1 + (x1 - ds->x1)*rw_scalestep; + + // Texture must be cached before setting colfunc_2s, + // otherwise texture[texnum]->holes may be false when it shouldn't be + R_CheckTextureCache(texnum); + + // handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures + // are not stored per-column with post info in SRB2 + if (textures[texnum]->holes) + { + if (vertflip) // vertically flipped? + { + colfunc_2s = R_DrawFlippedMaskedColumn; + lengthcol = textures[texnum]->height; + } + else + colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture + } + else + { + // FIXME: Composite textures don't get flipped vertically + colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info) + lengthcol = textures[texnum]->height; + } + + // Setup lighting based on the presence/lack-of 3D floors. + R_PrepareMaskedSegLightlist(ds, range); + + mfloorclip = overlayopening[1]; + mceilingclip = overlayopening[0]; + + if (sidedef->flags & GET_SIDEFLAG_EDGEWRAP(which)) + { + fixed_t high, low; + + if (backsector) + { + if (IS_TOP_EDGE_TEXTURE(which)) + { + high = max( + P_GetSectorCeilingZAt(frontsector, ds_p->leftpos.x, ds_p->leftpos.y), + P_GetSectorCeilingZAt(frontsector, ds_p->rightpos.x, ds_p->rightpos.y) + ); + low = min( + P_GetSectorCeilingZAt(backsector, ds_p->leftpos.x, ds_p->leftpos.y), + P_GetSectorCeilingZAt(backsector, ds_p->rightpos.x, ds_p->rightpos.y) + ); + } + else + { + high = max( + P_GetSectorFloorZAt(frontsector, ds_p->leftpos.x, ds_p->leftpos.y), + P_GetSectorFloorZAt(frontsector, ds_p->rightpos.x, ds_p->rightpos.y) + ); + low = min( + P_GetSectorFloorZAt(backsector, ds_p->leftpos.x, ds_p->leftpos.y), + P_GetSectorFloorZAt(backsector, ds_p->rightpos.x, ds_p->rightpos.y) + ); + } + } + else + { + high = max( + P_GetSectorCeilingZAt(frontsector, ds_p->leftpos.x, ds_p->leftpos.y), + P_GetSectorCeilingZAt(frontsector, ds_p->rightpos.x, ds_p->rightpos.y) + ); + low = min( + P_GetSectorFloorZAt(backsector, ds_p->leftpos.x, ds_p->leftpos.y), + P_GetSectorFloorZAt(backsector, ds_p->rightpos.x, ds_p->rightpos.y) + ); + } + + repeats = (high - low)/textureheight[texnum]; + if ((high-low)%textureheight[texnum]) + repeats++; // tile an extra time to fill the gap -- Monster Iestyn + } + else + repeats = 1; + + for (times = 0; times < repeats; times++) + { + if (times > 0) + { + rw_scalestep = scalestep; + spryscale = scale1 + (x1 - ds->x1)*rw_scalestep; + + // reset all lights to their starting heights + for (i = 0; i < dc_numlights; i++) + { + rlight = &dc_lightlist[i]; + rlight->height = rlight->startheight; + } + } + + dc_texheight = textureheight[texnum]>>FRACBITS; + + // draw the columns + for (dc_x = x1; dc_x <= x2; dc_x++) + { + dc_texturemid = extraheight[dc_x]; + + if (IS_LOWER_EDGE_TEXTURE(which)) + dc_texturemid += (textureheight[texnum])*times + textureheight[texnum]; + else + dc_texturemid -= (textureheight[texnum])*times; + + // Check for overflows first + overflow_test = (INT64)centeryfrac - (((INT64)dc_texturemid*spryscale)>>FRACBITS); + if (overflow_test < 0) overflow_test = -overflow_test; + if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) + { + // Eh, no, go away, don't waste our time + if (dc_numlights) + { + for (i = 0; i < dc_numlights; i++) + { + rlight = &dc_lightlist[i]; + rlight->height += rlight->heightstep; + } + } + spryscale += rw_scalestep; + continue; + } + + // calculate lighting + if (dc_numlights) + { + lighttable_t **xwalllights; + + sprbotscreen = INT32_MAX; + 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 = (column_t *)((UINT8 *)R_GetColumn(texnum, (texturecol[dc_x] >> FRACBITS)) - 3); + + for (i = 0; i < dc_numlights; i++) + { + rlight = &dc_lightlist[i]; + + if ((rlight->flags & FOF_NOSHADE)) + continue; + + if (rlight->lightnum < 0) + xwalllights = scalelight[0]; + else if (rlight->lightnum >= LIGHTLEVELS) + xwalllights = scalelight[LIGHTLEVELS-1]; + else + xwalllights = scalelight[rlight->lightnum]; + + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; + + if (pindex >= MAXLIGHTSCALE) + pindex = MAXLIGHTSCALE - 1; + + if (rlight->extra_colormap) + rlight->rcolormap = rlight->extra_colormap->colormap + (xwalllights[pindex] - colormaps); + else + rlight->rcolormap = xwalllights[pindex]; + + height = rlight->height; + rlight->height += rlight->heightstep; + + if (height <= windowtop) + { + dc_colormap = rlight->rcolormap; + continue; + } + + windowbottom = height; + if (windowbottom >= realbot) + { + windowbottom = realbot; + colfunc_2s(col); + for (i++; i < dc_numlights; i++) + { + rlight = &dc_lightlist[i]; + rlight->height += rlight->heightstep; + } + + continue; + } + colfunc_2s(col); + windowtop = windowbottom + 1; + dc_colormap = rlight->rcolormap; + } + windowbottom = realbot; + if (windowtop < windowbottom) + colfunc_2s(col); + + spryscale += rw_scalestep; + continue; + } + + // calculate lighting + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; + + if (pindex >= MAXLIGHTSCALE) + pindex = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[pindex]; + + 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 = (column_t *)((UINT8 *)R_GetColumn(texnum, (texturecol[dc_x] >> FRACBITS)) - 3); + colfunc_2s(col); + + spryscale += rw_scalestep; + } + } +} + // Loop through R_DrawMaskedColumn calls static void R_DrawRepeatMaskedColumn(column_t *col) { @@ -790,8 +1080,9 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) // Texture must be cached before setting colfunc_2s, // otherwise texture[texnum]->holes may be false when it shouldn't be R_CheckTextureCache(texnum); - //faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures - // are not stored per-column with post info anymore in Doom Legacy + + // handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures + // are not stored per-column with post info in SRB2 if (textures[texnum]->holes) { if (textures[texnum]->flip & 2) // vertically flipped? @@ -1077,6 +1368,15 @@ static void R_RenderSegLoop (void) INT32 bottom; INT32 i; + fixed_t oldoverlaycolumn[NUM_WALL_OVERLAYS]; + fixed_t overlaycolumn[NUM_WALL_OVERLAYS]; + + if (hasoverlaytexture) + { + for (i = 0; i < NUM_WALL_OVERLAYS; i++) + oldoverlaycolumn[i] = -1; + } + for (; rw_x < rw_stopx; rw_x++) { // mark floor / ceiling areas @@ -1469,6 +1769,47 @@ static void R_RenderSegLoop (void) oldtexturecolumn = texturecolumn; } + if (hasoverlaytexture) + { +#define ALIGN_UPPER(which) \ + if (overlaytexture[which]) \ + overlaytextureheight[which][rw_x] = max(rw_overlay[which].mid, rw_overlay[which].back) +#define ALIGN_LOWER(which) \ + if (overlaytexture[which]) \ + overlaytextureheight[which][rw_x] = min(rw_overlay[which].mid, rw_overlay[which].back) + + ALIGN_UPPER(EDGE_TEXTURE_TOP_UPPER); + ALIGN_LOWER(EDGE_TEXTURE_TOP_LOWER); + ALIGN_UPPER(EDGE_TEXTURE_BOTTOM_UPPER); + ALIGN_LOWER(EDGE_TEXTURE_BOTTOM_LOWER); + +#undef ALIGN_UPPER +#undef ALIGN_LOWER + + for (i = 0; i < NUM_WALL_OVERLAYS; i++) + { + fixed_t offset; + + if (!overlaytexture[i]) + continue; + + offset = sidedef->textureoffset + rw_overlay[i].offsetx; + if (rw_overlay[i].scalex < 0) + offset = -offset; + + overlaycolumn[i] = FixedDiv(textureoffset, rw_overlay[i].invscalex); + overlaytexturecol[i][rw_x] = overlaycolumn[i] + offset; + + if (oldoverlaycolumn[i] != -1) + { + rw_overlay[i].mid += FixedMul(rw_overlay[i].slide, oldoverlaycolumn[i]-overlaycolumn[i]); + rw_overlay[i].back += FixedMul(rw_overlay[i].backslide, oldoverlaycolumn[i]-overlaycolumn[i]); + } + + oldoverlaycolumn[i] = overlaycolumn[i]; + } + } + if (invscale) invscale[rw_x] = 0xffffffffu / (unsigned)rw_scale; @@ -1560,7 +1901,7 @@ static void R_AllocClippingTables(size_t range) static void R_AllocTextureColumnTables(size_t range) { size_t pos = curtexturecolumntable - texturecolumntable; - size_t need = range * 3; + size_t need = range * 4; if (pos + need < texturecolumntablesize) return; @@ -1581,12 +1922,109 @@ static void R_AllocTextureColumnTables(size_t range) for (drawseg_t *ds = drawsegs; ds < ds_p; ds++) { // Check if it's in range of the tables - if (ds->maskedtexturecol + ds->x1 >= oldtable && ds->maskedtexturecol + ds->x1 <= oldlast) - ds->maskedtexturecol = (ds->maskedtexturecol - oldtable) + texturecolumntable; - if (ds->thicksidecol + ds->x1 >= oldtable && ds->thicksidecol + ds->x1 <= oldlast) - ds->thicksidecol = (ds->thicksidecol - oldtable) + texturecolumntable; - if (ds->invscale + ds->x1 >= oldtable && ds->invscale + ds->x1 <= oldlast) - ds->invscale = (ds->invscale - oldtable) + texturecolumntable; +#define CHECK(which) \ + if (which + ds->x1 >= oldtable && which + ds->x1 <= oldlast) \ + which = (which - oldtable) + texturecolumntable + + CHECK(ds->maskedtexturecol); + CHECK(ds->maskedtextureheight); + CHECK(ds->thicksidecol); + CHECK(ds->invscale); + +#undef CHECK + } +} + +static void R_AddOverlayTextures(fixed_t ceilingfrontslide, fixed_t floorfrontslide, fixed_t ceilingbackslide, fixed_t floorbackslide) +{ + INT32 texnums[4] = { + R_GetTextureNum(sidedef->overlays[0].texture), + R_GetTextureNum(sidedef->overlays[1].texture), + R_GetTextureNum(sidedef->overlays[2].texture), + R_GetTextureNum(sidedef->overlays[3].texture) + }; + + if (!backsector) + { + // If one-sided, render just the upper top and the lower bottom overlays + overlaytexture[0] = texnums[0] ? texnums[0] : texnums[2]; + overlaytexture[3] = texnums[1] ? texnums[1] : texnums[3]; + } + else + { + overlaytexture[0] = texnums[0]; + overlaytexture[1] = texnums[1]; + overlaytexture[2] = texnums[2]; + overlaytexture[3] = texnums[3]; + } + + for (unsigned i = 0; i < NUM_WALL_OVERLAYS; i++) + { + if (overlaytexture[i] > 0 && overlaytexture[i] < numtextures) + { + if (!texcoltables) + { + // allocate space for masked texture tables + R_AllocTextureColumnTables(rw_stopx - rw_x); + + texcoltables = true; + } + + if (!maskedtexturecol) + { + ds_p->maskedtexturecol = maskedtexturecol = curtexturecolumntable - rw_x; + curtexturecolumntable += rw_stopx - rw_x; + } + + maskedtexture = hasoverlaytexture = true; + + rw_overlay[i].scalex = sidedef->overlays[i].scalex; + rw_overlay[i].scaley = sidedef->overlays[i].scaley; + rw_overlay[i].offsetx = sidedef->overlays[i].offsetx; + rw_overlay[i].offsety = sidedef->overlays[i].offsety; + + rw_overlay[i].invscalex = FixedDiv(FRACUNIT, rw_overlay[i].scalex); + + if (sidedef->flags & GET_SIDEFLAG_EDGENOSKEW(i)) + { + if (IS_BOTTOM_EDGE_TEXTURE(i)) + { + rw_overlay[i].mid = frontsector->floorheight; + rw_overlay[i].back = backsector ? backsector->floorheight : rw_overlay[i].mid; + } + else + { + rw_overlay[i].mid = frontsector->ceilingheight; + rw_overlay[i].back = backsector ? backsector->ceilingheight : rw_overlay[i].mid; + } + + rw_overlay[i].slide = 0; + rw_overlay[i].backslide = 0; + + rw_overlay[i].mid -= viewz; + rw_overlay[i].back -= viewz; + } + else if (IS_BOTTOM_EDGE_TEXTURE(i)) + { + rw_overlay[i].mid = worldbottom; + rw_overlay[i].slide = floorfrontslide; + rw_overlay[i].back = backsector ? worldlow : rw_overlay[i].mid; + rw_overlay[i].backslide = backsector ? floorbackslide : rw_overlay[i].slide; + } + else + { + rw_overlay[i].mid = worldtop; + rw_overlay[i].slide = ceilingfrontslide; + rw_overlay[i].back = backsector ? worldhigh : rw_overlay[i].mid; + rw_overlay[i].backslide = backsector ? ceilingbackslide : rw_overlay[i].slide; + } + + rw_overlay[i].mid = FixedMul(rw_overlay[i].mid, abs(rw_overlay[i].scaley)); + rw_overlay[i].back = FixedMul(rw_overlay[i].back, abs(rw_overlay[i].scaley)); + + rw_overlay[i].mid += sidedef->rowoffset + rw_overlay[i].offsety; + rw_overlay[i].back += sidedef->rowoffset + rw_overlay[i].offsety; + } } } @@ -1768,12 +2206,18 @@ void R_StoreWallRange(INT32 start, INT32 stop) worldbottomslope -= viewz; midtexture = toptexture = bottomtexture = maskedtexture = 0; + hasoverlaytexture = false; + memset(overlaytexture, 0, sizeof(overlaytexture)); + ds_p->maskedtexturecol = NULL; + ds_p->maskedtextureheight = NULL; ds_p->numthicksides = numthicksides = 0; ds_p->thicksidecol = NULL; ds_p->invscale = NULL; ds_p->tsilheight = 0; + texcoltables = false; + numbackffloors = 0; for (i = 0; i < MAXFFLOORS; i++) @@ -2133,6 +2577,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) // allocate space for masked texture tables R_AllocTextureColumnTables(rw_stopx - start); + texcoltables = true; + if (frontsector && backsector && !Tag_Compare(&frontsector->tags, &backsector->tags) && (backsector->ffloors || frontsector->ffloors)) { ffloor_t *rover; @@ -2331,7 +2777,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) ds_p->maskedtexturecol = maskedtexturecol = curtexturecolumntable - rw_x; curtexturecolumntable += rw_stopx - rw_x; - maskedtextureheight = ds_p->maskedtextureheight; // note to red, this == &(ds_p->maskedtextureheight[0]) + ds_p->maskedtextureheight = maskedtextureheight = curtexturecolumntable - rw_x; + curtexturecolumntable += rw_stopx - rw_x; maskedtexture = true; @@ -2355,7 +2802,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_midtexturemid = rw_midtextureback = max(frontsector->floorheight, backsector->floorheight) - viewz; else rw_midtexturemid = rw_midtextureback = min(frontsector->ceilingheight, backsector->ceilingheight) - viewz; - } else if (linedef->flags & ML_MIDPEG) { @@ -2378,14 +2824,16 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid; rw_midtextureback += sidedef->rowoffset + sidedef->offsety_mid; - - maskedtexture = true; } } - // calculate rw_offset (only needed for textured lines) segtextured = midtexture || toptexture || bottomtexture || maskedtexture || (numthicksides > 0); + // check if extra textures need to be rendered + if (segtextured) + R_AddOverlayTextures(ceilingfrontslide, floorfrontslide, ceilingbackslide, floorbackslide); + + // calculate rw_offset (only needed for textured lines) if (segtextured) { fixed_t sideoffset = sidedef->textureoffset; @@ -2851,6 +3299,14 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_tsilheight = &(ds_p->tsilheight); rw_bsilheight = &(ds_p->bsilheight); + // Overlay rendering needs to use the floor/ceiling clip from before the wall is rendered + // (otherwise it would be clipped and therefore wouldn't be visible) + if (hasoverlaytexture) + { + M_Memcpy(&overlayopening[0][start], ceilingclip + start, 2*(rw_stopx - start)); + M_Memcpy(&overlayopening[1][start], floorclip + start, 2*(rw_stopx - start)); + } + R_RenderSegLoop(); colfunc = colfuncs[BASEDRAWFUNC]; @@ -2889,5 +3345,16 @@ void R_StoreWallRange(INT32 start, INT32 stop) ds_p->silhouette |= SIL_BOTTOM; ds_p->bsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MAX : INT32_MIN; } + + // Render up to four extra textures when everything is done + if (hasoverlaytexture) + { + for (i = 0; i < NUM_WALL_OVERLAYS; i++) + { + if (overlaytexture[i]) + R_RenderExtraTexture(ds_p, i, ds_p->x1, ds_p->x2); + } + } + ds_p++; } diff --git a/src/r_things.c b/src/r_things.c index 7291594eb..adf9c2580 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3642,7 +3642,7 @@ static void R_DrawMaskedList (drawnode_t* head) R_DoneWithNode(r2); r2 = next; } - else if (r2->seg && r2->seg->maskedtexturecol != NULL) + else if (r2->seg && r2->seg->maskedtexturecol != NULL && r2->seg->maskedtextureheight != NULL) { next = r2->prev; R_RenderMaskedSegRange(r2->seg, r2->seg->x1, r2->seg->x2);