/*
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"

entity_t  r_worldentity;

qboolean  r_cache_thrash;   // compatability

vec3_t    modelorg, r_entorigin;
entity_t  *currententity;

int     r_visframecount;  // bumped when going to a new PVS
int     r_framecount;   // used for dlight push checking

mplane_t  frustum[4];

int     c_brush_polys, c_alias_polys;

qboolean  envmap;       // true during envmap command capture 

int     currenttexture = -1;    // to avoid unnecessary texture sets

int     cnttextures[2] = {-1, -1};     // cached

int     particletexture;  // little dot for particles
int     playertextures;   // up to 16 color translated skins

int     mirrortexturenum; // quake texturenum, not gltexturenum
qboolean  mirror;
mplane_t  *mirror_plane;

//
// view origin
//
vec3_t  vup;
vec3_t  vpn;
vec3_t  vright;
vec3_t  r_origin;

float r_world_matrix[16];
float r_base_world_matrix[16];

//
// screen size info
//
refdef_t  r_refdef;

mleaf_t   *r_viewleaf, *r_oldviewleaf;

texture_t *r_notexture_mip;

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


void R_MarkLeaves (void);

cvar_t  r_norefresh = {"r_norefresh","0"};
cvar_t  r_drawentities = {"r_drawentities","1"};
cvar_t  r_drawviewmodel = {"r_drawviewmodel","1"};
cvar_t  r_speeds = {"r_speeds","0"};
cvar_t  r_fullbright = {"r_fullbright","0"};
cvar_t  r_lightmap = {"r_lightmap","0"};
cvar_t  r_shadows = {"r_shadows","0"};
cvar_t  r_mirroralpha = {"r_mirroralpha","1"};
cvar_t  r_wateralpha = {"r_wateralpha","1"};
cvar_t  r_dynamic = {"r_dynamic","1"};
cvar_t  r_novis = {"r_novis","0"};

cvar_t  gl_finish = {"gl_finish","0"};
cvar_t  gl_clear = {"gl_clear","0"};
cvar_t  gl_cull = {"gl_cull","1"};
cvar_t  gl_texsort = {"gl_texsort","1"};
cvar_t  gl_smoothmodels = {"gl_smoothmodels","1"};
cvar_t  gl_affinemodels = {"gl_affinemodels","0"};
cvar_t  gl_polyblend = {"gl_polyblend","1"};
cvar_t  gl_flashblend = {"gl_flashblend","1"};
cvar_t  gl_playermip = {"gl_playermip","0"};
cvar_t  gl_nocolors = {"gl_nocolors","0"};
cvar_t  gl_keeptjunctions = {"gl_keeptjunctions","0"};
cvar_t  gl_reporttjunctions = {"gl_reporttjunctions","0"};
cvar_t  gl_doubleeyes = {"gl_doubleeys", "1"};

extern  cvar_t  gl_ztrick;

/*
=================
R_CullBox

Returns true if the box is completely outside the frustom
=================
*/
qboolean R_CullBox (vec3_t mins, vec3_t maxs)
{
  int   i;

  for (i=0 ; i<4 ; i++)
    if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2)
      return true;
  return false;
}


void R_RotateForEntity (entity_t *e)
{
    glTranslatef (e->origin[0],  e->origin[1],  e->origin[2]);

    glRotatef (e->angles[1],  0, 0, 1);
    glRotatef (-e->angles[0],  0, 1, 0);
    glRotatef (e->angles[2],  1, 0, 0);
}

/*
=============================================================

  SPRITE MODELS

=============================================================
*/

/*
================
R_GetSpriteFrame
================
*/
mspriteframe_t *R_GetSpriteFrame (entity_t *currententity)
{
  msprite_t   *psprite;
  mspritegroup_t  *pspritegroup;
  mspriteframe_t  *pspriteframe;
  int       i, numframes, frame;
  float     *pintervals, fullinterval, targettime, time;

  psprite = currententity->model->cache.data;
  frame = currententity->frame;

  if ((frame >= psprite->numframes) || (frame < 0))
  {
    Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
    frame = 0;
  }

  if (psprite->frames[frame].type == SPR_SINGLE)
  {
    pspriteframe = psprite->frames[frame].frameptr;
  }
  else
  {
    pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
    pintervals = pspritegroup->intervals;
    numframes = pspritegroup->numframes;
    fullinterval = pintervals[numframes-1];

    time = cl.time + currententity->syncbase;

  // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
  // are positive, so we don't have to worry about division by 0
    targettime = time - ((int)(time / fullinterval)) * fullinterval;

    for (i=0 ; i<(numframes-1) ; i++)
    {
      if (pintervals[i] > targettime)
        break;
    }

    pspriteframe = pspritegroup->frames[i];
  }

  return pspriteframe;
}


