quakespasm/Quake/gl_sky.c
Eric Wasylishen 9c6f93eb8a Remove R_DrawSequentialPoly and use texture chains for drawing brush models.
To make this work with the drawing order we use in gl_rmain (world excluding water, opaque ents, world water, transparent ents), I made each texture_t have two texture chains, one for world and one for bmodels. This way they can be cleared during entity drawing without having to regenerate the world ones.

Other than this the changes are mostly minor adjustments to the R_DrawTextureChains_* functions so they can work for things other than the world.

git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@979 af15c1b1-3010-417e-b628-4374ebc0bcbd
2014-08-29 08:27:22 +00:00

1010 lines
21 KiB
C

/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
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_sky.c
#include "quakedef.h"
#define MAX_CLIP_VERTS 64
float Fog_GetDensity(void);
float *Fog_GetColor(void);
extern qmodel_t *loadmodel;
extern int rs_skypolys; //for r_speeds readout
extern int rs_skypasses; //for r_speeds readout
float skyflatcolor[3];
float skymins[2][6], skymaxs[2][6];
char skybox_name[32] = ""; //name of current skybox, or "" if no skybox
gltexture_t *skybox_textures[6];
gltexture_t *solidskytexture, *alphaskytexture;
extern cvar_t gl_farclip;
cvar_t r_fastsky = {"r_fastsky", "0", CVAR_NONE};
cvar_t r_sky_quality = {"r_sky_quality", "12", CVAR_NONE};
cvar_t r_skyalpha = {"r_skyalpha", "1", CVAR_NONE};
cvar_t r_skyfog = {"r_skyfog","0.5",CVAR_NONE};
int skytexorder[6] = {0,2,1,3,4,5}; //for skybox
vec3_t skyclip[6] = {
{1,1,0},
{1,-1,0},
{0,-1,1},
{0,1,1},
{1,0,1},
{-1,0,1}
};
int st_to_vec[6][3] =
{
{3,-1,2},
{-3,1,2},
{1,3,2},
{-1,-3,2},
{-2,-1,3}, // straight up
{2,-1,-3} // straight down
};
int vec_to_st[6][3] =
{
{-2,3,1},
{2,3,-1},
{1,3,2},
{-1,3,-2},
{-2,-1,3},
{-2,1,-3}
};
//==============================================================================
//
// INIT
//
//==============================================================================
/*
=============
Sky_LoadTexture
A sky texture is 256*128, with the left side being a masked overlay
==============
*/
void Sky_LoadTexture (texture_t *mt)
{
char texturename[64];
int i, j, p, r, g, b, count;
byte *src;
static byte front_data[128*128]; //FIXME: Hunk_Alloc
static byte back_data[128*128]; //FIXME: Hunk_Alloc
unsigned *rgba;
src = (byte *)mt + mt->offsets[0];
// extract back layer and upload
for (i=0 ; i<128 ; i++)
for (j=0 ; j<128 ; j++)
back_data[(i*128) + j] = src[i*256 + j + 128];
q_snprintf(texturename, sizeof(texturename), "%s:%s_back", loadmodel->name, mt->name);
solidskytexture = TexMgr_LoadImage (loadmodel, texturename, 128, 128, SRC_INDEXED, back_data, "", (src_offset_t)back_data, TEXPREF_NONE);
// extract front layer and upload
for (i=0 ; i<128 ; i++)
for (j=0 ; j<128 ; j++)
{
front_data[(i*128) + j] = src[i*256 + j];
if (front_data[(i*128) + j] == 0)
front_data[(i*128) + j] = 255;
}
q_snprintf(texturename, sizeof(texturename), "%s:%s_front", loadmodel->name, mt->name);
alphaskytexture = TexMgr_LoadImage (loadmodel, texturename, 128, 128, SRC_INDEXED, front_data, "", (src_offset_t)front_data, TEXPREF_ALPHA);
// calculate r_fastsky color based on average of all opaque foreground colors
r = g = b = count = 0;
for (i=0 ; i<128 ; i++)
for (j=0 ; j<128 ; j++)
{
p = src[i*256 + j];
if (p != 0)
{
rgba = &d_8to24table[p];
r += ((byte *)rgba)[0];
g += ((byte *)rgba)[1];
b += ((byte *)rgba)[2];
count++;
}
}
skyflatcolor[0] = (float)r/(count*255);
skyflatcolor[1] = (float)g/(count*255);
skyflatcolor[2] = (float)b/(count*255);
}
/*
==================
Sky_LoadSkyBox
==================
*/
const char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
void Sky_LoadSkyBox (const char *name)
{
int i, mark, width, height;
char filename[MAX_OSPATH];
byte *data;
qboolean nonefound = true;
if (strcmp(skybox_name, name) == 0)
return; //no change
//purge old textures
for (i=0; i<6; i++)
{
if (skybox_textures[i] && skybox_textures[i] != notexture)
TexMgr_FreeTexture (skybox_textures[i]);
skybox_textures[i] = NULL;
}
//turn off skybox if sky is set to ""
if (name[0] == 0)
{
skybox_name[0] = 0;
return;
}
//load textures
for (i=0; i<6; i++)
{
mark = Hunk_LowMark ();
q_snprintf (filename, sizeof(filename), "gfx/env/%s%s", name, suf[i]);
data = Image_LoadImage (filename, &width, &height);
if (data)
{
skybox_textures[i] = TexMgr_LoadImage (cl.worldmodel, filename, width, height, SRC_RGBA, data, filename, 0, TEXPREF_NONE);
nonefound = false;
}
else
{
Con_Printf ("Couldn't load %s\n", filename);
skybox_textures[i] = notexture;
}
Hunk_FreeToLowMark (mark);
}
if (nonefound) // go back to scrolling sky if skybox is totally missing
{
for (i=0; i<6; i++)
{
if (skybox_textures[i] && skybox_textures[i] != notexture)
TexMgr_FreeTexture (skybox_textures[i]);
skybox_textures[i] = NULL;
}
skybox_name[0] = 0;
return;
}
strcpy(skybox_name, name);
}
/*
=================
Sky_NewMap
=================
*/
void Sky_NewMap (void)
{
char key[128], value[4096];
const char *data;
int i;
//
// initially no sky
//
skybox_name[0] = 0;
for (i=0; i<6; i++)
skybox_textures[i] = NULL;
//
// read worldspawn (this is so ugly, and shouldn't it be done on the server?)
//
data = cl.worldmodel->entities;
if (!data)
return; //FIXME: how could this possibly ever happen? -- if there's no
// worldspawn then the sever wouldn't send the loadmap message to the client
data = COM_Parse(data);
if (!data) //should never happen
return; // error
if (com_token[0] != '{') //should never happen
return; // error
while (1)
{
data = COM_Parse(data);
if (!data)
return; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
strcpy(key, com_token + 1);
else
strcpy(key, com_token);
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
if (!data)
return; // error
strcpy(value, com_token);
if (!strcmp("sky", key))
Sky_LoadSkyBox(value);
#if 1 //also accept non-standard keys
else if (!strcmp("skyname", key)) //half-life
Sky_LoadSkyBox(value);
else if (!strcmp("qlsky", key)) //quake lives
Sky_LoadSkyBox(value);
#endif
}
}
/*
=================
Sky_SkyCommand_f
=================
*/
void Sky_SkyCommand_f (void)
{
switch (Cmd_Argc())
{
case 1:
Con_Printf("\"sky\" is \"%s\"\n", skybox_name);
break;
case 2:
Sky_LoadSkyBox(Cmd_Argv(1));
break;
default:
Con_Printf("usage: sky <skyname>\n");
}
}
/*
=============
Sky_Init
=============
*/
void Sky_Init (void)
{
int i;
Cvar_RegisterVariable (&r_fastsky);
Cvar_RegisterVariable (&r_sky_quality);
Cvar_RegisterVariable (&r_skyalpha);
Cvar_RegisterVariable (&r_skyfog);
Cmd_AddCommand ("sky",Sky_SkyCommand_f);
for (i=0; i<6; i++)
skybox_textures[i] = NULL;
}
//==============================================================================
//
// PROCESS SKY SURFS
//
//==============================================================================
/*
=================
Sky_ProjectPoly
update sky bounds
=================
*/
void Sky_ProjectPoly (int nump, vec3_t vecs)
{
int i,j;
vec3_t v, av;
float s, t, dv;
int axis;
float *vp;
// decide which face it maps to
VectorCopy (vec3_origin, v);
for (i=0, vp=vecs ; i<nump ; i++, vp+=3)
{
VectorAdd (vp, v, v);
}
av[0] = fabs(v[0]);
av[1] = fabs(v[1]);
av[2] = fabs(v[2]);
if (av[0] > av[1] && av[0] > av[2])
{
if (v[0] < 0)
axis = 1;
else
axis = 0;
}
else if (av[1] > av[2] && av[1] > av[0])
{
if (v[1] < 0)
axis = 3;
else
axis = 2;
}
else
{
if (v[2] < 0)
axis = 5;
else
axis = 4;
}
// project new texture coords
for (i=0 ; i<nump ; i++, vecs+=3)
{
j = vec_to_st[axis][2];
if (j > 0)
dv = vecs[j - 1];
else
dv = -vecs[-j - 1];
j = vec_to_st[axis][0];
if (j < 0)
s = -vecs[-j -1] / dv;
else
s = vecs[j-1] / dv;
j = vec_to_st[axis][1];
if (j < 0)
t = -vecs[-j -1] / dv;
else
t = vecs[j-1] / dv;
if (s < skymins[0][axis])
skymins[0][axis] = s;
if (t < skymins[1][axis])
skymins[1][axis] = t;
if (s > skymaxs[0][axis])
skymaxs[0][axis] = s;
if (t > skymaxs[1][axis])
skymaxs[1][axis] = t;
}
}
/*
=================
Sky_ClipPoly
=================
*/
void Sky_ClipPoly (int nump, vec3_t vecs, int stage)
{
float *norm;
float *v;
qboolean front, back;
float d, e;
float dists[MAX_CLIP_VERTS];
int sides[MAX_CLIP_VERTS];
vec3_t newv[2][MAX_CLIP_VERTS];
int newc[2];
int i, j;
if (nump > MAX_CLIP_VERTS-2)
Sys_Error ("Sky_ClipPoly: MAX_CLIP_VERTS");
if (stage == 6) // fully clipped
{
Sky_ProjectPoly (nump, vecs);
return;
}
front = back = false;
norm = skyclip[stage];
for (i=0, v = vecs ; i<nump ; i++, v+=3)
{
d = DotProduct (v, norm);
if (d > ON_EPSILON)
{
front = true;
sides[i] = SIDE_FRONT;
}
else if (d < ON_EPSILON)
{
back = true;
sides[i] = SIDE_BACK;
}
else
sides[i] = SIDE_ON;
dists[i] = d;
}
if (!front || !back)
{ // not clipped
Sky_ClipPoly (nump, vecs, stage+1);
return;
}
// clip it
sides[i] = sides[0];
dists[i] = dists[0];
VectorCopy (vecs, (vecs+(i*3)) );
newc[0] = newc[1] = 0;
for (i=0, v = vecs ; i<nump ; i++, v+=3)
{
switch (sides[i])
{
case SIDE_FRONT:
VectorCopy (v, newv[0][newc[0]]);
newc[0]++;
break;
case SIDE_BACK:
VectorCopy (v, newv[1][newc[1]]);
newc[1]++;
break;
case SIDE_ON:
VectorCopy (v, newv[0][newc[0]]);
newc[0]++;
VectorCopy (v, newv[1][newc[1]]);
newc[1]++;
break;
}
if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
d = dists[i] / (dists[i] - dists[i+1]);
for (j=0 ; j<3 ; j++)
{
e = v[j] + d*(v[j+3] - v[j]);
newv[0][newc[0]][j] = e;
newv[1][newc[1]][j] = e;
}
newc[0]++;
newc[1]++;
}
// continue
Sky_ClipPoly (newc[0], newv[0][0], stage+1);
Sky_ClipPoly (newc[1], newv[1][0], stage+1);
}
/*
================
Sky_ProcessPoly
================
*/
void Sky_ProcessPoly (glpoly_t *p)
{
int i;
vec3_t verts[MAX_CLIP_VERTS];
//draw it
DrawGLPoly(p);
rs_brushpasses++;
//update sky bounds
if (!r_fastsky.value)
{
for (i=0 ; i<p->numverts ; i++)
VectorSubtract (p->verts[i], r_origin, verts[i]);
Sky_ClipPoly (p->numverts, verts[0], 0);
}
}
/*
================
Sky_ProcessTextureChains -- handles sky polys in world model
================
*/
void Sky_ProcessTextureChains (void)
{
int i;
msurface_t *s;
texture_t *t;
if (!r_drawworld_cheatsafe)
return;
for (i=0 ; i<cl.worldmodel->numtextures ; i++)
{
t = cl.worldmodel->textures[i];
if (!t || !t->texturechains[chain_world] || !(t->texturechains[chain_world]->flags & SURF_DRAWSKY))
continue;
for (s = t->texturechains[chain_world]; s; s = s->texturechain)
if (!s->culled)
Sky_ProcessPoly (s->polys);
}
}
/*
================
Sky_ProcessEntities -- handles sky polys on brush models
================
*/
void Sky_ProcessEntities (void)
{
entity_t *e;
msurface_t *s;
glpoly_t *p;
int i,j,k,mark;
float dot;
qboolean rotated;
vec3_t temp, forward, right, up;
if (!r_drawentities.value)
return;
for (i=0 ; i<cl_numvisedicts ; i++)
{
e = cl_visedicts[i];
if (e->model->type != mod_brush)
continue;
if (R_CullModelForEntity(e))
continue;
if (e->alpha == ENTALPHA_ZERO)
continue;
VectorSubtract (r_refdef.vieworg, e->origin, modelorg);
if (e->angles[0] || e->angles[1] || e->angles[2])
{
rotated = true;
AngleVectors (e->angles, forward, right, up);
VectorCopy (modelorg, temp);
modelorg[0] = DotProduct (temp, forward);
modelorg[1] = -DotProduct (temp, right);
modelorg[2] = DotProduct (temp, up);
}
else
rotated = false;
s = &e->model->surfaces[e->model->firstmodelsurface];
for (j=0 ; j<e->model->nummodelsurfaces ; j++, s++)
{
if (s->flags & SURF_DRAWSKY)
{
dot = DotProduct (modelorg, s->plane->normal) - s->plane->dist;
if (((s->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(s->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
{
//copy the polygon and translate manually, since Sky_ProcessPoly needs it to be in world space
mark = Hunk_LowMark();
p = (glpoly_t *) Hunk_Alloc (sizeof(*s->polys)); //FIXME: don't allocate for each poly
p->numverts = s->polys->numverts;
for (k=0; k<p->numverts; k++)
{
if (rotated)
{
p->verts[k][0] = e->origin[0] + s->polys->verts[k][0] * forward[0]
- s->polys->verts[k][1] * right[0]
+ s->polys->verts[k][2] * up[0];
p->verts[k][1] = e->origin[1] + s->polys->verts[k][0] * forward[1]
- s->polys->verts[k][1] * right[1]
+ s->polys->verts[k][2] * up[1];
p->verts[k][2] = e->origin[2] + s->polys->verts[k][0] * forward[2]
- s->polys->verts[k][1] * right[2]
+ s->polys->verts[k][2] * up[2];
}
else
VectorAdd(s->polys->verts[k], e->origin, p->verts[k]);
}
Sky_ProcessPoly (p);
Hunk_FreeToLowMark (mark);
}
}
}
}
}
//==============================================================================
//
// RENDER SKYBOX
//
//==============================================================================
/*
==============
Sky_EmitSkyBoxVertex
==============
*/
void Sky_EmitSkyBoxVertex (float s, float t, int axis)
{
vec3_t v, b;
int j, k;
float w, h;
b[0] = s * gl_farclip.value / sqrt(3.0);
b[1] = t * gl_farclip.value / sqrt(3.0);
b[2] = gl_farclip.value / sqrt(3.0);
for (j=0 ; j<3 ; j++)
{
k = st_to_vec[axis][j];
if (k < 0)
v[j] = -b[-k - 1];
else
v[j] = b[k - 1];
v[j] += r_origin[j];
}
// convert from range [-1,1] to [0,1]
s = (s+1)*0.5;
t = (t+1)*0.5;
// avoid bilerp seam
w = skybox_textures[skytexorder[axis]]->width;
h = skybox_textures[skytexorder[axis]]->height;
s = s * (w-1)/w + 0.5/w;
t = t * (h-1)/h + 0.5/h;
t = 1.0 - t;
glTexCoord2f (s, t);
glVertex3fv (v);
}
/*
==============
Sky_DrawSkyBox
FIXME: eliminate cracks by adding an extra vert on tjuncs
==============
*/
void Sky_DrawSkyBox (void)
{
int i;
for (i=0 ; i<6 ; i++)
{
if (skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i])
continue;
GL_Bind (skybox_textures[skytexorder[i]]);
#if 1 //FIXME: this is to avoid tjunctions until i can do it the right way
skymins[0][i] = -1;
skymins[1][i] = -1;
skymaxs[0][i] = 1;
skymaxs[1][i] = 1;
#endif
glBegin (GL_QUADS);
Sky_EmitSkyBoxVertex (skymins[0][i], skymins[1][i], i);
Sky_EmitSkyBoxVertex (skymins[0][i], skymaxs[1][i], i);
Sky_EmitSkyBoxVertex (skymaxs[0][i], skymaxs[1][i], i);
Sky_EmitSkyBoxVertex (skymaxs[0][i], skymins[1][i], i);
glEnd ();
rs_skypolys++;
rs_skypasses++;
if (Fog_GetDensity() > 0 && r_skyfog.value > 0)
{
float *c;
c = Fog_GetColor();
glEnable (GL_BLEND);
glDisable (GL_TEXTURE_2D);
glColor4f (c[0],c[1],c[2], CLAMP(0.0,r_skyfog.value,1.0));
glBegin (GL_QUADS);
Sky_EmitSkyBoxVertex (skymins[0][i], skymins[1][i], i);
Sky_EmitSkyBoxVertex (skymins[0][i], skymaxs[1][i], i);
Sky_EmitSkyBoxVertex (skymaxs[0][i], skymaxs[1][i], i);
Sky_EmitSkyBoxVertex (skymaxs[0][i], skymins[1][i], i);
glEnd ();
glColor3f (1, 1, 1);
glEnable (GL_TEXTURE_2D);
glDisable (GL_BLEND);
rs_skypasses++;
}
}
}
//==============================================================================
//
// RENDER CLOUDS
//
//==============================================================================
/*
==============
Sky_SetBoxVert
==============
*/
void Sky_SetBoxVert (float s, float t, int axis, vec3_t v)
{
vec3_t b;
int j, k;
b[0] = s * gl_farclip.value / sqrt(3.0);
b[1] = t * gl_farclip.value / sqrt(3.0);
b[2] = gl_farclip.value / sqrt(3.0);
for (j=0 ; j<3 ; j++)
{
k = st_to_vec[axis][j];
if (k < 0)
v[j] = -b[-k - 1];
else
v[j] = b[k - 1];
v[j] += r_origin[j];
}
}
/*
=============
Sky_GetTexCoord
=============
*/
void Sky_GetTexCoord (vec3_t v, float speed, float *s, float *t)
{
vec3_t dir;
float length, scroll;
VectorSubtract (v, r_origin, dir);
dir[2] *= 3; // flatten the sphere
length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2];
length = sqrt (length);
length = 6*63/length;
scroll = cl.time*speed;
scroll -= (int)scroll & ~127;
*s = (scroll + dir[0] * length) * (1.0/128);
*t = (scroll + dir[1] * length) * (1.0/128);
}
/*
===============
Sky_DrawFaceQuad
===============
*/
void Sky_DrawFaceQuad (glpoly_t *p)
{
float s, t;
float *v;
int i;
if (gl_mtexable && r_skyalpha.value >= 1.0)
{
GL_Bind (solidskytexture);
GL_EnableMultitexture();
GL_Bind (alphaskytexture);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glBegin (GL_QUADS);
for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE)
{
Sky_GetTexCoord (v, 8, &s, &t);
GL_MTexCoord2fFunc (TEXTURE0, s, t);
Sky_GetTexCoord (v, 16, &s, &t);
GL_MTexCoord2fFunc (TEXTURE1, s, t);
glVertex3fv (v);
}
glEnd ();
GL_DisableMultitexture();
rs_skypolys++;
rs_skypasses++;
}
else
{
GL_Bind (solidskytexture);
if (r_skyalpha.value < 1.0)
glColor3f (1, 1, 1);
glBegin (GL_QUADS);
for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE)
{
Sky_GetTexCoord (v, 8, &s, &t);
glTexCoord2f (s, t);
glVertex3fv (v);
}
glEnd ();
GL_Bind (alphaskytexture);
glEnable (GL_BLEND);
if (r_skyalpha.value < 1.0)
glColor4f (1, 1, 1, r_skyalpha.value);
glBegin (GL_QUADS);
for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE)
{
Sky_GetTexCoord (v, 16, &s, &t);
glTexCoord2f (s, t);
glVertex3fv (v);
}
glEnd ();
glDisable (GL_BLEND);
rs_skypolys++;
rs_skypasses += 2;
}
if (Fog_GetDensity() > 0 && r_skyfog.value > 0)
{
float *c;
c = Fog_GetColor();
glEnable (GL_BLEND);
glDisable (GL_TEXTURE_2D);
glColor4f (c[0],c[1],c[2], CLAMP(0.0,r_skyfog.value,1.0));
glBegin (GL_QUADS);
for (i=0, v=p->verts[0] ; i<4 ; i++, v+=VERTEXSIZE)
glVertex3fv (v);
glEnd ();
glColor3f (1, 1, 1);
glEnable (GL_TEXTURE_2D);
glDisable (GL_BLEND);
rs_skypasses++;
}
}
/*
==============
Sky_DrawFace
==============
*/
void Sky_DrawFace (int axis)
{
glpoly_t *p;
vec3_t verts[4];
int i, j, start;
float di,qi,dj,qj;
vec3_t vup, vright, temp, temp2;
Sky_SetBoxVert(-1.0, -1.0, axis, verts[0]);
Sky_SetBoxVert(-1.0, 1.0, axis, verts[1]);
Sky_SetBoxVert(1.0, 1.0, axis, verts[2]);
Sky_SetBoxVert(1.0, -1.0, axis, verts[3]);
start = Hunk_LowMark ();
p = (glpoly_t *) Hunk_Alloc(sizeof(glpoly_t));
VectorSubtract(verts[2],verts[3],vup);
VectorSubtract(verts[2],verts[1],vright);
di = q_max((int)r_sky_quality.value, 1);
qi = 1.0 / di;
dj = (axis < 4) ? di*2 : di; //subdivide vertically more than horizontally on skybox sides
qj = 1.0 / dj;
for (i=0; i<di; i++)
{
for (j=0; j<dj; j++)
{
if (i*qi < skymins[0][axis]/2+0.5 - qi || i*qi > skymaxs[0][axis]/2+0.5 ||
j*qj < skymins[1][axis]/2+0.5 - qj || j*qj > skymaxs[1][axis]/2+0.5)
continue;
//if (i&1 ^ j&1) continue; //checkerboard test
VectorScale (vright, qi*i, temp);
VectorScale (vup, qj*j, temp2);
VectorAdd(temp,temp2,temp);
VectorAdd(verts[0],temp,p->verts[0]);
VectorScale (vup, qj, temp);
VectorAdd (p->verts[0],temp,p->verts[1]);
VectorScale (vright, qi, temp);
VectorAdd (p->verts[1],temp,p->verts[2]);
VectorAdd (p->verts[0],temp,p->verts[3]);
Sky_DrawFaceQuad (p);
}
}
Hunk_FreeToLowMark (start);
}
/*
==============
Sky_DrawSkyLayers
draws the old-style scrolling cloud layers
==============
*/
void Sky_DrawSkyLayers (void)
{
int i;
if (r_skyalpha.value < 1.0)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
for (i=0 ; i<6 ; i++)
if (skymins[0][i] < skymaxs[0][i] && skymins[1][i] < skymaxs[1][i])
Sky_DrawFace (i);
if (r_skyalpha.value < 1.0)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
/*
==============
Sky_DrawSky
called once per frame before drawing anything else
==============
*/
void Sky_DrawSky (void)
{
int i;
//in these special render modes, the sky faces are handled in the normal world/brush renderer
if (r_drawflat_cheatsafe || r_lightmap_cheatsafe )
return;
//
// reset sky bounds
//
for (i=0 ; i<6 ; i++)
{
skymins[0][i] = skymins[1][i] = 9999;
skymaxs[0][i] = skymaxs[1][i] = -9999;
}
//
// process world and bmodels: draw flat-shaded sky surfs, and update skybounds
//
Fog_DisableGFog ();
glDisable (GL_TEXTURE_2D);
if (Fog_GetDensity() > 0)
glColor3fv (Fog_GetColor());
else
glColor3fv (skyflatcolor);
Sky_ProcessTextureChains ();
Sky_ProcessEntities ();
glColor3f (1, 1, 1);
glEnable (GL_TEXTURE_2D);
//
// render slow sky: cloud layers or skybox
//
if (!r_fastsky.value && !(Fog_GetDensity() > 0 && r_skyfog.value >= 1))
{
glDepthFunc(GL_GEQUAL);
glDepthMask(0);
if (skybox_name[0])
Sky_DrawSkyBox ();
else
Sky_DrawSkyLayers();
glDepthMask(1);
glDepthFunc(GL_LEQUAL);
}
Fog_EnableGFog ();
}