shithub: hexen

ref: 78e9b63d292b42b7fc988e814fed81bab6b338c7
dir: /r_main.c/

View raw version

//**************************************************************************
//**
//** r_main.c : Heretic 2 : Raven Software, Corp.
//**
//** $Revision: 373 $
//** $Date: 2009-05-19 21:14:28 +0300 (Tue, 19 May 2009) $
//**
//**************************************************************************

#include "h2stdinc.h"

#ifndef RENDER3D

#include "h2def.h"
#include "r_local.h"

int			viewangleoffset;

int			validcount = 1;		// increment every time a check is made

lighttable_t		*fixedcolormap;
extern	lighttable_t	**walllights;

int			centerx, centery;
fixed_t			centerxfrac, centeryfrac;
fixed_t			projection;

int			framecount;		// just for profiling purposes

int			sscount, linecount, loopcount;

fixed_t			viewx, viewy, viewz;
angle_t			viewangle;
fixed_t			viewcos, viewsin;
player_t		*viewplayer;

int			detailshift;		// 0 = high, 1 = low

//
// precalculated math tables
//
angle_t			clipangle;

// The viewangletox[viewangle + FINEANGLES/4] lookup maps the visible view
// angles  to screen X coordinates, flattening the arc to a flat projection
// plane.  There will be many angles mapped to the same X.
int			viewangletox[FINEANGLES/2];

// The xtoviewangleangle[] table maps a screen pixel to the lowest viewangle
// that maps back to x ranges from clipangle to -clipangle
angle_t			xtoviewangle[SCREENWIDTH+1];

// the finetangentgent[angle+FINEANGLES/4] table holds the fixed_t tangent
// values for view angles, ranging from H2MININT to 0 to H2MAXINT.
// fixed_t		finetangent[FINEANGLES/2];

// fixed_t		finesine[5*FINEANGLES/4];
fixed_t			*finecosine = &finesine[FINEANGLES/4];

lighttable_t		*scalelight[LIGHTLEVELS][MAXLIGHTSCALE];
lighttable_t		*scalelightfixed[MAXLIGHTSCALE];
lighttable_t		*zlight[LIGHTLEVELS][MAXLIGHTZ];

int			extralight;		// bumped light from gun blasts

void			(*colfunc) (void);
void			(*basecolfunc) (void);
void			(*fuzzcolfunc) (void);
void			(*transcolfunc) (void);
void			(*spanfunc) (void);

/*
===================
=
= R_AddPointToBox
=
===================
*/

/*
void R_AddPointToBox (int x, int y, fixed_t *box)
{
	if (x < box[BOXLEFT])
		box[BOXLEFT] = x;
	if (x > box[BOXRIGHT])
		box[BOXRIGHT] = x;
	if (y < box[BOXBOTTOM])
		box[BOXBOTTOM] = y;
	if (y > box[BOXTOP])
		box[BOXTOP] = y;
}
*/


/*
===============================================================================
=
= R_PointOnSide
=
= Returns side 0 (front) or 1 (back)
===============================================================================
*/

int	R_PointOnSide (fixed_t x, fixed_t y, node_t *node)
{
	fixed_t	dx, dy;
	fixed_t	left, right;

	if (!node->dx)
	{
		if (x <= node->x)
			return node->dy > 0;
		return node->dy < 0;
	}
	if (!node->dy)
	{
		if (y <= node->y)
			return node->dx < 0;
		return node->dx > 0;
	}

	dx = (x - node->x);
	dy = (y - node->y);

// try to quickly decide by looking at sign bits
	if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000)
	{
		if ((node->dy ^ dx) & 0x80000000)
			return 1;	// (left is negative)
		return 0;
	}

	left = FixedMul (node->dy>>FRACBITS , dx);
	right = FixedMul (dy , node->dx>>FRACBITS);

	if (right < left)
		return 0;		// front side
	return 1;			// back side
}


