/*
Copyright (C) 1996-1997 Id Software, Inc.

This program 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 2
of the License, or (at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// r_main.c

#include "quakedef.h"
#include "r_local.h"

//define	PASSAGES

void		*colormap;
//vec3_t		viewlightvec; // changed alias models lighting - removed
//alight_t	r_viewlighting = {128, 192, viewlightvec}; // changed alias models lighting - removed
float		r_time1;
int			r_numallocatededges;
qboolean	r_drawpolys;
qboolean	r_drawculledpolys;
qboolean	r_worldpolysbacktofront;
qboolean	r_recursiveaffinetriangles = true;
#define		r_pixbytes sizeof(pixel_t)
//int			r_pixbytes = 1;
float		r_aliasuvscale = 1.0;
int			r_outofsurfaces;
int			r_outofedges;

qboolean	r_dowarp, r_dowarpold, r_viewchanged;

int			numbtofpolys;
btofpoly_t	*pbtofpolys;
mvertex_t	*r_pcurrentvertbase;

int			c_surf;
int			r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs;
qboolean	r_surfsonstack;
int			r_clipflags;

#if !defined(FLASH)
byte		*r_stack_start;
#endif

pixel_t		*r_warpbuffer;

byte		*r_stack_start;

qboolean	r_fov_greater_than_90;

//
// view origin
//
vec3_t	vup, base_vup;
vec3_t	vpn, base_vpn;
vec3_t	vright, base_vright;
vec3_t	r_origin;

//
// screen size info
//
refdef_t	r_refdef;
float		xcenter, ycenter;
float		xscale, yscale;
float		xscaleinv, yscaleinv;
float		xscaleshrink, yscaleshrink;
float		aliasxscale, aliasyscale, aliasxcenter, aliasycenter;

int		screenwidth;

float	pixelAspect;
float	screenAspect;
float	verticalFieldOfView;
float	xOrigin, yOrigin;

mplane_t	screenedge[4];

//
// refresh flags
//
int		r_framecount = 1;	// so frame counts initialized to 0 don't match
int		r_visframecount;
int		d_spanpixcount;
int		r_polycount;
int		r_drawnpolycount;
int		r_wholepolycount;

#define		VIEWMODNAME_LENGTH	256
char		viewmodname[VIEWMODNAME_LENGTH+1];
int			modcount;

int			*pfrustum_indexes[4];
int			r_frustum_indexes[4*6];

int		reinit_surfcache = 1;	// if 1, surface cache is currently empty and
// must be reinitialized for current cache size

mleaf_t		*r_viewleaf, *r_oldviewleaf;

texture_t	*r_notexture_mip;

float		r_aliastransition, r_resfudge;

int		d_lightstylevalue[256];	// 8.8 fraction of base light value

float	dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2;
float	se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2;

void R_MarkLeaves(void);

cvar_t	r_draworder = {"r_draworder","0"};
cvar_t	r_speeds = {"r_speeds","0"};
cvar_t	r_timegraph = {"r_timegraph","0"};
cvar_t	r_graphheight = {"r_graphheight","10"};
cvar_t	r_clearcolor = {"r_clearcolor","2"};
cvar_t	r_waterwarp = {"r_waterwarp","0"};
cvar_t	r_fullbright = {"r_fullbright","0"};
cvar_t	r_drawentities = {"r_drawentities","1"};
cvar_t	r_drawviewmodel = {"r_drawviewmodel","1", true}; // saved in the config file - edited
cvar_t	r_aliasstats = {"r_polymodelstats","0"};
cvar_t	r_dspeeds = {"r_dspeeds","0"};
cvar_t	r_drawflat = {"r_drawflat", "0"};
cvar_t	r_ambient = {"r_ambient", "0"};
cvar_t	r_reportsurfout = {"r_reportsurfout", "0"};
cvar_t	r_maxsurfs = {"r_maxsurfs", "0"};
cvar_t	r_numsurfs = {"r_numsurfs", "0"};
cvar_t	r_reportedgeout = {"r_reportedgeout", "0"};
cvar_t	r_maxedges = {"r_maxedges", "0"};
cvar_t	r_numedges = {"r_numedges", "0"};
cvar_t	r_aliastransbase = {"r_aliastransbase", "200"};
cvar_t	r_aliastransadj = {"r_aliastransadj", "100"};
//cvar_t	r_letterbox = {"r_letterbox","0"}; // r_letterbox
// changed alias models lighting - begin
cvar_t	r_light_vec_x = {"r_light_vec_x", "-1"};
cvar_t	r_light_vec_y = {"r_light_vec_y", "0"};
cvar_t	r_light_vec_z = {"r_light_vec_z", "-1"};
cvar_t	r_light_style = {"r_light_style", "0", true};
// changed alias models lighting - end
cvar_t	r_wateralpha = {"r_wateralpha","1", true}; // translucent water
cvar_t	r_stereo_separation = {"r_stereo_separation","0", true}; // stereo 3D
int		r_stereo_side; // stereo 3D


extern 	cvar_t			scr_fov;
extern	unsigned int	d_8to24table[256];

//void CreatePassages (void); // removed
//void SetVisibilityByPassages (void); // removed

/*
==================
R_InitTextures
==================
*/
void	R_InitTextures(void)
{
    int		x,y, m;
    pixel_t	*dest;

// create a simple checkerboard texture for the default
    r_notexture_mip = Hunk_AllocName(sizeof(texture_t) + (16*16+8*8+4*4+2*2)*sizeof(pixel_t), "notexture");

    r_notexture_mip->width = r_notexture_mip->height = 16;
    r_notexture_mip->offsets[0] = sizeof(texture_t);
    r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16*sizeof(pixel_t);
    r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8*sizeof(pixel_t);
    r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4*sizeof(pixel_t);

    for (m=0 ; m<4 ; m++) {
        dest = (pixel_t*)((byte *)r_notexture_mip + r_notexture_mip->offsets[m]);
        for (y=0 ; y< (16>>m) ; y++)
            for (x=0 ; x< (16>>m) ; x++) {
                if (sizeof(pixel_t)==4) {
                    *dest++ =  d_8to24table[((y< (8>>m)) ^(x< (8>>m)))?0:0xff];
                } else {
                    *dest++ = ((y< (8>>m)) ^(x< (8>>m)))?0:0xff;
                }
            }
    }
}

