mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-10 07:21:58 +00:00
01faf4e5b6
new worldspawn keys: _wateralpha, _lavaalpha, _slimealpha, _telealpha, _skyfog (unique to Quakespasm) The lava/slime/telealpha cvars are non-archived, and default to 0, which means to use the value of r_wateralpha, so they have no effect by default. The worldspawn keys allow custom maps to set these values in a way that only applies while the map is loaded, and doesn't change the cvar value. (similar to the behaviour of the "fog" worldspawn key.) They are accepted with or without the underscore, like "fog". see also: http://forums.insideqc.com/viewtopic.php?f=3&t=5532 http://celephais.net/board/view_thread.php?id=60452&start=937 git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1238 af15c1b1-3010-417e-b628-4374ebc0bcbd
1146 lines
29 KiB
C
1146 lines
29 KiB
C
/*
|
|
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_world.c: world model rendering
|
|
|
|
#include "quakedef.h"
|
|
|
|
extern cvar_t gl_fullbrights, r_drawflat, gl_overbright, r_oldwater, r_oldskyleaf, r_showtris; //johnfitz
|
|
|
|
extern glpoly_t *lightmap_polys[MAX_LIGHTMAPS];
|
|
|
|
byte *SV_FatPVS (vec3_t org, qmodel_t *worldmodel);
|
|
extern byte mod_novis[MAX_MAP_LEAFS/8];
|
|
int vis_changed; //if true, force pvs to be refreshed
|
|
|
|
//==============================================================================
|
|
//
|
|
// SETUP CHAINS
|
|
//
|
|
//==============================================================================
|
|
|
|
/*
|
|
================
|
|
R_ClearTextureChains -- ericw
|
|
|
|
clears texture chains for all textures used by the given model, and also
|
|
clears the lightmap chains
|
|
================
|
|
*/
|
|
void R_ClearTextureChains (qmodel_t *mod, texchain_t chain)
|
|
{
|
|
int i;
|
|
|
|
// set all chains to null
|
|
for (i=0 ; i<mod->numtextures ; i++)
|
|
if (mod->textures[i])
|
|
mod->textures[i]->texturechains[chain] = NULL;
|
|
|
|
// clear lightmap chains
|
|
memset (lightmap_polys, 0, sizeof(lightmap_polys));
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_ChainSurface -- ericw -- adds the given surface to its texture chain
|
|
================
|
|
*/
|
|
void R_ChainSurface (msurface_t *surf, texchain_t chain)
|
|
{
|
|
surf->texturechain = surf->texinfo->texture->texturechains[chain];
|
|
surf->texinfo->texture->texturechains[chain] = surf;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_MarkSurfaces -- johnfitz -- mark surfaces based on PVS and rebuild texture chains
|
|
===============
|
|
*/
|
|
void R_MarkSurfaces (void)
|
|
{
|
|
byte *vis;
|
|
mleaf_t *leaf;
|
|
mnode_t *node;
|
|
msurface_t *surf, **mark;
|
|
int i, j;
|
|
qboolean nearwaterportal;
|
|
|
|
// clear lightmap chains
|
|
memset (lightmap_polys, 0, sizeof(lightmap_polys));
|
|
|
|
// check this leaf for water portals
|
|
// TODO: loop through all water surfs and use distance to leaf cullbox
|
|
nearwaterportal = false;
|
|
for (i=0, mark = r_viewleaf->firstmarksurface; i < r_viewleaf->nummarksurfaces; i++, mark++)
|
|
if ((*mark)->flags & SURF_DRAWTURB)
|
|
nearwaterportal = true;
|
|
|
|
// choose vis data
|
|
if (r_novis.value || r_viewleaf->contents == CONTENTS_SOLID || r_viewleaf->contents == CONTENTS_SKY)
|
|
vis = &mod_novis[0];
|
|
else if (nearwaterportal)
|
|
vis = SV_FatPVS (r_origin, cl.worldmodel);
|
|
else
|
|
vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel);
|
|
|
|
// if surface chains don't need regenerating, just add static entities and return
|
|
if (r_oldviewleaf == r_viewleaf && !vis_changed && !nearwaterportal)
|
|
{
|
|
leaf = &cl.worldmodel->leafs[1];
|
|
for (i=0 ; i<cl.worldmodel->numleafs ; i++, leaf++)
|
|
if (vis[i>>3] & (1<<(i&7)))
|
|
if (leaf->efrags)
|
|
R_StoreEfrags (&leaf->efrags);
|
|
return;
|
|
}
|
|
|
|
vis_changed = false;
|
|
r_visframecount++;
|
|
r_oldviewleaf = r_viewleaf;
|
|
|
|
// iterate through leaves, marking surfaces
|
|
leaf = &cl.worldmodel->leafs[1];
|
|
for (i=0 ; i<cl.worldmodel->numleafs ; i++, leaf++)
|
|
{
|
|
if (vis[i>>3] & (1<<(i&7)))
|
|
{
|
|
if (r_oldskyleaf.value || leaf->contents != CONTENTS_SKY)
|
|
for (j=0, mark = leaf->firstmarksurface; j<leaf->nummarksurfaces; j++, mark++)
|
|
(*mark)->visframe = r_visframecount;
|
|
|
|
// add static models
|
|
if (leaf->efrags)
|
|
R_StoreEfrags (&leaf->efrags);
|
|
}
|
|
}
|
|
|
|
// set all chains to null
|
|
for (i=0 ; i<cl.worldmodel->numtextures ; i++)
|
|
if (cl.worldmodel->textures[i])
|
|
cl.worldmodel->textures[i]->texturechains[chain_world] = NULL;
|
|
|
|
// rebuild chains
|
|
|
|
#if 1
|
|
//iterate through surfaces one node at a time to rebuild chains
|
|
//need to do it this way if we want to work with tyrann's skip removal tool
|
|
//becuase his tool doesn't actually remove the surfaces from the bsp surfaces lump
|
|
//nor does it remove references to them in each leaf's marksurfaces list
|
|
for (i=0, node = cl.worldmodel->nodes ; i<cl.worldmodel->numnodes ; i++, node++)
|
|
for (j=0, surf=&cl.worldmodel->surfaces[node->firstsurface] ; j<node->numsurfaces ; j++, surf++)
|
|
if (surf->visframe == r_visframecount)
|
|
{
|
|
R_ChainSurface(surf, chain_world);
|
|
}
|
|
#else
|
|
//the old way
|
|
surf = &cl.worldmodel->surfaces[cl.worldmodel->firstmodelsurface];
|
|
for (i=0 ; i<cl.worldmodel->nummodelsurfaces ; i++, surf++)
|
|
{
|
|
if (surf->visframe == r_visframecount)
|
|
{
|
|
R_ChainSurface(surf, chain_world);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_BackFaceCull -- johnfitz -- returns true if the surface is facing away from vieworg
|
|
================
|
|
*/
|
|
qboolean R_BackFaceCull (msurface_t *surf)
|
|
{
|
|
double dot;
|
|
|
|
switch (surf->plane->type)
|
|
{
|
|
case PLANE_X:
|
|
dot = r_refdef.vieworg[0] - surf->plane->dist;
|
|
break;
|
|
case PLANE_Y:
|
|
dot = r_refdef.vieworg[1] - surf->plane->dist;
|
|
break;
|
|
case PLANE_Z:
|
|
dot = r_refdef.vieworg[2] - surf->plane->dist;
|
|
break;
|
|
default:
|
|
dot = DotProduct (r_refdef.vieworg, surf->plane->normal) - surf->plane->dist;
|
|
break;
|
|
}
|
|
|
|
if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_CullSurfaces -- johnfitz
|
|
================
|
|
*/
|
|
void R_CullSurfaces (void)
|
|
{
|
|
msurface_t *s;
|
|
int i;
|
|
texture_t *t;
|
|
|
|
if (!r_drawworld_cheatsafe)
|
|
return;
|
|
|
|
// ericw -- instead of testing (s->visframe == r_visframecount) on all world
|
|
// surfaces, use the chained surfaces, which is exactly the same set of sufaces
|
|
for (i=0 ; i<cl.worldmodel->numtextures ; i++)
|
|
{
|
|
t = cl.worldmodel->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain_world])
|
|
continue;
|
|
|
|
for (s = t->texturechains[chain_world]; s; s = s->texturechain)
|
|
{
|
|
if (R_CullBox(s->mins, s->maxs) || R_BackFaceCull (s))
|
|
s->culled = true;
|
|
else
|
|
{
|
|
s->culled = false;
|
|
rs_brushpolys++; //count wpolys here
|
|
if (s->texinfo->texture->warpimage)
|
|
s->texinfo->texture->update_warp = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_BuildLightmapChains -- johnfitz -- used for r_lightmap 1
|
|
|
|
ericw -- now always used at the start of R_DrawTextureChains for the
|
|
mh dynamic lighting speedup
|
|
================
|
|
*/
|
|
void R_BuildLightmapChains (qmodel_t *model, texchain_t chain)
|
|
{
|
|
texture_t *t;
|
|
msurface_t *s;
|
|
int i;
|
|
|
|
// clear lightmap chains (already done in r_marksurfaces, but clearing them here to be safe becuase of r_stereo)
|
|
memset (lightmap_polys, 0, sizeof(lightmap_polys));
|
|
|
|
// now rebuild them
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain])
|
|
continue;
|
|
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
R_RenderDynamicLightmaps (s);
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
//
|
|
// DRAW CHAINS
|
|
//
|
|
//==============================================================================
|
|
|
|
/*
|
|
=============
|
|
R_BeginTransparentDrawing -- ericw
|
|
=============
|
|
*/
|
|
static void R_BeginTransparentDrawing (float entalpha)
|
|
{
|
|
if (entalpha < 1.0f)
|
|
{
|
|
glDepthMask (GL_FALSE);
|
|
glEnable (GL_BLEND);
|
|
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glColor4f (1,1,1,entalpha);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_EndTransparentDrawing -- ericw
|
|
=============
|
|
*/
|
|
static void R_EndTransparentDrawing (float entalpha)
|
|
{
|
|
if (entalpha < 1.0f)
|
|
{
|
|
glDepthMask (GL_TRUE);
|
|
glDisable (GL_BLEND);
|
|
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glColor3f (1, 1, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_ShowTris -- johnfitz
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_ShowTris (qmodel_t *model, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
glpoly_t *p;
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
if (!t)
|
|
continue;
|
|
|
|
if (r_oldwater.value && t->texturechains[chain] && (t->texturechains[chain]->flags & SURF_DRAWTURB))
|
|
{
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
for (p = s->polys->next; p; p = p->next)
|
|
{
|
|
DrawGLTriangleFan (p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
DrawGLTriangleFan (s->polys);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_Drawflat -- johnfitz
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_Drawflat (qmodel_t *model, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
glpoly_t *p;
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
if (!t)
|
|
continue;
|
|
|
|
if (r_oldwater.value && t->texturechains[chain] && (t->texturechains[chain]->flags & SURF_DRAWTURB))
|
|
{
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
for (p = s->polys->next; p; p = p->next)
|
|
{
|
|
srand((unsigned int) (uintptr_t) p);
|
|
glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0);
|
|
DrawGLPoly (p);
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
srand((unsigned int) (uintptr_t) s->polys);
|
|
glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0);
|
|
DrawGLPoly (s->polys);
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
}
|
|
glColor3f (1,1,1);
|
|
srand ((int) (cl.time * 1000));
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_Glow -- johnfitz
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_Glow (qmodel_t *model, entity_t *ent, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
gltexture_t *glt;
|
|
qboolean bound;
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain] || !(glt = R_TextureAnimation(t, ent != NULL ? ent->frame : 0)->fullbright))
|
|
continue;
|
|
|
|
bound = false;
|
|
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
GL_Bind (glt);
|
|
bound = true;
|
|
}
|
|
DrawGLPoly (s->polys);
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
//
|
|
// VBO SUPPORT
|
|
//
|
|
//==============================================================================
|
|
|
|
static unsigned int R_NumTriangleIndicesForSurf (msurface_t *s)
|
|
{
|
|
return 3 * (s->numedges - 2);
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_TriangleIndicesForSurf
|
|
|
|
Writes out the triangle indices needed to draw s as a triangle list.
|
|
The number of indices it will write is given by R_NumTriangleIndicesForSurf.
|
|
================
|
|
*/
|
|
static void R_TriangleIndicesForSurf (msurface_t *s, unsigned int *dest)
|
|
{
|
|
int i;
|
|
for (i=2; i<s->numedges; i++)
|
|
{
|
|
*dest++ = s->vbo_firstvert;
|
|
*dest++ = s->vbo_firstvert + i - 1;
|
|
*dest++ = s->vbo_firstvert + i;
|
|
}
|
|
}
|
|
|
|
#define MAX_BATCH_SIZE 4096
|
|
|
|
static unsigned int vbo_indices[MAX_BATCH_SIZE];
|
|
static unsigned int num_vbo_indices;
|
|
|
|
/*
|
|
================
|
|
R_ClearBatch
|
|
================
|
|
*/
|
|
static void R_ClearBatch ()
|
|
{
|
|
num_vbo_indices = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_FlushBatch
|
|
|
|
Draw the current batch if non-empty and clears it, ready for more R_BatchSurface calls.
|
|
================
|
|
*/
|
|
static void R_FlushBatch ()
|
|
{
|
|
if (num_vbo_indices > 0)
|
|
{
|
|
glDrawElements (GL_TRIANGLES, num_vbo_indices, GL_UNSIGNED_INT, vbo_indices);
|
|
num_vbo_indices = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_BatchSurface
|
|
|
|
Add the surface to the current batch, or just draw it immediately if we're not
|
|
using VBOs.
|
|
================
|
|
*/
|
|
static void R_BatchSurface (msurface_t *s)
|
|
{
|
|
int num_surf_indices;
|
|
|
|
num_surf_indices = R_NumTriangleIndicesForSurf (s);
|
|
|
|
if (num_vbo_indices + num_surf_indices > MAX_BATCH_SIZE)
|
|
R_FlushBatch();
|
|
|
|
R_TriangleIndicesForSurf (s, &vbo_indices[num_vbo_indices]);
|
|
num_vbo_indices += num_surf_indices;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_Multitexture -- johnfitz
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_Multitexture (qmodel_t *model, entity_t *ent, texchain_t chain)
|
|
{
|
|
int i, j;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
float *v;
|
|
qboolean bound;
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTILED | SURF_NOTEXTURE))
|
|
continue;
|
|
|
|
bound = false;
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
GL_Bind ((R_TextureAnimation(t, ent != NULL ? ent->frame : 0))->gltexture);
|
|
|
|
if (t->texturechains[chain]->flags & SURF_DRAWFENCE)
|
|
glEnable (GL_ALPHA_TEST); // Flip alpha test back on
|
|
|
|
GL_EnableMultitexture(); // selects TEXTURE1
|
|
bound = true;
|
|
}
|
|
GL_Bind (lightmap_textures[s->lightmaptexturenum]);
|
|
glBegin(GL_POLYGON);
|
|
v = s->polys->verts[0];
|
|
for (j=0 ; j<s->polys->numverts ; j++, v+= VERTEXSIZE)
|
|
{
|
|
GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]);
|
|
GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]);
|
|
glVertex3fv (v);
|
|
}
|
|
glEnd ();
|
|
rs_brushpasses++;
|
|
}
|
|
GL_DisableMultitexture(); // selects TEXTURE0
|
|
|
|
if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE)
|
|
glDisable (GL_ALPHA_TEST); // Flip alpha test back off
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_NoTexture -- johnfitz
|
|
|
|
draws surfs whose textures were missing from the BSP
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_NoTexture (qmodel_t *model, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
qboolean bound;
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_NOTEXTURE))
|
|
continue;
|
|
|
|
bound = false;
|
|
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
GL_Bind (t->gltexture);
|
|
bound = true;
|
|
}
|
|
DrawGLPoly (s->polys);
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_TextureOnly -- johnfitz
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_TextureOnly (qmodel_t *model, entity_t *ent, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
qboolean bound;
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTURB | SURF_DRAWSKY))
|
|
continue;
|
|
|
|
bound = false;
|
|
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
GL_Bind ((R_TextureAnimation(t, ent != NULL ? ent->frame : 0))->gltexture);
|
|
|
|
if (t->texturechains[chain]->flags & SURF_DRAWFENCE)
|
|
glEnable (GL_ALPHA_TEST); // Flip alpha test back on
|
|
|
|
bound = true;
|
|
}
|
|
DrawGLPoly (s->polys);
|
|
rs_brushpasses++;
|
|
}
|
|
|
|
if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE)
|
|
glDisable (GL_ALPHA_TEST); // Flip alpha test back off
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
GL_WaterAlphaForEntitySurface -- ericw
|
|
|
|
Returns the water alpha to use for the entity and surface combination.
|
|
================
|
|
*/
|
|
float GL_WaterAlphaForEntitySurface (entity_t *ent, msurface_t *s)
|
|
{
|
|
float entalpha;
|
|
if (ent == NULL || ent->alpha == ENTALPHA_DEFAULT)
|
|
entalpha = GL_WaterAlphaForSurface(s);
|
|
else
|
|
entalpha = ENTALPHA_DECODE(ent->alpha);
|
|
return entalpha;
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_Water -- johnfitz
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
glpoly_t *p;
|
|
qboolean bound;
|
|
float entalpha;
|
|
|
|
if (r_drawflat_cheatsafe || r_lightmap_cheatsafe) // ericw -- !r_drawworld_cheatsafe check moved to R_DrawWorld_Water ()
|
|
return;
|
|
|
|
if (r_oldwater.value)
|
|
{
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_DRAWTURB))
|
|
continue;
|
|
bound = false;
|
|
entalpha = 1.0f;
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
entalpha = GL_WaterAlphaForEntitySurface (ent, s);
|
|
R_BeginTransparentDrawing (entalpha);
|
|
GL_Bind (t->gltexture);
|
|
bound = true;
|
|
}
|
|
for (p = s->polys->next; p; p = p->next)
|
|
{
|
|
DrawWaterPoly (p);
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
R_EndTransparentDrawing (entalpha);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_DRAWTURB))
|
|
continue;
|
|
bound = false;
|
|
entalpha = 1.0f;
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
entalpha = GL_WaterAlphaForEntitySurface (ent, s);
|
|
R_BeginTransparentDrawing (entalpha);
|
|
GL_Bind (t->warpimage);
|
|
|
|
if (model != cl.worldmodel)
|
|
{
|
|
// ericw -- this is copied from R_DrawSequentialPoly.
|
|
// If the poly is not part of the world we have to
|
|
// set this flag
|
|
t->update_warp = true; // FIXME: one frame too late!
|
|
}
|
|
|
|
bound = true;
|
|
}
|
|
DrawGLPoly (s->polys);
|
|
rs_brushpasses++;
|
|
}
|
|
R_EndTransparentDrawing (entalpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_White -- johnfitz -- draw sky and water as white polys when r_lightmap is 1
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_White (qmodel_t *model, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
|
|
glDisable (GL_TEXTURE_2D);
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain] || !(t->texturechains[chain]->flags & SURF_DRAWTILED))
|
|
continue;
|
|
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
DrawGLPoly (s->polys);
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
glEnable (GL_TEXTURE_2D);
|
|
}
|
|
|
|
/*
|
|
================
|
|
R_DrawLightmapChains -- johnfitz -- R_BlendLightmaps stripped down to almost nothing
|
|
================
|
|
*/
|
|
void R_DrawLightmapChains (void)
|
|
{
|
|
int i, j;
|
|
glpoly_t *p;
|
|
float *v;
|
|
|
|
for (i=0 ; i<MAX_LIGHTMAPS ; i++)
|
|
{
|
|
if (!lightmap_polys[i])
|
|
continue;
|
|
|
|
GL_Bind (lightmap_textures[i]);
|
|
for (p = lightmap_polys[i]; p; p=p->chain)
|
|
{
|
|
glBegin (GL_POLYGON);
|
|
v = p->verts[0];
|
|
for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE)
|
|
{
|
|
glTexCoord2f (v[5], v[6]);
|
|
glVertex3fv (v);
|
|
}
|
|
glEnd ();
|
|
rs_brushpasses++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern GLuint gl_bmodel_vbo;
|
|
|
|
/*
|
|
================
|
|
R_DrawTextureChains_Multitexture_VBO -- ericw
|
|
|
|
Draw lightmapped surfaces with fulbrights in one pass, using VBO.
|
|
Requires 3 TMUs, GL_COMBINE_EXT, and GL_ADD.
|
|
================
|
|
*/
|
|
void R_DrawTextureChains_Multitexture_VBO (qmodel_t *model, entity_t *ent, texchain_t chain)
|
|
{
|
|
int i;
|
|
msurface_t *s;
|
|
texture_t *t;
|
|
qboolean bound;
|
|
int lastlightmap;
|
|
gltexture_t *fullbright = NULL;
|
|
|
|
// Bind the buffers
|
|
GL_BindBuffer (GL_ARRAY_BUFFER, gl_bmodel_vbo);
|
|
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); // indices come from client memory!
|
|
|
|
// Setup vertex array pointers
|
|
glVertexPointer (3, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0));
|
|
glEnableClientState (GL_VERTEX_ARRAY);
|
|
|
|
GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB);
|
|
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
|
|
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
|
|
GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB);
|
|
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 5);
|
|
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
|
|
// TMU 2 is for fullbrights; same texture coordinates as TMU 0
|
|
GL_ClientActiveTextureFunc (GL_TEXTURE2_ARB);
|
|
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
|
|
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
|
|
// Setup TMU 1 (lightmap)
|
|
GL_SelectTexture (GL_TEXTURE1_ARB);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbright.value ? 2.0f : 1.0f);
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
// Setup TMU 2 (fullbrights)
|
|
GL_SelectTexture (GL_TEXTURE2_ARB);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
|
|
|
|
for (i=0 ; i<model->numtextures ; i++)
|
|
{
|
|
t = model->textures[i];
|
|
|
|
if (!t || !t->texturechains[chain] || t->texturechains[chain]->flags & (SURF_DRAWTILED | SURF_NOTEXTURE))
|
|
continue;
|
|
|
|
// Enable/disable TMU 2 (fullbrights)
|
|
GL_SelectTexture (GL_TEXTURE2_ARB);
|
|
if (gl_fullbrights.value && (fullbright = R_TextureAnimation(t, ent != NULL ? ent->frame : 0)->fullbright))
|
|
{
|
|
glEnable(GL_TEXTURE_2D);
|
|
GL_Bind (fullbright);
|
|
}
|
|
else
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
R_ClearBatch ();
|
|
|
|
bound = false;
|
|
lastlightmap = 0; // avoid compiler warning
|
|
for (s = t->texturechains[chain]; s; s = s->texturechain)
|
|
if (!s->culled)
|
|
{
|
|
if (!bound) //only bind once we are sure we need this texture
|
|
{
|
|
GL_SelectTexture (GL_TEXTURE0_ARB);
|
|
GL_Bind ((R_TextureAnimation(t, ent != NULL ? ent->frame : 0))->gltexture);
|
|
|
|
if (t->texturechains[chain]->flags & SURF_DRAWFENCE)
|
|
glEnable (GL_ALPHA_TEST); // Flip alpha test back on
|
|
|
|
bound = true;
|
|
lastlightmap = s->lightmaptexturenum;
|
|
}
|
|
|
|
if (s->lightmaptexturenum != lastlightmap)
|
|
R_FlushBatch ();
|
|
|
|
GL_SelectTexture (GL_TEXTURE1_ARB);
|
|
GL_Bind (lightmap_textures[s->lightmaptexturenum]);
|
|
lastlightmap = s->lightmaptexturenum;
|
|
R_BatchSurface (s);
|
|
|
|
rs_brushpasses++;
|
|
}
|
|
|
|
R_FlushBatch ();
|
|
|
|
if (bound && t->texturechains[chain]->flags & SURF_DRAWFENCE)
|
|
glDisable (GL_ALPHA_TEST); // Flip alpha test back off
|
|
}
|
|
|
|
// Reset TMU states
|
|
GL_SelectTexture (GL_TEXTURE2_ARB);
|
|
glDisable (GL_TEXTURE_2D);
|
|
|
|
GL_SelectTexture (GL_TEXTURE1_ARB);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glDisable (GL_TEXTURE_2D);
|
|
|
|
GL_SelectTexture (GL_TEXTURE0_ARB);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
|
|
// Disable client state
|
|
glDisableClientState (GL_VERTEX_ARRAY);
|
|
|
|
GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB);
|
|
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
|
|
GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB);
|
|
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
|
|
GL_ClientActiveTextureFunc (GL_TEXTURE2_ARB);
|
|
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawWorld -- johnfitz -- rewritten
|
|
=============
|
|
*/
|
|
void R_DrawTextureChains (qmodel_t *model, entity_t *ent, texchain_t chain)
|
|
{
|
|
float entalpha;
|
|
|
|
if (ent != NULL)
|
|
entalpha = ENTALPHA_DECODE(ent->alpha);
|
|
else
|
|
entalpha = 1;
|
|
|
|
// ericw -- the mh dynamic lightmap speedup: make a first pass through all
|
|
// surfaces we are going to draw, and rebuild any lightmaps that need it.
|
|
// this also chains surfaces by lightmap which is used by r_lightmap 1.
|
|
// the previous implementation of the speedup uploaded lightmaps one frame
|
|
// late which was visible under some conditions, this method avoids that.
|
|
R_BuildLightmapChains (model, chain);
|
|
R_UploadLightmaps ();
|
|
|
|
if (r_drawflat_cheatsafe)
|
|
{
|
|
glDisable (GL_TEXTURE_2D);
|
|
R_DrawTextureChains_Drawflat (model, chain);
|
|
glEnable (GL_TEXTURE_2D);
|
|
return;
|
|
}
|
|
|
|
if (r_fullbright_cheatsafe)
|
|
{
|
|
R_BeginTransparentDrawing (entalpha);
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
R_EndTransparentDrawing (entalpha);
|
|
goto fullbrights;
|
|
}
|
|
|
|
if (r_lightmap_cheatsafe)
|
|
{
|
|
if (!gl_overbright.value)
|
|
{
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glColor3f(0.5, 0.5, 0.5);
|
|
}
|
|
R_DrawLightmapChains ();
|
|
if (!gl_overbright.value)
|
|
{
|
|
glColor3f(1,1,1);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
R_DrawTextureChains_White (model, chain);
|
|
return;
|
|
}
|
|
|
|
R_BeginTransparentDrawing (entalpha);
|
|
|
|
R_DrawTextureChains_NoTexture (model, chain);
|
|
|
|
if (gl_vbo_able && gl_texture_env_combine && gl_texture_env_add && gl_mtexable && gl_max_texture_units >= 3)
|
|
{
|
|
R_DrawTextureChains_Multitexture_VBO (model, ent, chain);
|
|
R_EndTransparentDrawing (entalpha);
|
|
return;
|
|
}
|
|
|
|
if (gl_overbright.value)
|
|
{
|
|
if (gl_texture_env_combine && gl_mtexable) //case 1: texture and lightmap in one pass, overbright using texture combiners
|
|
{
|
|
GL_EnableMultitexture ();
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f);
|
|
GL_DisableMultitexture ();
|
|
R_DrawTextureChains_Multitexture (model, ent, chain);
|
|
GL_EnableMultitexture ();
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
GL_DisableMultitexture ();
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
else if (entalpha < 1) //case 2: can't do multipass if entity has alpha, so just draw the texture
|
|
{
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
}
|
|
else //case 3: texture in one pass, lightmap in second pass using 2x modulation blend func, fog in third pass
|
|
{
|
|
//to make fog work with multipass lightmapping, need to do one pass
|
|
//with no fog, one modulate pass with black fog, and one additive
|
|
//pass with black geometry and normal fog
|
|
Fog_DisableGFog ();
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
Fog_EnableGFog ();
|
|
glDepthMask (GL_FALSE);
|
|
glEnable (GL_BLEND);
|
|
glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR); //2x modulate
|
|
Fog_StartAdditive ();
|
|
R_DrawLightmapChains ();
|
|
Fog_StopAdditive ();
|
|
if (Fog_GetDensity() > 0)
|
|
{
|
|
glBlendFunc(GL_ONE, GL_ONE); //add
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glColor3f(0,0,0);
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
glColor3f(1,1,1);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDisable (GL_BLEND);
|
|
glDepthMask (GL_TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (gl_mtexable) //case 4: texture and lightmap in one pass, regular modulation
|
|
{
|
|
GL_EnableMultitexture ();
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
GL_DisableMultitexture ();
|
|
R_DrawTextureChains_Multitexture (model, ent, chain);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
else if (entalpha < 1) //case 5: can't do multipass if entity has alpha, so just draw the texture
|
|
{
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
}
|
|
else //case 6: texture in one pass, lightmap in a second pass, fog in third pass
|
|
{
|
|
//to make fog work with multipass lightmapping, need to do one pass
|
|
//with no fog, one modulate pass with black fog, and one additive
|
|
//pass with black geometry and normal fog
|
|
Fog_DisableGFog ();
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
Fog_EnableGFog ();
|
|
glDepthMask (GL_FALSE);
|
|
glEnable (GL_BLEND);
|
|
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //modulate
|
|
Fog_StartAdditive ();
|
|
R_DrawLightmapChains ();
|
|
Fog_StopAdditive ();
|
|
if (Fog_GetDensity() > 0)
|
|
{
|
|
glBlendFunc(GL_ONE, GL_ONE); //add
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glColor3f(0,0,0);
|
|
R_DrawTextureChains_TextureOnly (model, ent, chain);
|
|
glColor3f(1,1,1);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDisable (GL_BLEND);
|
|
glDepthMask (GL_TRUE);
|
|
}
|
|
}
|
|
|
|
R_EndTransparentDrawing (entalpha);
|
|
|
|
fullbrights:
|
|
if (gl_fullbrights.value)
|
|
{
|
|
glDepthMask (GL_FALSE);
|
|
glEnable (GL_BLEND);
|
|
glBlendFunc (GL_ONE, GL_ONE);
|
|
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glColor3f (entalpha, entalpha, entalpha);
|
|
Fog_StartAdditive ();
|
|
R_DrawTextureChains_Glow (model, ent, chain);
|
|
Fog_StopAdditive ();
|
|
glColor3f (1, 1, 1);
|
|
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDisable (GL_BLEND);
|
|
glDepthMask (GL_TRUE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawWorld -- ericw -- moved from R_DrawTextureChains, which is no longer specific to the world.
|
|
=============
|
|
*/
|
|
void R_DrawWorld (void)
|
|
{
|
|
if (!r_drawworld_cheatsafe)
|
|
return;
|
|
|
|
R_DrawTextureChains (cl.worldmodel, NULL, chain_world);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawWorld_Water -- ericw -- moved from R_DrawTextureChains_Water, which is no longer specific to the world.
|
|
=============
|
|
*/
|
|
void R_DrawWorld_Water (void)
|
|
{
|
|
if (!r_drawworld_cheatsafe)
|
|
return;
|
|
|
|
R_DrawTextureChains_Water (cl.worldmodel, NULL, chain_world);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
R_DrawWorld_ShowTris -- ericw -- moved from R_DrawTextureChains_ShowTris, which is no longer specific to the world.
|
|
=============
|
|
*/
|
|
void R_DrawWorld_ShowTris (void)
|
|
{
|
|
if (!r_drawworld_cheatsafe)
|
|
return;
|
|
|
|
R_DrawTextureChains_ShowTris (cl.worldmodel, chain_world);
|
|
}
|