int	R_PointOnSegSide (fixed_t x, fixed_t y, seg_t *line)
{
	fixed_t	lx, ly;
	fixed_t	ldx, ldy;
	fixed_t	dx, dy;
	fixed_t	left, right;

	lx = line->v1->x;
	ly = line->v1->y;

	ldx = line->v2->x - lx;
	ldy = line->v2->y - ly;

	if (!ldx)
	{
		if (x <= lx)
			return ldy > 0;
		return ldy < 0;
	}
	if (!ldy)
	{
		if (y <= ly)
			return ldx < 0;
		return ldx > 0;
	}

	dx = (x - lx);
	dy = (y - ly);

// try to quickly decide by looking at sign bits
	if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000)
	{
		if ((ldy ^ dx) & 0x80000000)
			return 1;	// (left is negative)
		return 0;
	}

	left = FixedMul (ldy>>FRACBITS, dx);
	right = FixedMul (dy, ldx>>FRACBITS);

	if (right < left)
		return 0;		// front side
	return 1;			// back side
}


/*
===============================================================================
=
= R_PointToAngle
=
===============================================================================
*/

// to get a global angle from cartesian coordinates, the coordinates are
// flipped until they are in the first octant of the coordinate system, then
// the y (<=x) is scaled and divided by x to get a tangent (slope) value
// which is looked up in the tantoangle[] table.  The +1 size is to handle
// the case when x==y without additional checking.
#define	SLOPERANGE	2048
#define	SLOPEBITS	11
#define	DBITS		(FRACBITS-SLOPEBITS)

extern	int	tantoangle[SLOPERANGE+1];		// get from tables.c

//int	tantoangle[SLOPERANGE+1];

static int SlopeDiv (unsigned num, unsigned den)
{
	unsigned ans;
	if (den < 512)
		return SLOPERANGE;
	ans = (num<<3) / (den>>8);
	return ans <= SLOPERANGE ? ans : SLOPERANGE;
}

angle_t R_PointToAngle (fixed_t x, fixed_t y)
{
	x -= viewx;
	y -= viewy;
	if ( (!x) && (!y) )
		return 0;
	if (x >= 0)
	{	// x >= 0
		if (y >= 0)
		{	// y >= 0
			if (x > y)
				return tantoangle[SlopeDiv(y,x)];	// octant 0
			else
				return ANG90 - 1 - tantoangle[SlopeDiv(x,y)];	// octant 1
		}
		else
		{	// y < 0
			y = -y;
			if (x > y)
				return -tantoangle[SlopeDiv(y,x)];	// octant 8
			else
				return ANG270 + tantoangle[SlopeDiv(x,y)];	// octant 7
		}
	}
	else
	{	// x < 0
		x = -x;
		if (y >= 0)
		{	// y >= 0
			if (x > y)
				return ANG180 - 1 - tantoangle[SlopeDiv(y,x)];	// octant 3
			else
				return ANG90 + tantoangle[SlopeDiv(x,y)];	// octant 2
		}
		else
		{	// y < 0
			y = -y;
			if (x > y)
				return ANG180 + tantoangle[SlopeDiv(y,x)];	// octant 4
			else
				return ANG270 - 1 - tantoangle[SlopeDiv(x,y)];	// octant 5
		}
	}

	return 0;
}


angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2)
{
	viewx = x1;
	viewy = y1;
	return R_PointToAngle (x2, y2);
}


fixed_t	R_PointToDist (fixed_t x, fixed_t y)
{
	int		angle;
	fixed_t	dx, dy, temp;
	fixed_t	dist;

	dx = abs(x - viewx);
	dy = abs(y - viewy);

	if (dy > dx)
	{
		temp = dx;
		dx = dy;
		dy = temp;
	}

	angle = (tantoangle[ FixedDiv(dy,dx)>>DBITS ]+ANG90) >> ANGLETOFINESHIFT;

	dist = FixedDiv (dx, finesine[angle]);	// use as cosine

	return dist;
}


/*
=================
=
= R_InitPointToAngle
=
=================
*/

void R_InitPointToAngle (void)
{
// now getting from tables.c
#if 0
	int	i;
	int	t;	/* int32_t */
	float	f;
//
// slope (tangent) to angle lookup
//
	for (i = 0; i <= SLOPERANGE; i++)
	{
		f = atan((float)i / SLOPERANGE) / (3.141592657*2);
		t = 0xffffffff * f;
		tantoangle[i] = t;
	}
#endif
}

//=============================================================================

