/*
  Copyright (C) 2010-2011  Charles Pence <charles@charlespence.net>
  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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "pch.h"
#include "quakedef.h"

#define    MAX_LIGHTMAPS    64
int            active_lightmaps;

GLuint    lightmap_textures[MAX_LIGHTMAPS] = {0};

unsigned        blocklights[18*18*3];

#define    BLOCK_WIDTH        128
#define    BLOCK_HEIGHT    128

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

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
uint16_t lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT];

/*
===============
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;
    float       cred, cblue, cgreen, brightness;
    unsigned    *bl;

    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];

        bl = blocklights;
        cred = cl_dlights[lnum].color[0] * 256.0f;
        cgreen = cl_dlights[lnum].color[1] * 256.0f;
        cblue = cl_dlights[lnum].color[2] * 256.0f;

        if (cl_dlights[lnum].dark)
        {
            cred = -cred;
            cgreen = -cgreen;
            cblue = -cblue;
        }

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

                    bl[0] = clamp(bl[0] + brightness * cred, 0, UINT_MAX);
                    bl[1] = clamp(bl[1] + brightness * cblue, 0, UINT_MAX);
                    bl[2] = clamp(bl[2] + brightness * cgreen, 0, UINT_MAX);
                }
                bl += 3;
            }
        }
    }
}


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

Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
void R_BuildLightMap (msurface_t *surf, uint16_t *dest, int stride)
{
    int            smax, tmax;
    int            t;
    int            i, j, size;
    byte        *lightmap;
    unsigned    scale;
    int            maps;
    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 (Cvar_VariableValue("r_fullbright") || !cl.worldmodel->lightdata)
    {
        bl = blocklights;
        for (i=0 ; i<size*3 ; i++)
            *bl++ = 255*256;
        goto store;
    }

// clear to no light
    bl = blocklights;
    for (i=0 ; i<size*3 ; i++)
        *bl++ = 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
            bl = blocklights;
            for (i=0 ; i<size*3 ; i++)
                *bl++ += lightmap[i] * scale;
            lightmap += size*3;    // skip to next lightmap
        }
    }

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

// bound, invert, and shift
store:
    stride -= (smax<<2);
    bl = blocklights;
    for (i=0 ; i<tmax ; i++, dest += stride)
    {
        for (j=0 ; j<smax ; j++)
        {
            t = *bl++; t >>= 1; if (t > 65535) t = 65535; *dest++ = t;
            t = *bl++; t >>= 1; if (t > 65535) t = 65535; *dest++ = t;
            t = *bl++; t >>= 1; if (t > 65535) t = 65535; *dest++ = t;
            *dest++ = 65535;
        }
    }
}


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

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

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

    glBegin (GL_POLYGON);
    v = p->verts[0];
    for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
    {
        glMultiTexCoord2f (GL_TEXTURE0, v[3], v[4]);
        glMultiTexCoord2f (GL_TEXTURE1, v[5], v[6]);
        glVertex3fv (v);
    }
    glEnd ();
}

/*
================
R_RenderBrushPoly

Texture 0 should already be bound when this is called
================
*/
void R_RenderBrushPoly (msurface_t *fa)
{
    c_brush_polys++;

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

    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, lightmap_textures[fa->lightmaptexturenum]);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
    glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 4.0f);

    R_DrawPoly(fa->polys);

    glDisable(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE0);
}

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

    c_brush_polys++;

    if (fa->flags & SURF_NOLIGHTMAPS)
        return;

    // 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 (Cvar_VariableValue("r_dynamic"))
        {
            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*4*BLOCK_WIDTH*BLOCK_HEIGHT;
            base += fa->light_t * BLOCK_WIDTH * 4 + fa->light_s * 4;
            R_BuildLightMap (fa, base, BLOCK_WIDTH*4);
        }
    }
}

