v4k-git-backup/demos/ports/doom/src/r_segs.c

729 lines
20 KiB
C
Raw Normal View History

2023-08-10 14:30:56 +00:00
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// All the clipping: columns, horizontal spans, sky columns.
//
//-----------------------------------------------------------------------------
#include "doom_config.h"
#include "i_system.h"
#include "doomdef.h"
#include "doomstat.h"
#include "r_local.h"
#include "r_sky.h"
#define HEIGHTBITS 12
#define HEIGHTUNIT (1<<HEIGHTBITS)
// OPTIMIZE: closed two sided lines as single sided
// True if any of the segs textures might be visible.
doom_boolean segtextured;
// False if the back side is the same plane.
doom_boolean markfloor;
doom_boolean markceiling;
doom_boolean maskedtexture;
int toptexture;
int bottomtexture;
int midtexture;
angle_t rw_normalangle;
// angle to line origin
int rw_angle1;
//
// regular wall
//
int rw_x;
int rw_stopx;
angle_t rw_centerangle;
fixed_t rw_offset;
fixed_t rw_distance;
fixed_t rw_scale;
fixed_t rw_scalestep;
fixed_t rw_midtexturemid;
fixed_t rw_toptexturemid;
fixed_t rw_bottomtexturemid;
int worldtop;
int worldbottom;
int worldhigh;
int worldlow;
fixed_t pixhigh;
fixed_t pixlow;
fixed_t pixhighstep;
fixed_t pixlowstep;
fixed_t topfrac;
fixed_t topstep;
fixed_t bottomfrac;
fixed_t bottomstep;
lighttable_t** walllights;
short* maskedtexturecol;
//
// R_RenderMaskedSegRange
//
void R_RenderMaskedSegRange(drawseg_t* ds, int x1, int x2)
{
unsigned index;
column_t* col;
int lightnum;
int texnum;
// Calculate light table.
// Use different light tables
// for horizontal / vertical / diagonal. Diagonal?
// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
curline = ds->curline;
frontsector = curline->frontsector;
backsector = curline->backsector;
texnum = texturetranslation[curline->sidedef->midtexture];
lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight;
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];
maskedtexturecol = ds->maskedtexturecol;
rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep;
mfloorclip = ds->sprbottomclip;
mceilingclip = ds->sprtopclip;
// find positioning
if (curline->linedef->flags & ML_DONTPEGBOTTOM)
{
dc_texturemid = frontsector->floorheight > backsector->floorheight
? frontsector->floorheight : backsector->floorheight;
dc_texturemid = dc_texturemid + textureheight[texnum] - viewz;
}
else
{
dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight
? frontsector->ceilingheight : backsector->ceilingheight;
dc_texturemid = dc_texturemid - viewz;
}
dc_texturemid += curline->sidedef->rowoffset;
if (fixedcolormap)
dc_colormap = fixedcolormap;
// draw the columns
for (dc_x = x1; dc_x <= x2; dc_x++)
{
// calculate lighting
if (maskedtexturecol[dc_x] != DOOM_MAXSHORT)
{
if (!fixedcolormap)
{
index = spryscale >> LIGHTSCALESHIFT;
if (index >= MAXLIGHTSCALE)
index = MAXLIGHTSCALE - 1;
dc_colormap = walllights[index];
}
sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
dc_iscale = 0xffffffffu / (unsigned)spryscale;
// draw the texture
col = (column_t*)(
(byte*)R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3);
R_DrawMaskedColumn(col);
maskedtexturecol[dc_x] = DOOM_MAXSHORT;
}
spryscale += rw_scalestep;
}
}
//
// R_RenderSegLoop
// Draws zero, one, or two textures (and possibly a masked
// texture) for walls.
// Can draw or mark the starting pixel of floor and ceiling
// textures.
// CALLED: CORE LOOPING ROUTINE.
//
void R_RenderSegLoop(void)
{
angle_t angle;
unsigned index;
int yl;
int yh;
int mid;
fixed_t texturecolumn;
int top;
int bottom;
for (; rw_x < rw_stopx; rw_x++)
{
// mark floor / ceiling areas
yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS;
// no space above wall?
if (yl < ceilingclip[rw_x] + 1)
yl = ceilingclip[rw_x] + 1;
if (markceiling)
{
top = ceilingclip[rw_x] + 1;
bottom = yl - 1;
if (bottom >= floorclip[rw_x])
bottom = floorclip[rw_x] - 1;
if (top <= bottom)
{
ceilingplane->top[rw_x] = top;
ceilingplane->bottom[rw_x] = bottom;
}
}
yh = bottomfrac >> HEIGHTBITS;
if (yh >= floorclip[rw_x])
yh = floorclip[rw_x] - 1;
if (markfloor)
{
top = yh + 1;
bottom = floorclip[rw_x] - 1;
if (top <= ceilingclip[rw_x])
top = ceilingclip[rw_x] + 1;
if (top <= bottom)
{
floorplane->top[rw_x] = top;
floorplane->bottom[rw_x] = bottom;
}
}
// texturecolumn and lighting are independent of wall tiers
if (segtextured)
{
// calculate texture offset
angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT;
texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance);
texturecolumn >>= FRACBITS;
// calculate lighting
index = rw_scale >> LIGHTSCALESHIFT;
if (index >= MAXLIGHTSCALE)
index = MAXLIGHTSCALE - 1;
dc_colormap = walllights[index];
dc_x = rw_x;
dc_iscale = 0xffffffffu / (unsigned)rw_scale;
}
// draw the wall tiers
if (midtexture)
{
// single sided line
dc_yl = yl;
dc_yh = yh;
dc_texturemid = rw_midtexturemid;
dc_source = R_GetColumn(midtexture, texturecolumn);
colfunc();
ceilingclip[rw_x] = viewheight;
floorclip[rw_x] = -1;
}
else
{
// two sided line
if (toptexture)
{
// top wall
mid = pixhigh >> HEIGHTBITS;
pixhigh += pixhighstep;
if (mid >= floorclip[rw_x])
mid = floorclip[rw_x] - 1;
if (mid >= yl)
{
dc_yl = yl;
dc_yh = mid;
dc_texturemid = rw_toptexturemid;
dc_source = R_GetColumn(toptexture, texturecolumn);
colfunc();
ceilingclip[rw_x] = mid;
}
else
ceilingclip[rw_x] = yl - 1;
}
else
{
// no top wall
if (markceiling)
ceilingclip[rw_x] = yl - 1;
}
if (bottomtexture)
{
// bottom wall
mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS;
pixlow += pixlowstep;
// no space above wall?
if (mid <= ceilingclip[rw_x])
mid = ceilingclip[rw_x] + 1;
if (mid <= yh)
{
dc_yl = mid;
dc_yh = yh;
dc_texturemid = rw_bottomtexturemid;
dc_source = R_GetColumn(bottomtexture,
texturecolumn);
colfunc();
floorclip[rw_x] = mid;
}
else
floorclip[rw_x] = yh + 1;
}
else
{
// no bottom wall
if (markfloor)
floorclip[rw_x] = yh + 1;
}
if (maskedtexture)
{
// save texturecol
// for backdrawing of masked mid texture
maskedtexturecol[rw_x] = texturecolumn;
}
}
rw_scale += rw_scalestep;
topfrac += topstep;
bottomfrac += bottomstep;
}
}
//
// R_StoreWallRange
// A wall segment will be drawn
// between start and stop pixels (inclusive).
//
void R_StoreWallRange(int start, int stop)
{
fixed_t hyp;
fixed_t sineval;
angle_t distangle, offsetangle;
fixed_t vtop;
int lightnum;
// don't overflow and crash
if (ds_p == &drawsegs[MAXDRAWSEGS])
return;
#ifdef RANGECHECK
if (start >= viewwidth || start > stop)
{
//I_Error("Error: Bad R_RenderWallRange: %i to %i", start, stop);
doom_strcpy(error_buf, "Error: Bad R_RenderWallRange: ");
doom_concat(error_buf, doom_itoa(start, 10));
doom_concat(error_buf, " to ");
doom_concat(error_buf, doom_itoa(stop, 10));
I_Error(error_buf);
}
#endif
sidedef = curline->sidedef;
linedef = curline->linedef;
// mark the segment as visible for auto map
linedef->flags |= ML_MAPPED;
// calculate rw_distance for scale calculation
rw_normalangle = curline->angle + ANG90;
offsetangle = (angle_t)doom_abs((int)rw_normalangle - (int)rw_angle1);
if (offsetangle > ANG90)
offsetangle = ANG90;
distangle = ANG90 - offsetangle;
hyp = R_PointToDist(curline->v1->x, curline->v1->y);
sineval = finesine[distangle >> ANGLETOFINESHIFT];
rw_distance = FixedMul(hyp, sineval);
ds_p->x1 = rw_x = start;
ds_p->x2 = stop;
ds_p->curline = curline;
rw_stopx = stop + 1;
// calculate scale at both ends and step
ds_p->scale1 = rw_scale =
R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]);
if (stop > start)
{
ds_p->scale2 = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[stop]);
ds_p->scalestep = rw_scalestep =
(ds_p->scale2 - rw_scale) / (stop - start);
}
else
{
// UNUSED: try to fix the stretched line bug
#if 0
if (rw_distance < FRACUNIT / 2)
{
fixed_t trx, try;
fixed_t gxt, gyt;
trx = curline->v1->x - viewx;
try = curline->v1->y - viewy;
gxt = FixedMul(trx, viewcos);
gyt = -FixedMul(try, viewsin);
ds_p->scale1 = FixedDiv(projection, gxt - gyt) << detailshift;
}
#endif
ds_p->scale2 = ds_p->scale1;
}
// calculate texture boundaries
// and decide if floor / ceiling marks are needed
worldtop = frontsector->ceilingheight - viewz;
worldbottom = frontsector->floorheight - viewz;
midtexture = toptexture = bottomtexture = maskedtexture = 0;
ds_p->maskedtexturecol = 0;
if (!backsector)
{
// single sided line
midtexture = texturetranslation[sidedef->midtexture];
// a single sided line is terminal, so it must mark ends
markfloor = markceiling = true;
if (linedef->flags & ML_DONTPEGBOTTOM)
{
vtop = frontsector->floorheight +
textureheight[sidedef->midtexture];
// bottom of texture at bottom
rw_midtexturemid = vtop - viewz;
}
else
{
// top of texture at top
rw_midtexturemid = worldtop;
}
rw_midtexturemid += sidedef->rowoffset;
ds_p->silhouette = SIL_BOTH;
ds_p->sprtopclip = screenheightarray;
ds_p->sprbottomclip = negonearray;
ds_p->bsilheight = DOOM_MAXINT;
ds_p->tsilheight = DOOM_MININT;
}
else
{
// two sided line
ds_p->sprtopclip = ds_p->sprbottomclip = 0;
ds_p->silhouette = 0;
if (frontsector->floorheight > backsector->floorheight)
{
ds_p->silhouette = SIL_BOTTOM;
ds_p->bsilheight = frontsector->floorheight;
}
else if (backsector->floorheight > viewz)
{
ds_p->silhouette = SIL_BOTTOM;
ds_p->bsilheight = DOOM_MAXINT;
// ds_p->sprbottomclip = negonearray;
}
if (frontsector->ceilingheight < backsector->ceilingheight)
{
ds_p->silhouette |= SIL_TOP;
ds_p->tsilheight = frontsector->ceilingheight;
}
else if (backsector->ceilingheight < viewz)
{
ds_p->silhouette |= SIL_TOP;
ds_p->tsilheight = DOOM_MININT;
// ds_p->sprtopclip = screenheightarray;
}
if (backsector->ceilingheight <= frontsector->floorheight)
{
ds_p->sprbottomclip = negonearray;
ds_p->bsilheight = DOOM_MAXINT;
ds_p->silhouette |= SIL_BOTTOM;
}
if (backsector->floorheight >= frontsector->ceilingheight)
{
ds_p->sprtopclip = screenheightarray;
ds_p->tsilheight = DOOM_MININT;
ds_p->silhouette |= SIL_TOP;
}
worldhigh = backsector->ceilingheight - viewz;
worldlow = backsector->floorheight - viewz;
// hack to allow height changes in outdoor areas
if (frontsector->ceilingpic == skyflatnum
&& backsector->ceilingpic == skyflatnum)
{
worldtop = worldhigh;
}
if (worldlow != worldbottom
|| backsector->floorpic != frontsector->floorpic
|| backsector->lightlevel != frontsector->lightlevel)
{
markfloor = true;
}
else
{
// same plane on both sides
markfloor = false;
}
if (worldhigh != worldtop
|| backsector->ceilingpic != frontsector->ceilingpic
|| backsector->lightlevel != frontsector->lightlevel)
{
markceiling = true;
}
else
{
// same plane on both sides
markceiling = false;
}
if (backsector->ceilingheight <= frontsector->floorheight
|| backsector->floorheight >= frontsector->ceilingheight)
{
// closed door
markceiling = markfloor = true;
}
if (worldhigh < worldtop)
{
// top texture
toptexture = texturetranslation[sidedef->toptexture];
if (linedef->flags & ML_DONTPEGTOP)
{
// top of texture at top
rw_toptexturemid = worldtop;
}
else
{
vtop =
backsector->ceilingheight
+ textureheight[sidedef->toptexture];
// bottom of texture
rw_toptexturemid = vtop - viewz;
}
}
if (worldlow > worldbottom)
{
// bottom texture
bottomtexture = texturetranslation[sidedef->bottomtexture];
if (linedef->flags & ML_DONTPEGBOTTOM)
{
// bottom of texture at bottom
// top of texture at top
rw_bottomtexturemid = worldtop;
}
else // top of texture at top
rw_bottomtexturemid = worldlow;
}
rw_toptexturemid += sidedef->rowoffset;
rw_bottomtexturemid += sidedef->rowoffset;
// allocate space for masked texture tables
if (sidedef->midtexture)
{
// masked midtexture
maskedtexture = true;
ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x;
lastopening += rw_stopx - rw_x;
}
}
// calculate rw_offset (only needed for textured lines)
segtextured = midtexture | toptexture | bottomtexture | maskedtexture;
if (segtextured)
{
offsetangle = rw_normalangle - rw_angle1;
if (offsetangle > ANG180)
#pragma warning(push)
#pragma warning(disable : 4146)
offsetangle = -offsetangle;
#pragma warning(pop)
if (offsetangle > ANG90)
offsetangle = ANG90;
sineval = finesine[offsetangle >> ANGLETOFINESHIFT];
rw_offset = FixedMul(hyp, sineval);
if (rw_normalangle - rw_angle1 < ANG180)
rw_offset = -rw_offset;
rw_offset += sidedef->textureoffset + curline->offset;
rw_centerangle = ANG90 + viewangle - rw_normalangle;
// calculate light table
// use different light tables
// for horizontal / vertical / diagonal
// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
if (!fixedcolormap)
{
lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight;
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];
}
}
// if a floor / ceiling plane is on the wrong side
// of the view plane, it is definitely invisible
// and doesn't need to be marked.
if (frontsector->floorheight >= viewz)
{
// above view plane
markfloor = false;
}
if (frontsector->ceilingheight <= viewz
&& frontsector->ceilingpic != skyflatnum)
{
// below view plane
markceiling = false;
}
// calculate incremental stepping values for texture edges
worldtop >>= 4;
worldbottom >>= 4;
topstep = -FixedMul(rw_scalestep, worldtop);
topfrac = (centeryfrac >> 4) - FixedMul(worldtop, rw_scale);
bottomstep = -FixedMul(rw_scalestep, worldbottom);
bottomfrac = (centeryfrac >> 4) - FixedMul(worldbottom, rw_scale);
if (backsector)
{
worldhigh >>= 4;
worldlow >>= 4;
if (worldhigh < worldtop)
{
pixhigh = (centeryfrac >> 4) - FixedMul(worldhigh, rw_scale);
pixhighstep = -FixedMul(rw_scalestep, worldhigh);
}
if (worldlow > worldbottom)
{
pixlow = (centeryfrac >> 4) - FixedMul(worldlow, rw_scale);
pixlowstep = -FixedMul(rw_scalestep, worldlow);
}
}
// render it
if (markceiling)
ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1);
if (markfloor)
floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1);
R_RenderSegLoop();
// save sprite clipping info
if (((ds_p->silhouette & SIL_TOP) || maskedtexture)
&& !ds_p->sprtopclip)
{
doom_memcpy(lastopening, ceilingclip + start, 2 * (rw_stopx - start));
ds_p->sprtopclip = lastopening - start;
lastopening += rw_stopx - start;
}
if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture)
&& !ds_p->sprbottomclip)
{
doom_memcpy(lastopening, floorclip + start, 2 * (rw_stopx - start));
ds_p->sprbottomclip = lastopening - start;
lastopening += rw_stopx - start;
}
if (maskedtexture && !(ds_p->silhouette & SIL_TOP))
{
ds_p->silhouette |= SIL_TOP;
ds_p->tsilheight = DOOM_MININT;
}
if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
{
ds_p->silhouette |= SIL_BOTTOM;
ds_p->bsilheight = DOOM_MAXINT;
}
ds_p++;
}