/*
===============
R_Init
===============
*/
//void SCR_Adjust (void); // Edited
void R_LoadSky_f(void);  // skyboxes
void R_Init(void)
{

#if !defined(FLASH)
    int		dummy;

// get stack position so we can guess if we are going to overflow
    r_stack_start = (byte *)&dummy;
#endif


    R_InitTurb();

    Cmd_AddCommand("timerefresh", R_TimeRefresh_f);
    Cmd_AddCommand("pointfile", R_ReadPointFile_f);
    Cmd_AddCommand("loadsky", R_LoadSky_f);  // skyboxes

    Cvar_RegisterVariable(&r_draworder);
    Cvar_RegisterVariable(&r_speeds);
    Cvar_RegisterVariable(&r_timegraph);
    Cvar_RegisterVariable(&r_graphheight);
    Cvar_RegisterVariable(&r_drawflat);
    Cvar_RegisterVariable(&r_ambient);
    Cvar_RegisterVariable(&r_clearcolor);
    Cvar_RegisterVariable(&r_waterwarp);
    Cvar_RegisterVariable(&r_fullbright);
    Cvar_RegisterVariable(&r_drawentities);
    Cvar_RegisterVariable(&r_drawviewmodel);
    Cvar_RegisterVariable(&r_aliasstats);
    Cvar_RegisterVariable(&r_dspeeds);
    Cvar_RegisterVariable(&r_reportsurfout);
    Cvar_RegisterVariable(&r_maxsurfs);
    Cvar_RegisterVariable(&r_numsurfs);
    Cvar_RegisterVariable(&r_reportedgeout);
    Cvar_RegisterVariable(&r_maxedges);
    Cvar_RegisterVariable(&r_numedges);
    Cvar_RegisterVariable(&r_aliastransbase);
    Cvar_RegisterVariable(&r_aliastransadj);
    Cvar_RegisterVariable(&r_skyname);  // skyboxes
//	Cvar_RegisterVariableWithCallback (&r_letterbox, SCR_Adjust); // r_letterbox
    // changed alias models lighting - begin
    Cvar_RegisterVariable(&r_light_vec_x);
    Cvar_RegisterVariable(&r_light_vec_y);
    Cvar_RegisterVariable(&r_light_vec_z);
    Cvar_RegisterVariable(&r_light_style);
    // changed alias models lighting - end
    Cvar_RegisterVariable(&r_interpolation);  // model interpolation
    Cvar_RegisterVariable(&r_wateralpha);  // translucent water
    Cvar_RegisterVariable(&r_particlealpha);  // Edited
    Cvar_RegisterVariable(&sw_stipplealpha);  // Edited
    Cvar_RegisterVariable(&r_sprite_addblend);  // Edited
    Cvar_RegisterVariable(&r_stereo_separation);  // stereo 3D

    Cvar_SetValue("r_maxedges", (float)NUMSTACKEDGES);
    Cvar_SetValue("r_maxsurfs", (float)NUMSTACKSURFACES);

    view_clipplanes[0].leftedge = true;
    view_clipplanes[1].rightedge = true;
    view_clipplanes[1].leftedge = view_clipplanes[2].leftedge =
                                      view_clipplanes[3].leftedge = false;
    view_clipplanes[0].rightedge = view_clipplanes[2].rightedge =
                                       view_clipplanes[3].rightedge = false;

    r_refdef.xOrigin = XCENTERING;
    r_refdef.yOrigin = YCENTERING;

    R_InitParticles();

// TODO: collect 386-specific code in one place
#if	id386
    Sys_MakeCodeWriteable((long)R_EdgeCodeStart,
                          (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart);
#endif	// id386

    D_Init();
}

// skyboxes - begin
void CL_ParseEntityLump(char *entdata)
{
    char *data;
    char key[128], value[4096];
    extern cvar_t r_skyname;

    data = entdata;

    if (!data) {
        return;
    }
    data = COM_Parse(data);
    if (!data || com_token[0] != '{') {
        return;    // error
    }

    while (1) {
        data = COM_Parse(data);
        if (!data) {
            return;    // error
        }
        if (com_token[0] == '}') {
            break;    // end of worldspawn
        }

        if (com_token[0] == '_') {
            strcpy(key, com_token + 1);
        } else {
            strcpy(key, com_token);
        }

        while (key[strlen(key)-1] == ' ') {
            key[strlen(key)-1] = 0;    // remove trailing spaces
        }

        data = COM_Parse(data);
        if (!data) {
            return;    // error
        }
        strcpy(value, com_token);

        if (strcmp(key, "sky") == 0 || strcmp(key, "skyname") == 0 ||
                strcmp(key, "qlsky") == 0)
            //	Cvar_Set ("r_skyname", value);
            // begin
        {
            Cbuf_AddText(va("wait;loadsky %s\n", value));
            //	R_LoadSky(value);
            return; // Edited
        }
        // end
        // more checks here..
    }
    // begin
    if (r_skyname.string[0]) {
        Cbuf_AddText(va("wait;loadsky %s\n", r_skyname.string));
    }
    //	R_LoadSky(r_skyname.string); // crashes the engine if the r_skyname cvar is set before the game boots
    else {
        R_LoadSky("");
    }
    // end
}
// skyboxes - end

/*
===============
R_NewMap
===============
*/
void R_NewMap(void)
{
    int		i;

// clear out efrags in case the level hasn't been reloaded
// FIXME: is this one short?
    for (i=0 ; i<cl.worldmodel->numleafs ; i++) {
        cl.worldmodel->leafs[i].efrags = NULL;
    }

    CL_ParseEntityLump(cl.worldmodel->entities);  // skyboxes
    r_viewleaf = NULL;
    R_ClearParticles();
    R_InitSkyBox();  // skyboxes

    r_cnumsurfs = r_maxsurfs.value;

    if (r_cnumsurfs <= MINSURFACES) {
        r_cnumsurfs = MINSURFACES;
    }

    if (r_cnumsurfs > NUMSTACKSURFACES) {
        surfaces = Hunk_AllocName(r_cnumsurfs * sizeof(surf_t), "surfaces");
        surface_p = surfaces;
        surf_max = &surfaces[r_cnumsurfs];
        r_surfsonstack = false;
        // surface 0 doesn't really exist; it's just a dummy because index 0
        // is used to indicate no edge attached to surface
        surfaces--;
#if	id386 // no 386 assembler, no MASM
        R_SurfacePatch();
#endif // no 386 assembler, no MASM
    } else {
        r_surfsonstack = true;
    }

    r_maxedgesseen = 0;
    r_maxsurfsseen = 0;

    r_numallocatededges = r_maxedges.value;

    if (r_numallocatededges < MINEDGES) {
        r_numallocatededges = MINEDGES;
    }

    if (r_numallocatededges <= NUMSTACKEDGES) {
        auxedges = NULL;
    } else {
        auxedges = Hunk_AllocName(r_numallocatededges * sizeof(edge_t),
                                  "edges");
    }

    r_dowarpold = false;
    r_viewchanged = false;
#ifdef PASSAGES
    CreatePassages();
#endif
}


/*
===============
R_SetVrect
===============
*/
extern cvar_t temp1;
void R_SetVrect(vrect_t *pvrectin, vrect_t *pvrect, int lineadj)
{
    int		h;
    float	size;

    size = scr_viewsize.value > 100 ? 100 : scr_viewsize.value;
    if (cl.intermission) {
        size = 100;
        lineadj = 0;
    }
    size /= 100.0; // fixed

    h = pvrectin->height - lineadj;
    pvrect->width = pvrectin->width * size;
    if (pvrect->width < 96) {
        size = 96.0 / pvrectin->width;
        pvrect->width = 96;	// min for icons
    }
    pvrect->width &= ~7;
    pvrect->height = pvrectin->height * size;
    if (pvrect->height > h) { //pvrectin->height - lineadj) // optimized
        pvrect->height = h;    //pvrectin->height - lineadj; // optimized
    }

    pvrect->height &= ~1;

    pvrect->x = (pvrectin->width - pvrect->width)/2;
    pvrect->y = (h - pvrect->height)/2;

    // screen positioning - begin
    {
        int hspace = (int)(vid.width  / 40);//80);
        int vspace = (int)(vid.height / 30);//60);

        if (screen_left > hspace) {
            pvrect->x += screen_left - hspace;
            pvrect->width -= screen_left - hspace;
        }

        if (screen_right > hspace) {
            pvrect->width -= screen_right - hspace;
        }

        /*	if ((scr_con_current > screen_top) && (scr_con_current < vid.height))
        	{
        		pvrect->y += scr_con_current;
        		pvrect->height -= scr_con_current;
        	}
        	else	*/
        if (screen_top > vspace) {
            pvrect->y += screen_top - vspace;
            pvrect->height -= screen_top - vspace;
        }

        if (sb_lines) {
            pvrect->height -= screen_bottom;
            vid.aspect = ((float)(pvrect->height + sb_lines + min(vspace, screen_bottom)) / (float)pvrect->width) * (320.0 / 240.0);
        } else {
            if (screen_bottom > vspace) {
                pvrect->height -= screen_bottom - vspace;
            }
            vid.aspect = ((float)pvrect->height / (float)pvrect->width) * (320.0 / 240.0);
        }
        // svc_letterbox - start
        if (cl.letterbox) {
            int letterbox_height = (int)((float)pvrect->height * (cl.letterbox/2.0));
            pvrect->y += letterbox_height; // - sb_lines/2
            pvrect->height -= letterbox_height*2;
        }
        // svc_letterbox - end
        // pvrect->width and pvrect->height must be a multiple of 4! (I guess)
        {
            int width;
            //	int height;
            width  = (pvrect->width /4)*4;
            if (pvrect->width != width) { // pvrect->width is not a multiple of 4
                pvrect->x = (pvrect->x/4)*4;
                pvrect->width = width+4;
            }
            /*
            // disabled this for now, because forcing pvrect->height to a multiple of 4
            // may make animated letterboxes to not look good
            height = (pvrect->height/4)*4;
            if (pvrect->height != height) // pvrect->height is not a multiple of 4
            {
            	pvrect->y = (pvrect->y/4)*4;
            	pvrect->height = height+4;
            }
            */
        }
        //	Con_Printf("x=%i w=%i y=%i h=%i\n", pvrect->x, pvrect->width, pvrect->y, pvrect->height);
    }
    // stereo 3D - begin
    if (r_stereo_separation.value) {
        // right side
        if (r_stereo_side) {
            pvrect->width = pvrect->width/2 - 1;
            pvrect->height = pvrect->height/2 - 1;
            pvrect->y += pvrect->height/2;
            pvrect->x += pvrect->width + 2;
        }
        // left side
        else { //if (!r_stereo_side)
            pvrect->width = pvrect->width/2 - 1;
            pvrect->height = pvrect->height/2 - 1;
            pvrect->y += pvrect->height/2;
        }
    }
    // stereo 3D - end
    // split screen - begin
    /*
    // this is just a hack
    if (temp1.value == 1)
    {
    	pvrect->width = pvrect->width/2 - 1;
    	pvrect->height = pvrect->height/2 - 1;
    }
    if (temp1.value == 2)
    {
    	pvrect->width = pvrect->width/2 - 1;
    	pvrect->height = pvrect->height/2 - 1;
    	pvrect->x += pvrect->width + 2;
    }
    if (temp1.value == 3)
    {
    	pvrect->width = pvrect->width/2 - 1;
    	pvrect->height = pvrect->height/2 - 1;
    	pvrect->y += pvrect->height + 2;
    }
    if (temp1.value == 4)
    {
    	pvrect->width = pvrect->width/2 - 1;
    	pvrect->height = pvrect->height/2 - 1;
    	pvrect->x += pvrect->width + 2;
    	pvrect->y += pvrect->height + 2;
    }
    */
    // split screen - end
    // screen positioning - end

    {
        if (lcd_x.value) {
            vid.aspect *= 0.5;
            pvrect->y >>= 1;
            pvrect->height >>= 1;

            /*			// begin
            			pvrect->y += (r_framecount&1);
            			pvrect->height -= (r_framecount&1);
            			if ((r_framecount&1))
            				vid.buffer += vid.rowbytes>>1;
            			else
            				vid.buffer -= vid.rowbytes>>1;
            */			// end
        }
    }
}


/*
===============
R_ViewChanged

Called every time the vid structure or r_refdef changes.
Guaranteed to be called before the first refresh
===============
*/
void R_ViewChanged(vrect_t *pvrect, int lineadj, float aspect)
{
    int		i;
    float	res_scale;

    r_viewchanged = true;

    R_SetVrect(pvrect, &r_refdef.vrect, lineadj);

    r_refdef.horizontalFieldOfView = 2.0 * tan(r_refdef.fov_x/360*M_PI);
    r_refdef.fvrectx = (float)r_refdef.vrect.x;
    r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5;
    r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<<20) + (1<<19) - 1;
    r_refdef.fvrecty = (float)r_refdef.vrect.y;
    r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5;
    r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width;
    r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1;
    r_refdef.fvrectright = (float)r_refdef.vrectright;
    r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5;
    r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99;
    r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height;
    r_refdef.fvrectbottom = (float)r_refdef.vrectbottom;
    r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5;

    r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale);
    r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale);
    r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale);
    r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale);
    r_refdef.aliasvrectright = r_refdef.aliasvrect.x +
                               r_refdef.aliasvrect.width;
    r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y +
                                r_refdef.aliasvrect.height;

    pixelAspect = aspect;
    xOrigin = r_refdef.xOrigin;
    yOrigin = r_refdef.yOrigin;

    screenAspect = r_refdef.vrect.width*pixelAspect /
                   r_refdef.vrect.height;
