/*
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_surf.c: surface-related refresh code

#include "quakedef.h"

int			skytexturenum;

#ifndef GL_RGBA4
#define	GL_RGBA4	0
#endif


int		lightmap_bytes;		// 1, 2, or 4

int		lightmap_textures;

unsigned		blocklights[18*18];

#define	BLOCK_WIDTH		128
#define	BLOCK_HEIGHT	128

#define	MAX_LIGHTMAPS	64
int			active_lightmaps;

typedef struct glRect_s {
  unsigned char l,t,w,h;
} glRect_t;

glpoly_t	*lightmap_polys[MAX_LIGHTMAPS];
qboolean	lightmap_modified[MAX_LIGHTMAPS];
glRect_t	lightmap_rectchange[MAX_LIGHTMAPS];

int			allocated[MAX_LIGHTMAPS][BLOCK_WIDTH];

// the lightmap texture data needs to be kept in
// main memory so texsubimage can update properly
byte		lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT];

// For gl_texsort 0
msurface_t  *skychain = NULL;
msurface_t  *waterchain = NULL;

void R_RenderDynamicLightmaps (msurface_t *fa);

/*
===============
R_AddDynamicLights
===============
*/
void R_AddDynamicLights (msurface_t *surf)
{
  int			lnum;
  int			sd, td;
  float		dist, rad, minlight;
  vec3_t		impact, local;
  int			s, t;
  int			i;
  int			smax, tmax;
  mtexinfo_t	*tex;

  smax = (surf->extents[0]>>4)+1;
  tmax = (surf->extents[1]>>4)+1;
  tex = surf->texinfo;

  for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
  {
    if ( !(surf->dlightbits & (1<<lnum) ) )
      continue;		// not lit by this light

    rad = cl_dlights[lnum].radius;
    dist = DotProduct (cl_dlights[lnum].origin, surf->plane->normal) -
        surf->plane->dist;
    rad -= fabs(dist);
    minlight = cl_dlights[lnum].minlight;
    if (rad < minlight)
      continue;
    minlight = rad - minlight;

    for (i=0 ; i<3 ; i++)
    {
      impact[i] = cl_dlights[lnum].origin[i] -
          surf->plane->normal[i]*dist;
    }

    local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3];
    local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3];

    local[0] -= surf->texturemins[0];
    local[1] -= surf->texturemins[1];

    for (t = 0 ; t<tmax ; t++)
    {
      td = (int)(local[1] - t*16);
      if (td < 0)
        td = -td;
      for (s=0 ; s<smax ; s++)
      {
        sd = (int)(local[0] - s*16);
        if (sd < 0)
          sd = -sd;
        if (sd > td)
          dist = sd + (td>>1);
        else
          dist = td + (sd>>1);
        if (dist < minlight)
          blocklights[t*smax + s] += (int)((rad - dist)*256);
      }
    }
  }
}


/*
===============
R_BuildLightMap

Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
{
  int			smax, tmax;
  int			t;
  int			i, j, size;
  byte		*lightmap;
  unsigned	scale;
  int			maps;
  int			lightadj[4];
  unsigned	*bl;

  surf->cached_dlight = (surf->dlightframe == r_framecount);

  smax = (surf->extents[0]>>4)+1;
  tmax = (surf->extents[1]>>4)+1;
  size = smax*tmax;
  lightmap = surf->samples;

// set to full bright if no light data
  if (r_fullbright.value || !cl.worldmodel->lightdata)
  {
    for (i=0 ; i<size ; i++)
      blocklights[i] = 255*256;
    goto store;
  }

// clear to no light
  for (i=0 ; i<size ; i++)
    blocklights[i] = 0;

// add all the lightmaps
  if (lightmap)
    for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ;
       maps++)
    {
      scale = d_lightstylevalue[surf->styles[maps]];
      surf->cached_light[maps] = scale;	// 8.8 fraction
      for (i=0 ; i<size ; i++)
        blocklights[i] += lightmap[i] * scale;
      lightmap += size;	// skip to next lightmap
    }

// add all the dynamic lights
  if (surf->dlightframe == r_framecount)
    R_AddDynamicLights (surf);

// bound, invert, and shift
store:
  switch (gl_lightmap_format)
  {
  case GL_RGBA:
    stride -= (smax<<2);
    bl = blocklights;
    for (i=0 ; i<tmax ; i++, dest += stride)
    {
      for (j=0 ; j<smax ; j++)
      {
        t = *bl++;
        t >>= 7;
        if (t > 255)
          t = 255;
        dest[3] = 255-t;
        dest += 4;
      }
    }
    break;
  case GL_ALPHA:
  case GL_LUMINANCE:
  case GL_INTENSITY:
    bl = blocklights;
    for (i=0 ; i<tmax ; i++, dest += stride)
    {
      for (j=0 ; j<smax ; j++)
      {
        t = *bl++;
        t >>= 7;
        if (t > 255)
          t = 255;
        dest[j] = 255-t;
      }
    }
    break;
  default:
    Sys_Error ("Bad lightmap format");
  }
}


/*
===============
R_TextureAnimation

Returns the proper texture for a given time and base texture
===============
*/
texture_t *R_TextureAnimation (texture_t *base)
{
  int		reletive;
  int		count;

  if (currententity->frame)
  {
    if (base->alternate_anims)
      base = base->alternate_anims;
  }

  if (!base->anim_total)
    return base;

  reletive = (int)(cl.time*10) % base->anim_total;

  count = 0;
  while (base->anim_min > reletive || base->anim_max <= reletive)
  {
    base = base->anim_next;
    if (!base)
      Sys_Error ("R_TextureAnimation: broken cycle");
    if (++count > 100)
      Sys_Error ("R_TextureAnimation: infinite cycle");
  }

  return base;
}


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

  BRUSH MODELS

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


