mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-14 00:50:38 +00:00
b9032b7815
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1059 af15c1b1-3010-417e-b628-4374ebc0bcbd
1011 lines
21 KiB
C
1011 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
|
|
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.
|
|
|
|
*/
|
|
//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 (GL_TEXTURE0_ARB, s, t);
|
|
Sky_GetTexCoord (v, 16, &s, &t);
|
|
GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, 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 ();
|
|
}
|