// 320*200 1.0 pixelAspect = 1.6 screenAspect
// 320*240 1.0 pixelAspect = 1.3333 screenAspect
// proper 320*200 pixelAspect = 0.8333333

    verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect;

// values for perspective projection
// if math were exact, the values would range from 0.5 to to range+0.5
// hopefully they wll be in the 0.000001 to range+.999999 and truncate
// the polygon rasterization will never render in the first row or column
// but will definately render in the [range] row and column, so adjust the
// buffer origin to get an exact edge to edge fill
    xcenter = ((float)r_refdef.vrect.width * XCENTERING) +
              r_refdef.vrect.x - 0.5;
    aliasxcenter = xcenter * r_aliasuvscale;
    ycenter = ((float)r_refdef.vrect.height * YCENTERING) +
              r_refdef.vrect.y - 0.5;
    aliasycenter = ycenter * r_aliasuvscale;

    xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView;
    aliasxscale = xscale * r_aliasuvscale;
    xscaleinv = 1.0 / xscale;
    yscale = xscale * pixelAspect;
    aliasyscale = yscale * r_aliasuvscale;
    yscaleinv = 1.0 / yscale;
    xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView;
    yscaleshrink = xscaleshrink*pixelAspect;

// left side clip
    screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView);
    screenedge[0].normal[1] = 0;
    screenedge[0].normal[2] = 1;
    screenedge[0].type = PLANE_ANYZ;

