mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
2dcc5719e3
Explicitly set cull type for skybox to front, instead of using whatever cull type the previous shader used (which could result in the skybox not being visible due to only drawing back faces). The sky cloud stages set the cull type so they are not affected by previous cull type.
912 lines
22 KiB
C
912 lines
22 KiB
C
/*
|
|
===========================================================================
|
|
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<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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
RB_ClipSkyPolygons
|
|
================
|
|
*/
|
|
void RB_ClipSkyPolygons( shaderCommands_t *input )
|
|
{
|
|
vec3_t p[5]; // need one extra point for clipping
|
|
int i, j;
|
|
|
|
ClearSkyBox();
|
|
|
|
for ( i = 0; i < input->numIndexes; 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;
|
|
int minIndex = tess.minIndex;
|
|
int maxIndex = tess.maxIndex;
|
|
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][0] = s_skyTexCoords[t][s][0];
|
|
tess.texCoords[tess.numVertexes][0][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;
|
|
}
|
|
}
|
|
|
|
tess.minIndex = firstVertex;
|
|
tess.maxIndex = tess.numVertexes;
|
|
|
|
// 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] = backEnd.refdef.colorScale;
|
|
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);
|
|
}
|
|
|
|
R_DrawElementsVao(tess.numIndexes - tess.firstIndex, tess.firstIndex, tess.minIndex, tess.maxIndex);
|
|
|
|
//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;
|
|
tess.minIndex = minIndex;
|
|
tess.maxIndex = maxIndex;
|
|
}
|
|
|
|
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][0] = s_skyTexCoords[t][s][0];
|
|
tess.texCoords[tess.numVertexes][0][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 );
|
|
}
|
|
|
|
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 ) {
|
|
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
|
|
if ( r_showsky->integer ) {
|
|
qglDepthRange( 0.0, 0.0 );
|
|
} else {
|
|
qglDepthRange( 1.0, 1.0 );
|
|
}
|
|
|
|
// 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 );
|
|
|
|
}
|
|
|
|
DrawSkyBox( tess.shader );
|
|
|
|
GL_SetModelviewMatrix( oldmodelview );
|
|
}
|
|
|
|
// 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
|
|
|
|
|
|
// back to normal depth range
|
|
qglDepthRange( 0.0, 1.0 );
|
|
|
|
// note that sky was drawn so we will draw a sun later
|
|
backEnd.skyRenderedThisView = qtrue;
|
|
}
|
|
|
|
|
|
|
|
|
|
|