/* =========================================================================== Copyright (C) 1997-2001 Id Software, Inc. This file is part of Quake 2 source code. Quake 2 source code 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. Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 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 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 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 ; ipolys ; p ; p=p->next) { for (i=0 ; inumverts ; 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); } 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); 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); 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; }*/ } }