// right side clip
    screenedge[1].normal[0] =
        1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView);
    screenedge[1].normal[1] = 0;
    screenedge[1].normal[2] = 1;
    screenedge[1].type = PLANE_ANYZ;

// top side clip
    screenedge[2].normal[0] = 0;
    screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView);
    screenedge[2].normal[2] = 1;
    screenedge[2].type = PLANE_ANYZ;

// bottom side clip
    screenedge[3].normal[0] = 0;
    screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView);
    screenedge[3].normal[2] = 1;
    screenedge[3].type = PLANE_ANYZ;

    for (i=0 ; i<4 ; i++) {
        VectorNormalize(screenedge[i].normal);
    }

    res_scale = sqrt((double)(r_refdef.vrect.width * r_refdef.vrect.height) /
                     (320.0 * 152.0)) *
                (2.0 / r_refdef.horizontalFieldOfView);
    r_aliastransition = r_aliastransbase.value * res_scale;
    r_resfudge = r_aliastransadj.value * res_scale;

    if (scr_fov.value <= 90.0) {
        r_fov_greater_than_90 = false;
    } else {
        r_fov_greater_than_90 = true;
    }

// TODO: collect 386-specific code in one place
#if	id386
    gdfgdfg
    if (r_pixbytes == 1) {
        Sys_MakeCodeWriteable((long)R_Surf8Start,
                              (long)R_Surf8End - (long)R_Surf8Start);
        colormap = vid.colormap;
        R_Surf8Patch();
    } else {
        Sys_MakeCodeWriteable((long)R_Surf16Start,
                              (long)R_Surf16End - (long)R_Surf16Start);
        colormap = vid.colormap16;
        R_Surf16Patch();
    }
#endif	// id386

    D_ViewChanged();
}