/*
================
=
= R_ScaleFromGlobalAngle
=
= Returns the texture mapping scale for the current line at the given angle
= rw_distance must be calculated first
================
*/

fixed_t R_ScaleFromGlobalAngle (angle_t visangle)
{
	fixed_t		scale;
	int		anglea, angleb;
	int		sinea, sineb;
	fixed_t		num, den;

#if 0
{
	fixed_t		dist, z;
	fixed_t		sinv, cosv;

	sinv = finesine[(visangle - rw_normalangle)>>ANGLETOFINESHIFT];
	dist = FixedDiv (rw_distance, sinv);
	cosv = finecosine[(viewangle - visangle)>>ANGLETOFINESHIFT];
	z = abs(FixedMul (dist, cosv));
	scale = FixedDiv(projection, z);
	return scale;
}
#endif

	anglea = ANG90 + (visangle - viewangle);
	angleb = ANG90 + (visangle - rw_normalangle);
// bothe sines are allways positive
	sinea = finesine[anglea>>ANGLETOFINESHIFT];
	sineb = finesine[angleb>>ANGLETOFINESHIFT];
	num = FixedMul(projection, sineb)<<detailshift;
	den = FixedMul(rw_distance, sinea);
	if (den > num>>16)
	{
		scale = FixedDiv (num, den);
		if (scale > 64*FRACUNIT)
			scale = 64*FRACUNIT;
		else if (scale < 256)
			scale = 256;
	}
	else
		scale = 64*FRACUNIT;

	return scale;
}


/*
=================
=
= R_InitTables
=
=================
*/

void R_InitTables (void)
{
// now getting from tables.c
#if 0
	int		i;
	float		a, fv;
	int		t;

//
// viewangle tangent table
//
	for (i = 0; i < FINEANGLES/2; i++)
	{
		a = (i - FINEANGLES/4 + 0.5) * PI * 2 / FINEANGLES;
		fv = FRACUNIT * tan(a);
		t = fv;
		finetangent[i] = t;
	}

//
// finesine table
//
	for (i = 0; i < 5*FINEANGLES/4; i++)
	{
// OPTIMIZE: mirror...
		a = (i + 0.5) * PI * 2 / FINEANGLES;
		t = FRACUNIT * sin(a);
		finesine[i] = t;
	}
#endif
}


/*
=================
=
= R_InitTextureMapping
=
=================
*/

void R_InitTextureMapping (void)
{
	int			i;
	int			x;
	int			t;
	fixed_t		focallength;

//
// use tangent table to generate viewangletox
// viewangletox will give the next greatest x after the view angle
//
	// calc focallength so FIELDOFVIEW angles covers SCREENWIDTH
	focallength = FixedDiv (centerxfrac, finetangent[FINEANGLES/4 + FIELDOFVIEW/2]);

	for (i = 0; i < FINEANGLES/2; i++)
	{
		if (finetangent[i] > FRACUNIT*2)
			t = -1;
		else if (finetangent[i] < -FRACUNIT*2)
			t = viewwidth + 1;
		else
		{
			t = FixedMul (finetangent[i], focallength);
			t = (centerxfrac - t + FRACUNIT - 1)>>FRACBITS;
			if (t < -1)
				t = -1;
			else if (t > viewwidth + 1)
				t = viewwidth + 1;
		}
		viewangletox[i] = t;
	}

//
// scan viewangletox[] to generate xtoviewangleangle[]
//
// xtoviewangle will give the smallest view angle that maps to x
	for (x = 0; x <= viewwidth; x++)
	{
		i = 0;
		while (viewangletox[i] > x)
			i++;
		xtoviewangle[x] = (i<<ANGLETOFINESHIFT) - ANG90;
	}

//
// take out the fencepost cases from viewangletox
//
	for (i = 0; i < FINEANGLES/2; i++)
	{
		t = FixedMul (finetangent[i], focallength);
		t = centerx - t;
		if (viewangletox[i] == -1)
			viewangletox[i] = 0;
		else if (viewangletox[i] == viewwidth + 1)
			viewangletox[i]  = viewwidth;
	}

	clipangle = xtoviewangle[0];
}

//=============================================================================

/*
====================
=
= R_InitLightTables
=
= Only inits the zlight table, because the scalelight table changes
= with view size
=
====================
*/