extern	int		solidskytexture;
extern	int		alphaskytexture;
extern	float	speedscale;		// for top sky and bottom sky

void DrawGLWaterPoly (glpoly_t *p);
void DrawGLWaterPolyLightmap (glpoly_t *p);

#ifdef _WIN32
lpMTexFUNC qglMTexCoord2fSGIS = NULL;
lpSelTexFUNC qglSelectTextureSGIS = NULL;
#endif

qboolean mtexenabled = false;

void GL_SelectTexture (GLenum target);

void GL_DisableMultitexture(void)
{
  if (mtexenabled) {
    glDisable(GL_TEXTURE_2D);
    GL_SelectTexture(TEXTURE0_SGIS);
    mtexenabled = false;
  }
}

void GL_EnableMultitexture(void)
{
  if (gl_mtexable) {
    GL_SelectTexture(TEXTURE1_SGIS);
    glEnable(GL_TEXTURE_2D);
    mtexenabled = true;
  }
}

#if 0
/*
================
R_DrawSequentialPoly

Systems that have fast state and texture changes can
just do everything as it passes with no need to sort
================
*/
void R_DrawSequentialPoly (msurface_t *s)
{
  glpoly_t	*p;
  float		*v;
  int			i;
  texture_t	*t;

  //
  // normal lightmaped poly
  //
  if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER) ) )
  {
    p = s->polys;

    t = R_TextureAnimation (s->texinfo->texture);
    GL_Bind (t->gl_texturenum);
    glBegin (GL_POLYGON);
    v = p->verts[0];
    for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
    {
      glTexCoord2f (v[3], v[4]);
      glVertex3fv (v);
    }
    glEnd ();

    GL_Bind (lightmap_textures + s->lightmaptexturenum);
    glEnable (GL_BLEND);
    glBegin (GL_POLYGON);
    v = p->verts[0];
    for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
    {
      glTexCoord2f (v[5], v[6]);
      glVertex3fv (v);
    }
    glEnd ();

    glDisable (GL_BLEND);

    return;
  }

  //
  // subdivided water surface warp
  //
  if (s->flags & SURF_DRAWTURB)
  {
    GL_Bind (s->texinfo->texture->gl_texturenum);
    EmitWaterPolys (s);
    return;
  }

  //
  // subdivided sky warp
  //
  if (s->flags & SURF_DRAWSKY)
  {
    GL_Bind (solidskytexture);
    speedscale = realtime*8;
    speedscale -= (int)speedscale;

    EmitSkyPolys (s);

    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    GL_Bind (alphaskytexture);
    speedscale = realtime*16;
    speedscale -= (int)speedscale;
    EmitSkyPolys (s);
    if (gl_lightmap_format == GL_LUMINANCE)
      glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR);

    glDisable (GL_BLEND);
  }

  //
  // underwater warped with lightmap
  //
  p = s->polys;

  t = R_TextureAnimation (s->texinfo->texture);
  GL_Bind (t->gl_texturenum);
  DrawGLWaterPoly (p);

  GL_Bind (lightmap_textures + s->lightmaptexturenum);
  glEnable (GL_BLEND);
  DrawGLWaterPolyLightmap (p);
  glDisable (GL_BLEND);
}
#else
/*
================
R_DrawSequentialPoly

Systems that have fast state and texture changes can
just do everything as it passes with no need to sort
================
*/
void R_DrawSequentialPoly (msurface_t *s)
{
  glpoly_t	*p;
  float		*v;
  int			i;
  texture_t	*t;
  vec3_t		nv, dir;
  float		ss, ss2, length;
  float		s1, t1;
  glRect_t	*theRect;

  //
  // normal lightmaped poly
  //

  if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER) ) )
  {
    R_RenderDynamicLightmaps (s);
    if (gl_mtexable) {
      p = s->polys;

      t = R_TextureAnimation (s->texinfo->texture);
      // Binds world to texture env 0
      GL_SelectTexture(TEXTURE0_SGIS);
      GL_Bind (t->gl_texturenum);
      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

      // Binds lightmap to texenv 1
      GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1)
      GL_Bind (lightmap_textures + s->lightmaptexturenum);
      i = s->lightmaptexturenum;
      if (lightmap_modified[i])
      {
        lightmap_modified[i] = false;
        theRect = &lightmap_rectchange[i];
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t,
          BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
          lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes);
        theRect->l = BLOCK_WIDTH;
        theRect->t = BLOCK_HEIGHT;
        theRect->h = 0;
        theRect->w = 0;
      }
      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);

#ifdef USE_OPENGLES

      glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][3]);
            glClientActiveTexture(GL_TEXTURE1);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][5]);
      glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]);
            glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glClientActiveTexture(GL_TEXTURE0);