/*
===============
R_MarkLeaves
===============
*/
void R_MarkLeaves(void)
{
    byte	*vis;
    mnode_t	*node;
    int		i;

    if (r_oldviewleaf == r_viewleaf) {
        return;
    }

    r_visframecount++;
    r_oldviewleaf = r_viewleaf;

    vis = Mod_LeafPVS(r_viewleaf, cl.worldmodel);

    for (i=0 ; i<cl.worldmodel->numleafs ; i++) {
        if (vis[i>>3] & (1<<(i&7))) {
            node = (mnode_t *)&cl.worldmodel->leafs[i+1];
            do {
                if (node->visframe == r_visframecount) {
                    break;
                }
                node->visframe = r_visframecount;
                node = node->parent;
            } while (node);
        }
    }
}


/*
=============
R_DrawEntitiesOnList
=============
*/
void R_DrawEntity(int i)  // Edited
{
    if (i != -666) { // Edited
        currententity = cl_visedicts[i];
    }
    if (currententity == &cl_entities[cl.viewentity])
        // 2000-01-09 ChaseCam fix by FrikaC  start
    {
        if (!chase_active.value) {
            // 2000-01-09 ChaseCam fix by FrikaC  end
            return;	// don't draw the player
            // 2000-01-09 ChaseCam fix by FrikaC  start
        } else {
//				currententity->angles[0] *= 0.3;
            currententity->angles[0] = 0;
            currententity->angles[2] = 0;
        }
    }
    // 2000-01-09 ChaseCam fix by FrikaC  end

    switch (currententity->model->type) {
    case mod_sprite:
        VectorCopy(currententity->origin, r_entorigin);
        VectorSubtract(r_origin, r_entorigin, modelorg);
        R_DrawSprite();
        break;

    case mod_alias:
        VectorCopy(currententity->origin, r_entorigin);
        VectorSubtract(r_origin, r_entorigin, modelorg);
        R_AliasDrawModel(/* &lighting */);  // changed alias models lighting - edited

        break;

    default:
        break;
    }
}
void R_DrawEntitiesOnList(void)
{
    int			i; // Edited

    if (!r_drawentities.value) {
        return;
    }

    for (i=0 ; i<cl_numvisedicts ; i++) {
        cl_visedicts[i]->effects -= cl_visedicts[i]->effects & (EF_CELSHADING|EF_REFLECTIVE); // for betatesting
        // begin
        if (cl_visedicts[i]->alpha != 1.0f				// QC Alpha
                || cl_visedicts[i]->effects & (EF_ADDITIVE|EF_SHADOW|EF_CELSHADING)) {	// Edited
            continue;    // skip translucent objects
        }
#if	id386
        if (cl_visedicts[i]->effects & EF_REFLECTIVE) {
            use_c_renderer = true;
        }
#endif
        R_DrawEntity(i);
#if	id386
        use_c_renderer = false;
#endif
        // end
    }

    // begin
#if	id386
    use_c_renderer = true;
#endif
    // end
    // EF_CELSHADING - begin
    // EF_CELSHADING - end
    // EF_SHADOW - begin
    for (i=0 ; i<cl_numvisedicts ; i++) {
        if (!(cl_visedicts[i]->effects & EF_SHADOW)) {
            continue;
        }
        R_DrawEntity(i);
    }
    // EF_SHADOW - end
    // QC Alpha - begin
    for (i=0 ; i<cl_numvisedicts ; i++) {
        if (cl_visedicts[i]->alpha == 1.0f
                && !(cl_visedicts[i]->effects & EF_ADDITIVE)) {	// additive rendering
            continue;
        }
        if (cl_visedicts[i]->effects & EF_SHADOW) {
            continue;
        }
        R_DrawEntity(i);
    }
    // QC Alpha - end
}

/*
=============
R_DrawViewModel
=============
*/
void R_DrawViewModel(int opaque)
{
    /* // changed alias models lighting - removed - begin
    // FIXME: remove and do real lighting
    float		lightvec[3] = {-1, 0, 0};
    int			j;
    int			lnum;
    vec3_t		dist;
    float		add;
    dlight_t	*dl;
    */ // changed alias models lighting - removed - end

    currententity = &cl.viewent;
    // edited - begin
    if ((!currententity->model)
            || (cl.stats[STAT_HEALTH] <= 0)
//	|| ((cl.items & IT_INVISIBILITY) && (r_framecount & 1)) // stipple alpha - removed
            || (chase_active.value)
            || (!r_drawviewmodel.value) // || r_fov_greater_than_90)
       )
        // edited - end
    {
        currententity->reset_frame_interpolation = true; // model interpolation
        return;
    }
    VectorCopy(currententity->origin, r_entorigin);
    VectorSubtract(r_origin, r_entorigin, modelorg);
    // QC Alpha Scale Begin
    if (cl.items & IT_INVISIBILITY) {
        if (cl_entities[cl.viewentity].alpha == 1) {
            currententity->alpha = 0.3f;
        } else {
            currententity->alpha = cl_entities[cl.viewentity].alpha;
        }
    } else {
        currententity->alpha = cl_entities[cl.viewentity].alpha;
    }
    currententity->effects = cl_entities[cl.viewentity].effects;
    if (opaque && (currententity->alpha != 1.0f || (currententity->effects & EF_ADDITIVE))) {
        return;
    }
    if (!opaque && currententity->alpha == 1.0f && !(currententity->effects & EF_ADDITIVE)) {
        return;
    }
#if	id386
    use_c_renderer = ((currententity->alpha != 1.0f) || (currententity->effects & (EF_ADDITIVE|EF_SHADOW|EF_REFLECTIVE)));
#endif
    currententity->scale2 = cl_entities[cl.viewentity].scale2;
    VectorCopy(cl_entities[cl.viewentity].scalev, currententity->scalev);
    // QC Alpha Scale End

    /* // changed alias models lighting - removed - begin
    VectorCopy (vup, viewlightvec);
    VectorInverse (viewlightvec);

    j = R_LightPoint (currententity->origin);

    if (j < 24)
    	j = 24;		// allways give some light on gun
    r_viewlighting.ambientlight = j;
    r_viewlighting.shadelight = j;

    // add dynamic lights
    for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
    {
    	dl = &cl_dlights[lnum];
    	if (!dl->radius)
    		continue;
    	if (!dl->radius)
    		continue;
    	if (dl->die < cl.time)
    		continue;

    	VectorSubtract (currententity->origin, dl->origin, dist);
    	add = dl->radius - Length(dist);
    	if (add > 0)
    		r_viewlighting.ambientlight += add;
    }

    // clamp lighting so it doesn't overbright as much
    if (r_viewlighting.ambientlight > 128)
    	r_viewlighting.ambientlight = 128;
    if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192)
    	r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight;

    r_viewlighting.plightvec = lightvec;

    #ifdef QUAKE2
    cl.light_level = r_viewlighting.ambientlight;
    #endif
    */ // changed alias models lighting - removed - end

    if (r_dspeeds.value) { // Edited
        dv_time1 = Sys_FloatTime();    // draw viewmodel time
    }
    R_AliasDrawModel(/* &r_viewlighting */);  // changed alias models lighting - edited
    /*	// EF_CELSHADING - begin
    	if (currententity->effects & EF_CELSHADING)
    	{
    		r_drawoutlines = 1;
    		R_AliasDrawModel ();
    		r_drawoutlines = false;
    	}
    */	// EF_CELSHADING - end
    if (r_dspeeds.value) { // Edited
        dv_time2 = Sys_FloatTime();    // draw viewmodel time
    }
}