/*
=================
R_DrawSpriteModel

=================
*/
void R_DrawSpriteModel (entity_t *e)
{
  vec3_t  point;
  mspriteframe_t  *frame;
  float   *up, *right;
  vec3_t    v_forward, v_right, v_up;
  msprite_t   *psprite;

  // don't even bother culling, because it's just a single
  // polygon without a surface cache
  frame = R_GetSpriteFrame (e);
  psprite = currententity->model->cache.data;

  if (psprite->type == SPR_ORIENTED)
  { // bullet marks on walls
    AngleVectors (currententity->angles, v_forward, v_right, v_up);
    up = v_up;
    right = v_right;
  }
  else
  { // normal sprite
    up = vup;
    right = vright;
  }

  glColor3f (1,1,1);

  GL_DisableMultitexture();

    GL_Bind(frame->gl_texturenum);

  glEnable (GL_ALPHA_TEST);
  glBegin (GL_QUADS);

  glTexCoord2f (0, 1);
  VectorMA (e->origin, frame->down, up, point);
  VectorMA (point, frame->left, right, point);
  glVertex3fv (point);

  glTexCoord2f (0, 0);
  VectorMA (e->origin, frame->up, up, point);
  VectorMA (point, frame->left, right, point);
  glVertex3fv (point);

  glTexCoord2f (1, 0);
  VectorMA (e->origin, frame->up, up, point);
  VectorMA (point, frame->right, right, point);
  glVertex3fv (point);

  glTexCoord2f (1, 1);
  VectorMA (e->origin, frame->down, up, point);
  VectorMA (point, frame->right, right, point);
  glVertex3fv (point);
  
  glEnd ();

  glDisable (GL_ALPHA_TEST);
}

/*
=============================================================

  ALIAS MODELS

=============================================================
*/


#define NUMVERTEXNORMALS  162

float r_avertexnormals[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};

vec3_t  shadevector;
float shadelight, ambientlight;

// precalculated dot products for quantized angles
#define SHADEDOT_QUANT 16
float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
#include "anorm_dots.h"
;

float *shadedots = r_avertexnormal_dots[0];

int lastposenum;

/*
=============
GL_DrawAliasFrame
=============
*/
void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum)
{
  float s, t;
  float   l;
  int   i, j;
  int   index;
  trivertx_t  *v, *verts;
  int   list;
  int   *order;
  vec3_t  point;
  float *normal;
  int   count;

lastposenum = posenum;

  verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
  verts += posenum * paliashdr->poseverts;
  order = (int *)((byte *)paliashdr + paliashdr->commands);

  while (1)
  {
    // get the vertex count and primitive type
    count = *order++;
    if (!count)
      break;    // done
    if (count < 0)
    {
      count = -count;
      glBegin (GL_TRIANGLE_FAN);
    }
    else
      glBegin (GL_TRIANGLE_STRIP);

    do
    {
      // texture coordinates come from the draw list
      glTexCoord2f (((float *)order)[0], ((float *)order)[1]);
      order += 2;

      // normals and vertexes come from the frame list
      l = shadedots[verts->lightnormalindex] * shadelight;
      glColor3f (l, l, l);
      glVertex3f (verts->v[0], verts->v[1], verts->v[2]);
      verts++;
    } while (--count);

    glEnd ();
  }
}


/*
=============
GL_DrawAliasShadow
=============
*/
extern  vec3_t      lightspot;

