doom3-bfg/doomclassic/doom/r_segs.cpp
2012-11-26 12:58:24 -06:00

718 lines
19 KiB
C++

/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "Precompiled.h"
#include "globaldata.h"
#include <stdlib.h>
#include "i_system.h"
#include "doomdef.h"
#include "doomstat.h"
#include "r_local.h"
#include "r_sky.h"
// OPTIMIZE: closed two sided ::g->lines as single sided
// True if any of the ::g->segs textures might be visible.
// False if the back side is the same plane.
// angle to line origin
//
// regular wall
//
//
// R_RenderMaskedSegRange
//
void
R_RenderMaskedSegRange
( drawseg_t* ds,
int x1,
int x2 )
{
unsigned index;
postColumn_t* col;
int lightnum;
int texnum;
// Calculate light table.
// Use different light tables
// for horizontal / vertical / diagonal. Diagonal?
// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
::g->curline = ds->curline;
::g->frontsector = ::g->curline->frontsector;
::g->backsector = ::g->curline->backsector;
texnum = ::g->texturetranslation[::g->curline->sidedef->midtexture];
lightnum = (::g->frontsector->lightlevel >> LIGHTSEGSHIFT)+::g->extralight;
if (::g->curline->v1->y == ::g->curline->v2->y)
lightnum--;
else if (::g->curline->v1->x == ::g->curline->v2->x)
lightnum++;
if (lightnum < 0)
::g->walllights = ::g->scalelight[0];
else if (lightnum >= LIGHTLEVELS)
::g->walllights = ::g->scalelight[LIGHTLEVELS-1];
else
::g->walllights = ::g->scalelight[lightnum];
::g->maskedtexturecol = ds->maskedtexturecol;
::g->rw_scalestep = ds->scalestep;
::g->spryscale = ds->scale1 + (x1 - ds->x1)*::g->rw_scalestep;
::g->mfloorclip = ds->sprbottomclip;
::g->mceilingclip = ds->sprtopclip;
// find positioning
if (::g->curline->linedef->flags & ML_DONTPEGBOTTOM)
{
::g->dc_texturemid = ::g->frontsector->floorheight > ::g->backsector->floorheight
? ::g->frontsector->floorheight : ::g->backsector->floorheight;
::g->dc_texturemid = ::g->dc_texturemid + ::g->s_textureheight[texnum] - ::g->viewz;
}
else
{
::g->dc_texturemid =::g->frontsector->ceilingheight < ::g->backsector->ceilingheight
? ::g->frontsector->ceilingheight : ::g->backsector->ceilingheight;
::g->dc_texturemid = ::g->dc_texturemid - ::g->viewz;
}
::g->dc_texturemid += ::g->curline->sidedef->rowoffset;
if (::g->fixedcolormap)
::g->dc_colormap = ::g->fixedcolormap;
// draw the columns
for (::g->dc_x = x1 ; ::g->dc_x <= x2 ; ::g->dc_x++)
{
// calculate lighting
if (::g->maskedtexturecol[::g->dc_x] != SHRT_MAX)
{
if (!::g->fixedcolormap)
{
index = ::g->spryscale>>LIGHTSCALESHIFT;
if (index >= MAXLIGHTSCALE )
index = MAXLIGHTSCALE-1;
::g->dc_colormap = ::g->walllights[index];
}
::g->sprtopscreen = ::g->centeryfrac - FixedMul(::g->dc_texturemid, ::g->spryscale);
::g->dc_iscale = 0xffffffffu / (unsigned)::g->spryscale;
// draw the texture
col = (postColumn_t *)(
(byte *)R_GetColumn(texnum,::g->maskedtexturecol[::g->dc_x]) -3);
R_DrawMaskedColumn (col);
::g->maskedtexturecol[::g->dc_x] = SHRT_MAX;
}
::g->spryscale += ::g->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;
texturecolumn = 0; // shut up compiler warning
for ( ; ::g->rw_x < ::g->rw_stopx ; ::g->rw_x++)
{
// mark floor / ceiling areas
yl = (::g->topfrac+HEIGHTUNIT-1)>>HEIGHTBITS;
// no space above wall?
if (yl < ::g->ceilingclip[::g->rw_x]+1)
yl = ::g->ceilingclip[::g->rw_x]+1;
if (::g->markceiling)
{
top = ::g->ceilingclip[::g->rw_x]+1;
bottom = yl-1;
if (bottom >= ::g->floorclip[::g->rw_x])
bottom = ::g->floorclip[::g->rw_x]-1;
if (top <= bottom)
{
::g->ceilingplane->top[::g->rw_x] = top;
::g->ceilingplane->bottom[::g->rw_x] = bottom;
}
}
yh = ::g->bottomfrac>>HEIGHTBITS;
if (yh >= ::g->floorclip[::g->rw_x])
yh = ::g->floorclip[::g->rw_x]-1;
if (::g->markfloor)
{
top = yh+1;
bottom = ::g->floorclip[::g->rw_x]-1;
if (top <= ::g->ceilingclip[::g->rw_x])
top = ::g->ceilingclip[::g->rw_x]+1;
if (top <= bottom)
{
::g->floorplane->top[::g->rw_x] = top;
::g->floorplane->bottom[::g->rw_x] = bottom;
}
}
// texturecolumn and lighting are independent of wall tiers
if (::g->segtextured)
{
// calculate texture offset
angle = (::g->rw_centerangle + ::g->xtoviewangle[::g->rw_x])>>ANGLETOFINESHIFT;
texturecolumn = ::g->rw_offset-FixedMul(finetangent[angle],::g->rw_distance);
texturecolumn >>= FRACBITS;
// calculate lighting
index = ::g->rw_scale>>LIGHTSCALESHIFT;
if (index >= MAXLIGHTSCALE )
index = MAXLIGHTSCALE-1;
::g->dc_colormap = ::g->walllights[index];
::g->dc_x = ::g->rw_x;
::g->dc_iscale = 0xffffffffu / (unsigned)::g->rw_scale;
}
// draw the wall tiers
if (::g->midtexture)
{
// single sided line
::g->dc_yl = yl;
::g->dc_yh = yh;
::g->dc_texturemid = ::g->rw_midtexturemid;
::g->dc_source = R_GetColumn(::g->midtexture,texturecolumn);
colfunc ( ::g->dc_colormap, ::g->dc_source );
::g->ceilingclip[::g->rw_x] = ::g->viewheight;
::g->floorclip[::g->rw_x] = -1;
}
else
{
// two sided line
if (::g->toptexture)
{
// top wall
mid = ::g->pixhigh>>HEIGHTBITS;
::g->pixhigh += ::g->pixhighstep;
if (mid >= ::g->floorclip[::g->rw_x])
mid = ::g->floorclip[::g->rw_x]-1;
if (mid >= yl)
{
::g->dc_yl = yl;
::g->dc_yh = mid;
::g->dc_texturemid = ::g->rw_toptexturemid;
::g->dc_source = R_GetColumn(::g->toptexture,texturecolumn);
colfunc ( ::g->dc_colormap, ::g->dc_source );
::g->ceilingclip[::g->rw_x] = mid;
}
else
::g->ceilingclip[::g->rw_x] = yl-1;
}
else
{
// no top wall
if (::g->markceiling)
::g->ceilingclip[::g->rw_x] = yl-1;
}
if (::g->bottomtexture)
{
// bottom wall
mid = (::g->pixlow+HEIGHTUNIT-1)>>HEIGHTBITS;
::g->pixlow += ::g->pixlowstep;
// no space above wall?
if (mid <= ::g->ceilingclip[::g->rw_x])
mid = ::g->ceilingclip[::g->rw_x]+1;
if (mid <= yh)
{
::g->dc_yl = mid;
::g->dc_yh = yh;
::g->dc_texturemid = ::g->rw_bottomtexturemid;
::g->dc_source = R_GetColumn(::g->bottomtexture,
texturecolumn);
colfunc ( ::g->dc_colormap, ::g->dc_source );
::g->floorclip[::g->rw_x] = mid;
}
else
::g->floorclip[::g->rw_x] = yh+1;
}
else
{
// no bottom wall
if (::g->markfloor)
::g->floorclip[::g->rw_x] = yh+1;
}
if (::g->maskedtexture)
{
// save texturecol
// for backdrawing of masked mid texture
::g->maskedtexturecol[::g->rw_x] = texturecolumn;
}
}
::g->rw_scale += ::g->rw_scalestep;
::g->topfrac += ::g->topstep;
::g->bottomfrac += ::g->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 (::g->ds_p == &::g->drawsegs[MAXDRAWSEGS])
return;
#ifdef RANGECHECK
if (start >=::g->viewwidth || start > stop)
I_Error ("Bad R_RenderWallRange: %i to %i", start , stop);
#endif
::g->sidedef = ::g->curline->sidedef;
::g->linedef = ::g->curline->linedef;
// mark the segment as visible for auto map
::g->linedef->flags |= ML_MAPPED;
// calculate ::g->rw_distance for scale calculation
::g->rw_normalangle = ::g->curline->angle + ANG90;
offsetangle = abs((long)(::g->rw_normalangle-::g->rw_angle1));
if (offsetangle > ANG90)
offsetangle = ANG90;
distangle = ANG90 - offsetangle;
hyp = R_PointToDist (::g->curline->v1->x, ::g->curline->v1->y);
sineval = finesine[distangle>>ANGLETOFINESHIFT];
::g->rw_distance = FixedMul (hyp, sineval);
::g->ds_p->x1 = ::g->rw_x = start;
::g->ds_p->x2 = stop;
::g->ds_p->curline = ::g->curline;
::g->rw_stopx = stop+1;
// calculate scale at both ends and step
extern angle_t GetViewAngle();
::g->ds_p->scale1 = ::g->rw_scale =
R_ScaleFromGlobalAngle (GetViewAngle() + ::g->xtoviewangle[start]);
if (stop > start )
{
::g->ds_p->scale2 = R_ScaleFromGlobalAngle (GetViewAngle() + ::g->xtoviewangle[stop]);
::g->ds_p->scalestep = ::g->rw_scalestep =
(::g->ds_p->scale2 - ::g->rw_scale) / (stop-start);
}
else
{
// UNUSED: try to fix the stretched line bug
#if 0
if (::g->rw_distance < FRACUNIT/2)
{
fixed_t trx,try;
fixed_t gxt,gyt;
extern fixed_t GetViewX(); extern fixed_t GetViewY();
trx = ::g->curline->v1->x - GetViewX();
try = ::g->curline->v1->y - GetVewY();
gxt = FixedMul(trx,::g->viewcos);
gyt = -FixedMul(try,::g->viewsin);
::g->ds_p->scale1 = FixedDiv(::g->projection, gxt-gyt) << ::g->detailshift;
}
#endif
::g->ds_p->scale2 = ::g->ds_p->scale1;
}
// calculate texture boundaries
// and decide if floor / ceiling marks are needed
::g->worldtop = ::g->frontsector->ceilingheight - ::g->viewz;
::g->worldbottom = ::g->frontsector->floorheight - ::g->viewz;
::g->midtexture = ::g->toptexture = ::g->bottomtexture = ::g->maskedtexture = 0;
::g->ds_p->maskedtexturecol = NULL;
if (!::g->backsector)
{
// single sided line
::g->midtexture = ::g->texturetranslation[::g->sidedef->midtexture];
// a single sided line is terminal, so it must mark ends
::g->markfloor = ::g->markceiling = true;
if (::g->linedef->flags & ML_DONTPEGBOTTOM)
{
vtop = ::g->frontsector->floorheight +
::g->s_textureheight[::g->sidedef->midtexture];
// bottom of texture at bottom
::g->rw_midtexturemid = vtop - ::g->viewz;
}
else
{
// top of texture at top
::g->rw_midtexturemid = ::g->worldtop;
}
::g->rw_midtexturemid += ::g->sidedef->rowoffset;
::g->ds_p->silhouette = SIL_BOTH;
::g->ds_p->sprtopclip = ::g->screenheightarray;
::g->ds_p->sprbottomclip = ::g->negonearray;
::g->ds_p->bsilheight = MAXINT;
::g->ds_p->tsilheight = MININT;
}
else
{
// two sided line
::g->ds_p->sprtopclip = ::g->ds_p->sprbottomclip = NULL;
::g->ds_p->silhouette = 0;
if (::g->frontsector->floorheight > ::g->backsector->floorheight)
{
::g->ds_p->silhouette = SIL_BOTTOM;
::g->ds_p->bsilheight = ::g->frontsector->floorheight;
}
else if (::g->backsector->floorheight > ::g->viewz)
{
::g->ds_p->silhouette = SIL_BOTTOM;
::g->ds_p->bsilheight = MAXINT;
// ::g->ds_p->sprbottomclip = ::g->negonearray;
}
if (::g->frontsector->ceilingheight < ::g->backsector->ceilingheight)
{
::g->ds_p->silhouette |= SIL_TOP;
::g->ds_p->tsilheight = ::g->frontsector->ceilingheight;
}
else if (::g->backsector->ceilingheight < ::g->viewz)
{
::g->ds_p->silhouette |= SIL_TOP;
::g->ds_p->tsilheight = MININT;
// ::g->ds_p->sprtopclip = ::g->screenheightarray;
}
if (::g->backsector->ceilingheight <= ::g->frontsector->floorheight)
{
::g->ds_p->sprbottomclip = ::g->negonearray;
::g->ds_p->bsilheight = MAXINT;
::g->ds_p->silhouette |= SIL_BOTTOM;
}
if (::g->backsector->floorheight >= ::g->frontsector->ceilingheight)
{
::g->ds_p->sprtopclip = ::g->screenheightarray;
::g->ds_p->tsilheight = MININT;
::g->ds_p->silhouette |= SIL_TOP;
}
::g->worldhigh = ::g->backsector->ceilingheight - ::g->viewz;
::g->worldlow = ::g->backsector->floorheight - ::g->viewz;
// hack to allow height changes in outdoor areas
if (::g->frontsector->ceilingpic == ::g->skyflatnum
&& ::g->backsector->ceilingpic == ::g->skyflatnum)
{
::g->worldtop = ::g->worldhigh;
}
if (::g->worldlow != ::g->worldbottom
|| ::g->backsector->floorpic != ::g->frontsector->floorpic
|| ::g->backsector->lightlevel != ::g->frontsector->lightlevel)
{
::g->markfloor = true;
}
else
{
// same plane on both ::g->sides
::g->markfloor = false;
}
if (::g->worldhigh != ::g->worldtop
|| ::g->backsector->ceilingpic != ::g->frontsector->ceilingpic
|| ::g->backsector->lightlevel != ::g->frontsector->lightlevel)
{
::g->markceiling = true;
}
else
{
// same plane on both ::g->sides
::g->markceiling = false;
}
if (::g->backsector->ceilingheight <= ::g->frontsector->floorheight
|| ::g->backsector->floorheight >= ::g->frontsector->ceilingheight)
{
// closed door
::g->markceiling = ::g->markfloor = true;
}
if (::g->worldhigh < ::g->worldtop)
{
// top texture
::g->toptexture = ::g->texturetranslation[::g->sidedef->toptexture];
if (::g->linedef->flags & ML_DONTPEGTOP)
{
// top of texture at top
::g->rw_toptexturemid = ::g->worldtop;
}
else
{
vtop =
::g->backsector->ceilingheight
+ ::g->s_textureheight[::g->sidedef->toptexture];
// bottom of texture
::g->rw_toptexturemid = vtop - ::g->viewz;
}
}
if (::g->worldlow > ::g->worldbottom)
{
// bottom texture
::g->bottomtexture = ::g->texturetranslation[::g->sidedef->bottomtexture];
if (::g->linedef->flags & ML_DONTPEGBOTTOM )
{
// bottom of texture at bottom
// top of texture at top
::g->rw_bottomtexturemid = ::g->worldtop;
}
else // top of texture at top
::g->rw_bottomtexturemid = ::g->worldlow;
}
::g->rw_toptexturemid += ::g->sidedef->rowoffset;
::g->rw_bottomtexturemid += ::g->sidedef->rowoffset;
// allocate space for masked texture tables
if (::g->sidedef->midtexture)
{
// masked ::g->midtexture
::g->maskedtexture = true;
::g->ds_p->maskedtexturecol = ::g->maskedtexturecol = ::g->lastopening - ::g->rw_x;
::g->lastopening += ::g->rw_stopx - ::g->rw_x;
}
}
// calculate ::g->rw_offset (only needed for textured ::g->lines)
::g->segtextured = ::g->midtexture | ::g->toptexture | ::g->bottomtexture | ::g->maskedtexture;
if (::g->segtextured)
{
offsetangle = ::g->rw_normalangle-::g->rw_angle1;
if (offsetangle > ANG180)
offsetangle = -offsetangle; // ALANHACK UNSIGNED
if (offsetangle > ANG90)
offsetangle = ANG90;
sineval = finesine[offsetangle >>ANGLETOFINESHIFT];
::g->rw_offset = FixedMul (hyp, sineval);
if (::g->rw_normalangle-::g->rw_angle1 < ANG180)
::g->rw_offset = -::g->rw_offset;
::g->rw_offset += ::g->sidedef->textureoffset + ::g->curline->offset;
::g->rw_centerangle = ANG90 + GetViewAngle() - ::g->rw_normalangle;
// calculate light table
// use different light tables
// for horizontal / vertical / diagonal
// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
if (!::g->fixedcolormap)
{
lightnum = (::g->frontsector->lightlevel >> LIGHTSEGSHIFT)+::g->extralight;
if (::g->curline->v1->y == ::g->curline->v2->y)
lightnum--;
else if (::g->curline->v1->x == ::g->curline->v2->x)
lightnum++;
if (lightnum < 0)
::g->walllights = ::g->scalelight[0];
else if (lightnum >= LIGHTLEVELS)
::g->walllights = ::g->scalelight[LIGHTLEVELS-1];
else
::g->walllights = ::g->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 (::g->frontsector->floorheight >= ::g->viewz)
{
// above view plane
::g->markfloor = false;
}
if (::g->frontsector->ceilingheight <= ::g->viewz
&& ::g->frontsector->ceilingpic != ::g->skyflatnum)
{
// below view plane
::g->markceiling = false;
}
// calculate incremental stepping values for texture edges
::g->worldtop >>= 4;
::g->worldbottom >>= 4;
::g->topstep = -FixedMul (::g->rw_scalestep, ::g->worldtop);
::g->topfrac = (::g->centeryfrac>>4) - FixedMul (::g->worldtop, ::g->rw_scale);
::g->bottomstep = -FixedMul (::g->rw_scalestep,::g->worldbottom);
::g->bottomfrac = (::g->centeryfrac>>4) - FixedMul (::g->worldbottom, ::g->rw_scale);
if (::g->backsector)
{
::g->worldhigh >>= 4;
::g->worldlow >>= 4;
if (::g->worldhigh < ::g->worldtop)
{
::g->pixhigh = (::g->centeryfrac>>4) - FixedMul (::g->worldhigh, ::g->rw_scale);
::g->pixhighstep = -FixedMul (::g->rw_scalestep,::g->worldhigh);
}
if (::g->worldlow > ::g->worldbottom)
{
::g->pixlow = (::g->centeryfrac>>4) - FixedMul (::g->worldlow, ::g->rw_scale);
::g->pixlowstep = -FixedMul (::g->rw_scalestep,::g->worldlow);
}
}
// render it
if (::g->markceiling)
::g->ceilingplane = R_CheckPlane (::g->ceilingplane, ::g->rw_x, ::g->rw_stopx-1);
if (::g->markfloor)
::g->floorplane = R_CheckPlane (::g->floorplane, ::g->rw_x, ::g->rw_stopx-1);
R_RenderSegLoop ();
// save sprite clipping info
if ( ((::g->ds_p->silhouette & SIL_TOP) || ::g->maskedtexture)
&& !::g->ds_p->sprtopclip)
{
memcpy (::g->lastopening, ::g->ceilingclip+start, 2*(::g->rw_stopx-start));
::g->ds_p->sprtopclip = ::g->lastopening - start;
::g->lastopening += ::g->rw_stopx - start;
}
if ( ((::g->ds_p->silhouette & SIL_BOTTOM) || ::g->maskedtexture)
&& !::g->ds_p->sprbottomclip)
{
memcpy (::g->lastopening, ::g->floorclip+start, 2*(::g->rw_stopx-start));
::g->ds_p->sprbottomclip = ::g->lastopening - start;
::g->lastopening += ::g->rw_stopx - start;
}
if (::g->maskedtexture && !(::g->ds_p->silhouette&SIL_TOP))
{
::g->ds_p->silhouette |= SIL_TOP;
::g->ds_p->tsilheight = MININT;
}
if (::g->maskedtexture && !(::g->ds_p->silhouette&SIL_BOTTOM))
{
::g->ds_p->silhouette |= SIL_BOTTOM;
::g->ds_p->bsilheight = MAXINT;
}
::g->ds_p++;
}