/*
=============
R_BmodelCheckBBox
=============
*/
int R_BmodelCheckBBox(model_t *clmodel, float *minmaxs)
{
    int			i, *pindex, clipflags;
    vec3_t		acceptpt, rejectpt;
    double		d;

    clipflags = 0;

    if (currententity->angles[0] || currententity->angles[1]
            || currententity->angles[2]) {
        for (i=0 ; i<4 ; i++) {
            d = DotProduct(currententity->origin, view_clipplanes[i].normal);
            d -= view_clipplanes[i].dist;

            if (d <= -clmodel->radius) {
                return BMODEL_FULLY_CLIPPED;
            }

            if (d <= clmodel->radius) {
                clipflags |= (1<<i);
            }
        }
    } else {
        for (i=0 ; i<4 ; i++) {
            // generate accept and reject points
            // FIXME: do with fast look-ups or integer tests based on the sign bit
            // of the floating point values

            pindex = pfrustum_indexes[i];

            rejectpt[0] = minmaxs[pindex[0]];
            rejectpt[1] = minmaxs[pindex[1]];
            rejectpt[2] = minmaxs[pindex[2]];

            d = DotProduct(rejectpt, view_clipplanes[i].normal);
            d -= view_clipplanes[i].dist;

            if (d <= 0) {
                return BMODEL_FULLY_CLIPPED;
            }

            acceptpt[0] = minmaxs[pindex[3+0]];
            acceptpt[1] = minmaxs[pindex[3+1]];
            acceptpt[2] = minmaxs[pindex[3+2]];

            d = DotProduct(acceptpt, view_clipplanes[i].normal);
            d -= view_clipplanes[i].dist;

            if (d <= 0) {
                clipflags |= (1<<i);
            }
        }
    }

    return clipflags;
}


/*
=============
R_DrawBEntitiesOnList
=============
*/
void R_DrawBEntitiesOnList(void)
{
    int			i, j, k, clipflags;
    vec3_t		oldorigin;
    model_t		*clmodel;
    float		minmaxs[6];

    if (!r_drawentities.value) {
        return;
    }

    VectorCopy(modelorg, oldorigin);
    insubmodel = true;
    r_dlightframecount = r_framecount;

    for (i=0 ; i<cl_numvisedicts ; i++) {
        currententity = cl_visedicts[i];

        switch (currententity->model->type) {
        case mod_brush:

            clmodel = currententity->model;

            // see if the bounding box lets us trivially reject, also sets
            // trivial accept status
            for (j=0 ; j<3 ; j++) {
                minmaxs[j] = currententity->origin[j] +
                             clmodel->mins[j];
                minmaxs[3+j] = currententity->origin[j] +
                               clmodel->maxs[j];
            }

            clipflags = R_BmodelCheckBBox(clmodel, minmaxs);

            if (clipflags != BMODEL_FULLY_CLIPPED) {
                VectorCopy(currententity->origin, r_entorigin);
                VectorSubtract(r_origin, r_entorigin, modelorg);
                // FIXME: is this needed?
                //	VectorCopy (modelorg, r_worldmodelorg); // removed

                r_pcurrentvertbase = clmodel->vertexes;

                // FIXME: stop transforming twice
                R_RotateBmodel();

                // calculate dynamic lighting for bmodel if it's not an
                // instanced model
                if (clmodel->firstmodelsurface != 0) {
                    for (k=0 ; k<MAX_DLIGHTS ; k++) {
                        if ((cl_dlights[k].die < cl.time) ||
                                (!cl_dlights[k].radius)) {
                            continue;
                        }

                        R_MarkLights(&cl_dlights[k], 1<<k,
                                     clmodel->nodes + clmodel->hulls[0].firstclipnode);
                    }
                }

                // if the driver wants polygons, deliver those. Z-buffering is on
                // at this point, so no clipping to the world tree is needed, just
                // frustum clipping
                if (r_drawpolys | r_drawculledpolys) {
                    R_ZDrawSubmodelPolys(clmodel);
                } else {
                    r_pefragtopnode = NULL;

                    for (j=0 ; j<3 ; j++) {
                        r_emins[j] = minmaxs[j];
                        r_emaxs[j] = minmaxs[3+j];
                    }

                    R_SplitEntityOnNode2(cl.worldmodel->nodes);

                    if (r_pefragtopnode) {
                        currententity->topnode = r_pefragtopnode;

                        if (r_pefragtopnode->contents >= 0) {
                            // not a leaf; has to be clipped to the world BSP
                            r_clipflags = clipflags;
                            R_DrawSolidClippedSubmodelPolygons(clmodel);
                        } else {
                            // falls entirely in one leaf, so we just put all the
                            // edges in the edge list and let 1/z sorting handle
                            // drawing order
                            R_DrawSubmodelPolygons(clmodel, clipflags);
                        }

                        currententity->topnode = NULL;
                    }
                }

                // put back world rotation and frustum clipping
                // FIXME: R_RotateBmodel should just work off base_vxx
                VectorCopy(base_vpn, vpn);
                VectorCopy(base_vup, vup);
                VectorCopy(base_vright, vright);
                VectorCopy(base_modelorg, modelorg);
                VectorCopy(oldorigin, modelorg);
                R_TransformFrustum();
            }

            break;

        default:
            break;
        }
    }

    insubmodel = false;
}