void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum)
{
  float s, t, l;
  int   i, j;
  int   index;
  trivertx_t  *v, *verts;
  int   list;
  int   *order;
  vec3_t  point;
  float *normal;
  float height, lheight;
  int   count;

  lheight = currententity->origin[2] - lightspot[2];

  height = 0;
  verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
  verts += posenum * paliashdr->poseverts;
  order = (int *)((byte *)paliashdr + paliashdr->commands);

  height = -lheight + 1.0;

  while (1)
  {
    // get the vertex count and primitive type
    count = *order++;
    if (!count)
      break;    // done
    if (count < 0)
    {
      count = -count;
      glBegin (GL_TRIANGLE_FAN);
    }
    else
      glBegin (GL_TRIANGLE_STRIP);

    do
    {
      // texture coordinates come from the draw list
      // (skipped for shadows) glTexCoord2fv ((float *)order);
      order += 2;

      // normals and vertexes come from the frame list
      point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0];
      point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1];
      point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2];

      point[0] -= shadevector[0]*(point[2]+lheight);
      point[1] -= shadevector[1]*(point[2]+lheight);
      point[2] = height;
//      height -= 0.001;
      glVertex3fv (point);

      verts++;
    } while (--count);

    glEnd ();
  } 
}



/*
=================
R_SetupAliasFrame

=================
*/
void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr)
{
  int       pose, numposes;
  float     interval;

  if ((frame >= paliashdr->numframes) || (frame < 0))
  {
    Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame);
    frame = 0;
  }

  pose = paliashdr->frames[frame].firstpose;
  numposes = paliashdr->frames[frame].numposes;

  if (numposes > 1)
  {
    interval = paliashdr->frames[frame].interval;
    pose += (int)(cl.time / interval) % numposes;
  }

  GL_DrawAliasFrame (paliashdr, pose);
}



/*
=================
R_DrawAliasModel

=================
*/
void R_DrawAliasModel (entity_t *e)
{
  int     i, j;
  int     lnum;
  vec3_t    dist;
  float   add;
  model_t   *clmodel;
  vec3_t    mins, maxs;
  aliashdr_t  *paliashdr;
  trivertx_t  *verts, *v;
  int     index;
  float   s, t, an;
  int     anim;

  clmodel = currententity->model;

  VectorAdd (currententity->origin, clmodel->mins, mins);
  VectorAdd (currententity->origin, clmodel->maxs, maxs);

  if (R_CullBox (mins, maxs))
    return;


  VectorCopy (currententity->origin, r_entorigin);
  VectorSubtract (r_origin, r_entorigin, modelorg);

  //
  // get lighting information
  //

  ambientlight = shadelight = R_LightPoint (currententity->origin);

  // allways give the gun some light
  if (e == &cl.viewent && ambientlight < 24)
    ambientlight = shadelight = 24;

  for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
  {
    if (cl_dlights[lnum].die >= cl.time)
    {
      VectorSubtract (currententity->origin,
              cl_dlights[lnum].origin,
              dist);
      add = cl_dlights[lnum].radius - Length(dist);

      if (add > 0) {
        ambientlight += add;
        //ZOID models should be affected by dlights as well
        shadelight += add;
      }
    }
  }

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

  // ZOID: never allow players to go totally black
  i = currententity - cl_entities;
  if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
    if (ambientlight < 8)
      ambientlight = shadelight = 8;

  // HACK HACK HACK -- no fullbright colors, so make torches full light
  if (!strcmp (clmodel->name, "progs/flame2.mdl")
    || !strcmp (clmodel->name, "progs/flame.mdl") )
    ambientlight = shadelight = 256;

  shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
  shadelight = shadelight / 200.0;
  
  an = e->angles[1]/180*M_PI;
  shadevector[0] = cos(-an);
  shadevector[1] = sin(-an);
  shadevector[2] = 1;
  VectorNormalize (shadevector);

  //
  // locate the proper data
  //
  paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);

  c_alias_polys += paliashdr->numtris;

  //
  // draw all the triangles
  //

  GL_DisableMultitexture();

    glPushMatrix ();
  R_RotateForEntity (e);

  if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) {
    glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8));