#define	DISTMAP		2

void R_InitLightTables (void)
{
	int		i, j, level, start_map;
	int		scale;

//
// Calculate the light levels to use for each level / distance combination
//
	for (i = 0; i < LIGHTLEVELS; i++)
	{
		start_map = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
		for (j = 0; j < MAXLIGHTZ; j++)
		{
			scale = FixedDiv ((SCREENWIDTH/2*FRACUNIT), (j + 1)<<LIGHTZSHIFT);
			scale >>= LIGHTSCALESHIFT;
			level = start_map - scale/DISTMAP;
			if (level < 0)
				level = 0;
			if (level >= NUMCOLORMAPS)
				level = NUMCOLORMAPS-1;
			zlight[i][j] = colormaps + level*256;
		}
	}
}


/*
==============
=
= R_SetViewSize
=
= Don't really change anything here, because i might be in the middle of
= a refresh.  The change will take effect next refresh.
=
==============
*/

static int	setblocks, setdetail;
boolean		setsizeneeded;

void R_SetViewSize (int blocks, int detail)
{
	setsizeneeded = true;
	setblocks = blocks;
	setdetail = detail;
}

/*
==============
=
= R_ExecuteSetViewSize
=
==============
*/

void R_ExecuteSetViewSize (void)
{
	fixed_t	cosadj, dy;
	int		i, j, level, start_map;

	setsizeneeded = false;

	if (setblocks == 11)
	{
		scaledviewwidth = SCREENWIDTH;
		viewheight = SCREENHEIGHT;
	}
	else
	{
		scaledviewwidth = setblocks*32;
		viewheight = (setblocks*161/10);
	}

	detailshift = setdetail;
	viewwidth = scaledviewwidth>>detailshift;

	centery = viewheight/2;
	centerx = viewwidth/2;
	centerxfrac = centerx<<FRACBITS;
	centeryfrac = centery<<FRACBITS;
	projection = centerxfrac;

	if (!detailshift)
	{
		colfunc = basecolfunc = R_DrawColumn;
		fuzzcolfunc = R_DrawFuzzColumn;
		transcolfunc = R_DrawTranslatedColumn;
		spanfunc = R_DrawSpan;
	}
	else
	{
		colfunc = basecolfunc = R_DrawColumnLow;
		fuzzcolfunc = R_DrawFuzzColumn;
		transcolfunc = R_DrawTranslatedColumn;
		spanfunc = R_DrawSpanLow;
	}

	R_InitBuffer (scaledviewwidth, viewheight);

	R_InitTextureMapping ();

//
// psprite scales
//
	pspritescale = FRACUNIT*viewwidth/SCREENWIDTH;
	pspriteiscale = FRACUNIT*SCREENWIDTH/viewwidth;

//
// thing clipping
//
	for (i = 0; i < viewwidth; i++)
		screenheightarray[i] = viewheight;

//
// planes
//
	for (i = 0; i < viewheight; i++)
	{
		dy = ((i - viewheight/2)<<FRACBITS) + FRACUNIT/2;
		dy = abs(dy);
		yslope[i] = FixedDiv ((viewwidth<<detailshift)/2*FRACUNIT, dy);
	}

	for (i = 0; i < viewwidth; i++)
	{
		cosadj = abs(finecosine[xtoviewangle[i]>>ANGLETOFINESHIFT]);
		distscale[i] = FixedDiv (FRACUNIT, cosadj);
	}

//
// Calculate the light levels to use for each level / scale combination
//
	for (i = 0; i < LIGHTLEVELS; i++)
	{
		start_map = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
		for (j = 0; j < MAXLIGHTSCALE; j++)
		{
			level = start_map - j*SCREENWIDTH/(viewwidth<<detailshift)/DISTMAP;
			if (level < 0)
				level = 0;
			if (level >= NUMCOLORMAPS)
				level = NUMCOLORMAPS-1;
			scalelight[i][j] = colormaps + level*256;
		}
	}

//
// draw the border
//
	R_DrawViewBorder ();	// erase old menu stuff
}


/*
==============
=
= R_Init
=
==============
*/

int detailLevel;
int screenblocks;