#else
      glBegin(GL_POLYGON);
      v = p->verts[0];
      for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
      {
        qglMTexCoord2fSGIS (TEXTURE0_SGIS, v[3], v[4]);
        qglMTexCoord2fSGIS (TEXTURE1_SGIS, v[5], v[6]);
        glVertex3fv (v);
      }
      glEnd ();
#endif
      return;
    } else {
      p = s->polys;

      t = R_TextureAnimation (s->texinfo->texture);
      GL_Bind (t->gl_texturenum);
#ifdef USE_OPENGLES
      glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]);
      glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][3]);
      glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
#else
      glBegin (GL_POLYGON);
      v = p->verts[0];
      for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
      {
        glTexCoord2f (v[3], v[4]);
        glVertex3fv (v);
      }
      glEnd ();
#endif

      GL_Bind (lightmap_textures + s->lightmaptexturenum);
      glEnable (GL_BLEND);
#ifdef USE_OPENGLES
      glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][5]);
      glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
#else
      glBegin (GL_POLYGON);
      v = p->verts[0];
      for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
      {
        glTexCoord2f (v[5], v[6]);
        glVertex3fv (v);
      }
      glEnd ();
#endif

      glDisable (GL_BLEND);
    }

    return;
  }

  //
  // subdivided water surface warp
  //

  if (s->flags & SURF_DRAWTURB)
  {
    GL_DisableMultitexture();
    GL_Bind (s->texinfo->texture->gl_texturenum);
    EmitWaterPolys (s);
    return;
  }

  //
  // subdivided sky warp
  //
  if (s->flags & SURF_DRAWSKY)
  {
    GL_DisableMultitexture();
    GL_Bind (solidskytexture);
    speedscale = realtime*8;
    speedscale -= (int)speedscale & ~127;

    EmitSkyPolys (s);

    glEnable (GL_BLEND);
    GL_Bind (alphaskytexture);
    speedscale = realtime*16;
    speedscale -= (int)speedscale & ~127;
    EmitSkyPolys (s);

    glDisable (GL_BLEND);
    return;
  }

  //
  // underwater warped with lightmap
  //
  R_RenderDynamicLightmaps (s);
  if (gl_mtexable) {
    p = s->polys;

    t = R_TextureAnimation (s->texinfo->texture);
    GL_SelectTexture(TEXTURE0_SGIS);
    GL_Bind (t->gl_texturenum);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    GL_EnableMultitexture();
    GL_Bind (lightmap_textures + s->lightmaptexturenum);
    i = s->lightmaptexturenum;
    if (lightmap_modified[i])
    {
      lightmap_modified[i] = false;
      theRect = &lightmap_rectchange[i];
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t,
        BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
        lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes);
      theRect->l = BLOCK_WIDTH;
      theRect->t = BLOCK_HEIGHT;
      theRect->h = 0;
      theRect->w = 0;
    }
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
#ifdef USE_OPENGLES
    {
      float* pPos = gVertexBuffer;
      v = p->verts[0];
      for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
      {
        *pPos++ = v[0] + 8*sinf(v[1]*0.05f+realtime)*sinf(v[2]*0.05f+realtime);
        *pPos++ = v[1] + 8*sinf(v[0]*0.05f+realtime)*sinf(v[2]*0.05f+realtime);
        *pPos++ = v[2];
      }
    }
        glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][3]);
        glClientActiveTexture(GL_TEXTURE1);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][5]);
        glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]);
        glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glClientActiveTexture(GL_TEXTURE0);
#else
    glBegin (GL_TRIANGLE_FAN);
    v = p->verts[0];
    for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
    {
      qglMTexCoord2fSGIS (TEXTURE0_SGIS, v[3], v[4]);
      qglMTexCoord2fSGIS (TEXTURE1_SGIS, v[5], v[6]);

      nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
      nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
      nv[2] = v[2];

      glVertex3fv (nv);
    }
    glEnd ();
#endif

  } else {
    p = s->polys;

    t = R_TextureAnimation (s->texinfo->texture);
    GL_Bind (t->gl_texturenum);
    DrawGLWaterPoly (p);

    GL_Bind (lightmap_textures + s->lightmaptexturenum);
    glEnable (GL_BLEND);
    DrawGLWaterPolyLightmap (p);
    glDisable (GL_BLEND);
  }
}
#endif


/*
================
DrawGLWaterPoly

Warp the vertex coordinates
================
*/
void DrawGLWaterPoly (glpoly_t *p)
{
  int		i;
  float	*v;
  float	s, t, os, ot;
  vec3_t	nv;

  GL_DisableMultitexture();

#ifdef USE_OPENGLES
  glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer);
  glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][3]);

  v = p->verts[0];
  {
    float* pnv = gVertexBuffer;
    for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
    {
      pnv[0] = v[0] + 8*sinf(v[1]*0.05f+realtime)*sinf(v[2]*0.05f+realtime);
      pnv[1] = v[1] + 8*sinf(v[0]*0.05f+realtime)*sinf(v[2]*0.05f+realtime);
      pnv[2] = v[2];

      pnv += 3;
    }
  }
  glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