// double size of eyes, since they are really hard to see in gl
    glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2);
  } else {
    glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
    glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
  }

  anim = (int)(cl.time*10) & 3;
    GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]);

  // we can't dynamically colormap textures, so they are cached
  // seperately for the players.  Heads are just uncolored.
  if (currententity->colormap != vid.colormap && !gl_nocolors.value)
  {
    i = currententity - cl_entities;
    if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
        GL_Bind(playertextures - 1 + i);
  }

  if (gl_smoothmodels.value)
    glShadeModel (GL_SMOOTH);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  if (gl_affinemodels.value)
    glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);

  R_SetupAliasFrame (currententity->frame, paliashdr);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

  glShadeModel (GL_FLAT);
  if (gl_affinemodels.value)
    glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

  glPopMatrix ();

  if (r_shadows.value)
  {
    glPushMatrix ();
    R_RotateForEntity (e);
    glDisable (GL_TEXTURE_2D);
    glEnable (GL_BLEND);
    glColor4f (0,0,0,0.5);
    GL_DrawAliasShadow (paliashdr, lastposenum);
    glEnable (GL_TEXTURE_2D);
    glDisable (GL_BLEND);
    glColor4f (1,1,1,1);
    glPopMatrix ();
  }

}

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

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

  if (!r_drawentities.value)
    return;

  // draw sprites seperately, because of alpha blending
  for (i=0 ; i<cl_numvisedicts ; i++)
  {
    currententity = cl_visedicts[i];

    switch (currententity->model->type)
    {
    case mod_alias:
      R_DrawAliasModel (currententity);
      break;

    case mod_brush:
      R_DrawBrushModel (currententity);
      break;

    default:
      break;
    }
  }

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

    switch (currententity->model->type)
    {
    case mod_sprite:
      R_DrawSpriteModel (currententity);
      break;
    }
  }
}

/*
=============
R_DrawViewModel
=============
*/
void R_DrawViewModel (void)
{
  float   ambient[4], diffuse[4];
  int     j;
  int     lnum;
  vec3_t    dist;
  float   add;
  dlight_t  *dl;
  int     ambientlight, shadelight;

  if (!r_drawviewmodel.value)
    return;

  if (chase_active.value)
    return;

  if (envmap)
    return;

  if (!r_drawentities.value)
    return;

  if (cl.items & IT_INVISIBILITY)
    return;

  if (cl.stats[STAT_HEALTH] <= 0)
    return;

  currententity = &cl.viewent;
  if (!currententity->model)
    return;

  j = R_LightPoint (currententity->origin);

  if (j < 24)
    j = 24;   // allways give some light on gun
  ambientlight = j;
  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)
      ambientlight += add;
  }

  ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128;
  diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight / 128;

  // hack the depth range to prevent view model from poking into walls
  glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
  R_DrawAliasModel (currententity);
  glDepthRange (gldepthmin, gldepthmax);
}


/*
============
R_PolyBlend
============
*/
void R_PolyBlend (void)
{
  if (!gl_polyblend.value)
    return;
  if (!v_blend[3])
    return;

  GL_DisableMultitexture();

  glDisable (GL_ALPHA_TEST);
  glEnable (GL_BLEND);
  glDisable (GL_DEPTH_TEST);
  glDisable (GL_TEXTURE_2D);

    glLoadIdentity ();

    glRotatef (-90,  1, 0, 0);      // put Z going up
    glRotatef (90,  0, 0, 1);     // put Z going up

  glColor4fv (v_blend);

  glBegin (GL_QUADS);

  glVertex3f (10, 100, 100);
  glVertex3f (10, -100, 100);
  glVertex3f (10, -100, -100);
  glVertex3f (10, 100, -100);
  glEnd ();

  glDisable (GL_BLEND);
  glEnable (GL_TEXTURE_2D);
  glEnable (GL_ALPHA_TEST);
}


int SignbitsForPlane (mplane_t *out)
{
  int bits, j;

  // for fast box on planeside test

  bits = 0;
  for (j=0 ; j<3 ; j++)
  {
    if (out->normal[j] < 0)
      bits |= 1<<j;
  }
  return bits;
}


void R_SetFrustum (void)
{
  int   i;

  if (r_refdef.fov_x == 90) 
  {
    // front side is visible

    VectorAdd (vpn, vright, frustum[0].normal);
    VectorSubtract (vpn, vright, frustum[1].normal);

    VectorAdd (vpn, vup, frustum[2].normal);
    VectorSubtract (vpn, vup, frustum[3].normal);
  }
  else
  {
    // rotate VPN right by FOV_X/2 degrees
    RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) );
    // rotate VPN left by FOV_X/2 degrees
    RotatePointAroundVector( frustum[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 );
    // rotate VPN up by FOV_X/2 degrees
    RotatePointAroundVector( frustum[2].normal, vright, vpn, 90-r_refdef.fov_y / 2 );
    // rotate VPN down by FOV_X/2 degrees
    RotatePointAroundVector( frustum[3].normal, vright, vpn, -( 90 - r_refdef.fov_y / 2 ) );
  }

  for (i=0 ; i<4 ; i++)
  {
    frustum[i].type = PLANE_ANYZ;
    frustum[i].dist = DotProduct (r_origin, frustum[i].normal);
    frustum[i].signbits = SignbitsForPlane (&frustum[i]);
  }
}



