/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2006-2011 Robert Beckebans This file is part of XreaL source code. XreaL 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. XreaL 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 XreaL 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" #include "gl_shader.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 < 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 < 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 < 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); } /* ============== ClearSkyBox ============== */ static void ClearSkyBox(void) { int i; for(i = 0; i < 6; i++) { sky_mins[0][i] = sky_mins[1][i] = 9999; sky_maxs[0][i] = sky_maxs[1][i] = -9999; } } /* ================ Tess_ClipSkyPolygons ================ */ void Tess_ClipSkyPolygons() { vec3_t p[5]; // need one extra point for clipping int i, j; ClearSkyBox(); for(i = 0; i < tess.numIndexes; i += 3) { for(j = 0; j < 3; j++) { VectorSubtract(tess.xyz[tess.indexes[i + j]], backEnd.viewParms.orientation.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, vec4_t outSt, vec4_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]; } } outXYZ[3] = 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; outSt[2] = 0; outSt[3] = 1; } } //static int sky_texorder[6] = { 0, 2, 1, 3, 4, 5 }; static vec4_t s_skyPoints[SKY_SUBDIVISIONS + 1][SKY_SUBDIVISIONS + 1]; static float s_skyTexCoords[SKY_SUBDIVISIONS + 1][SKY_SUBDIVISIONS + 1][4]; /* static void DrawSkySide(struct image_s *image, const int mins[2], const int maxs[2]) { int s, t; GL_SelectTexture(0); GL_Bind(image); for(t = mins[1] + HALF_SKY_SUBDIVISIONS; t < maxs[1] + HALF_SKY_SUBDIVISIONS; t++) { glBegin(GL_TRIANGLE_STRIP); for(s = mins[0] + HALF_SKY_SUBDIVISIONS; s <= maxs[0] + HALF_SKY_SUBDIVISIONS; s++) { glVertexAttrib4fvARB(ATTR_INDEX_TEXCOORD0, s_skyTexCoords[t][s]); glVertexAttrib4fvARB(ATTR_INDEX_POSITION, s_skyPoints[t][s]); glVertexAttrib4fvARB(ATTR_INDEX_TEXCOORD0, s_skyTexCoords[t + 1][s]); glVertexAttrib4fvARB(ATTR_INDEX_POSITION, s_skyPoints[t + 1][s]); } glEnd(); } } */ 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.orientation.origin, tess.xyz[tess.numVertexes]); tess.xyz[tess.numVertexes][3] = 1; tess.texCoords[tess.numVertexes][0] = s_skyTexCoords[t][s][0]; tess.texCoords[tess.numVertexes][1] = s_skyTexCoords[t][s][1]; tess.texCoords[tess.numVertexes][2] = 0; tess.texCoords[tess.numVertexes][3] = 1; tess.numVertexes++; if(tess.numVertexes >= SHADER_MAX_VERTEXES) { ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()\n"); } } } // 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 DrawSkyBox(shader_t * shader) { int i; sky_min = 0; sky_max = 1; Com_Memset(s_skyTexCoords, 0, sizeof(s_skyTexCoords)); // set up for drawing tess.multiDrawPrimitives = 0; tess.numIndexes = 0; tess.numVertexes = 0; 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); // only add indexes for first stage FillCloudySkySide(sky_mins_subd, sky_maxs_subd, qtrue); } // Tr3B: FIXME analyze required vertex attribs by the current material Tess_UpdateVBOs(0); Tess_DrawElements(); } 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] = Q_ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS); sky_mins_subd[1] = Q_ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS); sky_maxs_subd[0] = Q_ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS); sky_maxs_subd[1] = Q_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, (qboolean) (stage == 0)); } } static void BuildCloudData() { int i; shader_t *shader; shader = tess.surfaceShader; assert(shader->isSky); sky_min = 1.0 / 256.0f; // FIXME: not correct? sky_max = 255.0 / 256.0f; // set up for drawing tess.multiDrawPrimitives = 0; tess.numIndexes = 0; tess.numVertexes = 0; if(tess.surfaceShader->sky.cloudHeight) { for(i = 0; i < MAX_SHADER_STAGES; i++) { if(!tess.surfaceStages[i]) { break; } FillCloudBox(tess.surfaceShader, i); } } // Tr3B: FIXME analyze required vertex attribs by the current material Tess_UpdateVBOs(0); } /* ** R_InitSkyTexCoords ** Called when a sky shader is parsed */ void R_InitSkyTexCoords(float heightCloud) { int i, s, t; float radiusWorld = 4096; float p; float sRad, tRad; vec4_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(Square(skyVec[2]) * Square(radiusWorld) + 2 * Square(skyVec[0]) * radiusWorld * heightCloud + Square(skyVec[0]) * Square(heightCloud) + 2 * Square(skyVec[1]) * radiusWorld * heightCloud + Square(skyVec[1]) * Square(heightCloud) + 2 * Square(skyVec[2]) * radiusWorld * heightCloud + Square(skyVec[2]) * Square(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(void) { #if 0 float size; float dist; vec3_t origin, vec1, vec2; vec3_t temp; matrix_t transformMatrix; matrix_t modelViewMatrix; if(!backEnd.skyRenderedThisView) { return; } if(!r_drawSun->integer) { return; } GL_PushMatrix(); GL_BindProgram(&tr.genericShader); // set uniforms GLSL_SetUniform_TCGen_Environment(&tr.genericShader, qfalse); GLSL_SetUniform_InverseVertexColor(&tr.genericShader, qfalse); if(glConfig2.vboVertexSkinningAvailable) { GLSL_SetUniform_VertexSkinning(&tr.genericShader, qfalse); } GLSL_SetUniform_DeformGen(&tr.genericShader, DGEN_NONE); GLSL_SetUniform_AlphaTest(&tr.genericShader, -1.0); MatrixSetupTranslation(transformMatrix, backEnd.viewParms.orientation.origin[0], backEnd.viewParms.orientation.origin[1], backEnd.viewParms.orientation.origin[2]); Matrix4x4Multiply(backEnd.viewParms.world.viewMatrix, transformMatrix, modelViewMatrix); GL_LoadProjectionMatrix(backEnd.viewParms.projectionMatrix); GL_LoadModelViewMatrix(modelViewMatrix); GLSL_SetUniform_ModelMatrix(&tr.genericShader, backEnd.orientation.transformMatrix); GLSL_SetUniform_ModelViewProjectionMatrix(&tr.genericShader, glState.modelViewProjectionMatrix[glState.stackIndex]); GLSL_SetUniform_PortalClipping(&tr.genericShader, backEnd.viewParms.isPortal); if(backEnd.viewParms.isPortal) { float plane[4]; // clipping plane in world space plane[0] = backEnd.viewParms.portalPlane.normal[0]; plane[1] = backEnd.viewParms.portalPlane.normal[1]; plane[2] = backEnd.viewParms.portalPlane.normal[2]; plane[3] = backEnd.viewParms.portalPlane.dist; GLSL_SetUniform_PortalPlane(&tr.genericShader, plane); } dist = backEnd.viewParms.skyFar / 1.75; // div sqrt(3) size = dist * 0.4; 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 glDepthRange(1.0, 1.0); // FIXME: use quad stamp Tess_Begin(Tess_StageIteratorGeneric, tr.sunShader, NULL, tess.skipTangentSpaces, qfalse, -1, tess.fogNum); VectorCopy(origin, temp); VectorSubtract(temp, vec1, temp); VectorSubtract(temp, vec2, temp); VectorCopy(temp, tess.xyz[tess.numVertexes]); tess.xyz[tess.numVertexes][3] = 1; tess.texCoords[tess.numVertexes][0] = 0; tess.texCoords[tess.numVertexes][1] = 0; tess.texCoords[tess.numVertexes][2] = 0; tess.texCoords[tess.numVertexes][3] = 1; tess.colors[tess.numVertexes][0] = 1; tess.colors[tess.numVertexes][1] = 1; tess.colors[tess.numVertexes][2] = 1; tess.numVertexes++; VectorCopy(origin, temp); VectorAdd(temp, vec1, temp); VectorSubtract(temp, vec2, temp); VectorCopy(temp, tess.xyz[tess.numVertexes]); tess.xyz[tess.numVertexes][3] = 1; tess.texCoords[tess.numVertexes][0] = 0; tess.texCoords[tess.numVertexes][1] = 1; tess.texCoords[tess.numVertexes][2] = 0; tess.texCoords[tess.numVertexes][3] = 1; tess.colors[tess.numVertexes][0] = 1; tess.colors[tess.numVertexes][1] = 1; tess.colors[tess.numVertexes][2] = 1; tess.numVertexes++; VectorCopy(origin, temp); VectorAdd(temp, vec1, temp); VectorAdd(temp, vec2, temp); VectorCopy(temp, tess.xyz[tess.numVertexes]); tess.xyz[tess.numVertexes][3] = 1; tess.texCoords[tess.numVertexes][0] = 1; tess.texCoords[tess.numVertexes][1] = 1; tess.texCoords[tess.numVertexes][2] = 0; tess.texCoords[tess.numVertexes][3] = 1; tess.colors[tess.numVertexes][0] = 1; tess.colors[tess.numVertexes][1] = 1; tess.colors[tess.numVertexes][2] = 1; tess.numVertexes++; VectorCopy(origin, temp); VectorSubtract(temp, vec1, temp); VectorAdd(temp, vec2, temp); VectorCopy(temp, tess.xyz[tess.numVertexes]); tess.xyz[tess.numVertexes][3] = 1; tess.texCoords[tess.numVertexes][0] = 1; tess.texCoords[tess.numVertexes][1] = 0; tess.texCoords[tess.numVertexes][2] = 0; tess.texCoords[tess.numVertexes][3] = 1; tess.colors[tess.numVertexes][0] = 1; tess.colors[tess.numVertexes][1] = 1; tess.colors[tess.numVertexes][2] = 1; tess.numVertexes++; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 1; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 3; Tess_End(); // back to normal depth range glDepthRange(0.0, 1.0); GL_PopMatrix(); #endif } /* ================ Tess_StageIteratorSky All of the visible sky triangles are in tess Other things could be stuck in here, like birds in the sky, etc ================ */ void Tess_StageIteratorSky(void) { #if !defined(USE_D3D10) // log this call if(r_logFile->integer) { // don't just call LogComment, or we will get // a call to va() every frame! GLimp_LogComment(va ("--- Tess_StageIteratorSky( %s, %i vertices, %i triangles ) ---\n", tess.surfaceShader->name, tess.numVertexes, tess.numIndexes / 3)); } if(r_fastsky->integer) { return; } // trebor: HACK why does this happen with cg_draw2D 0 ? if(tess.stageIteratorFunc2 == NULL) { //tess.stageIteratorFunc2 = Tess_StageIteratorGeneric; ri.Error(ERR_FATAL, "tess.stageIteratorFunc == NULL"); } if(tess.stageIteratorFunc2 == &Tess_StageIteratorDepthFill) { // go through all the polygons and project them onto // the sky box to see which blocks on each side need // to be drawn Tess_ClipSkyPolygons(); // generate the vertexes for all the clouds, which will be drawn // by the generic shader routine BuildCloudData(); if(tess.numVertexes || tess.multiDrawPrimitives) tess.stageIteratorFunc2(); } else { if(tess.stageIteratorFunc2 == &Tess_StageIteratorGBuffer) { R_BindFBO(tr.geometricRenderFBO); } // go through all the polygons and project them onto // the sky box to see which blocks on each side need // to be drawn Tess_ClipSkyPolygons(); // 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 if(r_showSky->integer) { glDepthRange(0.0, 0.0); } else { glDepthRange(1.0, 1.0); } // draw the outer skybox if(tess.surfaceShader->sky.outerbox && tess.surfaceShader->sky.outerbox != tr.blackCubeImage) { #if 1 R_BindVBO(tess.vbo); R_BindIBO(tess.ibo); gl_skyboxShader->SetPortalClipping(backEnd.viewParms.isPortal); gl_skyboxShader->BindProgram(); gl_skyboxShader->SetUniform_ViewOrigin(backEnd.viewParms.orientation.origin); // in world space gl_skyboxShader->SetUniform_ModelMatrix(backEnd.orientation.transformMatrix); gl_skyboxShader->SetUniform_ModelViewProjectionMatrix(glState.modelViewProjectionMatrix[glState.stackIndex]); // u_PortalPlane if(backEnd.viewParms.isPortal) { float plane[4]; // clipping plane in world space plane[0] = backEnd.viewParms.portalPlane.normal[0]; plane[1] = backEnd.viewParms.portalPlane.normal[1]; plane[2] = backEnd.viewParms.portalPlane.normal[2]; plane[3] = backEnd.viewParms.portalPlane.dist; gl_skyboxShader->SetUniform_PortalPlane(plane); } gl_skyboxShader->SetRequiredVertexPointers(); // bind u_ColorMap GL_SelectTexture(0); GL_Bind(tess.surfaceShader->sky.outerbox); DrawSkyBox(tess.surfaceShader); #endif } // generate the vertexes for all the clouds, which will be drawn // by the generic shader routine BuildCloudData(); if(tess.numVertexes || tess.multiDrawPrimitives) tess.stageIteratorFunc2(); // Tr3B: TODO draw the inner skybox? if(tess.stageIteratorFunc2 == Tess_StageIteratorGBuffer) { R_BindNullFBO(); } if(tess.stageIteratorFunc2 != Tess_StageIteratorDepthFill) { // back to normal depth range glDepthRange(0.0, 1.0); // note that sky was drawn so we will draw a sun later backEnd.skyRenderedThisView = qtrue; } } #endif }