summaryrefslogtreecommitdiff
path: root/src/gl_sky.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gl_sky.c')
-rw-r--r--src/gl_sky.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/src/gl_sky.c b/src/gl_sky.c
new file mode 100644
index 0000000..163ae7f
--- /dev/null
+++ b/src/gl_sky.c
@@ -0,0 +1,417 @@
+/*
+Copyright (C) 2003-2006 Andrey Nazarov
+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.
+
+*/
+
+#include "gl_local.h"
+
+static float skyrotate;
+static vec3_t skyaxis;
+static int sky_images[6];
+
+static const vec3_t skyclip[6] = {
+ {1,1,0},
+ {1,-1,0},
+ {0,-1,1},
+ {0,1,1},
+ {1,0,1},
+ {-1,0,1}
+};
+
+// 1 = s, 2 = t, 3 = 2048
+static const 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
+};
+
+// s = [0]/[2], t = [1]/[2]
+static const int vec_to_st[6][3] = {
+ {-2,3,1},
+ {2,3,-1},
+
+ {1,3,2},
+ {-1,3,-2},
+
+ {-2,-1,3},
+ {-2,1,-3}
+};
+
+static float skymins[2][6], skymaxs[2][6];
+static const float sky_min = 1.0f / 512.0f;
+static const float sky_max = 511.0f / 512.0f;
+
+static void DrawSkyPolygon (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];
+ 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
+
+#define SIDE_FRONT 0
+#define SIDE_BACK 1
+#define SIDE_ON 2
+
+static void ClipSkyPolygon (int nump, vec3_t vecs, int stage)
+{
+ const 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)
+ Com_Error( ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS");
+ if (stage == 6)
+ { // fully clipped, so draw it
+ DrawSkyPolygon (nump, vecs);
+ return;
+ }
+
+ front = back = qfalse;
+ norm = skyclip[stage];
+ for (i=0, v = vecs ; i<nump ; i++, v+=3)
+ {
+ d = DotProduct (v, norm);
+ if (d > ON_EPSILON)
+ {
+ front = qtrue;
+ sides[i] = SIDE_FRONT;
+ }
+ else if (d < -ON_EPSILON)
+ {
+ back = qtrue;
+ 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( mface_t *fa ) {
+ int i;
+ vec3_t verts[MAX_CLIP_VERTS];
+ msurfedge_t *surfedge;
+ mvertex_t *vert;
+ medge_t *edge;
+
+ if( fa->numsurfedges > MAX_CLIP_VERTS ) {
+ Com_Error( ERR_DROP, "%s: too many verts", __func__ );
+ }
+
+ // calculate vertex values for sky box
+ surfedge = fa->firstsurfedge;
+ for( i = 0; i < fa->numsurfedges; i++, surfedge++ ) {
+ edge = surfedge->edge;
+ vert = edge->v[surfedge->vert];
+ VectorSubtract (vert->point, modelViewOrigin, verts[i]);
+ }
+ ClipSkyPolygon (fa->numsurfedges, 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;
+ }
+}
+
+
+static void MakeSkyVec (float s, float t, int axis)
+{
+ vec3_t v, b;
+ int j, k;
+
+ b[0] = s*4800;//2300;
+ b[1] = t*4800;//2300;
+ b[2] = 4800;//2300;
+
+ 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;
+ qglTexCoord2f (s, t);
+ qglVertex3fv (v);
+}
+
+#define SKY_VISIBLE( side ) \
+ ( skymins[0][side] < skymaxs[0][side] && \
+ skymins[1][side] < skymaxs[1][side] )
+
+/*
+==============
+R_DrawSkyBox
+==============
+*/
+void R_DrawSkyBox( void ) {
+ int i;
+ static const int skytexorder[6] = {0,2,1,3,4,5};
+
+ if( skyrotate ) {
+ // hack, forces full sky to draw when rotating
+ for( i = 0; i < 6; i++ ) {
+ skymins[0][i] = -1;
+ skymins[1][i] = -1;
+ skymaxs[0][i] = 1;
+ skymaxs[1][i] = 1;
+ }
+ } else {
+ // check for no sky at all
+ for( i = 0; i < 6; i++ ) {
+ if( SKY_VISIBLE( i ) ) {
+ break;
+ }
+ }
+ if( i == 6 ) {
+ return; // nothing visible
+ }
+ }
+
+ qglPushMatrix ();
+ qglTranslatef (modelViewOrigin[0], modelViewOrigin[1], modelViewOrigin[2]);
+ if( skyrotate ) {
+ qglRotatef (glr.fd.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]);
+ }
+
+ qglColor3f( 1, 1, 1 );
+ GL_Bits( GLS_DEFAULT );
+
+ for( i = 0; i < 6; i++ ) {
+ if( !SKY_VISIBLE( i ) ) {
+ continue;
+ }
+
+ GL_BindTexture (sky_images[skytexorder[i]]);
+
+ qglBegin (GL_QUADS);
+ 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);
+ qglEnd ();
+ }
+ qglPopMatrix ();
+}
+
+void R_UnsetSky( void ) {
+ int i;
+
+ skyrotate = 0;
+ for( i = 0; i < 6; i++ ) {
+ sky_images[i] = TEXNUM_BLACK;
+ }
+}
+
+/*
+============
+R_SetSky
+============
+*/
+void R_SetSky( const char *name, float rotate, vec3_t axis ) {
+ int i;
+ char pathname[MAX_QPATH];
+ image_t *image;
+ size_t len;
+ // 3dstudio environment map names
+ static const char suf[6][3] = { "rt", "bk", "lf", "ft", "up", "dn" };
+
+ if( !gl_drawsky->integer ) {
+ R_UnsetSky();
+ return;
+ }
+
+ skyrotate = rotate;
+ VectorCopy (axis, skyaxis);
+
+ for( i = 0; i < 6; i++ ) {
+ len = Q_concat( pathname, sizeof( pathname ),
+ "env/", name, suf[i], ".tga", NULL );
+ if( len >= sizeof( pathname ) ) {
+ R_UnsetSky();
+ return;
+ }
+ image = IMG_Find( pathname, it_sky );
+ if( !image ) {
+ R_UnsetSky();
+ return;
+ }
+ sky_images[i] = image->texnum;
+ }
+}
+