#else
  glBegin (GL_TRIANGLE_FAN);
  v = p->verts[0];
  for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
  {
    glTexCoord2f (v[3], v[4]);

    nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
    nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
    nv[2] = v[2];

    glVertex3fv (nv);
  }
  glEnd ();
#endif
}

void DrawGLWaterPolyLightmap (glpoly_t *p)
{
  int		i;
  float	*v;
  float	s, t, os, ot;
  vec3_t	nv;

  GL_DisableMultitexture();

#ifdef USE_OPENGLES
  glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer);
  glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][5]);

  v = p->verts[0];
  {
    float* pnv = gVertexBuffer;
    for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
    {
      pnv[0] = v[0] + 8*sinf(v[1]*0.05f+realtime)*sinf(v[2]*0.05f+realtime);
      pnv[1] = v[1] + 8*sinf(v[0]*0.05f+realtime)*sinf(v[2]*0.05f+realtime);
      pnv[2] = v[2];

      pnv += 3;
    }
  }
  glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);

#else
  glBegin (GL_TRIANGLE_FAN);
  v = p->verts[0];
  for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
  {
    glTexCoord2f (v[5], v[6]);

    nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime);
    nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime);
    nv[2] = v[2];

    glVertex3fv (nv);
  }
  glEnd ();
#endif
}

/*
================
DrawGLPoly
================
*/
void DrawGLPoly (glpoly_t *p)
{
  int		i;
  float	*v;

#ifdef USE_OPENGLES
  glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]);
  glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][3]);
  glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
#else
  glBegin (GL_POLYGON);
  v = p->verts[0];
  for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
  {
    glTexCoord2f (v[3], v[4]);
    glVertex3fv (v);
  }
  glEnd ();
#endif
}


/*
================
R_BlendLightmaps
================
*/
void R_BlendLightmaps (void)
{
  int			i, j;
  glpoly_t	*p;
  float		*v;
  glRect_t	*theRect;

  if (r_fullbright.value)
    return;
  if (!gl_texsort.value)
    return;

  glDepthMask (0);		// don't bother writing Z

  if (gl_lightmap_format == GL_LUMINANCE)
    glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
  else if (gl_lightmap_format == GL_INTENSITY)
  {
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glColor4f (0,0,0,1);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  }

  if (!r_lightmap.value)
  {
    glEnable (GL_BLEND);
  }

  for (i=0 ; i<MAX_LIGHTMAPS ; i++)
  {
    p = lightmap_polys[i];
    if (!p)
      continue;
    GL_Bind(lightmap_textures+i);
    if (lightmap_modified[i])
    {
      lightmap_modified[i] = false;
      theRect = &lightmap_rectchange[i];
//			glTexImage2DHelper (GL_TEXTURE_2D, 0, lightmap_bytes
//			, BLOCK_WIDTH, BLOCK_HEIGHT, 0,
//			gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes);
//			glTexImage2DHelper (GL_TEXTURE_2D, 0, lightmap_bytes
//				, BLOCK_WIDTH, theRect->h, 0,
//				gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+(i*BLOCK_HEIGHT+theRect->t)*BLOCK_WIDTH*lightmap_bytes);
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t,
        BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE,
        lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes);
      theRect->l = BLOCK_WIDTH;
      theRect->t = BLOCK_HEIGHT;
      theRect->h = 0;
      theRect->w = 0;
    }
    for ( ; p ; p=p->chain)
    {
      if (p->flags & SURF_UNDERWATER)
        DrawGLWaterPolyLightmap (p);
      else
      {
#ifdef USE_OPENGLES
        glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]);
        glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][5]);
        glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts);
#else
        glBegin (GL_POLYGON);
        v = p->verts[0];
        for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE)
        {
          glTexCoord2f (v[5], v[6]);
          glVertex3fv (v);
        }
        glEnd ();
#endif
      }
    }
  }

  glDisable (GL_BLEND);
  if (gl_lightmap_format == GL_LUMINANCE)
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  else if (gl_lightmap_format == GL_INTENSITY)
  {
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glColor4f (1,1,1,1);
  }

  glDepthMask (1);		// back to normal Z buffering
}

