thirtyflightsofloving/renderer/r_sky.c

448 lines
8.8 KiB
C
Raw Normal View History

2019-03-13 19:20:07 +00:00
/*
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.
*/
// r_sky.c -- sky rendering
// Moved from r_warp.c
#include "r_local.h"
char skyname[MAX_QPATH];
float skyrotate;
vec3_t skyaxis;
image_t *sky_images[6];
vec3_t skyclip[6] = {
{1,1,0},
{1,-1,0},
{0,-1,1},
{0,1,1},
{1,0,1},
{-1,0,1}
};
int c_sky;
// 1 = s, 2 = t, 3 = 2048
int st_to_vec[6][3] =
{
{3,-1,2},
{-3,1,2},
{1,3,2},
{-1,-3,2},
{-2,-1,3}, // 0 degrees yaw, look straight up
{2,-1,-3} // look straight down
// {-1,2,3},
// {1,2,-3}
};
// s = [0]/[2], t = [1]/[2]
int vec_to_st[6][3] =
{
{-2,3,1},
{2,3,-1},
{1,3,2},
{-1,3,-2},
{-2,-1,3},
{-2,1,-3}
// {-1,2,3},
// {1,2,-3}
};
float skymins[2][6], skymaxs[2][6];
float sky_min, sky_max;
void DrawSkyPolygon (int nump, vec3_t vecs)
{
int i,j;
vec3_t v, av;
float s, t, dv;
int axis;
float *vp;
c_sky++;
// 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];
if (dv < 0.001)
continue; // don't divide by zero
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;
}
}
#define ON_EPSILON 0.1 // point on plane side epsilon
#define MAX_CLIP_VERTS 64
void ClipSkyPolygon (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)
VID_Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS");
if (stage == 6)
{ // fully clipped, so draw it
DrawSkyPolygon (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
ClipSkyPolygon (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
ClipSkyPolygon (newc[0], newv[0][0], stage+1);
ClipSkyPolygon (newc[1], newv[1][0], stage+1);
}
/*
=================
R_AddSkySurface
=================
*/
void R_AddSkySurface (msurface_t *fa)
{
int i;
vec3_t verts[MAX_CLIP_VERTS];
glpoly_t *p;
// calculate vertex values for sky box
for (p=fa->polys ; p ; p=p->next)
{
for (i=0 ; i<p->numverts ; i++)
{
VectorSubtract (p->verts[i], r_origin, verts[i]);
}
ClipSkyPolygon (p->numverts, verts[0], 0);
}
}
/*
==============
R_ClearSkyBox
==============
*/
void R_ClearSkyBox (void)
{
int i;
for (i=0 ; i<6 ; i++)
{
skymins[0][i] = skymins[1][i] = 9999;
skymaxs[0][i] = skymaxs[1][i] = -9999;
}
}
void MakeSkyVec (float s, float t, int axis)
{
vec3_t v, b;
int j, k;
//Knightmare- 12/26/2001- variable back clipping plane distance
b[0] = s * r_skydistance->value;
b[1] = t * r_skydistance->value;
b[2] = r_skydistance->value;
//end Knightmare
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];
}
// avoid bilerp seam
s = (s+1)*0.5;
t = (t+1)*0.5;
if (s < sky_min)
s = sky_min;
else if (s > sky_max)
s = sky_max;
if (t < sky_min)
t = sky_min;
else if (t > sky_max)
t = sky_max;
t = 1.0 - t;
VA_SetElem2(texCoordArray[0][rb_vertex], s, t);
VA_SetElem3(vertexArray[rb_vertex], v[0], v[1], v[2]);
VA_SetElem4(colorArray[rb_vertex], 1.0f, 1.0f, 1.0f, 1.0f);
rb_vertex++;
}
/*
==============
R_DrawSkyBox
==============
*/
int skytexorder[6] = {0,2,1,3,4,5};
void R_DrawSkyBox (void)
{
int i;
if (skyrotate)
{ // check for no sky at all
for (i=0 ; i<6 ; i++)
if (skymins[0][i] < skymaxs[0][i]
&& skymins[1][i] < skymaxs[1][i])
break;
if (i == 6)
return; // nothing visible
}
R_SetSkyFog (true); // set sky distance fog
qglPushMatrix ();
qglTranslatef (r_origin[0], r_origin[1], r_origin[2]);
qglRotatef (r_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]);
for (i=0; i<6; i++)
{
if (skyrotate)
{ // hack, forces full sky to draw when rotating
skymins[0][i] = -1;
skymins[1][i] = -1;
skymaxs[0][i] = 1;
skymaxs[1][i] = 1;
}
if (skymins[0][i] >= skymaxs[0][i]
|| skymins[1][i] >= skymaxs[1][i])
continue;
GL_Bind (sky_images[skytexorder[i]]->texnum);
rb_vertex = rb_index = 0;
indexArray[rb_index++] = rb_vertex+0;
indexArray[rb_index++] = rb_vertex+1;
indexArray[rb_index++] = rb_vertex+2;
indexArray[rb_index++] = rb_vertex+0;
indexArray[rb_index++] = rb_vertex+2;
indexArray[rb_index++] = rb_vertex+3;
MakeSkyVec (skymins[0][i], skymins[1][i], i);
MakeSkyVec (skymins[0][i], skymaxs[1][i], i);
MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i);
MakeSkyVec (skymaxs[0][i], skymins[1][i], i);
RB_RenderMeshGeneric (true);
}
qglPopMatrix ();
R_SetSkyFog (false); // restore normal distance fog
}
/*
============
R_SetSky
============
*/
// 3dstudio environment map names
char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
void R_SetSky (char *name, float rotate, vec3_t axis)
{
int i;
char pathname[MAX_QPATH];
float imagesize;
strncpy (skyname, name, sizeof(skyname)-1);
skyrotate = rotate;
VectorCopy (axis, skyaxis);
for (i=0 ; i<6 ; i++)
{
if (r_skymip->integer) { // take less memory
// r_picmip->integer++; // directly setting cvar values is bad bad bad!!!
Cvar_SetInteger("r_picmip", r_picmip->integer + 1);
}
2019-03-13 19:20:07 +00:00
Com_sprintf (pathname, sizeof(pathname), "env/%s%s.tga", skyname, suf[i]);
sky_images[i] = R_FindImage (pathname, it_sky);
// Knightmare- support .jpg skies
if (!sky_images[i]) {
Com_sprintf (pathname, sizeof(pathname), "env/%s%s.jpg", skyname, suf[i]);
sky_images[i] = R_FindImage (pathname, it_sky);
}
if (!sky_images[i])
sky_images[i] = glMedia.notexture;
if ((sky_images[i]->height == sky_images[i]->width) && (sky_images[i]->width >= 256))
imagesize = sky_images[i]->width;
else
imagesize = 256.0;
if (r_skymip->integer) { // take less memory
// r_picmip->integer--; // directly setting cvar values is bad bad bad!!!
Cvar_SetInteger("r_picmip", r_picmip->integer - 1);
2019-03-13 19:20:07 +00:00
imagesize = min(512.0, imagesize); // cap at 512
}
else
imagesize = min(1024.0, imagesize); // cap at 1024
sky_min = 1.0/imagesize; // was 256
sky_max = (imagesize-1.0)/imagesize;
/*if (r_skymip->integer) { // take less memory
// r_picmip->integer--; // directly setting cvar values is bad bad bad!!!
Cvar_SetInteger("r_picmip", r_picmip->integer - 1);
2019-03-13 19:20:07 +00:00
sky_min = 1.0/512; // was 256
sky_max = 511.0/512;
}
else // support max of 1024x1024
{
sky_min = 1.0/1024; // was 512
sky_max = 1023.0/1024;
}*/
}
}