mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-12-02 00:11:58 +00:00
3b4f4cdfa9
Some revision messages: Cache servers for each master server in q3_ui, otherwise servers from last updated master for shown for all Internet# sources. Play correct team sounds when in spectator mode and following a player. Check last listener number instead of clc.clientNum in S_AL_HearingThroughEntity so sound work correctly when spectate following a client. (Related to bug 5741.) When in third person, don't play player's sounds as full volume in Base sound system. OpenAL already does this. (Related to bug 5741.) really fix the confusion with game entity and refentity numbers to further reduce confusion, rename constants like MAX_ENTITIES to MAX_REFENTITIES Added Rend2, an alternate renderer. (Bug #4358) Fix restoring fs_game when default.cfg is missing. Fix restoring old fs_game upon leaving a server. Patch by Ensiform. Change more operator commands to require sv_running to be usable. Patch by Ensiform. Fix some "> MAX_*" to be ">= MAX_*". Fix follow command to find clients whose name begins with a number. Fix up "gc" command, make it more like "tell". Based on patch by Ensiform. Add usage messages for gc, tell, vtell, and votell commands. Check player names in gc, tell, vtell, and votell commands. #5799 - Change messagemode text box to display colors like in console input box. Improve "play" command, based on a patch from Ensiform. Check for invalid filename in OpenAL's RegisterSound function. Changed Base sound system to warn not error when sound filename is empty or too long. Remove references to non-existent functions CM_MarkFragments and CM_LerpTag.
965 lines
24 KiB
C
965 lines
24 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_Bind( image );
|
|
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()\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
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()\n");
|
|
}
|
|
|
|
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_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD);
|
|
/*
|
|
{
|
|
shaderProgram_t *sp = &tr.textureColorShader;
|
|
|
|
GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD);
|
|
GLSL_BindProgram(sp);
|
|
|
|
GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
|
|
|
|
color[0] =
|
|
color[1] =
|
|
color[2] = tr.identityLight;
|
|
color[3] = 1.0f;
|
|
GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color);
|
|
}
|
|
*/
|
|
{
|
|
shaderProgram_t *sp = &tr.lightallShader[0];
|
|
vec4_t vector;
|
|
|
|
GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD);
|
|
GLSL_BindProgram(sp);
|
|
|
|
GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection);
|
|
|
|
color[0] =
|
|
color[1] =
|
|
color[2] = tr.identityLight;
|
|
color[3] = 1.0f;
|
|
GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, color);
|
|
|
|
color[0] =
|
|
color[1] =
|
|
color[2] =
|
|
color[3] = 0.0f;
|
|
GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, color);
|
|
|
|
VectorSet4(vector, 1.0, 0.0, 0.0, 1.0);
|
|
GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector);
|
|
|
|
VectorSet4(vector, 0.0, 0.0, 0.0, 0.0);
|
|
GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector);
|
|
}
|
|
|
|
R_DrawElementsVBO(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(GL_INDEX_TYPE)));
|
|
|
|
//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( void ) {
|
|
float size;
|
|
float dist;
|
|
vec3_t origin, vec1, vec2;
|
|
vec3_t temp;
|
|
|
|
if ( !backEnd.skyRenderedThisView ) {
|
|
return;
|
|
}
|
|
if ( !r_drawSun->integer ) {
|
|
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
|
|
matrix_t trans, product;
|
|
|
|
Matrix16Translation( backEnd.viewParms.or.origin, trans );
|
|
Matrix16Multiply( backEnd.viewParms.world.modelMatrix, trans, product );
|
|
GL_SetModelviewMatrix( product );
|
|
}
|
|
|
|
dist = backEnd.viewParms.zFar / 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
|
|
qglDepthRange( 1.0, 1.0 );
|
|
|
|
// FIXME: use quad stamp
|
|
RB_BeginSurface( tr.sunShader, tess.fogNum );
|
|
VectorCopy( origin, temp );
|
|
VectorSubtract( temp, vec1, temp );
|
|
VectorSubtract( temp, vec2, temp );
|
|
VectorCopy( temp, tess.xyz[tess.numVertexes] );
|
|
tess.texCoords[tess.numVertexes][0][0] = 0;
|
|
tess.texCoords[tess.numVertexes][0][1] = 0;
|
|
tess.vertexColors[tess.numVertexes][0] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][1] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][2] = 1.0f;
|
|
tess.numVertexes++;
|
|
|
|
VectorCopy( origin, temp );
|
|
VectorAdd( temp, vec1, temp );
|
|
VectorSubtract( temp, vec2, temp );
|
|
VectorCopy( temp, tess.xyz[tess.numVertexes] );
|
|
tess.texCoords[tess.numVertexes][0][0] = 0;
|
|
tess.texCoords[tess.numVertexes][0][1] = 1;
|
|
tess.vertexColors[tess.numVertexes][0] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][1] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][2] = 1.0f;
|
|
tess.numVertexes++;
|
|
|
|
VectorCopy( origin, temp );
|
|
VectorAdd( temp, vec1, temp );
|
|
VectorAdd( temp, vec2, temp );
|
|
VectorCopy( temp, tess.xyz[tess.numVertexes] );
|
|
tess.texCoords[tess.numVertexes][0][0] = 1;
|
|
tess.texCoords[tess.numVertexes][0][1] = 1;
|
|
tess.vertexColors[tess.numVertexes][0] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][1] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][2] = 1.0f;
|
|
tess.numVertexes++;
|
|
|
|
VectorCopy( origin, temp );
|
|
VectorSubtract( temp, vec1, temp );
|
|
VectorAdd( temp, vec2, temp );
|
|
VectorCopy( temp, tess.xyz[tess.numVertexes] );
|
|
tess.texCoords[tess.numVertexes][0][0] = 1;
|
|
tess.texCoords[tess.numVertexes][0][1] = 0;
|
|
tess.vertexColors[tess.numVertexes][0] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][1] = 1.0f;
|
|
tess.vertexColors[tess.numVertexes][2] = 1.0f;
|
|
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;
|
|
|
|
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 ) {
|
|
matrix_t oldmodelview;
|
|
|
|
GL_State( 0 );
|
|
//qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]);
|
|
|
|
{
|
|
// FIXME: this could be a lot cleaner
|
|
matrix_t trans, product;
|
|
|
|
Matrix16Copy( glState.modelview, oldmodelview );
|
|
Matrix16Translation( backEnd.viewParms.or.origin, trans );
|
|
Matrix16Multiply( 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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|