/*
================
R_RenderBrushPoly
================
*/
void R_RenderBrushPoly (msurface_t *fa)
{
  texture_t	*t;
  byte		*base;
  int			maps;
  glRect_t    *theRect;
  int smax, tmax;

  c_brush_polys++;

  if (fa->flags & SURF_DRAWSKY)
  {	// warp texture, no lightmaps
    EmitBothSkyLayers (fa);
    return;
  }

  t = R_TextureAnimation (fa->texinfo->texture);
  GL_Bind (t->gl_texturenum);

  if (fa->flags & SURF_DRAWTURB)
  {	// warp texture, no lightmaps
    EmitWaterPolys (fa);
    return;
  }

  if (fa->flags & SURF_UNDERWATER)
    DrawGLWaterPoly (fa->polys);
  else
    DrawGLPoly (fa->polys);

  // add the poly to the proper lightmap chain

  fa->polys->chain = lightmap_polys[fa->lightmaptexturenum];
  lightmap_polys[fa->lightmaptexturenum] = fa->polys;

  // check for lightmap modification
  for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ;
     maps++)
    if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps])
      goto dynamic;

  if (fa->dlightframe == r_framecount	// dynamic this frame
    || fa->cached_dlight)			// dynamic previously
  {
dynamic:
    if (r_dynamic.value)
    {
      lightmap_modified[fa->lightmaptexturenum] = true;
      theRect = &lightmap_rectchange[fa->lightmaptexturenum];
      if (fa->light_t < theRect->t) {
        if (theRect->h)
          theRect->h += theRect->t - fa->light_t;
        theRect->t = fa->light_t;
      }
      if (fa->light_s < theRect->l) {
        if (theRect->w)
          theRect->w += theRect->l - fa->light_s;
        theRect->l = fa->light_s;
      }
      smax = (fa->extents[0]>>4)+1;
      tmax = (fa->extents[1]>>4)+1;
      if ((theRect->w + theRect->l) < (fa->light_s + smax))
        theRect->w = (fa->light_s-theRect->l)+smax;
      if ((theRect->h + theRect->t) < (fa->light_t + tmax))
        theRect->h = (fa->light_t-theRect->t)+tmax;
      base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT;
      base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes;
      R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes);
    }
  }
}

/*
================
R_RenderDynamicLightmaps
Multitexture
================
*/
void R_RenderDynamicLightmaps (msurface_t *fa)
{
  texture_t	*t;
  byte		*base;
  int			maps;
  glRect_t    *theRect;
  int smax, tmax;

  c_brush_polys++;

  if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) )
    return;

  fa->polys->chain = lightmap_polys[fa->lightmaptexturenum];
  lightmap_polys[fa->lightmaptexturenum] = fa->polys;

  // check for lightmap modification
  for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ;
     maps++)
    if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps])
      goto dynamic;

  if (fa->dlightframe == r_framecount	// dynamic this frame
    || fa->cached_dlight)			// dynamic previously
  {
dynamic:
    if (r_dynamic.value)
    {
      lightmap_modified[fa->lightmaptexturenum] = true;
      theRect = &lightmap_rectchange[fa->lightmaptexturenum];
      if (fa->light_t < theRect->t) {
        if (theRect->h)
          theRect->h += theRect->t - fa->light_t;
        theRect->t = fa->light_t;
      }
      if (fa->light_s < theRect->l) {
        if (theRect->w)
          theRect->w += theRect->l - fa->light_s;
        theRect->l = fa->light_s;
      }
      smax = (fa->extents[0]>>4)+1;
      tmax = (fa->extents[1]>>4)+1;
      if ((theRect->w + theRect->l) < (fa->light_s + smax))
        theRect->w = (fa->light_s-theRect->l)+smax;
      if ((theRect->h + theRect->t) < (fa->light_t + tmax))
        theRect->h = (fa->light_t-theRect->t)+tmax;
      base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT;
      base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes;
      R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes);
    }
  }
}

/*
================
R_MirrorChain
================
*/
void R_MirrorChain (msurface_t *s)
{
  if (mirror)
    return;
  mirror = true;
  mirror_plane = s->plane;
}


#if 0
/*
================
R_DrawWaterSurfaces
================
*/
void R_DrawWaterSurfaces (void)
{
  int			i;
  msurface_t	*s;
  texture_t	*t;

  if (r_wateralpha.value == 1.0)
    return;

  //
  // go back to the world matrix
  //
    glLoadMatrixf (r_world_matrix);

  glEnable (GL_BLEND);
  glColor4f (1,1,1,r_wateralpha.value);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  for (i=0 ; i<cl.worldmodel->numtextures ; i++)
  {
    t = cl.worldmodel->textures[i];
    if (!t)
      continue;
    s = t->texturechain;
    if (!s)
      continue;
    if ( !(s->flags & SURF_DRAWTURB) )
      continue;

    // set modulate mode explicitly
    GL_Bind (t->gl_texturenum);

    for ( ; s ; s=s->texturechain)
      R_RenderBrushPoly (s);

    t->texturechain = NULL;
  }

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

  glColor4f (1,1,1,1);
  glDisable (GL_BLEND);
}
#else
/*
================
R_DrawWaterSurfaces
================
*/
void R_DrawWaterSurfaces (void)
{
  int			i;
  msurface_t	*s;
  texture_t	*t;

  if (r_wateralpha.value == 1.0 && gl_texsort.value)
    return;

  //
  // go back to the world matrix
  //

    glLoadMatrixf (r_world_matrix);

  if (r_wateralpha.value < 1.0) {
    glEnable (GL_BLEND);
    glColor4f (1,1,1,r_wateralpha.value);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  }

  if (!gl_texsort.value) {
    if (!waterchain)
      return;

    for ( s = waterchain ; s ; s=s->texturechain) {
      GL_Bind (s->texinfo->texture->gl_texturenum);
      EmitWaterPolys (s);
    }

    waterchain = NULL;
  } else {

    for (i=0 ; i<cl.worldmodel->numtextures ; i++)
    {
      t = cl.worldmodel->textures[i];
      if (!t)
        continue;
      s = t->texturechain;
      if (!s)
        continue;
      if ( !(s->flags & SURF_DRAWTURB ) )
        continue;

      // set modulate mode explicitly

      GL_Bind (t->gl_texturenum);

      for ( ; s ; s=s->texturechain)
        EmitWaterPolys (s);

      t->texturechain = NULL;
    }

  }

  if (r_wateralpha.value < 1.0) {
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    glColor4f (1,1,1,1);
    glDisable (GL_BLEND);
  }

}