void R_Init(void)
{
	R_InitData();
	R_InitPointToAngle();
	R_InitTables();
	// viewwidth / viewheight / detailLevel are set by the defaults
	R_SetViewSize(screenblocks, detailLevel);
	R_InitPlanes();
	R_InitLightTables();
	R_InitSkyMap();
	R_InitTranslationTables();
	framecount = 0;
}

/*
==============
=
= R_PointInSubsector
=
==============
*/

subsector_t *R_PointInSubsector (fixed_t x, fixed_t y)
{
	node_t	*node;
	int		side, nodenum;

	if (!numnodes)	// single subsector is a special case
		return subsectors;

	nodenum = numnodes - 1;

	while (! (nodenum & NF_SUBSECTOR) )
	{
		node = &nodes[nodenum];
		side = R_PointOnSide (x, y, node);
		nodenum = node->children[side];
	}

	return &subsectors[nodenum & ~NF_SUBSECTOR];
}

//----------------------------------------------------------------------------
//
// PROC R_SetupFrame
//
//----------------------------------------------------------------------------

void R_SetupFrame(player_t *player)
{
	int i;
	int tableAngle;
	int tempCentery;
	int intensity;

	viewplayer = player;
	viewangle = player->mo->angle + viewangleoffset;
	tableAngle = viewangle>>ANGLETOFINESHIFT;
	viewx = player->mo->x;
	viewy = player->mo->y;

	if (localQuakeHappening[displayplayer] && !paused)
	{
		intensity = localQuakeHappening[displayplayer];
		viewx += ((M_Random() % (intensity<<2)) - (intensity<<1)) << FRACBITS;
		viewy += ((M_Random() % (intensity<<2)) - (intensity<<1)) << FRACBITS;
	}

	extralight = player->extralight;
	viewz = player->viewz;

	tempCentery = viewheight/2 + (player->lookdir)*screenblocks/10;
	if (centery != tempCentery)
	{
		centery = tempCentery;
		centeryfrac = centery<<FRACBITS;
		for (i = 0; i < viewheight; i++)
		{
			yslope[i] = FixedDiv (  (viewwidth<<detailshift)/2*FRACUNIT,
						abs(((i - centery)<<FRACBITS) + FRACUNIT/2) );
		}
	}
	viewsin = finesine[tableAngle];
	viewcos = finecosine[tableAngle];
	sscount = 0;
	if (player->fixedcolormap)
	{
		fixedcolormap = colormaps + player->fixedcolormap*256*sizeof(lighttable_t);
		walllights = scalelightfixed;
		for (i = 0; i < MAXLIGHTSCALE; i++)
		{
			scalelightfixed[i] = fixedcolormap;
		}
	}
	else
	{
		fixedcolormap = 0;
	}
	framecount++;
	validcount++;
	if (BorderNeedRefresh)
	{
		if (setblocks < 10)
		{
			R_DrawViewBorder();
		}
		BorderNeedRefresh = false;
		BorderTopRefresh = false;
		UpdateState |= I_FULLSCRN;
	}
	if (BorderTopRefresh)
	{
		if (setblocks < 10)
		{
			R_DrawTopBorder();
		}
		BorderTopRefresh = false;
		UpdateState |= I_MESSAGES;
	}
}

/*
==============
=
= R_RenderView
=
==============
*/

void R_RenderPlayerView (player_t *player)
{
	R_SetupFrame (player);

	R_ClearClipSegs ();
	R_ClearDrawSegs ();
	R_ClearPlanes ();

	R_ClearSprites ();
	NetUpdate ();	// check for new console commands

	// Make displayed player invisible locally
	if (localQuakeHappening[displayplayer] && gamestate == GS_LEVEL)
	{
		players[displayplayer].mo->flags2 |= MF2_DONTDRAW;
		R_RenderBSPNode (numnodes - 1);	// head node is the last node output
		players[displayplayer].mo->flags2 &= ~MF2_DONTDRAW;
	}
	else
	{
		R_RenderBSPNode (numnodes - 1);	// head node is the last node output
	}

	NetUpdate ();	// check for new console commands

	R_DrawPlanes ();
	NetUpdate ();	// check for new console commands

	R_DrawMasked ();
	NetUpdate ();	// check for new console commands
}

#endif	/* RENDER3D */