spingle/source/gl_sky.c

1032 lines
22 KiB
C
Raw Normal View History

2019-11-24 20:45:15 -08:00
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//gl_sky.c
2019-12-02 07:07:37 -08:00
#include "q_defs.h"
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
#define MAX_CLIP_VERTS 64
2019-11-24 20:45:15 -08:00
float Fog_GetDensity(void);
float *Fog_GetColor(void);
2019-11-25 17:40:18 -08:00
extern qmodel_t *loadmodel;
extern int32_t rs_skypolys; //for r_speeds readout
extern int32_t rs_skypasses; //for r_speeds readout
float skyflatcolor[3];
float skymins[2][6], skymaxs[2][6];
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
char skybox_name[32] = ""; //name of current skybox, or "" if no skybox
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
gltexture_t *skybox_textures[6];
gltexture_t *solidskytexture, *alphaskytexture;
2019-11-24 20:45:15 -08:00
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};
2019-11-25 17:40:18 -08:00
cvar_t r_skyfog = {"r_skyfog", "0.5", CVAR_NONE};
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
int32_t skytexorder[6] = {0, 2, 1, 3, 4, 5}; //for skybox
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
vec3_t skyclip[6] =
{
{1, 1, 0},
{1, -1, 0},
{0, -1, 1},
{0, 1, 1},
{1, 0, 1},
{-1, 0, 1}
2019-11-24 20:45:15 -08:00
};
2019-11-25 17:40:18 -08:00
int32_t st_to_vec[6][3] =
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
{3, -1, 2},
{-3, 1, 2},
{1, 3, 2},
{-1, -3, 2},
{-2, -1, 3}, // straight up
{2, -1, -3} // straight down
2019-11-24 20:45:15 -08:00
};
2019-11-25 17:40:18 -08:00
int32_t vec_to_st[6][3] =
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
{-2, 3, 1},
{2, 3, -1},
{1, 3, 2},
{-1, 3, -2},
{-2, -1, 3},
{-2, 1, -3}
2019-11-24 20:45:15 -08:00
};
2019-11-25 17:40:18 -08:00
float skyfog; // ericw
2019-11-24 20:45:15 -08:00
//==============================================================================
//
// INIT
//
//==============================================================================
/*
=============
Sky_LoadTexture
A sky texture is 256*128, with the left side being a masked overlay
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_LoadTexture(texture_t *mt)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
char texturename[64];
int32_t 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;
2019-11-24 20:45:15 -08:00
src = (byte *)mt + mt->offsets[0];
// extract back layer and upload
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 128 ; i++)
for(j = 0 ; j < 128 ; j++)
back_data[(i * 128) + j] = src[i * 256 + j + 128];
2019-11-24 20:45:15 -08:00
q_snprintf(texturename, sizeof(texturename), "%s:%s_back", loadmodel->name, mt->name);
2019-11-25 17:40:18 -08:00
solidskytexture = TexMgr_LoadImage(loadmodel, texturename, 128, 128, SRC_INDEXED, back_data, "", (src_offset_t)back_data, TEXPREF_NONE);
2019-11-24 20:45:15 -08:00
// extract front layer and upload
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 128 ; i++)
for(j = 0 ; j < 128 ; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
front_data[(i * 128) + j] = src[i * 256 + j];
if(front_data[(i * 128) + j] == 0)
front_data[(i * 128) + j] = 255;
2019-11-24 20:45:15 -08:00
}
q_snprintf(texturename, sizeof(texturename), "%s:%s_front", loadmodel->name, mt->name);
2019-11-25 17:40:18 -08:00
alphaskytexture = TexMgr_LoadImage(loadmodel, texturename, 128, 128, SRC_INDEXED, front_data, "", (src_offset_t)front_data, TEXPREF_ALPHA);
2019-11-24 20:45:15 -08:00
// calculate r_fastsky color based on average of all opaque foreground colors
r = g = b = count = 0;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 128 ; i++)
for(j = 0 ; j < 128 ; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
p = src[i * 256 + j];
if(p != 0)
2019-11-24 20:45:15 -08:00
{
rgba = &d_8to24table[p];
r += ((byte *)rgba)[0];
g += ((byte *)rgba)[1];
b += ((byte *)rgba)[2];
count++;
}
}
2019-11-25 17:40:18 -08:00
skyflatcolor[0] = (float)r / (count * 255);
skyflatcolor[1] = (float)g / (count * 255);
skyflatcolor[2] = (float)b / (count * 255);
2019-11-24 20:45:15 -08:00
}
/*
==================
Sky_LoadSkyBox
==================
*/
2019-11-25 17:40:18 -08:00
const char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
void Sky_LoadSkyBox(const char *name)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, mark, width, height;
char filename[MAX_OSPATH];
byte *data;
2019-11-25 15:28:38 -08:00
bool nonefound = true;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(strcmp(skybox_name, name) == 0)
2019-11-24 20:45:15 -08:00
return; //no change
//purge old textures
2019-11-25 17:40:18 -08:00
for(i = 0; i < 6; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(skybox_textures[i] && skybox_textures[i] != notexture)
TexMgr_FreeTexture(skybox_textures[i]);
2019-11-24 20:45:15 -08:00
skybox_textures[i] = NULL;
}
//turn off skybox if sky is set to ""
2019-11-25 17:40:18 -08:00
if(name[0] == 0)
2019-11-24 20:45:15 -08:00
{
skybox_name[0] = 0;
return;
}
//load textures
2019-11-25 17:40:18 -08:00
for(i = 0; i < 6; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
mark = Hunk_LowMark();
q_snprintf(filename, sizeof(filename), "gfx/env/%s%s", name, suf[i]);
data = Image_LoadImage(filename, &width, &height);
if(data)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
skybox_textures[i] = TexMgr_LoadImage(cl.worldmodel, filename, width, height, SRC_RGBA, data, filename, 0, TEXPREF_NONE);
2019-11-24 20:45:15 -08:00
nonefound = false;
}
else
{
2019-11-25 17:40:18 -08:00
Con_Printf("Couldn't load %s\n", filename);
2019-11-24 20:45:15 -08:00
skybox_textures[i] = notexture;
}
2019-11-25 17:40:18 -08:00
Hunk_FreeToLowMark(mark);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
if(nonefound) // go back to scrolling sky if skybox is totally missing
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(i = 0; i < 6; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(skybox_textures[i] && skybox_textures[i] != notexture)
TexMgr_FreeTexture(skybox_textures[i]);
2019-11-24 20:45:15 -08:00
skybox_textures[i] = NULL;
}
skybox_name[0] = 0;
return;
}
strcpy(skybox_name, name);
}
/*
=================
Sky_NewMap
=================
*/
2019-11-25 17:40:18 -08:00
void Sky_NewMap(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
char key[128], value[4096];
const char *data;
int32_t i;
2019-11-24 20:45:15 -08:00
//
// initially no sky
//
skybox_name[0] = 0;
2019-11-25 17:40:18 -08:00
for(i = 0; i < 6; i++)
2019-11-24 20:45:15 -08:00
skybox_textures[i] = NULL;
skyfog = r_skyfog.value;
//
// read worldspawn (this is so ugly, and shouldn't it be done on the server?)
//
data = cl.worldmodel->entities;
2019-11-25 17:40:18 -08:00
if(!data)
2019-11-24 20:45:15 -08:00
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);
2019-11-25 17:40:18 -08:00
if(!data) //should never happen
2019-11-24 20:45:15 -08:00
return; // error
2019-11-25 17:40:18 -08:00
if(com_token[0] != '{') //should never happen
2019-11-24 20:45:15 -08:00
return; // error
2019-11-25 17:40:18 -08:00
while(1)
2019-11-24 20:45:15 -08:00
{
data = COM_Parse(data);
2019-11-25 17:40:18 -08:00
if(!data)
2019-11-24 20:45:15 -08:00
return; // error
2019-11-25 17:40:18 -08:00
if(com_token[0] == '}')
2019-11-24 20:45:15 -08:00
break; // end of worldspawn
2019-11-25 17:40:18 -08:00
if(com_token[0] == '_')
2019-11-24 20:45:15 -08:00
strcpy(key, com_token + 1);
else
strcpy(key, com_token);
2019-11-25 17:40:18 -08:00
while(key[strlen(key) - 1] == ' ') // remove trailing spaces
key[strlen(key) - 1] = 0;
2019-11-24 20:45:15 -08:00
data = COM_Parse(data);
2019-11-25 17:40:18 -08:00
if(!data)
2019-11-24 20:45:15 -08:00
return; // error
strcpy(value, com_token);
2019-11-25 17:40:18 -08:00
if(!strcmp("sky", key))
2019-11-24 20:45:15 -08:00
Sky_LoadSkyBox(value);
2019-11-25 17:40:18 -08:00
if(!strcmp("skyfog", key))
2019-11-24 20:45:15 -08:00
skyfog = atof(value);
#if 1 //also accept non-standard keys
2019-11-25 17:40:18 -08:00
else if(!strcmp("skyname", key)) //half-life
2019-11-24 20:45:15 -08:00
Sky_LoadSkyBox(value);
2019-11-25 17:40:18 -08:00
else if(!strcmp("qlsky", key)) //quake lives
2019-11-24 20:45:15 -08:00
Sky_LoadSkyBox(value);
#endif
}
}
/*
=================
Sky_SkyCommand_f
=================
*/
2019-11-25 17:40:18 -08:00
void Sky_SkyCommand_f(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
switch(Cmd_Argc())
2019-11-24 20:45:15 -08:00
{
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");
}
}
/*
====================
R_SetSkyfog_f -- ericw
====================
*/
2019-11-25 17:40:18 -08:00
static void R_SetSkyfog_f(cvar_t *var)
2019-11-24 20:45:15 -08:00
{
// clear any skyfog setting from worldspawn
skyfog = var->value;
}
/*
=============
Sky_Init
=============
*/
2019-11-25 17:40:18 -08:00
void Sky_Init(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
Cvar_RegisterVariable(&r_fastsky);
Cvar_RegisterVariable(&r_sky_quality);
Cvar_RegisterVariable(&r_skyalpha);
Cvar_RegisterVariable(&r_skyfog);
Cvar_SetCallback(&r_skyfog, R_SetSkyfog_f);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
Cmd_AddCommand("sky", Sky_SkyCommand_f);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0; i < 6; i++)
2019-11-24 20:45:15 -08:00
skybox_textures[i] = NULL;
}
//==============================================================================
//
// PROCESS SKY SURFS
//
//==============================================================================
/*
=================
Sky_ProjectPoly
update sky bounds
=================
*/
2019-11-25 17:40:18 -08:00
void Sky_ProjectPoly(int32_t nump, vec3_t vecs)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i, j;
vec3_t v, av;
float s, t, dv;
int32_t axis;
float *vp;
2019-11-24 20:45:15 -08:00
// decide which face it maps to
2019-11-25 17:40:18 -08:00
VectorCopy(vec3_origin, v);
for(i = 0, vp = vecs ; i < nump ; i++, vp += 3)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
VectorAdd(vp, v, v);
2019-11-24 20:45:15 -08:00
}
av[0] = fabs(v[0]);
av[1] = fabs(v[1]);
av[2] = fabs(v[2]);
2019-11-25 17:40:18 -08:00
if(av[0] > av[1] && av[0] > av[2])
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(v[0] < 0)
2019-11-24 20:45:15 -08:00
axis = 1;
else
axis = 0;
}
2019-11-25 17:40:18 -08:00
else if(av[1] > av[2] && av[1] > av[0])
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(v[1] < 0)
2019-11-24 20:45:15 -08:00
axis = 3;
else
axis = 2;
}
else
{
2019-11-25 17:40:18 -08:00
if(v[2] < 0)
2019-11-24 20:45:15 -08:00
axis = 5;
else
axis = 4;
}
// project new texture coords
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < nump ; i++, vecs += 3)
2019-11-24 20:45:15 -08:00
{
j = vec_to_st[axis][2];
2019-11-25 17:40:18 -08:00
if(j > 0)
2019-11-24 20:45:15 -08:00
dv = vecs[j - 1];
else
dv = -vecs[-j - 1];
j = vec_to_st[axis][0];
2019-11-25 17:40:18 -08:00
if(j < 0)
s = -vecs[-j - 1] / dv;
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
s = vecs[j - 1] / dv;
2019-11-24 20:45:15 -08:00
j = vec_to_st[axis][1];
2019-11-25 17:40:18 -08:00
if(j < 0)
t = -vecs[-j - 1] / dv;
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
t = vecs[j - 1] / dv;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(s < skymins[0][axis])
2019-11-24 20:45:15 -08:00
skymins[0][axis] = s;
2019-11-25 17:40:18 -08:00
if(t < skymins[1][axis])
2019-11-24 20:45:15 -08:00
skymins[1][axis] = t;
2019-11-25 17:40:18 -08:00
if(s > skymaxs[0][axis])
2019-11-24 20:45:15 -08:00
skymaxs[0][axis] = s;
2019-11-25 17:40:18 -08:00
if(t > skymaxs[1][axis])
2019-11-24 20:45:15 -08:00
skymaxs[1][axis] = t;
}
}
/*
=================
Sky_ClipPoly
=================
*/
2019-11-25 17:40:18 -08:00
void Sky_ClipPoly(int32_t nump, vec3_t vecs, int32_t stage)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float *norm;
float *v;
bool front, back;
float d, e;
float dists[MAX_CLIP_VERTS];
int32_t sides[MAX_CLIP_VERTS];
vec3_t newv[2][MAX_CLIP_VERTS];
int32_t newc[2];
int32_t i, j;
if(nump > MAX_CLIP_VERTS - 2)
Sys_Error("Sky_ClipPoly: MAX_CLIP_VERTS");
if(stage == 6) // fully clipped
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Sky_ProjectPoly(nump, vecs);
2019-11-24 20:45:15 -08:00
return;
}
front = back = false;
norm = skyclip[stage];
2019-11-25 17:40:18 -08:00
for(i = 0, v = vecs ; i < nump ; i++, v += 3)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
d = DotProduct(v, norm);
if(d > ON_EPSILON)
2019-11-24 20:45:15 -08:00
{
front = true;
sides[i] = SIDE_FRONT;
}
2019-11-25 17:40:18 -08:00
else if(d < ON_EPSILON)
2019-11-24 20:45:15 -08:00
{
back = true;
sides[i] = SIDE_BACK;
}
else
sides[i] = SIDE_ON;
dists[i] = d;
}
2019-11-25 17:40:18 -08:00
if(!front || !back)
{
// not clipped
Sky_ClipPoly(nump, vecs, stage + 1);
2019-11-24 20:45:15 -08:00
return;
}
// clip it
sides[i] = sides[0];
dists[i] = dists[0];
2019-11-25 17:40:18 -08:00
VectorCopy(vecs, (vecs + (i * 3)));
2019-11-24 20:45:15 -08:00
newc[0] = newc[1] = 0;
2019-11-25 17:40:18 -08:00
for(i = 0, v = vecs ; i < nump ; i++, v += 3)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
switch(sides[i])
2019-11-24 20:45:15 -08:00
{
case SIDE_FRONT:
2019-11-25 17:40:18 -08:00
VectorCopy(v, newv[0][newc[0]]);
2019-11-24 20:45:15 -08:00
newc[0]++;
break;
case SIDE_BACK:
2019-11-25 17:40:18 -08:00
VectorCopy(v, newv[1][newc[1]]);
2019-11-24 20:45:15 -08:00
newc[1]++;
break;
case SIDE_ON:
2019-11-25 17:40:18 -08:00
VectorCopy(v, newv[0][newc[0]]);
2019-11-24 20:45:15 -08:00
newc[0]++;
2019-11-25 17:40:18 -08:00
VectorCopy(v, newv[1][newc[1]]);
2019-11-24 20:45:15 -08:00
newc[1]++;
break;
}
2019-11-25 17:40:18 -08:00
if(sides[i] == SIDE_ON || sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
d = dists[i] / (dists[i] - dists[i + 1]);
for(j = 0 ; j < 3 ; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
e = v[j] + d * (v[j + 3] - v[j]);
2019-11-24 20:45:15 -08:00
newv[0][newc[0]][j] = e;
newv[1][newc[1]][j] = e;
}
newc[0]++;
newc[1]++;
}
// continue
2019-11-25 17:40:18 -08:00
Sky_ClipPoly(newc[0], newv[0][0], stage + 1);
Sky_ClipPoly(newc[1], newv[1][0], stage + 1);
2019-11-24 20:45:15 -08:00
}
/*
================
Sky_ProcessPoly
================
*/
2019-11-25 17:40:18 -08:00
void Sky_ProcessPoly(glpoly_t *p)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
vec3_t verts[MAX_CLIP_VERTS];
2019-11-24 20:45:15 -08:00
//draw it
DrawGLPoly(p);
rs_brushpasses++;
//update sky bounds
2019-11-25 17:40:18 -08:00
if(!r_fastsky.value)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < p->numverts ; i++)
VectorSubtract(p->verts[i], r_origin, verts[i]);
Sky_ClipPoly(p->numverts, verts[0], 0);
2019-11-24 20:45:15 -08:00
}
}
/*
================
Sky_ProcessTextureChains -- handles sky polys in world model
================
*/
2019-11-25 17:40:18 -08:00
void Sky_ProcessTextureChains(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
msurface_t *s;
texture_t *t;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(!r_drawworld_cheatsafe)
2019-11-24 20:45:15 -08:00
return;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < cl.worldmodel->numtextures ; i++)
2019-11-24 20:45:15 -08:00
{
t = cl.worldmodel->textures[i];
2019-11-25 17:40:18 -08:00
if(!t || !t->texturechains[chain_world] || !(t->texturechains[chain_world]->flags & SURF_DRAWSKY))
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
for(s = t->texturechains[chain_world]; s; s = s->texturechain)
if(!s->culled)
Sky_ProcessPoly(s->polys);
2019-11-24 20:45:15 -08:00
}
}
/*
================
Sky_ProcessEntities -- handles sky polys on brush models
================
*/
2019-11-25 17:40:18 -08:00
void Sky_ProcessEntities(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
entity_t *e;
msurface_t *s;
glpoly_t *p;
int32_t i, j, k, mark;
float dot;
bool rotated;
vec3_t temp, forward, right, up;
if(!r_drawentities.value)
2019-11-24 20:45:15 -08:00
return;
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < cl_numvisedicts ; i++)
2019-11-24 20:45:15 -08:00
{
e = cl_visedicts[i];
2019-11-25 17:40:18 -08:00
if(e->model->type != mod_brush)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
if(R_CullModelForEntity(e))
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
if(e->alpha == ENTALPHA_ZERO)
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
VectorSubtract(r_refdef.vieworg, e->origin, modelorg);
if(e->angles[0] || e->angles[1] || e->angles[2])
2019-11-24 20:45:15 -08:00
{
rotated = true;
2019-11-25 17:40:18 -08:00
AngleVectors(e->angles, forward, right, up);
VectorCopy(modelorg, temp);
modelorg[0] = DotProduct(temp, forward);
modelorg[1] = -DotProduct(temp, right);
modelorg[2] = DotProduct(temp, up);
2019-11-24 20:45:15 -08:00
}
else
rotated = false;
s = &e->model->surfaces[e->model->firstmodelsurface];
2019-11-25 17:40:18 -08:00
for(j = 0 ; j < e->model->nummodelsurfaces ; j++, s++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(s->flags & SURF_DRAWSKY)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
dot = DotProduct(modelorg, s->plane->normal) - s->plane->dist;
if(((s->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(s->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
2019-11-24 20:45:15 -08:00
{
//copy the polygon and translate manually, since Sky_ProcessPoly needs it to be in world space
mark = Hunk_LowMark();
2019-11-25 17:40:18 -08:00
p = (glpoly_t *) Hunk_Alloc(sizeof(*s->polys)); //FIXME: don't allocate for each poly
2019-11-24 20:45:15 -08:00
p->numverts = s->polys->numverts;
2019-11-25 17:40:18 -08:00
for(k = 0; k < p->numverts; k++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(rotated)
2019-11-24 20:45:15 -08:00
{
p->verts[k][0] = e->origin[0] + s->polys->verts[k][0] * forward[0]
2019-11-25 17:40:18 -08:00
- s->polys->verts[k][1] * right[0]
+ s->polys->verts[k][2] * up[0];
2019-11-24 20:45:15 -08:00
p->verts[k][1] = e->origin[1] + s->polys->verts[k][0] * forward[1]
2019-11-25 17:40:18 -08:00
- s->polys->verts[k][1] * right[1]
+ s->polys->verts[k][2] * up[1];
2019-11-24 20:45:15 -08:00
p->verts[k][2] = e->origin[2] + s->polys->verts[k][0] * forward[2]
2019-11-25 17:40:18 -08:00
- s->polys->verts[k][1] * right[2]
+ s->polys->verts[k][2] * up[2];
2019-11-24 20:45:15 -08:00
}
else
VectorAdd(s->polys->verts[k], e->origin, p->verts[k]);
}
2019-11-25 17:40:18 -08:00
Sky_ProcessPoly(p);
Hunk_FreeToLowMark(mark);
2019-11-24 20:45:15 -08:00
}
}
}
}
}
//==============================================================================
//
// RENDER SKYBOX
//
//==============================================================================
/*
==============
Sky_EmitSkyBoxVertex
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_EmitSkyBoxVertex(float s, float t, int32_t axis)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
vec3_t v, b;
int32_t j, k;
float w, h;
2019-11-24 20:45:15 -08:00
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);
2019-11-25 17:40:18 -08:00
for(j = 0 ; j < 3 ; j++)
2019-11-24 20:45:15 -08:00
{
k = st_to_vec[axis][j];
2019-11-25 17:40:18 -08:00
if(k < 0)
2019-11-24 20:45:15 -08:00
v[j] = -b[-k - 1];
else
v[j] = b[k - 1];
v[j] += r_origin[j];
}
// convert from range [-1,1] to [0,1]
2019-11-25 17:40:18 -08:00
s = (s + 1) * 0.5;
t = (t + 1) * 0.5;
2019-11-24 20:45:15 -08:00
// avoid bilerp seam
w = skybox_textures[skytexorder[axis]]->width;
h = skybox_textures[skytexorder[axis]]->height;
2019-11-25 17:40:18 -08:00
s = s * (w - 1) / w + 0.5 / w;
t = t * (h - 1) / h + 0.5 / h;
2019-11-24 20:45:15 -08:00
t = 1.0 - t;
2019-11-25 17:40:18 -08:00
glTexCoord2f(s, t);
glVertex3fv(v);
2019-11-24 20:45:15 -08:00
}
/*
==============
Sky_DrawSkyBox
FIXME: eliminate cracks by adding an extra vert on tjuncs
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_DrawSkyBox(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 6 ; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i])
2019-11-24 20:45:15 -08:00
continue;
2019-11-25 17:40:18 -08:00
GL_Bind(skybox_textures[skytexorder[i]]);
2019-11-24 20:45:15 -08:00
#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
2019-11-25 17:40:18 -08:00
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();
2019-11-24 20:45:15 -08:00
rs_skypolys++;
rs_skypasses++;
2019-11-25 17:40:18 -08:00
if(Fog_GetDensity() > 0 && skyfog > 0)
2019-11-24 20:45:15 -08:00
{
float *c;
c = Fog_GetColor();
2019-11-25 17:40:18 -08:00
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glColor4f(c[0], c[1], c[2], CLAMP(0.0, skyfog, 1.0));
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
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();
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glColor3f(1, 1, 1);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
2019-11-24 20:45:15 -08:00
rs_skypasses++;
}
}
}
//==============================================================================
//
// RENDER CLOUDS
//
//==============================================================================
/*
==============
Sky_SetBoxVert
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_SetBoxVert(float s, float t, int32_t axis, vec3_t v)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
vec3_t b;
int32_t j, k;
2019-11-24 20:45:15 -08:00
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);
2019-11-25 17:40:18 -08:00
for(j = 0 ; j < 3 ; j++)
2019-11-24 20:45:15 -08:00
{
k = st_to_vec[axis][j];
2019-11-25 17:40:18 -08:00
if(k < 0)
2019-11-24 20:45:15 -08:00
v[j] = -b[-k - 1];
else
v[j] = b[k - 1];
v[j] += r_origin[j];
}
}
/*
=============
Sky_GetTexCoord
=============
*/
2019-11-25 17:40:18 -08:00
void Sky_GetTexCoord(vec3_t v, float speed, float *s, float *t)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
vec3_t dir;
float length, scroll;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
VectorSubtract(v, r_origin, dir);
dir[2] *= 3; // flatten the sphere
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
length = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
length = sqrt(length);
length = 6 * 63 / length;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
scroll = cl.time * speed;
2019-11-25 16:49:58 -08:00
scroll -= (int32_t)scroll & ~127;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
*s = (scroll + dir[0] * length) * (1.0 / 128);
*t = (scroll + dir[1] * length) * (1.0 / 128);
2019-11-24 20:45:15 -08:00
}
/*
===============
Sky_DrawFaceQuad
===============
*/
2019-11-25 17:40:18 -08:00
void Sky_DrawFaceQuad(glpoly_t *p)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float s, t;
float *v;
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(gl_mtexable && r_skyalpha.value >= 1.0)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
GL_Bind(solidskytexture);
2019-11-24 20:45:15 -08:00
GL_EnableMultitexture();
2019-11-25 17:40:18 -08:00
GL_Bind(alphaskytexture);
2019-11-24 20:45:15 -08:00
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
2019-11-25 17:40:18 -08:00
glBegin(GL_QUADS);
for(i = 0, v = p->verts[0] ; i < 4 ; i++, v += VERTEXSIZE)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
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);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
glEnd();
2019-11-24 20:45:15 -08:00
GL_DisableMultitexture();
rs_skypolys++;
rs_skypasses++;
}
else
{
2019-11-25 17:40:18 -08:00
GL_Bind(solidskytexture);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(r_skyalpha.value < 1.0)
glColor3f(1, 1, 1);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glBegin(GL_QUADS);
for(i = 0, v = p->verts[0] ; i < 4 ; i++, v += VERTEXSIZE)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Sky_GetTexCoord(v, 8, &s, &t);
glTexCoord2f(s, t);
glVertex3fv(v);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
glEnd();
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
GL_Bind(alphaskytexture);
glEnable(GL_BLEND);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(r_skyalpha.value < 1.0)
glColor4f(1, 1, 1, r_skyalpha.value);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glBegin(GL_QUADS);
for(i = 0, v = p->verts[0] ; i < 4 ; i++, v += VERTEXSIZE)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
Sky_GetTexCoord(v, 16, &s, &t);
glTexCoord2f(s, t);
glVertex3fv(v);
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
glEnd();
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glDisable(GL_BLEND);
2019-11-24 20:45:15 -08:00
rs_skypolys++;
rs_skypasses += 2;
}
2019-11-25 17:40:18 -08:00
if(Fog_GetDensity() > 0 && skyfog > 0)
2019-11-24 20:45:15 -08:00
{
float *c;
c = Fog_GetColor();
2019-11-25 17:40:18 -08:00
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glColor4f(c[0], c[1], c[2], CLAMP(0.0, skyfog, 1.0));
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glBegin(GL_QUADS);
for(i = 0, v = p->verts[0] ; i < 4 ; i++, v += VERTEXSIZE)
glVertex3fv(v);
glEnd();
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
glColor3f(1, 1, 1);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
2019-11-24 20:45:15 -08:00
rs_skypasses++;
}
}
/*
==============
Sky_DrawFace
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_DrawFace(int32_t axis)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
glpoly_t *p;
vec3_t verts[4];
int32_t i, j, start;
float di, qi, dj, qj;
vec3_t vup, vright, temp, temp2;
2019-11-24 20:45:15 -08:00
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]);
2019-11-25 17:40:18 -08:00
start = Hunk_LowMark();
2019-11-24 20:45:15 -08:00
p = (glpoly_t *) Hunk_Alloc(sizeof(glpoly_t));
2019-11-25 17:40:18 -08:00
VectorSubtract(verts[2], verts[3], vup);
VectorSubtract(verts[2], verts[1], vright);
2019-11-24 20:45:15 -08:00
2019-11-25 16:49:58 -08:00
di = q_max((int32_t)r_sky_quality.value, 1);
2019-11-24 20:45:15 -08:00
qi = 1.0 / di;
2019-11-25 17:40:18 -08:00
dj = (axis < 4) ? di * 2 : di; //subdivide vertically more than horizontally on skybox sides
2019-11-24 20:45:15 -08:00
qj = 1.0 / dj;
2019-11-25 17:40:18 -08:00
for(i = 0; i < di; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
for(j = 0; j < dj; j++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
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)
2019-11-24 20:45:15 -08:00
continue;
//if (i&1 ^ j&1) continue; //checkerboard test
2019-11-25 17:40:18 -08:00
VectorScale(vright, qi * i, temp);
VectorScale(vup, qj * j, temp2);
VectorAdd(temp, temp2, temp);
VectorAdd(verts[0], temp, p->verts[0]);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
VectorScale(vup, qj, temp);
VectorAdd(p->verts[0], temp, p->verts[1]);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
VectorScale(vright, qi, temp);
VectorAdd(p->verts[1], temp, p->verts[2]);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
VectorAdd(p->verts[0], temp, p->verts[3]);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
Sky_DrawFaceQuad(p);
2019-11-24 20:45:15 -08:00
}
}
2019-11-25 17:40:18 -08:00
Hunk_FreeToLowMark(start);
2019-11-24 20:45:15 -08:00
}
/*
==============
Sky_DrawSkyLayers
draws the old-style scrolling cloud layers
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_DrawSkyLayers(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(r_skyalpha.value < 1.0)
2019-11-24 20:45:15 -08:00
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 6 ; i++)
if(skymins[0][i] < skymaxs[0][i] && skymins[1][i] < skymaxs[1][i])
Sky_DrawFace(i);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(r_skyalpha.value < 1.0)
2019-11-24 20:45:15 -08:00
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
/*
==============
Sky_DrawSky
called once per frame before drawing anything else
==============
*/
2019-11-25 17:40:18 -08:00
void Sky_DrawSky(void)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
//in these special render modes, the sky faces are handled in the normal world/brush renderer
2019-11-25 17:40:18 -08:00
if(r_drawflat_cheatsafe || r_lightmap_cheatsafe)
2019-11-24 20:45:15 -08:00
return;
//
// reset sky bounds
//
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 6 ; i++)
2019-11-24 20:45:15 -08:00
{
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
//
2019-11-25 17:40:18 -08:00
Fog_DisableGFog();
glDisable(GL_TEXTURE_2D);
if(Fog_GetDensity() > 0)
glColor3fv(Fog_GetColor());
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
glColor3fv(skyflatcolor);
Sky_ProcessTextureChains();
Sky_ProcessEntities();
glColor3f(1, 1, 1);
glEnable(GL_TEXTURE_2D);
2019-11-24 20:45:15 -08:00
//
// render slow sky: cloud layers or skybox
//
2019-11-25 17:40:18 -08:00
if(!r_fastsky.value && !(Fog_GetDensity() > 0 && skyfog >= 1))
2019-11-24 20:45:15 -08:00
{
glDepthFunc(GL_GEQUAL);
glDepthMask(0);
2019-11-25 17:40:18 -08:00
if(skybox_name[0])
Sky_DrawSkyBox();
2019-11-24 20:45:15 -08:00
else
Sky_DrawSkyLayers();
glDepthMask(1);
glDepthFunc(GL_LEQUAL);
}
2019-11-25 17:40:18 -08:00
Fog_EnableGFog();
2019-11-24 20:45:15 -08:00
}