#endif

/*
================
DrawTextureChains
================
*/
void DrawTextureChains (void)
{
  int		i;
  msurface_t	*s;
  texture_t	*t;

  if (!gl_texsort.value) {
    GL_DisableMultitexture();

    if (skychain) {
      R_DrawSkyChain(skychain);
      skychain = NULL;
    }

    return;
  }

  for (i=0 ; i<cl.worldmodel->numtextures ; i++)
  {
    t = cl.worldmodel->textures[i];
    if (!t)
      continue;
    s = t->texturechain;
    if (!s)
      continue;
    if (i == skytexturenum)
      R_DrawSkyChain (s);
    else if (i == mirrortexturenum && r_mirroralpha.value != 1.0)
    {
      R_MirrorChain (s);
      continue;
    }
    else
    {
      if ((s->flags & SURF_DRAWTURB) && r_wateralpha.value != 1.0)
        continue;	// draw translucent water later
      for ( ; s ; s=s->texturechain)
        R_RenderBrushPoly (s);
    }

    t->texturechain = NULL;
  }
}

/*
=================
R_DrawBrushModel
=================
*/
void R_DrawBrushModel (entity_t *e)
{
  int			j, k;
  vec3_t		mins, maxs;
  int			i, numsurfaces;
  msurface_t	*psurf;
  float		dot;
  mplane_t	*pplane;
  model_t		*clmodel;
  qboolean	rotated;

  currententity = e;
  currenttexture = -1;

  clmodel = e->model;

  if (e->angles[0] || e->angles[1] || e->angles[2])
  {
    rotated = true;
    for (i=0 ; i<3 ; i++)
    {
      mins[i] = e->origin[i] - clmodel->radius;
      maxs[i] = e->origin[i] + clmodel->radius;
    }
  }
  else
  {
    rotated = false;
    VectorAdd (e->origin, clmodel->mins, mins);
    VectorAdd (e->origin, clmodel->maxs, maxs);
  }

  if (R_CullBox (mins, maxs))
    return;

  glColor3f (1,1,1);
  memset (lightmap_polys, 0, sizeof(lightmap_polys));

  VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
  if (rotated)
  {
    vec3_t	temp;
    vec3_t	forward, right, up;

    VectorCopy (modelorg, temp);
    AngleVectors (e->angles, forward, right, up);
    modelorg[0] = DotProduct (temp, forward);
    modelorg[1] = -DotProduct (temp, right);
    modelorg[2] = DotProduct (temp, up);
  }

  psurf = &clmodel->surfaces[clmodel->firstmodelsurface];

// calculate dynamic lighting for bmodel if it's not an
// instanced model
  if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value)
  {
    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);
    }
  }

    glPushMatrix ();
e->angles[0] = -e->angles[0];	// stupid quake bug
  R_RotateForEntity (e);
e->angles[0] = -e->angles[0];	// stupid quake bug

  //
  // draw texture
  //
  for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++)
  {
  // find which side of the node we are on
    pplane = psurf->plane;

    dot = DotProduct (modelorg, pplane->normal) - pplane->dist;

  // draw the polygon
    if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
      (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
    {
      if (gl_texsort.value)
        R_RenderBrushPoly (psurf);
      else
        R_DrawSequentialPoly (psurf);
    }
  }

  R_BlendLightmaps ();

  glPopMatrix ();
}

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

  WORLD MODEL

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

/*
================
R_RecursiveWorldNode
================
*/
void R_RecursiveWorldNode (mnode_t *node)
{
  int			i, c, side, *pindex;
  vec3_t		acceptpt, rejectpt;
  mplane_t	*plane;
  msurface_t	*surf, **mark;
  mleaf_t		*pleaf;
  double		d, dot;
  vec3_t		mins, maxs;

  if (node->contents == CONTENTS_SOLID)
    return;		// solid

  if (node->visframe != r_visframecount)
    return;
  if (R_CullBox (node->minmaxs, node->minmaxs+3))
    return;

// if a leaf node, draw stuff
  if (node->contents < 0)
  {
    pleaf = (mleaf_t *)node;

    mark = pleaf->firstmarksurface;
    c = pleaf->nummarksurfaces;

    if (c)
    {
      do
      {
        (*mark)->visframe = r_framecount;
        mark++;
      } while (--c);
    }

  // deal with model fragments in this leaf
    if (pleaf->efrags)
      R_StoreEfrags (&pleaf->efrags);

    return;
  }

// node is just a decision point, so go down the apropriate sides

// find which side of the node we are on
  plane = node->plane;

  switch (plane->type)
  {
  case PLANE_X:
    dot = modelorg[0] - plane->dist;
    break;
  case PLANE_Y:
    dot = modelorg[1] - plane->dist;
    break;
  case PLANE_Z:
    dot = modelorg[2] - plane->dist;
    break;
  default:
    dot = DotProduct (modelorg, plane->normal) - plane->dist;
    break;
  }

  if (dot >= 0)
    side = 0;
  else
    side = 1;

// recurse down the children, front side first
  R_RecursiveWorldNode (node->children[side]);

// draw stuff
  c = node->numsurfaces;

  if (c)
  {
    surf = cl.worldmodel->surfaces + node->firstsurface;

    if (dot < 0 -BACKFACE_EPSILON)
      side = SURF_PLANEBACK;
    else if (dot > BACKFACE_EPSILON)
      side = 0;
    {
      for ( ; c ; c--, surf++)
      {
        if (surf->visframe != r_framecount)
          continue;

        // don't backface underwater surfaces, because they warp
        if ( !(surf->flags & SURF_UNDERWATER) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) )
          continue;		// wrong side

        // if sorting by texture, just store it out
        if (gl_texsort.value)
        {
          if (!mirror
          || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum])
          {
            surf->texturechain = surf->texinfo->texture->texturechain;
            surf->texinfo->texture->texturechain = surf;
          }
        } else if (surf->flags & SURF_DRAWSKY) {
          surf->texturechain = skychain;
          skychain = surf;
        } else if (surf->flags & SURF_DRAWTURB) {
          surf->texturechain = waterchain;
          waterchain = surf;
        } else
          R_DrawSequentialPoly (surf);

      }
    }

  }