/*
===============
R_SetupFrame
===============
*/
void R_SetupFrame (void)
{
  int       edgecount;
  vrect_t     vrect;
  float     w, h;

// don't allow cheats in multiplayer
  if (cl.maxclients > 1)
    Cvar_Set ("r_fullbright", "0");

  R_AnimateLight ();

  r_framecount++;

// build the transformation matrix for the given view angles
  VectorCopy (r_refdef.vieworg, r_origin);

  AngleVectors (r_refdef.viewangles, vpn, vright, vup);

// current viewleaf
  r_oldviewleaf = r_viewleaf;
  r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel);

  V_SetContentsColor (r_viewleaf->contents);
  V_CalcBlend ();

  r_cache_thrash = false;

  c_brush_polys = 0;
  c_alias_polys = 0;

}


void MYgluPerspective( GLdouble fovy, GLdouble aspect,
         GLdouble zNear, GLdouble zFar )
{
   GLdouble xmin, xmax, ymin, ymax;

   ymax = zNear * tan( fovy * M_PI / 360.0 );
   ymin = -ymax;

   xmin = ymin * aspect;
   xmax = ymax * aspect;

   glFrustum( xmin, xmax, ymin, ymax, zNear, zFar );
}


/*
=============
R_SetupGL
=============
*/
void R_SetupGL (void)
{
  float screenaspect;
  float yfov;
  int   i;
  extern  int glwidth, glheight;
  int   x, x2, y2, y, w, h;

  //
  // set up viewpoint
  //
  glMatrixMode(GL_PROJECTION);
    glLoadIdentity ();
  x = r_refdef.vrect.x * glwidth/vid.width;
  x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width;
  y = (vid.height-r_refdef.vrect.y) * glheight/vid.height;
  y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height;

  // fudge around because of frac screen scale
  if (x > 0)
    x--;
  if (x2 < glwidth)
    x2++;
  if (y2 < 0)
    y2--;
  if (y < glheight)
    y++;

  w = x2 - x;
  h = y - y2;

  if (envmap)
  {
    x = y2 = 0;
    w = h = 256;
  }

  glViewport (glx + x, gly + y2, w, h);
    screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;
//  yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI;
    MYgluPerspective (r_refdef.fov_y,  screenaspect,  4,  4096);

  if (mirror)
  {
    if (mirror_plane->normal[2])
      glScalef (1, -1, 1);
    else
      glScalef (-1, 1, 1);
    glCullFace(GL_BACK);
  }
  else
    glCullFace(GL_FRONT);

  glMatrixMode(GL_MODELVIEW);
    glLoadIdentity ();

    glRotatef (-90,  1, 0, 0);      // put Z going up
    glRotatef (90,  0, 0, 1);     // put Z going up
    glRotatef (-r_refdef.viewangles[2],  1, 0, 0);
    glRotatef (-r_refdef.viewangles[0],  0, 1, 0);
    glRotatef (-r_refdef.viewangles[1],  0, 0, 1);
    glTranslatef (-r_refdef.vieworg[0],  -r_refdef.vieworg[1],  -r_refdef.vieworg[2]);

  glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix);

  //
  // set drawing parms
  //
  if (gl_cull.value)
    glEnable(GL_CULL_FACE);
  else
    glDisable(GL_CULL_FACE);

  glDisable(GL_BLEND);
  glDisable(GL_ALPHA_TEST);
  glEnable(GL_DEPTH_TEST);
}

/*
================
R_RenderScene

r_refdef must be set before the first call
================
*/
void R_RenderScene (void)
{
  R_SetupFrame ();

  R_SetFrustum ();

  R_SetupGL ();

  R_MarkLeaves ();  // done here so we know if we're in water

  R_DrawWorld ();   // adds static entities to the list

  S_ExtraUpdate (); // don't let sound get messed up if going slow

  R_DrawEntitiesOnList ();

  GL_DisableMultitexture();

  R_RenderDlights ();

  R_DrawParticles ();

#ifdef GLTEST
  Test_Draw ();
#endif

}


