spingle/source/r_brush.c

959 lines
23 KiB
C
Raw Normal View History

2019-11-24 20:45:15 -08:00
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
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_brush.c: brush model rendering. renamed from r_surf.c
2019-12-02 07:07:37 -08:00
#include "q_defs.h"
2019-11-24 20:45:15 -08:00
extern cvar_t gl_fullbrights, r_drawflat, gl_overbright, r_oldwater; //johnfitz
extern cvar_t gl_zfix; // QuakeSpasm z-fighting fix
2019-11-25 17:40:18 -08:00
int32_t gl_lightmap_format;
int32_t lightmap_bytes;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
#define BLOCK_WIDTH 128
#define BLOCK_HEIGHT 128
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
gltexture_t *lightmap_textures[MAX_LIGHTMAPS]; //johnfitz -- changed to an array
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
unsigned blocklights[BLOCK_WIDTH * BLOCK_HEIGHT * 3]; //johnfitz -- was 18*18, added lit support (*3) and loosened surface extents maximum (BLOCK_WIDTH*BLOCK_HEIGHT)
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
typedef struct glRect_s
{
uint8_t l, t, w, h;
2019-11-24 20:45:15 -08:00
} glRect_t;
2019-11-25 17:40:18 -08:00
glpoly_t *lightmap_polys[MAX_LIGHTMAPS];
bool lightmap_modified[MAX_LIGHTMAPS];
glRect_t lightmap_rectchange[MAX_LIGHTMAPS];
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
int32_t allocated[MAX_LIGHTMAPS][BLOCK_WIDTH];
int32_t last_lightmap_allocated; //ericw -- optimization: remember the index of the last lightmap AllocBlock stored a surf in
2019-11-24 20:45:15 -08:00
// the lightmap texture data needs to be kept in
// main memory so texsubimage can update properly
2019-11-25 17:40:18 -08:00
byte lightmaps[4 * MAX_LIGHTMAPS * BLOCK_WIDTH * BLOCK_HEIGHT];
2019-11-24 20:45:15 -08:00
/*
===============
R_TextureAnimation -- johnfitz -- added "frame" param to eliminate use of "currententity" global
Returns the proper texture for a given time and base texture
===============
*/
2019-11-25 17:40:18 -08:00
texture_t *R_TextureAnimation(texture_t *base, int32_t frame)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t relative;
int32_t count;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(frame)
if(base->alternate_anims)
2019-11-24 20:45:15 -08:00
base = base->alternate_anims;
2019-11-25 17:40:18 -08:00
if(!base->anim_total)
2019-11-24 20:45:15 -08:00
return base;
2019-11-25 17:40:18 -08:00
relative = (int32_t)(cl.time * 10) % base->anim_total;
2019-11-24 20:45:15 -08:00
count = 0;
2019-11-25 17:40:18 -08:00
while(base->anim_min > relative || base->anim_max <= relative)
2019-11-24 20:45:15 -08:00
{
base = base->anim_next;
2019-11-25 17:40:18 -08:00
if(!base)
Sys_Error("R_TextureAnimation: broken cycle");
if(++count > 100)
Sys_Error("R_TextureAnimation: infinite cycle");
2019-11-24 20:45:15 -08:00
}
return base;
}
/*
================
DrawGLPoly
================
*/
2019-11-25 17:40:18 -08:00
void DrawGLPoly(glpoly_t *p)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float *v;
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glBegin(GL_POLYGON);
2019-11-24 20:45:15 -08:00
v = p->verts[0];
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < p->numverts ; i++, v += VERTEXSIZE)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
glTexCoord2f(v[3], v[4]);
glVertex3fv(v);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
glEnd();
2019-11-24 20:45:15 -08:00
}
/*
================
DrawGLTriangleFan -- johnfitz -- like DrawGLPoly but for r_showtris
================
*/
2019-11-25 17:40:18 -08:00
void DrawGLTriangleFan(glpoly_t *p)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float *v;
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glBegin(GL_TRIANGLE_FAN);
2019-11-24 20:45:15 -08:00
v = p->verts[0];
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < p->numverts ; i++, v += VERTEXSIZE)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
glVertex3fv(v);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
glEnd();
2019-11-24 20:45:15 -08:00
}
/*
=============================================================
2019-11-25 17:40:18 -08:00
BRUSH MODELS
2019-11-24 20:45:15 -08:00
=============================================================
*/
/*
=================
R_DrawBrushModel
=================
*/
2019-11-25 17:40:18 -08:00
void R_DrawBrushModel(entity_t *e)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, k;
msurface_t *psurf;
float dot;
mplane_t *pplane;
qmodel_t *clmodel;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(R_CullModelForEntity(e))
2019-11-24 20:45:15 -08:00
return;
currententity = e;
clmodel = e->model;
2019-11-25 17:40:18 -08:00
VectorSubtract(r_refdef.vieworg, e->origin, modelorg);
if(e->angles[0] || e->angles[1] || e->angles[2])
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
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);
2019-11-24 20:45:15 -08:00
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
// calculate dynamic lighting for bmodel if it's not an
// instanced model
2019-11-25 17:40:18 -08:00
if(clmodel->firstmodelsurface != 0 && !gl_flashblend.value)
2019-11-24 20:45:15 -08:00
{
2019-11-25 19:00:40 -08:00
for(k = 0 ; k < cl_max_dlights ; k++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if((cl_dlights[k].die < cl.time) ||
(!cl_dlights[k].radius))
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
R_MarkLights(&cl_dlights[k], k,
clmodel->nodes + clmodel->hulls[0].firstclipnode);
2019-11-24 20:45:15 -08:00
}
}
2019-11-25 17:40:18 -08:00
glPushMatrix();
e->angles[0] = -e->angles[0]; // stupid bug
2019-11-25 17:40:18 -08:00
if(gl_zfix.value)
2019-11-24 20:45:15 -08:00
{
e->origin[0] -= DIST_EPSILON;
e->origin[1] -= DIST_EPSILON;
e->origin[2] -= DIST_EPSILON;
}
2019-11-25 17:40:18 -08:00
R_RotateForEntity(e->origin, e->angles);
if(gl_zfix.value)
2019-11-24 20:45:15 -08:00
{
e->origin[0] += DIST_EPSILON;
e->origin[1] += DIST_EPSILON;
e->origin[2] += DIST_EPSILON;
}
e->angles[0] = -e->angles[0]; // stupid bug
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
R_ClearTextureChains(clmodel, chain_model);
for(i = 0 ; i < clmodel->nummodelsurfaces ; i++, psurf++)
2019-11-24 20:45:15 -08:00
{
pplane = psurf->plane;
2019-11-25 17:40:18 -08:00
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
if(((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
R_ChainSurface(psurf, chain_model);
2019-11-24 20:45:15 -08:00
rs_brushpolys++;
}
}
2019-11-25 17:40:18 -08:00
R_DrawTextureChains(clmodel, e, chain_model);
R_DrawTextureChains_Water(clmodel, e, chain_model);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glPopMatrix();
2019-11-24 20:45:15 -08:00
}
/*
=================
R_DrawBrushModel_ShowTris -- johnfitz
=================
*/
2019-11-25 17:40:18 -08:00
void R_DrawBrushModel_ShowTris(entity_t *e)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
msurface_t *psurf;
float dot;
mplane_t *pplane;
qmodel_t *clmodel;
glpoly_t *p;
if(R_CullModelForEntity(e))
2019-11-24 20:45:15 -08:00
return;
currententity = e;
clmodel = e->model;
2019-11-25 17:40:18 -08:00
VectorSubtract(r_refdef.vieworg, e->origin, modelorg);
if(e->angles[0] || e->angles[1] || e->angles[2])
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
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);
2019-11-24 20:45:15 -08:00
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
2019-11-25 17:40:18 -08:00
glPushMatrix();
e->angles[0] = -e->angles[0]; // stupid bug
2019-11-25 17:40:18 -08:00
R_RotateForEntity(e->origin, e->angles);
e->angles[0] = -e->angles[0]; // stupid bug
2019-11-24 20:45:15 -08:00
//
// draw it
//
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < clmodel->nummodelsurfaces ; i++, psurf++)
2019-11-24 20:45:15 -08:00
{
pplane = psurf->plane;
2019-11-25 17:40:18 -08:00
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
if(((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if((psurf->flags & SURF_DRAWTURB) && r_oldwater.value)
for(p = psurf->polys->next; p; p = p->next)
DrawGLTriangleFan(p);
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
DrawGLTriangleFan(psurf->polys);
2019-11-24 20:45:15 -08:00
}
}
2019-11-25 17:40:18 -08:00
glPopMatrix();
2019-11-24 20:45:15 -08:00
}
/*
=============================================================
2019-11-25 17:40:18 -08:00
LIGHTMAPS
2019-11-24 20:45:15 -08:00
=============================================================
*/
/*
================
R_RenderDynamicLightmaps
called during rendering
================
*/
2019-11-25 17:40:18 -08:00
void R_RenderDynamicLightmaps(msurface_t *fa)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
byte *base;
int32_t maps;
2019-11-24 20:45:15 -08:00
glRect_t *theRect;
2019-11-25 16:49:58 -08:00
int32_t smax, tmax;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(fa->flags & SURF_DRAWTILED) //johnfitz -- not a lightmapped surface
2019-11-24 20:45:15 -08:00
return;
// add to lightmap chain
fa->polys->chain = lightmap_polys[fa->lightmaptexturenum];
lightmap_polys[fa->lightmaptexturenum] = fa->polys;
// check for lightmap modification
2019-11-25 17:40:18 -08:00
for(maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++)
if(d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps])
2019-11-24 20:45:15 -08:00
goto dynamic;
2019-11-25 17:40:18 -08:00
if(fa->dlightframe == r_framecount // dynamic this frame
|| fa->cached_dlight) // dynamic previously
2019-11-24 20:45:15 -08:00
{
dynamic:
2019-11-25 17:40:18 -08:00
if(r_dynamic.value)
2019-11-24 20:45:15 -08:00
{
lightmap_modified[fa->lightmaptexturenum] = true;
theRect = &lightmap_rectchange[fa->lightmaptexturenum];
2019-11-25 17:40:18 -08:00
if(fa->light_t < theRect->t)
{
if(theRect->h)
2019-11-24 20:45:15 -08:00
theRect->h += theRect->t - fa->light_t;
theRect->t = fa->light_t;
}
2019-11-25 17:40:18 -08:00
if(fa->light_s < theRect->l)
{
if(theRect->w)
2019-11-24 20:45:15 -08:00
theRect->w += theRect->l - fa->light_s;
theRect->l = fa->light_s;
}
2019-11-25 17:40:18 -08:00
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;
2019-11-24 20:45:15 -08:00
base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes;
2019-11-25 17:40:18 -08:00
R_BuildLightMap(fa, base, BLOCK_WIDTH * lightmap_bytes);
2019-11-24 20:45:15 -08:00
}
}
}
/*
========================
AllocBlock -- returns a texture number and the position inside it
========================
*/
2019-11-25 17:40:18 -08:00
int32_t AllocBlock(int32_t w, int32_t h, int32_t *x, int32_t *y)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, j;
int32_t best, best2;
int32_t texnum;
2019-11-24 20:45:15 -08:00
// ericw -- rather than searching starting at lightmap 0 every time,
// start at the last lightmap we allocated a surface in.
// This makes AllocBlock much faster on large levels (can shave off 3+ seconds
// of load time on a level with 180 lightmaps), at a cost of not quite packing
// lightmaps as tightly vs. not doing this (uses ~5% more lightmaps)
2019-11-25 17:40:18 -08:00
for(texnum = last_lightmap_allocated ; texnum < MAX_LIGHTMAPS ; texnum++, last_lightmap_allocated++)
2019-11-24 20:45:15 -08:00
{
best = BLOCK_HEIGHT;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < BLOCK_WIDTH - w ; i++)
2019-11-24 20:45:15 -08:00
{
best2 = 0;
2019-11-25 17:40:18 -08:00
for(j = 0 ; j < w ; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(allocated[texnum][i + j] >= best)
2019-11-24 20:45:15 -08:00
break;
2019-11-25 17:40:18 -08:00
if(allocated[texnum][i + j] > best2)
best2 = allocated[texnum][i + j];
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
if(j == w)
{
// this is a valid spot
2019-11-24 20:45:15 -08:00
*x = i;
*y = best = best2;
}
}
2019-11-25 17:40:18 -08:00
if(best + h > BLOCK_HEIGHT)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < w ; i++)
2019-11-24 20:45:15 -08:00
allocated[texnum][*x + i] = best + h;
return texnum;
}
2019-11-25 17:40:18 -08:00
Sys_Error("AllocBlock: full");
2019-11-24 20:45:15 -08:00
return 0; //johnfitz -- shut up compiler
}
2019-11-25 17:40:18 -08:00
mvertex_t *r_pcurrentvertbase;
qmodel_t *currentmodel;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
int32_t nColinElim;
2019-11-24 20:45:15 -08:00
/*
========================
GL_CreateSurfaceLightmap
========================
*/
2019-11-25 17:40:18 -08:00
void GL_CreateSurfaceLightmap(msurface_t *surf)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t smax, tmax;
byte *base;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
surf->lightmaptexturenum = AllocBlock(smax, tmax, &surf->light_s, &surf->light_t);
base = lightmaps + surf->lightmaptexturenum * lightmap_bytes * BLOCK_WIDTH * BLOCK_HEIGHT;
2019-11-24 20:45:15 -08:00
base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes;
2019-11-25 17:40:18 -08:00
R_BuildLightMap(surf, base, BLOCK_WIDTH * lightmap_bytes);
2019-11-24 20:45:15 -08:00
}
/*
================
BuildSurfaceDisplayList -- called at level load time
================
*/
2019-11-25 17:40:18 -08:00
void BuildSurfaceDisplayList(msurface_t *fa)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, lindex, lnumverts;
medge_t *pedges, *r_pedge;
float *vec;
float s, t;
glpoly_t *poly;
2019-11-24 20:45:15 -08:00
// reconstruct the polygon
pedges = currentmodel->edges;
lnumverts = fa->numedges;
//
// draw texture
//
poly = Hunk_AllocName(sizeof(glpoly_t) + (lnumverts - 4) * VERTEXSIZE * sizeof(float), __func__);
2019-11-24 20:45:15 -08:00
poly->next = fa->polys;
fa->polys = poly;
poly->numverts = lnumverts;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < lnumverts ; i++)
2019-11-24 20:45:15 -08:00
{
lindex = currentmodel->surfedges[fa->firstedge + i];
2019-11-25 17:40:18 -08:00
if(lindex > 0)
2019-11-24 20:45:15 -08:00
{
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;
}
2019-11-25 17:40:18 -08:00
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
2019-11-24 20:45:15 -08:00
s /= fa->texinfo->texture->width;
2019-11-25 17:40:18 -08:00
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
2019-11-24 20:45:15 -08:00
t /= fa->texinfo->texture->height;
2019-11-25 17:40:18 -08:00
VectorCopy(vec, poly->verts[i]);
2019-11-24 20:45:15 -08:00
poly->verts[i][3] = s;
poly->verts[i][4] = t;
//
// lightmap texture coordinates
//
2019-11-25 17:40:18 -08:00
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
2019-11-24 20:45:15 -08:00
s -= fa->texturemins[0];
2019-11-25 17:40:18 -08:00
s += fa->light_s * 16;
2019-11-24 20:45:15 -08:00
s += 8;
2019-11-25 17:40:18 -08:00
s /= BLOCK_WIDTH * 16; //fa->texinfo->texture->width;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
2019-11-24 20:45:15 -08:00
t -= fa->texturemins[1];
2019-11-25 17:40:18 -08:00
t += fa->light_t * 16;
2019-11-24 20:45:15 -08:00
t += 8;
2019-11-25 17:40:18 -08:00
t /= BLOCK_HEIGHT * 16; //fa->texinfo->texture->height;
2019-11-24 20:45:15 -08:00
poly->verts[i][5] = s;
poly->verts[i][6] = t;
}
//johnfitz -- removed gl_keeptjunctions code
poly->numverts = lnumverts;
}
/*
==================
GL_BuildLightmaps -- called at level load time
Builds the lightmap texture
with all the surfaces from all brush models
==================
*/
2019-11-25 17:40:18 -08:00
void GL_BuildLightmaps(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
char name[16];
byte *data;
int32_t i, j;
qmodel_t *m;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
memset(allocated, 0, sizeof(allocated));
2019-11-24 20:45:15 -08:00
last_lightmap_allocated = 0;
r_framecount = 1; // no dlightcache
//johnfitz -- null out array (the gltexture objects themselves were already freed by Mod_ClearAll)
2019-11-25 17:40:18 -08:00
for(i = 0; i < MAX_LIGHTMAPS; i++)
2019-11-24 20:45:15 -08:00
lightmap_textures[i] = NULL;
//johnfitz
gl_lightmap_format = GL_RGBA;//FIXME: hardcoded for now!
2019-11-25 17:40:18 -08:00
switch(gl_lightmap_format)
2019-11-24 20:45:15 -08:00
{
case GL_RGBA:
lightmap_bytes = 4;
break;
case GL_BGRA:
lightmap_bytes = 4;
break;
default:
2019-11-25 17:40:18 -08:00
Sys_Error("GL_BuildLightmaps: bad lightmap format");
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
for(j = 1 ; j < MAX_MODELS ; j++)
2019-11-24 20:45:15 -08:00
{
m = cl.model_precache[j];
2019-11-25 17:40:18 -08:00
if(!m)
2019-11-24 20:45:15 -08:00
break;
2019-11-25 17:40:18 -08:00
if(m->name[0] == '*')
2019-11-24 20:45:15 -08:00
continue;
r_pcurrentvertbase = m->vertexes;
currentmodel = m;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < m->numsurfaces ; i++)
2019-11-24 20:45:15 -08:00
{
//johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags
2019-11-25 17:40:18 -08:00
if(m->surfaces[i].flags & SURF_DRAWTILED)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
GL_CreateSurfaceLightmap(m->surfaces + i);
BuildSurfaceDisplayList(m->surfaces + i);
2019-11-24 20:45:15 -08:00
//johnfitz
}
}
//
// upload all lightmaps that were filled
//
2019-11-25 17:40:18 -08:00
for(i = 0; i < MAX_LIGHTMAPS; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!allocated[i][0])
break; // no more used
2019-11-24 20:45:15 -08:00
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;
//johnfitz -- use texture manager
sprintf(name, "lightmap%03" PRIi32, i);
2019-11-25 17:40:18 -08:00
data = lightmaps + i * BLOCK_WIDTH * BLOCK_HEIGHT * lightmap_bytes;
lightmap_textures[i] = TexMgr_LoadImage(cl.worldmodel, name, BLOCK_WIDTH, BLOCK_HEIGHT, SRC_LIGHTMAP, data, "", (src_offset_t)data, TEXPREF_LINEAR | TEXPREF_NOPICMIP);
2019-11-24 20:45:15 -08:00
//johnfitz
}
//johnfitz -- warn about exceeding old limits
2019-11-25 17:40:18 -08:00
if(i >= 64)
Con_DWarning("%" PRIi32 " lightmaps exceeds standard limit of 64 (max = %" PRIi32 ").\n", i, MAX_LIGHTMAPS);
2019-11-24 20:45:15 -08:00
//johnfitz
}
/*
=============================================================
2019-11-25 17:40:18 -08:00
VBO support
2019-11-24 20:45:15 -08:00
=============================================================
*/
GLuint gl_bmodel_vbo = 0;
2019-11-25 17:40:18 -08:00
void GL_DeleteBModelVertexBuffer(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!(gl_vbo_able && gl_mtexable && gl_max_texture_units >= 3))
2019-11-24 20:45:15 -08:00
return;
2019-11-25 17:40:18 -08:00
GL_DeleteBuffersFunc(1, &gl_bmodel_vbo);
2019-11-24 20:45:15 -08:00
gl_bmodel_vbo = 0;
2019-11-25 17:40:18 -08:00
GL_ClearBufferBindings();
2019-11-24 20:45:15 -08:00
}
/*
==================
GL_BuildBModelVertexBuffer
Deletes gl_bmodel_vbo if it already exists, then rebuilds it with all
surfaces from world + all brush models
==================
*/
2019-11-25 17:40:18 -08:00
void GL_BuildBModelVertexBuffer(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
uint32_t numverts, varray_bytes, varray_index;
int32_t i, j;
qmodel_t *m;
float *varray;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!(gl_vbo_able && gl_mtexable && gl_max_texture_units >= 3))
2019-11-24 20:45:15 -08:00
return;
// ask GL for a name for our VBO
2019-11-25 17:40:18 -08:00
GL_DeleteBuffersFunc(1, &gl_bmodel_vbo);
GL_GenBuffersFunc(1, &gl_bmodel_vbo);
2019-11-25 17:29:41 -08:00
2019-11-24 20:45:15 -08:00
// count all verts in all models
numverts = 0;
2019-11-25 17:40:18 -08:00
for(j = 1 ; j < MAX_MODELS ; j++)
2019-11-24 20:45:15 -08:00
{
m = cl.model_precache[j];
2019-11-25 17:40:18 -08:00
if(!m || m->name[0] == '*' || m->type != mod_brush)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < m->numsurfaces ; i++)
2019-11-24 20:45:15 -08:00
{
numverts += m->surfaces[i].numedges;
}
}
2019-11-25 17:29:41 -08:00
2019-11-24 20:45:15 -08:00
// build vertex array
varray_bytes = VERTEXSIZE * sizeof(float) * numverts;
2019-11-25 17:40:18 -08:00
varray = (float *) malloc(varray_bytes);
2019-11-24 20:45:15 -08:00
varray_index = 0;
2019-11-25 17:29:41 -08:00
2019-11-25 17:40:18 -08:00
for(j = 1 ; j < MAX_MODELS ; j++)
2019-11-24 20:45:15 -08:00
{
m = cl.model_precache[j];
2019-11-25 17:40:18 -08:00
if(!m || m->name[0] == '*' || m->type != mod_brush)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < m->numsurfaces ; i++)
2019-11-24 20:45:15 -08:00
{
msurface_t *s = &m->surfaces[i];
s->vbo_firstvert = varray_index;
2019-11-25 17:40:18 -08:00
memcpy(&varray[VERTEXSIZE * varray_index], s->polys->verts, VERTEXSIZE * sizeof(float) * s->numedges);
2019-11-24 20:45:15 -08:00
varray_index += s->numedges;
}
}
// upload to GPU
2019-11-25 17:40:18 -08:00
GL_BindBufferFunc(GL_ARRAY_BUFFER, gl_bmodel_vbo);
GL_BufferDataFunc(GL_ARRAY_BUFFER, varray_bytes, varray, GL_STATIC_DRAW);
free(varray);
2019-11-25 17:29:41 -08:00
2019-11-24 20:45:15 -08:00
// invalidate the cached bindings
2019-11-25 17:40:18 -08:00
GL_ClearBufferBindings();
2019-11-24 20:45:15 -08:00
}
/*
===============
R_AddDynamicLights
===============
*/
2019-11-25 17:40:18 -08:00
void R_AddDynamicLights(msurface_t *surf)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t lnum;
int32_t sd, td;
float dist, rad, minlight;
vec3_t impact, local;
int32_t s, t;
int32_t i;
int32_t smax, tmax;
mtexinfo_t *tex;
2019-11-24 20:45:15 -08:00
//johnfitz -- lit support via lordhavoc
2019-11-25 17:40:18 -08:00
float cred, cgreen, cblue, brightness;
unsigned *bl;
2019-11-24 20:45:15 -08:00
//johnfitz
2019-11-25 17:40:18 -08:00
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
2019-11-24 20:45:15 -08:00
tex = surf->texinfo;
2019-11-25 19:00:40 -08:00
for(lnum = 0 ; lnum < cl_max_dlights ; lnum++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!(surf->dlightbits[lnum >> 5] & (1U << (lnum & 31))))
continue; // not lit by this light
2019-11-24 20:45:15 -08:00
rad = cl_dlights[lnum].radius;
2019-11-25 17:40:18 -08:00
dist = DotProduct(cl_dlights[lnum].origin, surf->plane->normal) -
surf->plane->dist;
2019-11-24 20:45:15 -08:00
rad -= fabs(dist);
minlight = cl_dlights[lnum].minlight;
2019-11-25 17:40:18 -08:00
if(rad < minlight)
2019-11-24 20:45:15 -08:00
continue;
minlight = rad - minlight;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 3 ; i++)
2019-11-24 20:45:15 -08:00
{
impact[i] = cl_dlights[lnum].origin[i] -
2019-11-25 17:40:18 -08:00
surf->plane->normal[i] * dist;
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
local[0] = DotProduct(impact, tex->vecs[0]) + tex->vecs[0][3];
local[1] = DotProduct(impact, tex->vecs[1]) + tex->vecs[1][3];
2019-11-24 20:45:15 -08:00
local[0] -= surf->texturemins[0];
local[1] -= surf->texturemins[1];
//johnfitz -- lit support via lordhavoc
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;
//johnfitz
2019-11-25 17:40:18 -08:00
for(t = 0 ; t < tmax ; t++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
td = local[1] - t * 16;
if(td < 0)
2019-11-24 20:45:15 -08:00
td = -td;
2019-11-25 17:40:18 -08:00
for(s = 0 ; s < smax ; s++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
sd = local[0] - s * 16;
if(sd < 0)
2019-11-24 20:45:15 -08:00
sd = -sd;
2019-11-25 17:40:18 -08:00
if(sd > td)
dist = sd + (td >> 1);
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
dist = td + (sd >> 1);
if(dist < minlight)
//johnfitz -- lit support via lordhavoc
2019-11-24 20:45:15 -08:00
{
brightness = rad - dist;
2019-11-25 17:40:18 -08:00
bl[0] += (int32_t)(brightness * cred);
bl[1] += (int32_t)(brightness * cgreen);
bl[2] += (int32_t)(brightness * cblue);
2019-11-24 20:45:15 -08:00
}
bl += 3;
//johnfitz
}
}
}
}
/*
===============
R_BuildLightMap -- johnfitz -- revised for lit support via lordhavoc
Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
2019-11-25 17:40:18 -08:00
void R_BuildLightMap(msurface_t *surf, byte *dest, int32_t stride)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t smax, tmax;
int32_t r, g, b;
int32_t i, j, size;
byte *lightmap;
unsigned scale;
int32_t maps;
unsigned *bl;
2019-11-24 20:45:15 -08:00
surf->cached_dlight = (surf->dlightframe == r_framecount);
2019-11-25 17:40:18 -08:00
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
size = smax * tmax;
2019-11-24 20:45:15 -08:00
lightmap = surf->samples;
2019-11-25 17:40:18 -08:00
if(cl.worldmodel->lightdata)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
// clear to no light
memset(&blocklights[0], 0, size * 3 * sizeof(uint32_t)); //johnfitz -- lit support via lordhavoc
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
// add all the lightmaps
if(lightmap)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ;
maps++)
2019-11-24 20:45:15 -08:00
{
scale = d_lightstylevalue[surf->styles[maps]];
2019-11-25 17:40:18 -08:00
surf->cached_light[maps] = scale; // 8.8 fraction
2019-11-24 20:45:15 -08:00
//johnfitz -- lit support via lordhavoc
bl = blocklights;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < size ; i++)
2019-11-24 20:45:15 -08:00
{
*bl++ += *lightmap++ * scale;
*bl++ += *lightmap++ * scale;
*bl++ += *lightmap++ * scale;
}
//johnfitz
}
}
2019-11-25 17:40:18 -08:00
// add all the dynamic lights
if(surf->dlightframe == r_framecount)
R_AddDynamicLights(surf);
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
// set to full bright if no light data
memset(&blocklights[0], 255, size * 3 * sizeof(uint32_t)); //johnfitz -- lit support via lordhavoc
2019-11-24 20:45:15 -08:00
}
// bound, invert, and shift
// store:
2019-11-25 17:40:18 -08:00
switch(gl_lightmap_format)
2019-11-24 20:45:15 -08:00
{
case GL_RGBA:
stride -= smax * 4;
bl = blocklights;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < tmax ; i++, dest += stride)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(j = 0 ; j < smax ; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(gl_overbright.value)
2019-11-24 20:45:15 -08:00
{
r = *bl++ >> 8;
g = *bl++ >> 8;
b = *bl++ >> 8;
}
else
{
r = *bl++ >> 7;
g = *bl++ >> 7;
b = *bl++ >> 7;
}
2019-11-25 17:40:18 -08:00
*dest++ = (r > 255) ? 255 : r;
*dest++ = (g > 255) ? 255 : g;
*dest++ = (b > 255) ? 255 : b;
2019-11-24 20:45:15 -08:00
*dest++ = 255;
}
}
break;
case GL_BGRA:
stride -= smax * 4;
bl = blocklights;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < tmax ; i++, dest += stride)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(j = 0 ; j < smax ; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(gl_overbright.value)
2019-11-24 20:45:15 -08:00
{
r = *bl++ >> 8;
g = *bl++ >> 8;
b = *bl++ >> 8;
}
else
{
r = *bl++ >> 7;
g = *bl++ >> 7;
b = *bl++ >> 7;
}
2019-11-25 17:40:18 -08:00
*dest++ = (b > 255) ? 255 : b;
*dest++ = (g > 255) ? 255 : g;
*dest++ = (r > 255) ? 255 : r;
2019-11-24 20:45:15 -08:00
*dest++ = 255;
}
}
break;
default:
2019-11-25 17:40:18 -08:00
Sys_Error("R_BuildLightMap: bad lightmap format");
2019-11-24 20:45:15 -08:00
}
}
/*
===============
R_UploadLightmap -- johnfitz -- uploads the modified lightmap to opengl if necessary
assumes lightmap texture is already bound
===============
*/
2019-11-25 16:49:58 -08:00
static void R_UploadLightmap(int32_t lmap)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
glRect_t *theRect;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!lightmap_modified[lmap])
2019-11-24 20:45:15 -08:00
return;
lightmap_modified[lmap] = false;
theRect = &lightmap_rectchange[lmap];
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, BLOCK_WIDTH, theRect->h, gl_lightmap_format,
2019-11-25 17:40:18 -08:00
GL_UNSIGNED_BYTE, lightmaps + (lmap * BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH * lightmap_bytes);
2019-11-24 20:45:15 -08:00
theRect->l = BLOCK_WIDTH;
theRect->t = BLOCK_HEIGHT;
theRect->h = 0;
theRect->w = 0;
rs_dynamiclightmaps++;
}
2019-11-25 17:40:18 -08:00
void R_UploadLightmaps(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t lmap;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(lmap = 0; lmap < MAX_LIGHTMAPS; lmap++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!lightmap_modified[lmap])
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
GL_Bind(lightmap_textures[lmap]);
2019-11-24 20:45:15 -08:00
R_UploadLightmap(lmap);
}
}
/*
================
R_RebuildAllLightmaps -- johnfitz -- called when gl_overbright gets toggled
================
*/
2019-11-25 17:40:18 -08:00
void R_RebuildAllLightmaps(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, j;
qmodel_t *mod;
msurface_t *fa;
byte *base;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!cl.worldmodel) // is this the correct test?
2019-11-24 20:45:15 -08:00
return;
//for each surface in each model, rebuild lightmap with new scale
2019-11-25 17:40:18 -08:00
for(i = 1; i < MAX_MODELS; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!(mod = cl.model_precache[i]))
2019-11-24 20:45:15 -08:00
continue;
fa = &mod->surfaces[mod->firstmodelsurface];
2019-11-25 17:40:18 -08:00
for(j = 0; j < mod->nummodelsurfaces; j++, fa++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(fa->flags & SURF_DRAWTILED)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
base = lightmaps + fa->lightmaptexturenum * lightmap_bytes * BLOCK_WIDTH * BLOCK_HEIGHT;
2019-11-24 20:45:15 -08:00
base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes;
2019-11-25 17:40:18 -08:00
R_BuildLightMap(fa, base, BLOCK_WIDTH * lightmap_bytes);
2019-11-24 20:45:15 -08:00
}
}
//for each lightmap, upload it
2019-11-25 17:40:18 -08:00
for(i = 0; i < MAX_LIGHTMAPS; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(!allocated[i][0])
2019-11-24 20:45:15 -08:00
break;
2019-11-25 17:40:18 -08:00
GL_Bind(lightmap_textures[i]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, BLOCK_HEIGHT, gl_lightmap_format,
GL_UNSIGNED_BYTE, lightmaps + i * BLOCK_WIDTH * BLOCK_HEIGHT * lightmap_bytes);
2019-11-24 20:45:15 -08:00
}
}