// recurse down the back side
  R_RecursiveWorldNode (node->children[!side]);
}



/*
=============
R_DrawWorld
=============
*/
void R_DrawWorld (void)
{
  entity_t	ent;
  int			i;

  memset (&ent, 0, sizeof(ent));
  ent.model = cl.worldmodel;

  VectorCopy (r_refdef.vieworg, modelorg);

  currententity = &ent;
  currenttexture = -1;

  glColor3f (1,1,1);
  memset (lightmap_polys, 0, sizeof(lightmap_polys));
#ifdef QUAKE2
  R_ClearSkyBox ();
#endif

  R_RecursiveWorldNode (cl.worldmodel->nodes);

  DrawTextureChains ();

  R_BlendLightmaps ();

#ifdef QUAKE2
  R_DrawSkyBox ();
#endif
}


/*
===============
R_MarkLeaves
===============
*/
void R_MarkLeaves (void)
{
  byte	*vis;
  mnode_t	*node;
  int		i;
  byte	solid[4096];

  if (r_oldviewleaf == r_viewleaf && !r_novis.value)
    return;

  if (mirror)
    return;

  r_visframecount++;
  r_oldviewleaf = r_viewleaf;

  if (r_novis.value)
  {
    vis = solid;
    memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3);
  }
  else
    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);
    }
  }
}



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

  LIGHTMAP ALLOCATION

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

// returns a texture number and the position inside it
int AllocBlock (int w, int h, int *x, int *y)
{
  int		i, j;
  int		best, best2;
  int		bestx;
  int		texnum;

  for (texnum=0 ; texnum<MAX_LIGHTMAPS ; texnum++)
  {
    best = BLOCK_HEIGHT;

    for (i=0 ; i<BLOCK_WIDTH-w ; i++)
    {
      best2 = 0;

      for (j=0 ; j<w ; j++)
      {
        if (allocated[texnum][i+j] >= best)
          break;
        if (allocated[texnum][i+j] > best2)
          best2 = allocated[texnum][i+j];
      }
      if (j == w)
      {	// this is a valid spot
        *x = i;
        *y = best = best2;
      }
    }

    if (best + h > BLOCK_HEIGHT)
      continue;

    for (i=0 ; i<w ; i++)
      allocated[texnum][*x + i] = best + h;

    return texnum;
  }

  Sys_Error ("AllocBlock: full");
  return 0;
}


mvertex_t	*r_pcurrentvertbase;
model_t		*currentmodel;

int	nColinElim;

