/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // tr_sky.c #include "tr_local.h" #define SKY_SUBDIVISIONS 8 #define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2) static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; /* =================================================================================== POLYGON TO BOX SIDE PROJECTION =================================================================================== */ static vec3_t sky_clip[6] = { {1,1,0}, {1,-1,0}, {0,-1,1}, {0,1,1}, {1,0,1}, {-1,0,1} }; static float sky_mins[2][6], sky_maxs[2][6]; static float sky_min, sky_max; /* ================ AddSkyPolygon ================ */ static void AddSkyPolygon (int nump, vec3_t vecs) { int i,j; vec3_t v, av; float s, t, dv; int axis; float *vp; // s = [0]/[2], t = [1]/[2] static 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} }; // 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 < sky_mins[0][axis]) sky_mins[0][axis] = s; if (t < sky_mins[1][axis]) sky_mins[1][axis] = t; if (s > sky_maxs[0][axis]) sky_maxs[0][axis] = s; if (t > sky_maxs[1][axis]) sky_maxs[1][axis] = t; } } #define ON_EPSILON 0.1f // point on plane side epsilon #define MAX_CLIP_VERTS 64 /* ================ ClipSkyPolygon ================ */ static 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) ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); if (stage == 6) { // fully clipped, so draw it AddSkyPolygon (nump, vecs); return; } front = back = qfalse; norm = sky_clip[stage]; for (i=0, v = vecs ; i 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 ; inumIndexes; i += 3 ) { for (j = 0 ; j < 3 ; j++) { VectorSubtract( input->xyz[input->indexes[i+j]], backEnd.viewParms.or.origin, p[j] ); } ClipSkyPolygon( 3, p[0], 0 ); } } /* =================================================================================== CLOUD VERTEX GENERATION =================================================================================== */ /* ** MakeSkyVec ** ** Parms: s, t range from -1 to 1 */ static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ ) { // 1 = s, 2 = t, 3 = 2048 static 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 }; vec3_t b; int j, k; float boxSize; boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3) b[0] = s*boxSize; b[1] = t*boxSize; b[2] = boxSize; for (j=0 ; j<3 ; j++) { k = st_to_vec[axis][j]; if (k < 0) { outXYZ[j] = -b[-k - 1]; } else { outXYZ[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; if ( outSt ) { outSt[0] = s; outSt[1] = t; } } static int sky_texorder[6] = {0,2,1,3,4,5}; static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] ) { int s, t; int firstVertex = tess.numVertexes; //int firstIndex = tess.numIndexes; vec4_t color; //tess.numVertexes = 0; //tess.numIndexes = 0; tess.firstIndex = tess.numIndexes; GL_BindToTMU( image, TB_COLORMAP ); GL_Cull( CT_TWO_SIDED ); for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) { for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) { tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0]; tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1]; tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2]; tess.xyz[tess.numVertexes][3] = 1.0; tess.texCoords[tess.numVertexes][0] = s_skyTexCoords[t][s][0]; tess.texCoords[tess.numVertexes][1] = s_skyTexCoords[t][s][1]; tess.numVertexes++; if(tess.numVertexes >= SHADER_MAX_VERTEXES) { ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()"); } } } for ( t = 0; t < maxs[1] - mins[1]; t++ ) { for ( s = 0; s < maxs[0] - mins[0]; s++ ) { if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES) { ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()"); } tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex; tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; } } // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function RB_UpdateTessVao(ATTR_POSITION | ATTR_TEXCOORD); /* { shaderProgram_t *sp = &tr.textureColorShader; GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); GLSL_BindProgram(sp); GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); color[0] = color[1] = color[2] = tr.identityLight; color[3] = 1.0f; GLSL_SetUniformVec4(sp, UNIFORM_COLOR, color); } */ { shaderProgram_t *sp = &tr.lightallShader[0]; vec4_t vector; GLSL_BindProgram(sp); GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); color[0] = color[1] = color[2] = color[3] = 1.0f; GLSL_SetUniformVec4(sp, UNIFORM_BASECOLOR, color); color[0] = color[1] = color[2] = color[3] = 0.0f; GLSL_SetUniformVec4(sp, UNIFORM_VERTCOLOR, color); VectorSet4(vector, 1.0, 0.0, 0.0, 1.0); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXMATRIX, vector); VectorSet4(vector, 0.0, 0.0, 0.0, 0.0); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, vector); GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); } R_DrawElements(tess.numIndexes - tess.firstIndex, tess.firstIndex); //qglDrawElements(GL_TRIANGLES, tess.numIndexes - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(glIndex_t))); //R_BindNullVBO(); //R_BindNullIBO(); tess.numIndexes = tess.firstIndex; tess.numVertexes = firstVertex; tess.firstIndex = 0; } static void DrawSkyBox( shader_t *shader ) { int i; sky_min = 0; sky_max = 1; Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) ); for (i=0 ; i<6 ; i++) { int sky_mins_subd[2], sky_maxs_subd[2]; int s, t; sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || ( sky_mins[1][i] >= sky_maxs[1][i] ) ) { continue; } sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS; sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS; sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS; sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS; if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS; else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS; else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; // // iterate through the subdivisions // for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) { for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) { MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, i, s_skyTexCoords[t][s], s_skyPoints[t][s] ); } } DrawSkySide( shader->sky.outerbox[sky_texorder[i]], sky_mins_subd, sky_maxs_subd ); } } static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes ) { int s, t; int vertexStart = tess.numVertexes; int tHeight, sWidth; tHeight = maxs[1] - mins[1] + 1; sWidth = maxs[0] - mins[0] + 1; for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) { for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) { VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0] = s_skyTexCoords[t][s][0]; tess.texCoords[tess.numVertexes][1] = s_skyTexCoords[t][s][1]; tess.numVertexes++; if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) { ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); } } } // only add indexes for one pass, otherwise it would draw multiple times for each pass if ( addIndexes ) { for ( t = 0; t < tHeight-1; t++ ) { for ( s = 0; s < sWidth-1; s++ ) { tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth ); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); tess.numIndexes++; tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); tess.numIndexes++; } } } } static void FillCloudBox( const shader_t *shader, int stage ) { int i; for ( i =0; i < 6; i++ ) { int sky_mins_subd[2], sky_maxs_subd[2]; int s, t; float MIN_T; if ( 1 ) // FIXME? shader->sky.fullClouds ) { MIN_T = -HALF_SKY_SUBDIVISIONS; // still don't want to draw the bottom, even if fullClouds if ( i == 5 ) continue; } else { switch( i ) { case 0: case 1: case 2: case 3: MIN_T = -1; break; case 5: // don't draw clouds beneath you continue; case 4: // top default: MIN_T = -HALF_SKY_SUBDIVISIONS; break; } } sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || ( sky_mins[1][i] >= sky_maxs[1][i] ) ) { continue; } sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS); sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS); sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS); sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS); if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; if ( sky_mins_subd[1] < MIN_T ) sky_mins_subd[1] = MIN_T; else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; if ( sky_maxs_subd[1] < MIN_T ) sky_maxs_subd[1] = MIN_T; else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; // // iterate through the subdivisions // for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) { for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) { MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, i, NULL, s_skyPoints[t][s] ); s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0]; s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1]; } } // only add indexes for first stage FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) ); } } /* ** R_BuildCloudData */ void R_BuildCloudData( shaderCommands_t *input ) { int i; shader_t *shader; shader = input->shader; assert( shader->isSky ); sky_min = 1.0 / 256.0f; // FIXME: not correct? sky_max = 255.0 / 256.0f; // set up for drawing tess.numIndexes = 0; tess.numVertexes = 0; tess.firstIndex = 0; if ( shader->sky.cloudHeight ) { for ( i = 0; i < MAX_SHADER_STAGES; i++ ) { if ( !tess.xstages[i] ) { break; } FillCloudBox( shader, i ); } } } /* ** R_InitSkyTexCoords ** Called when a sky shader is parsed */ #define SQR( a ) ((a)*(a)) void R_InitSkyTexCoords( float heightCloud ) { int i, s, t; float radiusWorld = 4096; float p; float sRad, tRad; vec3_t skyVec; vec3_t v; // init zfar so MakeSkyVec works even though // a world hasn't been bounded backEnd.viewParms.zFar = 1024; for ( i = 0; i < 6; i++ ) { for ( t = 0; t <= SKY_SUBDIVISIONS; t++ ) { for ( s = 0; s <= SKY_SUBDIVISIONS; s++ ) { // compute vector from view origin to sky side integral point MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, i, NULL, skyVec ); // compute parametric value 'p' that intersects with cloud layer p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) * ( -2 * skyVec[2] * radiusWorld + 2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) + 2 * SQR( skyVec[0] ) * radiusWorld * heightCloud + SQR( skyVec[0] ) * SQR( heightCloud ) + 2 * SQR( skyVec[1] ) * radiusWorld * heightCloud + SQR( skyVec[1] ) * SQR( heightCloud ) + 2 * SQR( skyVec[2] ) * radiusWorld * heightCloud + SQR( skyVec[2] ) * SQR( heightCloud ) ) ); s_cloudTexP[i][t][s] = p; // compute intersection point based on p VectorScale( skyVec, p, v ); v[2] += radiusWorld; // compute vector from world origin to intersection point 'v' VectorNormalize( v ); sRad = Q_acos( v[0] ); tRad = Q_acos( v[1] ); s_cloudTexCoords[i][t][s][0] = sRad; s_cloudTexCoords[i][t][s][1] = tRad; } } } } //====================================================================================== /* ** RB_DrawSun */ void RB_DrawSun( float scale, shader_t *shader ) { float size; float dist; vec3_t origin, vec1, vec2; if ( !backEnd.skyRenderedThisView ) { return; } //qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); { // FIXME: this could be a lot cleaner mat4_t translation, modelview; Mat4Translation( backEnd.viewParms.or.origin, translation ); Mat4Multiply( backEnd.viewParms.world.modelMatrix, translation, modelview ); GL_SetModelviewMatrix( modelview, qtrue ); } dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) size = dist * scale; VectorScale( tr.sunDirection, dist, origin ); PerpendicularVector( vec1, tr.sunDirection ); CrossProduct( tr.sunDirection, vec1, vec2 ); VectorScale( vec1, size, vec1 ); VectorScale( vec2, size, vec2 ); // farthest depth range qglDepthRange( 1.0, 1.0 ); RB_BeginSurface( shader, 0, 0 ); RB_AddQuadStamp(origin, vec1, vec2, colorWhite); RB_EndSurface(); // back to normal depth range qglDepthRange( 0.0, 1.0 ); } /* ================ RB_StageIteratorSky All of the visible sky triangles are in tess Other things could be stuck in here, like birds in the sky, etc ================ */ void RB_StageIteratorSky( void ) { if ( r_fastsky->integer || vr_deathCam->integer ) { return; } // go through all the polygons and project them onto // the sky box to see which blocks on each side need // to be drawn RB_ClipSkyPolygons( &tess ); // r_showsky will let all the sky blocks be drawn in // front of everything to allow developers to see how // much sky is getting sucked in #ifdef __ANDROID__ if ( r_showsky->integer ) { glDepthRangef( 0.0, 0.0 ); } else { glDepthRangef( 1.0, 1.0 ); } #else if ( r_showsky->integer ) { qglDepthRange( 0.0, 0.0 ); } else { qglDepthRange( 1.0, 1.0 ); } #endif // draw the outer skybox if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { mat4_t oldmodelview; GL_State( 0 ); GL_Cull( CT_FRONT_SIDED ); //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); { // FIXME: this could be a lot cleaner mat4_t trans, product; Mat4Copy( glState.modelview, oldmodelview ); Mat4Translation( backEnd.viewParms.or.origin, trans ); Mat4Multiply( glState.modelview, trans, product ); GL_SetModelviewMatrix( product, qtrue ); } DrawSkyBox( tess.shader ); GL_SetModelviewMatrix( oldmodelview, qtrue ); } // generate the vertexes for all the clouds, which will be drawn // by the generic shader routine R_BuildCloudData( &tess ); RB_StageIteratorGeneric(); // draw the inner skybox #ifdef __ANDROID__ glDepthRangef( 0.0, 1.0 ); #else // back to normal depth range qglDepthRange( 0.0, 1.0 ); #endif // note that sky was drawn so we will draw a sun later backEnd.skyRenderedThisView = qtrue; }