quake2forge/ref_gl/gl_rsurf.c

1665 lines
34 KiB
C

/*
Copyright (C) 1997-2001 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.
*/
// GL_RSURF.C: surface-related refresh code
#include <ctype.h>
#include <assert.h>
#include "gl_local.h"
static vec3_t modelorg; // relative to viewpoint
msurface_t *r_alpha_surfaces;
#define DYNAMIC_LIGHT_WIDTH 128
#define DYNAMIC_LIGHT_HEIGHT 128
#define LIGHTMAP_BYTES 4
#define BLOCK_WIDTH 128
#define BLOCK_HEIGHT 128
#define MAX_LIGHTMAPS 128
int c_visible_lightmaps;
int c_visible_textures;
#define GL_LIGHTMAP_FORMAT GL_RGBA
typedef struct
{
int internal_format;
int current_lightmap_texture;
msurface_t *lightmap_surfaces[MAX_LIGHTMAPS];
int allocated[BLOCK_WIDTH];
// the lightmap texture data needs to be kept in
// main memory so texsubimage can update properly
byte lightmap_buffer[4*BLOCK_WIDTH*BLOCK_HEIGHT];
} gllightmapstate_t;
static gllightmapstate_t gl_lms;
static void LM_InitBlock( void );
static void LM_UploadBlock( qboolean dynamic );
static qboolean LM_AllocBlock (int w, int h, int *x, int *y);
extern void R_SetCacheState( msurface_t *surf );
extern void R_BuildLightMap (msurface_t *surf, byte *dest, int stride);
/*
=============================================================
BRUSH MODELS
=============================================================
*/
/*
===============
R_TextureAnimation
Returns the proper texture for a given time and base texture
===============
*/
image_t *R_TextureAnimation (mtexinfo_t *tex)
{
int c;
if (!tex->next)
return tex->image;
c = currententity->frame % tex->numframes;
while (c)
{
tex = tex->next;
c--;
}
return tex->image;
}
#if 0
/*
=================
WaterWarpPolyVerts
Mangles the x and y coordinates in a copy of the poly
so that any drawing routine can be water warped
=================
*/
glpoly_t *WaterWarpPolyVerts (glpoly_t *p)
{
int i;
float *v, *nv;
static byte buffer[1024];
glpoly_t *out;
out = (glpoly_t *)buffer;
out->numverts = p->numverts;
v = p->verts[0];
nv = out->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE, nv+=VERTEXSIZE)
{
nv[0] = v[0] + 4*sin(v[1]*0.05+r_newrefdef.time)*sin(v[2]*0.05+r_newrefdef.time);
nv[1] = v[1] + 4*sin(v[0]*0.05+r_newrefdef.time)*sin(v[2]*0.05+r_newrefdef.time);
nv[2] = v[2];
nv[3] = v[3];
nv[4] = v[4];
nv[5] = v[5];
nv[6] = v[6];
}
return out;
}
/*
================
DrawGLWaterPoly
Warp the vertex coordinates
================
*/
void DrawGLWaterPoly (glpoly_t *p)
{
int i;
float *v;
p = WaterWarpPolyVerts (p);
qglBegin (GL_TRIANGLE_FAN);
v = p->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
{
qglTexCoord2f (v[3], v[4]);
qglVertex3fv (v);
}
qglEnd ();
}
void DrawGLWaterPolyLightmap (glpoly_t *p)
{
int i;
float *v;
p = WaterWarpPolyVerts (p);
qglBegin (GL_TRIANGLE_FAN);
v = p->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
{
qglTexCoord2f (v[5], v[6]);
qglVertex3fv (v);
}
qglEnd ();
}
#endif
/*
================
DrawGLPoly
================
*/
void DrawGLPoly (glpoly_t *p)
{
int i;
float *v;
qglBegin (GL_POLYGON);
v = p->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
{
qglTexCoord2f (v[3], v[4]);
qglVertex3fv (v);
}
qglEnd ();
}
//============
//PGM
/*
================
DrawGLFlowingPoly -- version of DrawGLPoly that handles scrolling texture
================
*/
void DrawGLFlowingPoly (msurface_t *fa)
{
int i;
float *v;
glpoly_t *p;
float scroll;
p = fa->polys;
scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) );
if(scroll == 0.0)
scroll = -64.0;
qglBegin (GL_POLYGON);
v = p->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
{
qglTexCoord2f ((v[3] + scroll), v[4]);
qglVertex3fv (v);
}
qglEnd ();
}
//PGM
//============
/*
** R_DrawTriangleOutlines
*/
void R_DrawTriangleOutlines (void)
{
int i, j;
glpoly_t *p;
if (!gl_showtris->value)
return;
qglDisable (GL_TEXTURE_2D);
qglDisable (GL_DEPTH_TEST);
qglColor4f (1,1,1,1);
for (i=0 ; i<MAX_LIGHTMAPS ; i++)
{
msurface_t *surf;
for ( surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain )
{
p = surf->polys;
for ( ; p ; p=p->chain)
{
for (j=2 ; j<p->numverts ; j++ )
{
qglBegin (GL_LINE_STRIP);
qglVertex3fv (p->verts[0]);
qglVertex3fv (p->verts[j-1]);
qglVertex3fv (p->verts[j]);
qglVertex3fv (p->verts[0]);
qglEnd ();
}
}
}
}
qglEnable (GL_DEPTH_TEST);
qglEnable (GL_TEXTURE_2D);
}
/*
** DrawGLPolyChain
*/
void DrawGLPolyChain( glpoly_t *p, float soffset, float toffset )
{
if ( soffset == 0 && toffset == 0 )
{
for ( ; p != 0; p = p->chain )
{
float *v;
int j;
qglBegin (GL_POLYGON);
v = p->verts[0];
for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE)
{
qglTexCoord2f (v[5], v[6] );
qglVertex3fv (v);
}
qglEnd ();
}
}
else
{
for ( ; p != 0; p = p->chain )
{
float *v;
int j;
qglBegin (GL_POLYGON);
v = p->verts[0];
for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE)
{
qglTexCoord2f (v[5] - soffset, v[6] - toffset );
qglVertex3fv (v);
}
qglEnd ();
}
}
}
/*
** R_BlendLightMaps
**
** This routine takes all the given light mapped surfaces in the world and
** blends them into the framebuffer.
*/
void R_BlendLightmaps (void)
{
int i;
msurface_t *surf, *newdrawsurf = 0;
// don't bother if we're set to fullbright
if (r_fullbright->value)
return;
if (!r_worldmodel->lightdata)
return;
// don't bother writing Z
qglDepthMask( 0 );
/*
** set the appropriate blending mode unless we're only looking at the
** lightmaps.
*/
if (!gl_lightmap->value)
{
qglEnable (GL_BLEND);
if ( gl_saturatelighting->value )
{
qglBlendFunc( GL_ONE, GL_ONE );
}
else
{
if ( gl_monolightmap->string[0] != '0' )
{
switch ( toupper( gl_monolightmap->string[0] ) )
{
case 'I':
qglBlendFunc (GL_ZERO, GL_SRC_COLOR );
break;
case 'L':
qglBlendFunc (GL_ZERO, GL_SRC_COLOR );
break;
case 'A':
default:
qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
break;
}
}
else
{
qglBlendFunc (GL_ZERO, GL_SRC_COLOR );
}
}
}
if ( currentmodel == r_worldmodel )
c_visible_lightmaps = 0;
/*
** render static lightmaps first
*/
for ( i = 1; i < MAX_LIGHTMAPS; i++ )
{
if ( gl_lms.lightmap_surfaces[i] )
{
if (currentmodel == r_worldmodel)
c_visible_lightmaps++;
GL_Bind( gl_state.lightmap_textures + i);
for ( surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain )
{
if ( surf->polys )
DrawGLPolyChain( surf->polys, 0, 0 );
}
}
}
/*
** render dynamic lightmaps
*/
if ( gl_dynamic->value )
{
LM_InitBlock();
GL_Bind( gl_state.lightmap_textures+0 );
if (currentmodel == r_worldmodel)
c_visible_lightmaps++;
newdrawsurf = gl_lms.lightmap_surfaces[0];
for ( surf = gl_lms.lightmap_surfaces[0]; surf != 0; surf = surf->lightmapchain )
{
int smax, tmax;
byte *base;
smax = (surf->extents[0]>>4)+1;
tmax = (surf->extents[1]>>4)+1;
if ( LM_AllocBlock( smax, tmax, &surf->dlight_s, &surf->dlight_t ) )
{
base = gl_lms.lightmap_buffer;
base += ( surf->dlight_t * BLOCK_WIDTH + surf->dlight_s ) * LIGHTMAP_BYTES;
R_BuildLightMap (surf, base, BLOCK_WIDTH*LIGHTMAP_BYTES);
}
else
{
msurface_t *drawsurf;
// upload what we have so far
LM_UploadBlock( true );
// draw all surfaces that use this lightmap
for ( drawsurf = newdrawsurf; drawsurf != surf; drawsurf = drawsurf->lightmapchain )
{
if ( drawsurf->polys )
DrawGLPolyChain( drawsurf->polys,
( drawsurf->light_s - drawsurf->dlight_s ) * ( 1.0 / 128.0 ),
( drawsurf->light_t - drawsurf->dlight_t ) * ( 1.0 / 128.0 ) );
}
newdrawsurf = drawsurf;
// clear the block
LM_InitBlock();
// try uploading the block now
if ( !LM_AllocBlock( smax, tmax, &surf->dlight_s, &surf->dlight_t ) )
{
ri.Sys_Error( ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed (dynamic)\n", smax, tmax );
}
base = gl_lms.lightmap_buffer;
base += ( surf->dlight_t * BLOCK_WIDTH + surf->dlight_s ) * LIGHTMAP_BYTES;
R_BuildLightMap (surf, base, BLOCK_WIDTH*LIGHTMAP_BYTES);
}
}
/*
** draw remainder of dynamic lightmaps that haven't been uploaded yet
*/
if ( newdrawsurf )
LM_UploadBlock( true );
for ( surf = newdrawsurf; surf != 0; surf = surf->lightmapchain )
{
if ( surf->polys )
DrawGLPolyChain( surf->polys, ( surf->light_s - surf->dlight_s ) * ( 1.0 / 128.0 ), ( surf->light_t - surf->dlight_t ) * ( 1.0 / 128.0 ) );
}
}
/*
** restore state
*/
qglDisable (GL_BLEND);
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qglDepthMask( 1 );
}
/*
================
R_RenderBrushPoly
================
*/
void R_RenderBrushPoly (msurface_t *fa)
{
int maps;
image_t *image;
qboolean is_dynamic = false;
c_brush_polys++;
image = R_TextureAnimation (fa->texinfo);
if (fa->flags & SURF_DRAWTURB)
{
GL_Bind( image->texnum );
// warp texture, no lightmaps
GL_TexEnv( GL_MODULATE );
qglColor4f( gl_state.inverse_intensity,
gl_state.inverse_intensity,
gl_state.inverse_intensity,
1.0F );
EmitWaterPolys (fa);
GL_TexEnv( GL_REPLACE );
return;
}
else
{
GL_Bind( image->texnum );
GL_TexEnv( GL_REPLACE );
}
//======
//PGM
if(fa->texinfo->flags & SURF_FLOWING)
DrawGLFlowingPoly (fa);
else
DrawGLPoly (fa->polys);
//PGM
//======
/*
** check for lightmap modification
*/
for ( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ )
{
if ( r_newrefdef.lightstyles[fa->styles[maps]].white != fa->cached_light[maps] )
goto dynamic;
}
// dynamic this frame or dynamic previously
if ( ( fa->dlightframe == r_framecount ) )
{
dynamic:
if ( gl_dynamic->value )
{
if (!( fa->texinfo->flags & (SURF_SKY|SURF_TRANS33|SURF_TRANS66|SURF_WARP ) ) )
{
is_dynamic = true;
}
}
}
if ( is_dynamic )
{
if ( ( fa->styles[maps] >= 32 || fa->styles[maps] == 0 ) && ( fa->dlightframe != r_framecount ) )
{
unsigned temp[34*34];
int smax, tmax;
smax = (fa->extents[0]>>4)+1;
tmax = (fa->extents[1]>>4)+1;
R_BuildLightMap( fa, (void *)temp, smax*4 );
R_SetCacheState( fa );
GL_Bind( gl_state.lightmap_textures + fa->lightmaptexturenum );
qglTexSubImage2D( GL_TEXTURE_2D, 0,
fa->light_s, fa->light_t,
smax, tmax,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE, temp );
fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum];
gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa;
}
else
{
fa->lightmapchain = gl_lms.lightmap_surfaces[0];
gl_lms.lightmap_surfaces[0] = fa;
}
}
else
{
fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum];
gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa;
}
}
/*
================
R_DrawAlphaSurfaces
Draw water surfaces and windows.
The BSP tree is waled front to back, so unwinding the chain
of alpha_surfaces will draw back to front, giving proper ordering.
================
*/
void R_DrawAlphaSurfaces (void)
{
msurface_t *s;
float intens;
//
// go back to the world matrix
//
qglLoadMatrixf (r_world_matrix);
qglEnable (GL_BLEND);
GL_TexEnv( GL_MODULATE );
// the textures are prescaled up for a better lighting range,
// so scale it back down
intens = gl_state.inverse_intensity;
for (s=r_alpha_surfaces ; s ; s=s->texturechain)
{
GL_Bind(s->texinfo->image->texnum);
c_brush_polys++;
if (s->texinfo->flags & SURF_TRANS33)
qglColor4f (intens,intens,intens,0.33);
else if (s->texinfo->flags & SURF_TRANS66)
qglColor4f (intens,intens,intens,0.66);
else
qglColor4f (intens,intens,intens,1);
if (s->flags & SURF_DRAWTURB)
EmitWaterPolys (s);
else if(s->texinfo->flags & SURF_FLOWING) // PGM 9/16/98
DrawGLFlowingPoly (s); // PGM
else
DrawGLPoly (s->polys);
}
GL_TexEnv( GL_REPLACE );
qglColor4f (1,1,1,1);
qglDisable (GL_BLEND);
r_alpha_surfaces = NULL;
}
/*
================
DrawTextureChains
================
*/
void DrawTextureChains (void)
{
int i;
msurface_t *s;
image_t *image;
c_visible_textures = 0;
// GL_TexEnv( GL_REPLACE );
if ( !qglSelectTextureSGIS && !qglActiveTextureARB )
{
for ( i = 0, image=gltextures ; i<numgltextures ; i++,image++)
{
if (!image->registration_sequence)
continue;
s = image->texturechain;
if (!s)
continue;
c_visible_textures++;
for ( ; s ; s=s->texturechain)
R_RenderBrushPoly (s);
image->texturechain = NULL;
}
}
else
{
for ( i = 0, image=gltextures ; i<numgltextures ; i++,image++)
{
if (!image->registration_sequence)
continue;
if (!image->texturechain)
continue;
c_visible_textures++;
for ( s = image->texturechain; s ; s=s->texturechain)
{
if ( !( s->flags & SURF_DRAWTURB ) )
R_RenderBrushPoly (s);
}
}
GL_EnableMultitexture( false );
for ( i = 0, image=gltextures ; i<numgltextures ; i++,image++)
{
if (!image->registration_sequence)
continue;
s = image->texturechain;
if (!s)
continue;
for ( ; s ; s=s->texturechain)
{
if ( s->flags & SURF_DRAWTURB )
R_RenderBrushPoly (s);
}
image->texturechain = NULL;
}
// GL_EnableMultitexture( true );
}
GL_TexEnv( GL_REPLACE );
}
static void GL_RenderLightmappedPoly( msurface_t *surf )
{
int i, nv = surf->polys->numverts;
int map;
float *v;
image_t *image = R_TextureAnimation( surf->texinfo );
qboolean is_dynamic = false;
unsigned lmtex = surf->lightmaptexturenum;
glpoly_t *p;
for ( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
{
if ( r_newrefdef.lightstyles[surf->styles[map]].white != surf->cached_light[map] )
goto dynamic;
}
// dynamic this frame or dynamic previously
if ( ( surf->dlightframe == r_framecount ) )
{
dynamic:
if ( gl_dynamic->value )
{
if ( !(surf->texinfo->flags & (SURF_SKY|SURF_TRANS33|SURF_TRANS66|SURF_WARP ) ) )
{
is_dynamic = true;
}
}
}
if ( is_dynamic )
{
unsigned temp[128*128];
int smax, tmax;
if ( ( surf->styles[map] >= 32 || surf->styles[map] == 0 ) && ( surf->dlightframe != r_framecount ) )
{
smax = (surf->extents[0]>>4)+1;
tmax = (surf->extents[1]>>4)+1;
R_BuildLightMap( surf, (void *)temp, smax*4 );
R_SetCacheState( surf );
GL_MBind( GL_Texture1, gl_state.lightmap_textures + surf->lightmaptexturenum );
lmtex = surf->lightmaptexturenum;
qglTexSubImage2D( GL_TEXTURE_2D, 0,
surf->light_s, surf->light_t,
smax, tmax,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE, temp );
}
else
{
smax = (surf->extents[0]>>4)+1;
tmax = (surf->extents[1]>>4)+1;
R_BuildLightMap( surf, (void *)temp, smax*4 );
GL_MBind( GL_Texture1, gl_state.lightmap_textures + 0 );
lmtex = 0;
qglTexSubImage2D( GL_TEXTURE_2D, 0,
surf->light_s, surf->light_t,
smax, tmax,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE, temp );
}
c_brush_polys++;
GL_MBind( GL_Texture0, image->texnum );
GL_MBind( GL_Texture1, gl_state.lightmap_textures + lmtex );
//==========
//PGM
if (surf->texinfo->flags & SURF_FLOWING)
{
float scroll;
scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) );
if(scroll == 0.0)
scroll = -64.0;
for ( p = surf->polys; p; p = p->chain )
{
v = p->verts[0];
qglBegin (GL_POLYGON);
for (i=0 ; i< nv; i++, v+= VERTEXSIZE)
{
qglMTexCoord2fSGIS( GL_Texture0, (v[3]+scroll), v[4]);
qglMTexCoord2fSGIS( GL_Texture1, v[5], v[6]);
qglVertex3fv (v);
}
qglEnd ();
}
}
else
{
for ( p = surf->polys; p; p = p->chain )
{
v = p->verts[0];
qglBegin (GL_POLYGON);
for (i=0 ; i< nv; i++, v+= VERTEXSIZE)
{
qglMTexCoord2fSGIS( GL_Texture0, v[3], v[4]);
qglMTexCoord2fSGIS( GL_Texture1, v[5], v[6]);
qglVertex3fv (v);
}
qglEnd ();
}
}
//PGM
//==========
}
else
{
c_brush_polys++;
GL_MBind( GL_Texture0, image->texnum );
GL_MBind( GL_Texture1, gl_state.lightmap_textures + lmtex );
//==========
//PGM
if (surf->texinfo->flags & SURF_FLOWING)
{
float scroll;
scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) );
if(scroll == 0.0)
scroll = -64.0;
for ( p = surf->polys; p; p = p->chain )
{
v = p->verts[0];
qglBegin (GL_POLYGON);
for (i=0 ; i< nv; i++, v+= VERTEXSIZE)
{
qglMTexCoord2fSGIS( GL_Texture0, (v[3]+scroll), v[4]);
qglMTexCoord2fSGIS( GL_Texture1, v[5], v[6]);
qglVertex3fv (v);
}
qglEnd ();
}
}
else
{
//PGM
//==========
for ( p = surf->polys; p; p = p->chain )
{
v = p->verts[0];
qglBegin (GL_POLYGON);
for (i=0 ; i< nv; i++, v+= VERTEXSIZE)
{
qglMTexCoord2fSGIS( GL_Texture0, v[3], v[4]);
qglMTexCoord2fSGIS( GL_Texture1, v[5], v[6]);
qglVertex3fv (v);
}
qglEnd ();
}
//==========
//PGM
}
//PGM
//==========
}
}
/*
=================
R_DrawInlineBModel
=================
*/
void R_DrawInlineBModel (void)
{
int i, k;
cplane_t *pplane;
float dot;
msurface_t *psurf;
dlight_t *lt;
// calculate dynamic lighting for bmodel
if ( !gl_flashblend->value )
{
lt = r_newrefdef.dlights;
for (k=0 ; k<r_newrefdef.num_dlights ; k++, lt++)
{
R_MarkLights (lt, 1<<k, currentmodel->nodes + currentmodel->firstnode);
}
}
psurf = &currentmodel->surfaces[currentmodel->firstmodelsurface];
if ( currententity->flags & RF_TRANSLUCENT )
{
qglEnable (GL_BLEND);
qglColor4f (1,1,1,0.25);
GL_TexEnv( GL_MODULATE );
}
//
// draw texture
//
for (i=0 ; i<currentmodel->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 (psurf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66) )
{ // add to the translucent chain
psurf->texturechain = r_alpha_surfaces;
r_alpha_surfaces = psurf;
}
else if ( qglMTexCoord2fSGIS && !( psurf->flags & SURF_DRAWTURB ) )
{
GL_RenderLightmappedPoly( psurf );
}
else
{
GL_EnableMultitexture( false );
R_RenderBrushPoly( psurf );
GL_EnableMultitexture( true );
}
}
}
if ( !(currententity->flags & RF_TRANSLUCENT) )
{
if ( !qglMTexCoord2fSGIS )
R_BlendLightmaps ();
}
else
{
qglDisable (GL_BLEND);
qglColor4f (1,1,1,1);
GL_TexEnv( GL_REPLACE );
}
}
/*
=================
R_DrawBrushModel
=================
*/
void R_DrawBrushModel (entity_t *e)
{
vec3_t mins, maxs;
int i;
qboolean rotated;
if (currentmodel->nummodelsurfaces == 0)
return;
currententity = e;
gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1;
if (e->angles[0] || e->angles[1] || e->angles[2])
{
rotated = true;
for (i=0 ; i<3 ; i++)
{
mins[i] = e->origin[i] - currentmodel->radius;
maxs[i] = e->origin[i] + currentmodel->radius;
}
}
else
{
rotated = false;
VectorAdd (e->origin, currentmodel->mins, mins);
VectorAdd (e->origin, currentmodel->maxs, maxs);
}
if (R_CullBox (mins, maxs))
return;
qglColor3f (1,1,1);
memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces));
VectorSubtract (r_newrefdef.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);
}
qglPushMatrix ();
e->angles[0] = -e->angles[0]; // stupid quake bug
e->angles[2] = -e->angles[2]; // stupid quake bug
R_RotateForEntity (e);
e->angles[0] = -e->angles[0]; // stupid quake bug
e->angles[2] = -e->angles[2]; // stupid quake bug
GL_EnableMultitexture( true );
GL_SelectTexture( GL_Texture0);
GL_TexEnv( GL_REPLACE );
GL_SelectTexture( GL_Texture1);
GL_TexEnv( GL_MODULATE );
R_DrawInlineBModel ();
GL_EnableMultitexture( false );
qglPopMatrix ();
}
/*
=============================================================
WORLD MODEL
=============================================================
*/
/*
================
R_RecursiveWorldNode
================
*/
void R_RecursiveWorldNode (mnode_t *node)
{
int c, side, sidebit;
cplane_t *plane;
msurface_t *surf, **mark;
mleaf_t *pleaf;
float dot;
image_t *image;
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 != -1)
{
pleaf = (mleaf_t *)node;
// check for door connected areas
if (r_newrefdef.areabits)
{
if (! (r_newrefdef.areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) )
return; // not visible
}
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if (c)
{
do
{
(*mark)->visframe = r_framecount;
mark++;
} while (--c);
}
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;
sidebit = 0;
}
else
{
side = 1;
sidebit = SURF_PLANEBACK;
}
// recurse down the children, front side first
R_RecursiveWorldNode (node->children[side]);
// draw stuff
for ( c = node->numsurfaces, surf = r_worldmodel->surfaces + node->firstsurface; c ; c--, surf++)
{
if (surf->visframe != r_framecount)
continue;
if ( (surf->flags & SURF_PLANEBACK) != sidebit )
continue; // wrong side
if (surf->texinfo->flags & SURF_SKY)
{ // just adds to visible sky bounds
R_AddSkySurface (surf);
}
else if (surf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66))
{ // add to the translucent chain
surf->texturechain = r_alpha_surfaces;
r_alpha_surfaces = surf;
}
else
{
if ( qglMTexCoord2fSGIS && !( surf->flags & SURF_DRAWTURB ) )
{
GL_RenderLightmappedPoly( surf );
}
else
{
// the polygon is visible, so add it to the texture
// sorted chain
// FIXME: this is a hack for animation
image = R_TextureAnimation (surf->texinfo);
surf->texturechain = image->texturechain;
image->texturechain = surf;
}
}
}
// recurse down the back side
R_RecursiveWorldNode (node->children[!side]);
/*
for ( ; c ; c--, surf++)
{
if (surf->visframe != r_framecount)
continue;
if ( (surf->flags & SURF_PLANEBACK) != sidebit )
continue; // wrong side
if (surf->texinfo->flags & SURF_SKY)
{ // just adds to visible sky bounds
R_AddSkySurface (surf);
}
else if (surf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66))
{ // add to the translucent chain
// surf->texturechain = alpha_surfaces;
// alpha_surfaces = surf;
}
else
{
if ( qglMTexCoord2fSGIS && !( surf->flags & SURF_DRAWTURB ) )
{
GL_RenderLightmappedPoly( surf );
}
else
{
// the polygon is visible, so add it to the texture
// sorted chain
// FIXME: this is a hack for animation
image = R_TextureAnimation (surf->texinfo);
surf->texturechain = image->texturechain;
image->texturechain = surf;
}
}
}
*/
}
/*
=============
R_DrawWorld
=============
*/
void R_DrawWorld (void)
{
entity_t ent;
if (!r_drawworld->value)
return;
if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL )
return;
currentmodel = r_worldmodel;
VectorCopy (r_newrefdef.vieworg, modelorg);
// auto cycle the world frame for texture animation
memset (&ent, 0, sizeof(ent));
ent.frame = (int)(r_newrefdef.time*2);
currententity = &ent;
gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1;
qglColor3f (1,1,1);
memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces));
R_ClearSkyBox ();
if ( qglMTexCoord2fSGIS )
{
GL_EnableMultitexture( true );
GL_SelectTexture( GL_Texture0);
GL_TexEnv( GL_REPLACE );
GL_SelectTexture( GL_Texture1);
if ( gl_lightmap->value )
GL_TexEnv( GL_REPLACE );
else
GL_TexEnv( GL_MODULATE );
R_RecursiveWorldNode (r_worldmodel->nodes);
GL_EnableMultitexture( false );
}
else
{
R_RecursiveWorldNode (r_worldmodel->nodes);
}
/*
** theoretically nothing should happen in the next two functions
** if multitexture is enabled
*/
DrawTextureChains ();
R_BlendLightmaps ();
R_DrawSkyBox ();
R_DrawTriangleOutlines ();
}
/*
===============
R_MarkLeaves
Mark the leaves and nodes that are in the PVS for the current
cluster
===============
*/
void R_MarkLeaves (void)
{
byte *vis;
byte fatvis[MAX_MAP_LEAFS/8];
mnode_t *node;
int i, c;
mleaf_t *leaf;
int cluster;
if (r_oldviewcluster == r_viewcluster && r_oldviewcluster2 == r_viewcluster2 && !r_novis->value && r_viewcluster != -1)
return;
// development aid to let you run around and see exactly where
// the pvs ends
if (gl_lockpvs->value)
return;
r_visframecount++;
r_oldviewcluster = r_viewcluster;
r_oldviewcluster2 = r_viewcluster2;
if (r_novis->value || r_viewcluster == -1 || !r_worldmodel->vis)
{
// mark everything
for (i=0 ; i<r_worldmodel->numleafs ; i++)
r_worldmodel->leafs[i].visframe = r_visframecount;
for (i=0 ; i<r_worldmodel->numnodes ; i++)
r_worldmodel->nodes[i].visframe = r_visframecount;
return;
}
vis = Mod_ClusterPVS (r_viewcluster, r_worldmodel);
// may have to combine two clusters because of solid water boundaries
if (r_viewcluster2 != r_viewcluster)
{
memcpy (fatvis, vis, (r_worldmodel->numleafs+7)/8);
vis = Mod_ClusterPVS (r_viewcluster2, r_worldmodel);
c = (r_worldmodel->numleafs+31)/32;
for (i=0 ; i<c ; i++)
((int *)fatvis)[i] |= ((int *)vis)[i];
vis = fatvis;
}
for (i=0,leaf=r_worldmodel->leafs ; i<r_worldmodel->numleafs ; i++, leaf++)
{
cluster = leaf->cluster;
if (cluster == -1)
continue;
if (vis[cluster>>3] & (1<<(cluster&7)))
{
node = (mnode_t *)leaf;
do
{
if (node->visframe == r_visframecount)
break;
node->visframe = r_visframecount;
node = node->parent;
} while (node);
}
}
#if 0
for (i=0 ; i<r_worldmodel->vis->numclusters ; i++)
{
if (vis[i>>3] & (1<<(i&7)))
{
node = (mnode_t *)&r_worldmodel->leafs[i]; // FIXME: cluster
do
{
if (node->visframe == r_visframecount)
break;
node->visframe = r_visframecount;
node = node->parent;
} while (node);
}
}
#endif
}
/*
=============================================================================
LIGHTMAP ALLOCATION
=============================================================================
*/
static void LM_InitBlock( void )
{
memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated ) );
}
static void LM_UploadBlock( qboolean dynamic )
{
int texture;
int height = 0;
if ( dynamic )
{
texture = 0;
}
else
{
texture = gl_lms.current_lightmap_texture;
}
GL_Bind( gl_state.lightmap_textures + texture );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if ( dynamic )
{
int i;
for ( i = 0; i < BLOCK_WIDTH; i++ )
{
if ( gl_lms.allocated[i] > height )
height = gl_lms.allocated[i];
}
qglTexSubImage2D( GL_TEXTURE_2D,
0,
0, 0,
BLOCK_WIDTH, height,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE,
gl_lms.lightmap_buffer );
}
else
{
qglTexImage2D( GL_TEXTURE_2D,
0,
gl_lms.internal_format,
BLOCK_WIDTH, BLOCK_HEIGHT,
0,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE,
gl_lms.lightmap_buffer );
if ( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS )
ri.Sys_Error( ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n" );
}
}
// returns a texture number and the position inside it
static qboolean LM_AllocBlock (int w, int h, int *x, int *y)
{
int i, j;
int best, best2;
best = BLOCK_HEIGHT;
for (i=0 ; i<BLOCK_WIDTH-w ; i++)
{
best2 = 0;
for (j=0 ; j<w ; j++)
{
if (gl_lms.allocated[i+j] >= best)
break;
if (gl_lms.allocated[i+j] > best2)
best2 = gl_lms.allocated[i+j];
}
if (j == w)
{ // this is a valid spot
*x = i;
*y = best = best2;
}
}
if (best + h > BLOCK_HEIGHT)
return false;
for (i=0 ; i<w ; i++)
gl_lms.allocated[*x + i] = best + h;
return true;
}
/*
================
GL_BuildPolygonFromSurface
================
*/
void GL_BuildPolygonFromSurface(msurface_t *fa)
{
int i, lindex, lnumverts;
medge_t *pedges, *r_pedge;
int vertpage;
float *vec;
float s, t;
glpoly_t *poly;
vec3_t total;
// reconstruct the polygon
pedges = currentmodel->edges;
lnumverts = fa->numedges;
vertpage = 0;
VectorClear (total);
//
// draw texture
//
poly = 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 = currentmodel->vertexes[r_pedge->v[0]].position;
}
else
{
r_pedge = &pedges[-lindex];
vec = currentmodel->vertexes[r_pedge->v[1]].position;
}
s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
s /= fa->texinfo->image->width;
t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
t /= fa->texinfo->image->height;
VectorAdd (total, vec, total);
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;
byte *base;
if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))
return;
smax = (surf->extents[0]>>4)+1;
tmax = (surf->extents[1]>>4)+1;
if ( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) )
{
LM_UploadBlock( false );
LM_InitBlock();
if ( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) )
{
ri.Sys_Error( ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n", smax, tmax );
}
}
surf->lightmaptexturenum = gl_lms.current_lightmap_texture;
base = gl_lms.lightmap_buffer;
base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES;
R_SetCacheState( surf );
R_BuildLightMap (surf, base, BLOCK_WIDTH*LIGHTMAP_BYTES);
}
/*
==================
GL_BeginBuildingLightmaps
==================
*/
void GL_BeginBuildingLightmaps (model_t *m)
{
static lightstyle_t lightstyles[MAX_LIGHTSTYLES];
int i;
unsigned dummy[128*128];
memset( gl_lms.allocated, 0, sizeof(gl_lms.allocated) );
r_framecount = 1; // no dlightcache
GL_EnableMultitexture( true );
GL_SelectTexture( GL_Texture1);
/*
** setup the base lightstyles so the lightmaps won't have to be regenerated
** the first time they're seen
*/
for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
{
lightstyles[i].rgb[0] = 1;
lightstyles[i].rgb[1] = 1;
lightstyles[i].rgb[2] = 1;
lightstyles[i].white = 3;
}
r_newrefdef.lightstyles = lightstyles;
if (!gl_state.lightmap_textures)
{
gl_state.lightmap_textures = TEXNUM_LIGHTMAPS;
// gl_state.lightmap_textures = gl_state.texture_extension_number;
// gl_state.texture_extension_number = gl_state.lightmap_textures + MAX_LIGHTMAPS;
}
gl_lms.current_lightmap_texture = 1;
/*
** if mono lightmaps are enabled and we want to use alpha
** blending (a,1-a) then we're likely running on a 3DLabs
** Permedia2. In a perfect world we'd use a GL_ALPHA lightmap
** in order to conserve space and maximize bandwidth, however
** this isn't a perfect world.
**
** So we have to use alpha lightmaps, but stored in GL_RGBA format,
** which means we only get 1/16th the color resolution we should when
** using alpha lightmaps. If we find another board that supports
** only alpha lightmaps but that can at least support the GL_ALPHA
** format then we should change this code to use real alpha maps.
*/
if ( toupper( gl_monolightmap->string[0] ) == 'A' )
{
gl_lms.internal_format = gl_tex_alpha_format;
}
/*
** try to do hacked colored lighting with a blended texture
*/
else if ( toupper( gl_monolightmap->string[0] ) == 'C' )
{
gl_lms.internal_format = gl_tex_alpha_format;
}
else if ( toupper( gl_monolightmap->string[0] ) == 'I' )
{
gl_lms.internal_format = GL_INTENSITY8;
}
else if ( toupper( gl_monolightmap->string[0] ) == 'L' )
{
gl_lms.internal_format = GL_LUMINANCE8;
}
else
{
gl_lms.internal_format = gl_tex_solid_format;
}
/*
** initialize the dynamic lightmap texture
*/
GL_Bind( gl_state.lightmap_textures + 0 );
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
qglTexImage2D( GL_TEXTURE_2D,
0,
gl_lms.internal_format,
BLOCK_WIDTH, BLOCK_HEIGHT,
0,
GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE,
dummy );
}
/*
=======================
GL_EndBuildingLightmaps
=======================
*/
void GL_EndBuildingLightmaps (void)
{
LM_UploadBlock( false );
GL_EnableMultitexture( false );
}