/*
================
BuildSurfaceDisplayList
================
*/
void BuildSurfaceDisplayList (msurface_t *fa)
{
  int			i, lindex, lnumverts, s_axis, t_axis;
  float		dist, lastdist, lzi, scale, u, v, frac;
  unsigned	mask;
  vec3_t		local, transformed;
  medge_t		*pedges, *r_pedge;
  mplane_t	*pplane;
  int			vertpage, newverts, newpage, lastvert;
  qboolean	visible;
  float		*vec;
  float		s, t;
  glpoly_t	*poly;

// reconstruct the polygon
  pedges = currentmodel->edges;
  lnumverts = fa->numedges;
  vertpage = 0;

  //
  // draw texture
  //
  poly = (glpoly_t*) Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float));
  poly->next = fa->polys;
  poly->flags = fa->flags;
  fa->polys = poly;
  poly->numverts = lnumverts;

  for (i=0 ; i<lnumverts ; i++)
  {
    lindex = currentmodel->surfedges[fa->firstedge + i];

    if (lindex > 0)
    {
      r_pedge = &pedges[lindex];
      vec = r_pcurrentvertbase[r_pedge->v[0]].position;
    }
    else
    {
      r_pedge = &pedges[-lindex];
      vec = r_pcurrentvertbase[r_pedge->v[1]].position;
    }
    s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
    s /= fa->texinfo->texture->width;

    t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
    t /= fa->texinfo->texture->height;

    VectorCopy (vec, poly->verts[i]);
    poly->verts[i][3] = s;
    poly->verts[i][4] = t;

    //
    // lightmap texture coordinates
    //
    s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
    s -= fa->texturemins[0];
    s += fa->light_s*16;
    s += 8;
    s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width;

    t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
    t -= fa->texturemins[1];
    t += fa->light_t*16;
    t += 8;
    t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height;

    poly->verts[i][5] = s;
    poly->verts[i][6] = t;
  }

  //
  // remove co-linear points - Ed
  //
  if (!gl_keeptjunctions.value && !(fa->flags & SURF_UNDERWATER) )
  {
    for (i = 0 ; i < lnumverts ; ++i)
    {
      vec3_t v1, v2;
      float *prev, *thiz, *next;
      float f;

      prev = poly->verts[(i + lnumverts - 1) % lnumverts];
      thiz = poly->verts[i];
      next = poly->verts[(i + 1) % lnumverts];

      VectorSubtract( thiz, prev, v1 );
      VectorNormalize( v1 );
      VectorSubtract( next, prev, v2 );
      VectorNormalize( v2 );

      // skip co-linear points
      #define COLINEAR_EPSILON 0.001
      if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) &&
        (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) &&
        (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON))
      {
        int j;
        for (j = i + 1; j < lnumverts; ++j)
        {
          int k;
          for (k = 0; k < VERTEXSIZE; ++k)
            poly->verts[j - 1][k] = poly->verts[j][k];
        }
        --lnumverts;
        ++nColinElim;
        // retry next vertex next time, which is now current vertex
        --i;
      }
    }
  }
  poly->numverts = lnumverts;

}

/*
========================
GL_CreateSurfaceLightmap
========================
*/
void GL_CreateSurfaceLightmap (msurface_t *surf)
{
  int		smax, tmax, s, t, l, i;
  byte	*base;

  if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))
    return;

  smax = (surf->extents[0]>>4)+1;
  tmax = (surf->extents[1]>>4)+1;

  surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t);
  base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT;
  base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes;
  R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes);
}



void GL_UploadLightmaps()
{
   if (!gl_texsort.value)
     GL_SelectTexture(TEXTURE1_SGIS);

  //
  // upload all lightmaps that were filled
  //
  for (int i=0 ; i<MAX_LIGHTMAPS ; i++)
  {
    if (!allocated[i][0])
      break;		// no more used
    lightmap_modified[i] = false;
    lightmap_rectchange[i].l = BLOCK_WIDTH;
    lightmap_rectchange[i].t = BLOCK_HEIGHT;
    lightmap_rectchange[i].w = 0;
    lightmap_rectchange[i].h = 0;
    GL_Bind(lightmap_textures + i);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2DHelper (GL_TEXTURE_2D, 0, lightmap_bytes
    , BLOCK_WIDTH, BLOCK_HEIGHT, 0,
    gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes);
  }

   if (!gl_texsort.value)
     GL_SelectTexture(TEXTURE0_SGIS);

}

/*
==================
GL_BuildLightmaps

Builds the lightmap texture
with all the surfaces from all brush models
==================
*/
void GL_BuildLightmaps (void)
{
  int		i, j;
  model_t	*m;
  extern qboolean isPermedia;

  memset (allocated, 0, sizeof(allocated));

  r_framecount = 1;		// no dlightcache

  if (!lightmap_textures)
  {
    lightmap_textures = texture_extension_number;
    texture_extension_number += MAX_LIGHTMAPS;
  }

  gl_lightmap_format = GL_LUMINANCE;
  // default differently on the Permedia
  if (isPermedia)
    gl_lightmap_format = GL_RGBA;

  if (COM_CheckParm ("-lm_1"))
    gl_lightmap_format = GL_LUMINANCE;
  if (COM_CheckParm ("-lm_a"))
    gl_lightmap_format = GL_ALPHA;
  if (COM_CheckParm ("-lm_i"))
    gl_lightmap_format = GL_INTENSITY;
  if (COM_CheckParm ("-lm_2"))
    gl_lightmap_format = GL_RGBA4;
  if (COM_CheckParm ("-lm_4"))
    gl_lightmap_format = GL_RGBA;

  switch (gl_lightmap_format)
  {
  case GL_RGBA:
    lightmap_bytes = 4;
    break;
  case GL_RGBA4:
    lightmap_bytes = 2;
    break;
  case GL_LUMINANCE:
  case GL_INTENSITY:
  case GL_ALPHA:
    lightmap_bytes = 1;
    break;
  }

  for (j=1 ; j<MAX_MODELS ; j++)
  {
    m = cl.model_precache[j];
    if (!m)
      break;
    if (m->name[0] == '*')
      continue;
    r_pcurrentvertbase = m->vertexes;
    currentmodel = m;
    for (i=0 ; i<m->numsurfaces ; i++)
    {
      GL_CreateSurfaceLightmap (m->surfaces + i);
      if ( m->surfaces[i].flags & SURF_DRAWTURB )
        continue;
#ifndef QUAKE2
      if ( m->surfaces[i].flags & SURF_DRAWSKY )
        continue;
#endif
      BuildSurfaceDisplayList (m->surfaces + i);
    }
  }

  GL_UploadLightmaps();
}