/*
=============
R_Clear
=============
*/
void R_Clear (void)
{
  if (r_mirroralpha.value != 1.0)
  {
    if (gl_clear.value)
      glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    else
      glClear (GL_DEPTH_BUFFER_BIT);
    gldepthmin = 0;
    gldepthmax = 0.5;
    glDepthFunc (GL_LEQUAL);
  }
  else if (gl_ztrick.value)
  {
    static int trickframe;

    if (gl_clear.value)
      glClear (GL_COLOR_BUFFER_BIT);

    trickframe++;
    if (trickframe & 1)
    {
      gldepthmin = 0;
      gldepthmax = 0.49999;
      glDepthFunc (GL_LEQUAL);
    }
    else
    {
      gldepthmin = 1;
      gldepthmax = 0.5;
      glDepthFunc (GL_GEQUAL);
    }
  }
  else
  {
    if (gl_clear.value)
      glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    else
      glClear (GL_DEPTH_BUFFER_BIT);
    gldepthmin = 0;
    gldepthmax = 1;
    glDepthFunc (GL_LEQUAL);
  }

  glDepthRange (gldepthmin, gldepthmax);
}

/*
=============
R_Mirror
=============
*/
void R_Mirror (void)
{
  float   d;
  msurface_t  *s;
  entity_t  *ent;

  if (!mirror)
    return;

  memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix));

  d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist;
  VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg);

  d = DotProduct (vpn, mirror_plane->normal);
  VectorMA (vpn, -2*d, mirror_plane->normal, vpn);

  r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180;
  r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180;
  r_refdef.viewangles[2] = -r_refdef.viewangles[2];

  ent = &cl_entities[cl.viewentity];
  if (cl_numvisedicts < MAX_VISEDICTS)
  {
    cl_visedicts[cl_numvisedicts] = ent;
    cl_numvisedicts++;
  }

  gldepthmin = 0.5;
  gldepthmax = 1;
  glDepthRange (gldepthmin, gldepthmax);
  glDepthFunc (GL_LEQUAL);

  R_RenderScene ();
  R_DrawWaterSurfaces ();

  gldepthmin = 0;
  gldepthmax = 0.5;
  glDepthRange (gldepthmin, gldepthmax);
  glDepthFunc (GL_LEQUAL);

  // blend on top
  glEnable (GL_BLEND);
  glMatrixMode(GL_PROJECTION);
  if (mirror_plane->normal[2])
    glScalef (1,-1,1);
  else
    glScalef (-1,1,1);
  glCullFace(GL_FRONT);
  glMatrixMode(GL_MODELVIEW);

  glLoadMatrixf (r_base_world_matrix);

  glColor4f (1,1,1,r_mirroralpha.value);
  s = cl.worldmodel->textures[mirrortexturenum]->texturechain;
  for ( ; s ; s=s->texturechain)
    R_RenderBrushPoly (s);
  cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL;
  glDisable (GL_BLEND);
  glColor4f (1,1,1,1);
}

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

r_refdef must be set before the first call
================
*/
void R_RenderView (void)
{
  double  time1, time2;
  GLfloat colors[4] = {(GLfloat) 0.0, (GLfloat) 0.0, (GLfloat) 1, (GLfloat) 0.20};

  if (r_norefresh.value)
    return;

  if (!r_worldentity.model || !cl.worldmodel)
    Sys_Error ("R_RenderView: NULL worldmodel");

  if (r_speeds.value)
  {
    glFinish ();
    time1 = Sys_FloatTime ();
    c_brush_polys = 0;
    c_alias_polys = 0;
  }

  mirror = false;

  if (gl_finish.value)
    glFinish ();

  R_Clear ();

  // render normal view

/***** Experimental silly looking fog ******
****** Use r_fullbright if you enable ******
  glFogi(GL_FOG_MODE, GL_LINEAR);
  glFogfv(GL_FOG_COLOR, colors);
  glFogf(GL_FOG_END, 512.0);
  glEnable(GL_FOG);
********************************************/

  R_RenderScene ();
  R_DrawViewModel ();
  R_DrawWaterSurfaces ();

//  More fog right here :)
//  glDisable(GL_FOG);
//  End of all fog code...

  // render mirror view
  R_Mirror ();

  R_PolyBlend ();

  if (r_speeds.value)
  {
//    glFinish ();
    time2 = Sys_FloatTime ();
    Con_Printf ("%3i ms  %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); 
  }
}
