mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-04-25 02:52:12 +00:00
619 lines
12 KiB
C++
619 lines
12 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 "doomdef.h"
|
|
|
|
#include "m_bbox.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "r_main.h"
|
|
#include "r_plane.h"
|
|
#include "r_things.h"
|
|
|
|
// State.
|
|
#include "doomstat.h"
|
|
#include "r_state.h"
|
|
|
|
//#include "r_local.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
R_StoreWallRange
|
|
( int start,
|
|
int stop );
|
|
|
|
|
|
|
|
|
|
//
|
|
// R_ClearDrawSegs
|
|
//
|
|
void R_ClearDrawSegs( void )
|
|
{
|
|
::g->ds_p = ::g->drawsegs;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ClipWallSegment
|
|
// Clips the given range of columns
|
|
// and includes it in the new clip list.
|
|
//
|
|
|
|
|
|
|
|
// ::g->newend is one past the last valid seg
|
|
|
|
|
|
|
|
|
|
//
|
|
// R_ClipSolidWallSegment
|
|
// Does handle solid walls,
|
|
// e.g. single sided LineDefs (middle texture)
|
|
// that entirely block the view.
|
|
//
|
|
void
|
|
R_ClipSolidWallSegment
|
|
( int first,
|
|
int last )
|
|
{
|
|
cliprange_t* next;
|
|
cliprange_t* start;
|
|
|
|
// Find the first range that touches the range
|
|
// (adjacent pixels are touching).
|
|
start = ::g->solidsegs;
|
|
while( start->last < first - 1 )
|
|
{
|
|
start++;
|
|
}
|
|
|
|
if( first < start->first )
|
|
{
|
|
if( last < start->first - 1 )
|
|
{
|
|
// Post is entirely visible (above start),
|
|
// so insert a new clippost.
|
|
R_StoreWallRange( first, last );
|
|
next = ::g->newend;
|
|
::g->newend++;
|
|
|
|
while( next != start )
|
|
{
|
|
*next = *( next - 1 );
|
|
next--;
|
|
}
|
|
next->first = first;
|
|
next->last = last;
|
|
return;
|
|
}
|
|
|
|
// There is a fragment above *start.
|
|
R_StoreWallRange( first, start->first - 1 );
|
|
// Now adjust the clip size.
|
|
start->first = first;
|
|
}
|
|
|
|
// Bottom contained in start?
|
|
if( last <= start->last )
|
|
{
|
|
return;
|
|
}
|
|
|
|
next = start;
|
|
while( last >= ( next + 1 )->first - 1 )
|
|
{
|
|
// There is a fragment between two posts.
|
|
R_StoreWallRange( next->last + 1, ( next + 1 )->first - 1 );
|
|
next++;
|
|
|
|
if( last <= next->last )
|
|
{
|
|
// Bottom is contained in next.
|
|
// Adjust the clip size.
|
|
start->last = next->last;
|
|
goto crunch;
|
|
}
|
|
}
|
|
|
|
// There is a fragment after *next.
|
|
R_StoreWallRange( next->last + 1, last );
|
|
// Adjust the clip size.
|
|
start->last = last;
|
|
|
|
// Remove start+1 to next from the clip list,
|
|
// because start now covers their area.
|
|
crunch:
|
|
if( next == start )
|
|
{
|
|
// Post just extended past the bottom of one post.
|
|
return;
|
|
}
|
|
|
|
|
|
while( next++ != ::g->newend )
|
|
{
|
|
// Remove a post.
|
|
*++start = *next;
|
|
}
|
|
|
|
::g->newend = start + 1;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// R_ClipPassWallSegment
|
|
// Clips the given range of columns,
|
|
// but does not includes it in the clip list.
|
|
// Does handle windows,
|
|
// e.g. LineDefs with upper and lower texture.
|
|
//
|
|
void
|
|
R_ClipPassWallSegment
|
|
( int first,
|
|
int last )
|
|
{
|
|
cliprange_t* start;
|
|
|
|
// Find the first range that touches the range
|
|
// (adjacent pixels are touching).
|
|
start = ::g->solidsegs;
|
|
while( start->last < first - 1 )
|
|
{
|
|
start++;
|
|
}
|
|
|
|
if( first < start->first )
|
|
{
|
|
if( last < start->first - 1 )
|
|
{
|
|
// Post is entirely visible (above start).
|
|
R_StoreWallRange( first, last );
|
|
return;
|
|
}
|
|
|
|
// There is a fragment above *start.
|
|
R_StoreWallRange( first, start->first - 1 );
|
|
}
|
|
|
|
// Bottom contained in start?
|
|
if( last <= start->last )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while( last >= ( start + 1 )->first - 1 )
|
|
{
|
|
// There is a fragment between two posts.
|
|
R_StoreWallRange( start->last + 1, ( start + 1 )->first - 1 );
|
|
start++;
|
|
|
|
if( last <= start->last )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// There is a fragment after *next.
|
|
R_StoreWallRange( start->last + 1, last );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// R_ClearClipSegs
|
|
//
|
|
void R_ClearClipSegs( void )
|
|
{
|
|
::g->solidsegs[0].first = -0x7fffffff;
|
|
::g->solidsegs[0].last = -1;
|
|
::g->solidsegs[1].first = ::g->viewwidth;
|
|
::g->solidsegs[1].last = 0x7fffffff;
|
|
::g->newend = ::g->solidsegs + 2;
|
|
}
|
|
|
|
//
|
|
// R_AddLine
|
|
// Clips the given segment
|
|
// and adds any visible pieces to the line list.
|
|
//
|
|
void R_AddLine( seg_t* line )
|
|
{
|
|
int x1;
|
|
int x2;
|
|
angle_t angle1;
|
|
angle_t angle2;
|
|
angle_t span;
|
|
angle_t tspan;
|
|
|
|
::g->curline = line;
|
|
|
|
// OPTIMIZE: quickly reject orthogonal back ::g->sides.
|
|
angle1 = R_PointToAngle( line->v1->x, line->v1->y );
|
|
angle2 = R_PointToAngle( line->v2->x, line->v2->y );
|
|
|
|
// Clip to view edges.
|
|
// OPTIMIZE: make constant out of 2*::g->clipangle (FIELDOFVIEW).
|
|
span = angle1 - angle2;
|
|
|
|
// Back side? I.e. backface culling?
|
|
if( span >= ANG180 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
extern angle_t GetViewAngle();
|
|
// Global angle needed by segcalc.
|
|
::g->rw_angle1 = angle1;
|
|
angle1 -= GetViewAngle();
|
|
angle2 -= GetViewAngle();
|
|
|
|
tspan = angle1 + ::g->clipangle;
|
|
if( tspan > 2 *::g->clipangle )
|
|
{
|
|
tspan -= 2 *::g->clipangle;
|
|
|
|
// Totally off the left edge?
|
|
if( tspan >= span )
|
|
{
|
|
return;
|
|
}
|
|
|
|
angle1 = ::g->clipangle;
|
|
}
|
|
tspan = ::g->clipangle - angle2;
|
|
if( tspan > 2 *::g->clipangle )
|
|
{
|
|
tspan -= 2 *::g->clipangle;
|
|
|
|
// Totally off the left edge?
|
|
if( tspan >= span )
|
|
{
|
|
return;
|
|
}
|
|
angle2 = UINT_MAX - ::g->clipangle + 1; // ALANHACK UNSIGNED, SRS - make uint math explicit
|
|
}
|
|
|
|
// The seg is in the view range,
|
|
// but not necessarily visible.
|
|
angle1 = ( angle1 + ANG90 ) >> ANGLETOFINESHIFT;
|
|
angle2 = ( angle2 + ANG90 ) >> ANGLETOFINESHIFT;
|
|
x1 = ::g->viewangletox[angle1];
|
|
x2 = ::g->viewangletox[angle2];
|
|
|
|
// Does not cross a pixel?
|
|
if( x1 == x2 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
::g->backsector = line->backsector;
|
|
|
|
// Single sided line?
|
|
if( !::g->backsector )
|
|
{
|
|
goto clipsolid;
|
|
}
|
|
|
|
// Closed door.
|
|
if( ::g->backsector->ceilingheight <= ::g->frontsector->floorheight
|
|
|| ::g->backsector->floorheight >= ::g->frontsector->ceilingheight )
|
|
{
|
|
goto clipsolid;
|
|
}
|
|
|
|
// Window.
|
|
if( ::g->backsector->ceilingheight != ::g->frontsector->ceilingheight
|
|
|| ::g->backsector->floorheight != ::g->frontsector->floorheight )
|
|
{
|
|
goto clippass;
|
|
}
|
|
|
|
// Reject empty ::g->lines used for triggers
|
|
// and special ::g->events.
|
|
// Identical floor and ceiling on both ::g->sides,
|
|
// identical light levels on both ::g->sides,
|
|
// and no middle texture.
|
|
if( ::g->backsector->ceilingpic == ::g->frontsector->ceilingpic
|
|
&& ::g->backsector->floorpic == ::g->frontsector->floorpic
|
|
&& ::g->backsector->lightlevel == ::g->frontsector->lightlevel
|
|
&& ::g->curline->sidedef->midtexture == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
clippass:
|
|
R_ClipPassWallSegment( x1, x2 - 1 );
|
|
return;
|
|
|
|
clipsolid:
|
|
R_ClipSolidWallSegment( x1, x2 - 1 );
|
|
}
|
|
|
|
|
|
//
|
|
// R_CheckBBox
|
|
// Checks BSP node/subtree bounding box.
|
|
// Returns true
|
|
// if some part of the bbox might be visible.
|
|
//
|
|
|
|
|
|
qboolean R_CheckBBox( fixed_t* bspcoord )
|
|
{
|
|
int boxx;
|
|
int boxy;
|
|
int boxpos;
|
|
|
|
fixed_t x1;
|
|
fixed_t y1;
|
|
fixed_t x2;
|
|
fixed_t y2;
|
|
|
|
angle_t angle1;
|
|
angle_t angle2;
|
|
angle_t span;
|
|
angle_t tspan;
|
|
|
|
cliprange_t* start;
|
|
|
|
int sx1;
|
|
int sx2;
|
|
|
|
extern fixed_t GetViewX();
|
|
extern fixed_t GetViewY();
|
|
// Find the corners of the box
|
|
// that define the edges from current viewpoint.
|
|
if( GetViewX() <= bspcoord[BOXLEFT] )
|
|
{
|
|
boxx = 0;
|
|
}
|
|
else if( GetViewX() < bspcoord[BOXRIGHT] )
|
|
{
|
|
boxx = 1;
|
|
}
|
|
else
|
|
{
|
|
boxx = 2;
|
|
}
|
|
|
|
if( GetViewY() >= bspcoord[BOXTOP] )
|
|
{
|
|
boxy = 0;
|
|
}
|
|
else if( GetViewY() > bspcoord[BOXBOTTOM] )
|
|
{
|
|
boxy = 1;
|
|
}
|
|
else
|
|
{
|
|
boxy = 2;
|
|
}
|
|
|
|
boxpos = ( boxy << 2 ) + boxx;
|
|
if( boxpos == 5 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
x1 = bspcoord[::g->checkcoord[boxpos][0]];
|
|
y1 = bspcoord[::g->checkcoord[boxpos][1]];
|
|
x2 = bspcoord[::g->checkcoord[boxpos][2]];
|
|
y2 = bspcoord[::g->checkcoord[boxpos][3]];
|
|
|
|
// check clip list for an open space
|
|
extern angle_t GetViewAngle();
|
|
angle1 = R_PointToAngle( x1, y1 ) - GetViewAngle();
|
|
angle2 = R_PointToAngle( x2, y2 ) - GetViewAngle();
|
|
|
|
span = angle1 - angle2;
|
|
|
|
// Sitting on a line?
|
|
if( span >= ANG180 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
tspan = angle1 + ::g->clipangle;
|
|
|
|
if( tspan > 2 *::g->clipangle )
|
|
{
|
|
tspan -= 2 *::g->clipangle;
|
|
|
|
// Totally off the left edge?
|
|
if( tspan >= span )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
angle1 = ::g->clipangle;
|
|
}
|
|
tspan = ::g->clipangle - angle2;
|
|
if( tspan > 2 *::g->clipangle )
|
|
{
|
|
tspan -= 2 *::g->clipangle;
|
|
|
|
// Totally off the left edge?
|
|
if( tspan >= span )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
angle2 = UINT_MAX - ::g->clipangle + 1; // ALANHACK UNSIGNED, SRS - make uint math explicit
|
|
}
|
|
|
|
|
|
// Find the first clippost
|
|
// that touches the source post
|
|
// (adjacent pixels are touching).
|
|
angle1 = ( angle1 + ANG90 ) >> ANGLETOFINESHIFT;
|
|
angle2 = ( angle2 + ANG90 ) >> ANGLETOFINESHIFT;
|
|
sx1 = ::g->viewangletox[angle1];
|
|
sx2 = ::g->viewangletox[angle2];
|
|
|
|
// Does not cross a pixel.
|
|
if( sx1 == sx2 )
|
|
{
|
|
return false;
|
|
}
|
|
sx2--;
|
|
|
|
start = ::g->solidsegs;
|
|
while( start->last < sx2 )
|
|
{
|
|
start++;
|
|
}
|
|
|
|
if( sx1 >= start->first
|
|
&& sx2 <= start->last )
|
|
{
|
|
// The clippost contains the new span.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// R_Subsector
|
|
// Determine floor/ceiling planes.
|
|
// Add ::g->sprites of things in sector.
|
|
// Draw one or more line segments.
|
|
//
|
|
void R_Subsector( int num )
|
|
{
|
|
int count;
|
|
seg_t* line;
|
|
subsector_t* sub;
|
|
|
|
#ifdef RANGECHECK
|
|
if( num >=::g->numsubsectors )
|
|
I_Error( "R_Subsector: ss %i with numss = %i",
|
|
num,
|
|
::g->numsubsectors );
|
|
#endif
|
|
|
|
::g->sscount++;
|
|
sub = &::g->subsectors[num];
|
|
::g->frontsector = sub->sector;
|
|
count = sub->numlines;
|
|
line = &::g->segs[sub->firstline];
|
|
|
|
if( ::g->frontsector->floorheight < ::g->viewz )
|
|
{
|
|
::g->floorplane = R_FindPlane( ::g->frontsector->floorheight,
|
|
::g->frontsector->floorpic,
|
|
::g->frontsector->lightlevel );
|
|
}
|
|
else
|
|
{
|
|
::g->floorplane = NULL;
|
|
}
|
|
|
|
if( ::g->frontsector->ceilingheight > ::g->viewz
|
|
|| ::g->frontsector->ceilingpic == ::g->skyflatnum )
|
|
{
|
|
::g->ceilingplane = R_FindPlane( ::g->frontsector->ceilingheight,
|
|
::g->frontsector->ceilingpic,
|
|
::g->frontsector->lightlevel );
|
|
}
|
|
else
|
|
{
|
|
::g->ceilingplane = NULL;
|
|
}
|
|
|
|
R_AddSprites( ::g->frontsector );
|
|
|
|
while( count-- )
|
|
{
|
|
R_AddLine( line );
|
|
line++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// RenderBSPNode
|
|
// Renders all ::g->subsectors below a given node,
|
|
// traversing subtree recursively.
|
|
// Just call with BSP root.
|
|
void R_RenderBSPNode( int bspnum )
|
|
{
|
|
node_t* bsp;
|
|
int side;
|
|
|
|
// Found a subsector?
|
|
if( bspnum & NF_SUBSECTOR )
|
|
{
|
|
if( bspnum == -1 )
|
|
{
|
|
R_Subsector( 0 );
|
|
}
|
|
else
|
|
{
|
|
R_Subsector( bspnum & ( ~NF_SUBSECTOR ) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
bsp = &::g->nodes[bspnum];
|
|
|
|
extern fixed_t GetViewX();
|
|
extern fixed_t GetViewY();
|
|
// Decide which side the view point is on.
|
|
side = R_PointOnSide( GetViewX(), GetViewY(), bsp );
|
|
|
|
// Recursively divide front space.
|
|
R_RenderBSPNode( bsp->children[side] );
|
|
|
|
// Possibly divide back space.
|
|
if( R_CheckBBox( bsp->bbox[side ^ 1] ) )
|
|
{
|
|
R_RenderBSPNode( bsp->children[side ^ 1] );
|
|
}
|
|
}
|
|
|
|
|
|
|