/*
================
R_EdgeDrawing
================
*/
void R_EdgeDrawing(void)
{
    edge_t	ledges[NUMSTACKEDGES +
                   ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1];
    surf_t	lsurfs[NUMSTACKSURFACES +
                   ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1];

    if (auxedges) {
        r_edges = auxedges;
    } else {
        r_edges = (edge_t *)
                  (((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
    }

    if (r_surfsonstack) {
        surfaces = (surf_t *)
                   (((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
        surf_max = &surfaces[r_cnumsurfs];
        // surface 0 doesn't really exist; it's just a dummy because index 0
        // is used to indicate no edge attached to surface
        surfaces--;
#if	id386 // no 386 assembler, no MASM
        R_SurfacePatch();
#endif // no 386 assembler, no MASM
    }

    R_BeginEdgeFrame();

    if (r_dspeeds.value) {
        rw_time1 = Sys_FloatTime();
    }

    R_RenderWorld();

    if (r_drawculledpolys) {
        R_ScanEdges();
    }

// only the world can be drawn back to front with no z reads or compares, just
// z writes, so have the driver turn z compares on now
    D_TurnZOn();

    if (r_dspeeds.value) {
        rw_time2 = Sys_FloatTime();
        db_time1 = rw_time2;
    }

    R_DrawBEntitiesOnList();

    if (r_dspeeds.value) {
        db_time2 = Sys_FloatTime();
        se_time1 = db_time2;
    }

    if (!r_dspeeds.value) {
        VID_UnlockBuffer();
        S_ExtraUpdate();	// don't let sound get messed up if going slow
        VID_LockBuffer();
    }

    if (!(r_drawpolys | r_drawculledpolys)) {
        R_ScanEdges();
    }
}


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

r_refdef must be set before the first call
================
*/
pixel_t	*warpbuffer = NULL; // hi-res waterwarp & buffered video
void R_RenderView_(void)
{
//	byte	warpbuffer[WARP_WIDTH * WARP_HEIGHT]; // hi-res waterwarp & buffered video - removed
    // hi-res waterwarp & buffered video - begin
    if (warpbuffer) {
        free(warpbuffer);
        warpbuffer = NULL;
    }
    warpbuffer = malloc(vid.width*vid.height*sizeof(pixel_t));
    // hi-res waterwarp & buffered video - end

    r_warpbuffer = warpbuffer;

    if (r_timegraph.value || r_speeds.value || r_dspeeds.value) {
        r_time1 = Sys_FloatTime();
    }

    R_SetupFrame();

#ifdef PASSAGES
    SetVisibilityByPassages();
#else
    R_MarkLeaves();	// done here so we know if we're in water
#endif

// make FDIV fast. This reduces timing precision after we've been running for a
// while, so we don't do it globally.  This also sets chop mode, and we do it
// here so that setup stuff like the refresh area calculations match what's
// done in screen.c
    //Edited Sys_LowFPPrecision ();

    if (!cl_entities[0].model || !cl.worldmodel) {
        Sys_Error("R_RenderView: NULL worldmodel");
    }

    if (!r_dspeeds.value) {
        VID_UnlockBuffer();
        S_ExtraUpdate();	// don't let sound get messed up if going slow
        VID_LockBuffer();
    }

    r_foundwater = r_drawwater = false; // translucent water
    R_EdgeDrawing();

    if (!r_dspeeds.value) {
        VID_UnlockBuffer();
        S_ExtraUpdate();	// don't let sound get messed up if going slow
        VID_LockBuffer();
    }

    if (r_dspeeds.value) {
        se_time2 = Sys_FloatTime();  // scan edges time
//		de_time1 = se_time2; // draw entities time
    }

    R_DrawViewModel(true);

    // QC Alpha - begin
#if	id386
    use_c_renderer = false;
#endif
    // QC Alpha - end
    if (r_dspeeds.value) { // Edited
        de_time1 = Sys_FloatTime();    // draw entities time
    }
    CL_UpdateTEnts();  // Edited
    R_DrawEntitiesOnList();

    if (r_dspeeds.value) {
        de_time2 = Sys_FloatTime();  // draw entities time
        dp_time1 = de_time2; // draw particles time
    }

    R_DrawParticles();

    if (r_dspeeds.value) {
        dp_time2 = Sys_FloatTime();    // draw particles time
    }

    // translucent water - begin
    if (r_foundwater) {
        r_drawwater = true;
        R_EdgeDrawing();
    }
    // translucent water - end

    R_DrawViewModel(false);  // Edited

    // fog - begin
    if (additivemap)
        if (developer.value >= 100)
            if (developer.value < 356) {
                int			x, y, level;
                pixel_t		*pbuf;
                short		*pz;
                extern short		*d_pzbuffer;
                extern unsigned int	d_zwidth;
                extern int			d_scantable[1024];
                for (y=0 ; y<r_refdef.vrect.height/*vid.height*/ ; y++) {
                    //	pbuf = (byte *)(vid.buffer + vid.rowbytes*y);

                    for (x=0 ; x<r_refdef.vrect.width/*vid.width*/ ; x++) {
                        pz = d_pzbuffer + (d_zwidth * (y+r_refdef.vrect.y)) + x+r_refdef.vrect.x;
                        //*
                        level = 32 - (int)(*pz * (32.0/256.0));
                        //	level = fabs(level);
                        if (level > 0 && level <= 32) {
#ifndef _WIN32 // buffered video (bloody hack)
                            if (!r_dowarp) {
                                pbuf = vid.buffer + d_scantable[y+r_refdef.vrect.y] + x+r_refdef.vrect.x;
                            } else
#endif // buffered video (bloody hack)
                                pbuf = r_warpbuffer + d_scantable[y+r_refdef.vrect.y] + x+r_refdef.vrect.x;
                            *pbuf = (byte)vid.colormap[*pbuf + ((32+level) * 256)];
                            //	*pbuf = additivemap[(int)vid.colormap[*pbuf + ((32+level) * 256)]*256];
                            *pbuf = additivemap[*pbuf + (int)vid.colormap[(int)developer.value-100 + ((64-level) * 256)]*256];
                        }
                        /*/
                        	level = (int)(*pz * (32.0/256.0)) - 32;
                        //	if (level >= 32)
                        //		*pbuf = additivemap[*pbuf + (int)vid.colormap[(int)developer.value-100 + ((64-31) * 256)]*256];
                        //	else
                        	if (level > 0 && level < 32)
                        	{
                        		pbuf = r_warpbuffer + d_scantable[y] + x;
                        	//	*pbuf = additivemap[(int)vid.colormap[*pbuf + ((32+level) * 256)]*256];
                        		*pbuf = additivemap[*pbuf + (int)vid.colormap[(int)developer.value-100 + ((64-level) * 256)]*256];
                        	}
                        //*/
                    }
                }
            }
    // fog - end

    // buffered video (bloody hack) - begin
#ifdef _WIN32
    if (r_stereo_separation.value) {
        int		i;
        pixel_t	*src = r_warpbuffer + scr_vrect.y * vid.width;
        pixel_t	*dest = vid.buffer + scr_vrect.y * vid.width;
//		for (i=0 ; i<vid.height ; i++, src += vid.width, dest += vid.rowbytes)
        for (i=0 ; i<scr_vrect.height ; i++, src += vid.width, dest += vid.width) {
            Q_memcpy(dest, src, vid.width*sizeof(pixel_t));
        }
    } else if (r_dowarp) {
        D_WarpScreen();
    } else { // copy buffer to video
        int		i;
        pixel_t	*src = r_warpbuffer + scr_vrect.y * vid.width + scr_vrect.x;
        pixel_t	*dest = vid.buffer + scr_vrect.y * vid.width + scr_vrect.x;
        for (i=0 ; i<scr_vrect.height ; i++, src += vid.width, dest += vid.width) {
            Q_memcpy(dest, src, scr_vrect.width*sizeof(pixel_t));
        }
    }
#else
    if (r_stereo_separation.value && r_dowarp) {
        int		i;
        byte	*src = r_warpbuffer + scr_vrect.y * vid.width;
        byte	*dest = vid.buffer + scr_vrect.y * vid.rowbytes;
        for (i=0 ; i<scr_vrect.height ; i++, src += vid.width, dest += vid.rowbytes) {
            Q_memcpy(dest, src, vid.width);
        }
    } else
        // buffered video (bloody hack) - end
        if (r_dowarp) {
            D_WarpScreen();
        }
#endif // buffered video (bloody hack)

    V_SetContentsColor(r_viewleaf->contents);

    if (r_timegraph.value) {
        R_TimeGraph();
    }

    if (r_aliasstats.value) {
        R_PrintAliasStats();
    }

    if (r_speeds.value) {
        R_PrintTimes();
    }

    if (r_dspeeds.value) {
        R_PrintDSpeeds();
    }

    if (r_reportsurfout.value && r_outofsurfaces) {
        Con_Printf("Short %d surfaces\n", r_outofsurfaces);
    }

    if (r_reportedgeout.value && r_outofedges) {
        Con_Printf("Short roughly %d edges\n", r_outofedges * 2 / 3);
    }

// back to high floating-point precision
    //Edited  Sys_HighFPPrecision ();
}

void R_RenderView(void)
{
    int		dummy;
    int		delta;
    //This causes problems for Flash when not using -O3
#if !defined(FLASH)
    delta = (byte *)&dummy - r_stack_start;
    if (delta < -10000 || delta > 10000) {
        Sys_Error("R_RenderView: called without enough stack");
    }
#endif

    if (Hunk_LowMark() & 3) {
        Sys_Error("Hunk is missaligned");
    }

    if ((long)(&dummy) & 3) {
        Sys_Error("Stack is missaligned");
    }

    if ((long)(&r_warpbuffer) & 3) {
        Sys_Error("Globals are missaligned");
    }

    // split screen - begin
    /*
    if (developer.value == 4)
    {
    	r_viewchanged = true;
    	temp1.value = 1;
    	R_RenderView_ ();
    	r_viewchanged = true;
    	temp1.value = 2;
    	R_RenderView_ ();
    	r_viewchanged = true;
    	temp1.value = 3;
    	R_RenderView_ ();
    	r_viewchanged = true;
    	temp1.value = 4;
    	R_RenderView_ ();
    }
    else
    //*/
    // split screen - end
    R_RenderView_();
}

/*
================
R_InitTurb
================
*/
void R_InitTurb(void)
{
    int		i;

    for (i=0 ; i<(SIN_BUFFER_SIZE) ; i++) {
        sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP;
        intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2;	// AMP2, not 20
    }
}