void R_UploadLightmaps (void)
{
    int i;
    glRect_t *theRect;

    for (i = 0 ; i < MAX_LIGHTMAPS ; i++)
    {
        if (lightmap_modified[i])
        {
            lightmap_modified[i] = false;
            theRect = &lightmap_rectchange[i];
            glBindTexture(GL_TEXTURE_2D, lightmap_textures[i]);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t,
                BLOCK_WIDTH, theRect->h, GL_RGBA, GL_UNSIGNED_SHORT,
                lightmaps + (i * BLOCK_HEIGHT + theRect->t) * BLOCK_WIDTH * 4);
            theRect->l = BLOCK_WIDTH;
            theRect->t = BLOCK_HEIGHT;
            theRect->h = 0;
            theRect->w = 0;
        }
    }
}

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

    glColor4f (1, 1, 1, Cvar_VariableValue("r_wateralpha"));
    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;

        glBindTexture(GL_TEXTURE_2D, t->gl_texturenum);

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

        t->texturechain = NULL;
    }

    glColor4f (1,1,1,1);
}

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

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

        // Sky is done before this loop, and water after it
        if (!s || s->flags & SURF_DRAWTURB)
            continue;
        if (t->sky)
            continue;

        anim = R_TextureAnimation (t);
        glBindTexture(GL_TEXTURE_2D, anim->gl_texturenum);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

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

        t->texturechain = NULL;
    }
}

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

    currententity = e;

    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);

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

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

// 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);
        }
    }

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

    // update dynamic lightmaps on bmodel
    // FIXME: this makes bmodel lighting one frame behind, as
    // this updated lighting information won't be uploaded until *next*
    // frame
    psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
    for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++)
        R_RenderDynamicLightmaps(psurf);

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

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

        // draw the polygon
        if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
            (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
        {
            if (!psurf->texinfo || !psurf->texinfo->texture)
                continue;

            anim = R_TextureAnimation (psurf->texinfo->texture);
            glBindTexture(GL_TEXTURE_2D, anim->gl_texturenum);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

            R_RenderBrushPoly (psurf);
        }
    }

    glPopMatrix ();
}

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

    WORLD MODEL

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

/*
================
R_RecursiveWorldNode
================
*/
void R_RecursiveWorldNode (mnode_t *node)
{
    int            c, side;
    mplane_t    *plane;
    msurface_t    *surf, **mark;
    mleaf_t        *pleaf;
    double        dot;

    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 = r_refdef.origin[0] - plane->dist;
        break;
    case PLANE_Y:
        dot = r_refdef.origin[1] - plane->dist;
        break;
    case PLANE_Z:
        dot = r_refdef.origin[2] - plane->dist;
        break;
    default:
        dot = DotProduct (r_refdef.origin, 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

            // update dynamic lightmaps for this surface
            R_RenderDynamicLightmaps(surf);

            // store it out
            surf->texturechain = surf->texinfo->texture->texturechain;
            surf->texinfo->texture->texturechain = surf;
        }
    }

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



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

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

    currententity = &ent;

    glColor3f (1,1,1);

    R_RecursiveWorldNode (cl.worldmodel->nodes);

    R_UploadLightmaps ();

    R_DrawSky();

    DrawTextureChains ();
}


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

    if (r_oldviewleaf == r_viewleaf && !Cvar_VariableValue("r_novis"))
        return;

    r_visframecount++;
    r_oldviewleaf = r_viewleaf;

    if (Cvar_VariableValue("r_novis"))
    {
        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        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 -1;
}


mvertex_t    *r_pcurrentvertbase;
model_t        *currentmodel;

int    nColinElim;

/*
================
BuildSurfaceDisplayList
================
*/
void BuildSurfaceDisplayList (msurface_t *fa)
{
    int            i, lindex, lnumverts;
    medge_t        *pedges, *r_pedge;
    float        *vec;
    float        s, t;
    glpoly_t    *poly;

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

    //
    // 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;
    }

    poly->numverts = lnumverts;
}

/*
========================
GL_CreateSurfaceLightmap
========================
*/
void GL_CreateSurfaceLightmap (msurface_t *surf)
{
    int smax, tmax;
    uint16_t *base;

    if (surf->flags & SURF_NOLIGHTMAPS)
        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*4*BLOCK_WIDTH*BLOCK_HEIGHT;
    base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * 4;
    R_BuildLightMap (surf, base, BLOCK_WIDTH*4);
}


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

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

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

    r_framecount = 1;        // no dlightcache

    if (!lightmap_textures[0])
        glGenTextures(MAX_LIGHTMAPS, lightmap_textures);

    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;

            BuildSurfaceDisplayList (m->surfaces + i);
        }
    }

    //
    // upload all lightmaps that were filled
    //
    for (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;
        glBindTexture(GL_TEXTURE_2D, lightmap_textures[i]);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, BLOCK_WIDTH, BLOCK_HEIGHT, 0,
        GL_RGBA, GL_UNSIGNED_SHORT, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*4);
    }
}

