You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
662 lines
12 KiB
662 lines
12 KiB
/* |
|
Copyright (C) 1997-2001 Id Software, Inc. |
|
|
|
This program 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. |
|
|
|
This program 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 this program; if not, write to the Free Software |
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
|
|
*/ |
|
// gl_warp.c -- sky and water polygons |
|
|
|
#include "gl_local.h" |
|
|
|
extern model_t *loadmodel; |
|
|
|
char skyname[MAX_QPATH]; |
|
float skyrotate; |
|
vec3_t skyaxis; |
|
image_t *sky_images[6]; |
|
|
|
msurface_t *warpface; |
|
|
|
#define SUBDIVIDE_SIZE 64 |
|
//#define SUBDIVIDE_SIZE 1024 |
|
|
|
void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) |
|
{ |
|
int i, j; |
|
float *v; |
|
|
|
mins[0] = mins[1] = mins[2] = 9999; |
|
maxs[0] = maxs[1] = maxs[2] = -9999; |
|
v = verts; |
|
for (i=0 ; i<numverts ; i++) |
|
for (j=0 ; j<3 ; j++, v++) |
|
{ |
|
if (*v < mins[j]) |
|
mins[j] = *v; |
|
if (*v > maxs[j]) |
|
maxs[j] = *v; |
|
} |
|
} |
|
|
|
void SubdividePolygon (int numverts, float *verts) |
|
{ |
|
int i, j, k; |
|
vec3_t mins, maxs; |
|
float m; |
|
float *v; |
|
vec3_t front[64], back[64]; |
|
int f, b; |
|
float dist[64]; |
|
float frac; |
|
glpoly_t *poly; |
|
float s, t; |
|
vec3_t total; |
|
float total_s, total_t; |
|
|
|
if (numverts > 60) |
|
ri.Sys_Error (ERR_DROP, "numverts = %i", numverts); |
|
|
|
BoundPoly (numverts, verts, mins, maxs); |
|
|
|
for (i=0 ; i<3 ; i++) |
|
{ |
|
m = (mins[i] + maxs[i]) * 0.5; |
|
m = SUBDIVIDE_SIZE * floor (m/SUBDIVIDE_SIZE + 0.5); |
|
if (maxs[i] - m < 8) |
|
continue; |
|
if (m - mins[i] < 8) |
|
continue; |
|
|
|
// cut it |
|
v = verts + i; |
|
for (j=0 ; j<numverts ; j++, v+= 3) |
|
dist[j] = *v - m; |
|
|
|
// wrap cases |
|
dist[j] = dist[0]; |
|
v-=i; |
|
VectorCopy (verts, v); |
|
|
|
f = b = 0; |
|
v = verts; |
|
for (j=0 ; j<numverts ; j++, v+= 3) |
|
{ |
|
if (dist[j] >= 0) |
|
{ |
|
VectorCopy (v, front[f]); |
|
f++; |
|
} |
|
if (dist[j] <= 0) |
|
{ |
|
VectorCopy (v, back[b]); |
|
b++; |
|
} |
|
if (dist[j] == 0 || dist[j+1] == 0) |
|
continue; |
|
if ( (dist[j] > 0) != (dist[j+1] > 0) ) |
|
{ |
|
// clip point |
|
frac = dist[j] / (dist[j] - dist[j+1]); |
|
for (k=0 ; k<3 ; k++) |
|
front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); |
|
f++; |
|
b++; |
|
} |
|
} |
|
|
|
SubdividePolygon (f, front[0]); |
|
SubdividePolygon (b, back[0]); |
|
return; |
|
} |
|
|
|
// add a point in the center to help keep warp valid |
|
poly = Hunk_Alloc (sizeof(glpoly_t) + ((numverts-4)+2) * VERTEXSIZE*sizeof(float)); |
|
poly->next = warpface->polys; |
|
warpface->polys = poly; |
|
poly->numverts = numverts+2; |
|
VectorClear (total); |
|
total_s = 0; |
|
total_t = 0; |
|
for (i=0 ; i<numverts ; i++, verts+= 3) |
|
{ |
|
VectorCopy (verts, poly->verts[i+1]); |
|
s = DotProduct (verts, warpface->texinfo->vecs[0]); |
|
t = DotProduct (verts, warpface->texinfo->vecs[1]); |
|
|
|
total_s += s; |
|
total_t += t; |
|
VectorAdd (total, verts, total); |
|
|
|
poly->verts[i+1][3] = s; |
|
poly->verts[i+1][4] = t; |
|
} |
|
|
|
VectorScale (total, (1.0/numverts), poly->verts[0]); |
|
poly->verts[0][3] = total_s/numverts; |
|
poly->verts[0][4] = total_t/numverts; |
|
|
|
// copy first vertex to last |
|
memcpy (poly->verts[i+1], poly->verts[1], sizeof(poly->verts[0])); |
|
} |
|
|
|
/* |
|
================ |
|
GL_SubdivideSurface |
|
|
|
Breaks a polygon up along axial 64 unit |
|
boundaries so that turbulent and sky warps |
|
can be done reasonably. |
|
================ |
|
*/ |
|
void GL_SubdivideSurface (msurface_t *fa) |
|
{ |
|
vec3_t verts[64]; |
|
int numverts; |
|
int i; |
|
int lindex; |
|
float *vec; |
|
|
|
warpface = fa; |
|
|
|
// |
|
// convert edges back to a normal polygon |
|
// |
|
numverts = 0; |
|
for (i=0 ; i<fa->numedges ; i++) |
|
{ |
|
lindex = loadmodel->surfedges[fa->firstedge + i]; |
|
|
|
if (lindex > 0) |
|
vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; |
|
else |
|
vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; |
|
VectorCopy (vec, verts[numverts]); |
|
numverts++; |
|
} |
|
|
|
SubdividePolygon (numverts, verts[0]); |
|
} |
|
|
|
//========================================================= |
|
|
|
|
|
|
|
// speed up sin calculations - Ed |
|
float r_turbsin[] = |
|
{ |
|
#include "warpsin.h" |
|
}; |
|
#define TURBSCALE (256.0 / (2 * M_PI)) |
|
|
|
/* |
|
============= |
|
EmitWaterPolys |
|
|
|
Does a water warp on the pre-fragmented glpoly_t chain |
|
============= |
|
*/ |
|
void EmitWaterPolys (msurface_t *fa) |
|
{ |
|
glpoly_t *p, *bp; |
|
float *v; |
|
int i; |
|
float s, t, os, ot; |
|
float scroll; |
|
float rdt = r_newrefdef.time; |
|
|
|
if (fa->texinfo->flags & SURF_FLOWING) |
|
scroll = -64 * ( (r_newrefdef.time*0.5) - (int)(r_newrefdef.time*0.5) ); |
|
else |
|
scroll = 0; |
|
for (bp=fa->polys ; bp ; bp=bp->next) |
|
{ |
|
p = bp; |
|
|
|
qglBegin (GL_TRIANGLE_FAN); |
|
for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE) |
|
{ |
|
os = v[3]; |
|
ot = v[4]; |
|
|
|
#if !id386 |
|
s = os + r_turbsin[(int)((ot*0.125+r_newrefdef.time) * TURBSCALE) & 255]; |
|
#else |
|
s = os + r_turbsin[Q_ftol( ((ot*0.125+rdt) * TURBSCALE) ) & 255]; |
|
#endif |
|
s += scroll; |
|
s *= (1.0/64); |
|
|
|
#if !id386 |
|
t = ot + r_turbsin[(int)((os*0.125+rdt) * TURBSCALE) & 255]; |
|
#else |
|
t = ot + r_turbsin[Q_ftol( ((os*0.125+rdt) * TURBSCALE) ) & 255]; |
|
#endif |
|
t *= (1.0/64); |
|
|
|
qglTexCoord2f (s, t); |
|
qglVertex3fv (v); |
|
} |
|
qglEnd (); |
|
} |
|
} |
|
|
|
|
|
//=================================================================== |
|
|
|
|
|
vec3_t skyclip[6] = { |
|
{1,1,0}, |
|
{1,-1,0}, |
|
{0,-1,1}, |
|
{0,1,1}, |
|
{1,0,1}, |
|
{-1,0,1} |
|
}; |
|
int c_sky; |
|
|
|
// 1 = s, 2 = t, 3 = 2048 |
|
int st_to_vec[6][3] = |
|
{ |
|
{3,-1,2}, |
|
{-3,1,2}, |
|
|
|
{1,3,2}, |
|
{-1,-3,2}, |
|
|
|
{-2,-1,3}, // 0 degrees yaw, look straight up |
|
{2,-1,-3} // look straight down |
|
|
|
// {-1,2,3}, |
|
// {1,2,-3} |
|
}; |
|
|
|
// s = [0]/[2], t = [1]/[2] |
|
int vec_to_st[6][3] = |
|
{ |
|
{-2,3,1}, |
|
{2,3,-1}, |
|
|
|
{1,3,2}, |
|
{-1,3,-2}, |
|
|
|
{-2,-1,3}, |
|
{-2,1,-3} |
|
|
|
// {-1,2,3}, |
|
// {1,2,-3} |
|
}; |
|
|
|
float skymins[2][6], skymaxs[2][6]; |
|
float sky_min, sky_max; |
|
|
|
void DrawSkyPolygon (int nump, vec3_t vecs) |
|
{ |
|
int i,j; |
|
vec3_t v, av; |
|
float s, t, dv; |
|
int axis; |
|
float *vp; |
|
|
|
c_sky++; |
|
#if 0 |
|
glBegin (GL_POLYGON); |
|
for (i=0 ; i<nump ; i++, vecs+=3) |
|
{ |
|
VectorAdd(vecs, r_origin, v); |
|
qglVertex3fv (v); |
|
} |
|
glEnd(); |
|
return; |
|
#endif |
|
// 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 < skymins[0][axis]) |
|
skymins[0][axis] = s; |
|
if (t < skymins[1][axis]) |
|
skymins[1][axis] = t; |
|
if (s > skymaxs[0][axis]) |
|
skymaxs[0][axis] = s; |
|
if (t > skymaxs[1][axis]) |
|
skymaxs[1][axis] = t; |
|
} |
|
} |
|
|
|
#define ON_EPSILON 0.1 // point on plane side epsilon |
|
#define MAX_CLIP_VERTS 64 |
|
void ClipSkyPolygon (int nump, vec3_t vecs, int stage) |
|
{ |
|
float *norm; |
|
float *v; |
|
qboolean front, back; |
|
float d, e; |
|
float dists[MAX_CLIP_VERTS]; |
|
int sides[MAX_CLIP_VERTS]; |
|
vec3_t newv[2][MAX_CLIP_VERTS]; |
|
int newc[2]; |
|
int i, j; |
|
|
|
if (nump > MAX_CLIP_VERTS-2) |
|
ri.Sys_Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); |
|
if (stage == 6) |
|
{ // fully clipped, so draw it |
|
DrawSkyPolygon (nump, vecs); |
|
return; |
|
} |
|
|
|
front = back = false; |
|
norm = skyclip[stage]; |
|
for (i=0, v = vecs ; i<nump ; i++, v+=3) |
|
{ |
|
d = DotProduct (v, norm); |
|
if (d > ON_EPSILON) |
|
{ |
|
front = true; |
|
sides[i] = SIDE_FRONT; |
|
} |
|
else if (d < -ON_EPSILON) |
|
{ |
|
back = true; |
|
sides[i] = SIDE_BACK; |
|
} |
|
else |
|
sides[i] = SIDE_ON; |
|
dists[i] = d; |
|
} |
|
|
|
if (!front || !back) |
|
{ // not clipped |
|
ClipSkyPolygon (nump, vecs, stage+1); |
|
return; |
|
} |
|
|
|
// clip it |
|
sides[i] = sides[0]; |
|
dists[i] = dists[0]; |
|
VectorCopy (vecs, (vecs+(i*3)) ); |
|
newc[0] = newc[1] = 0; |
|
|
|
for (i=0, v = vecs ; 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); |
|
} |
|
|
|
/* |
|
================= |
|
R_AddSkySurface |
|
================= |
|
*/ |
|
void R_AddSkySurface (msurface_t *fa) |
|
{ |
|
int i; |
|
vec3_t verts[MAX_CLIP_VERTS]; |
|
glpoly_t *p; |
|
|
|
// calculate vertex values for sky box |
|
for (p=fa->polys ; p ; p=p->next) |
|
{ |
|
for (i=0 ; i<p->numverts ; i++) |
|
{ |
|
VectorSubtract (p->verts[i], r_origin, verts[i]); |
|
} |
|
ClipSkyPolygon (p->numverts, verts[0], 0); |
|
} |
|
} |
|
|
|
|
|
/* |
|
============== |
|
R_ClearSkyBox |
|
============== |
|
*/ |
|
void R_ClearSkyBox (void) |
|
{ |
|
int i; |
|
|
|
for (i=0 ; i<6 ; i++) |
|
{ |
|
skymins[0][i] = skymins[1][i] = 9999; |
|
skymaxs[0][i] = skymaxs[1][i] = -9999; |
|
} |
|
} |
|
|
|
|
|
void MakeSkyVec (float s, float t, int axis) |
|
{ |
|
vec3_t v, b; |
|
int j, k; |
|
|
|
b[0] = s*2300; |
|
b[1] = t*2300; |
|
b[2] = 2300; |
|
|
|
for (j=0 ; j<3 ; j++) |
|
{ |
|
k = st_to_vec[axis][j]; |
|
if (k < 0) |
|
v[j] = -b[-k - 1]; |
|
else |
|
v[j] = b[k - 1]; |
|
} |
|
|
|
// avoid bilerp seam |
|
s = (s+1)*0.5; |
|
t = (t+1)*0.5; |
|
|
|
if (s < sky_min) |
|
s = sky_min; |
|
else if (s > sky_max) |
|
s = sky_max; |
|
if (t < sky_min) |
|
t = sky_min; |
|
else if (t > sky_max) |
|
t = sky_max; |
|
|
|
t = 1.0 - t; |
|
qglTexCoord2f (s, t); |
|
qglVertex3fv (v); |
|
} |
|
|
|
/* |
|
============== |
|
R_DrawSkyBox |
|
============== |
|
*/ |
|
int skytexorder[6] = {0,2,1,3,4,5}; |
|
void R_DrawSkyBox (void) |
|
{ |
|
int i; |
|
|
|
#if 0 |
|
qglEnable (GL_BLEND); |
|
GL_TexEnv( GL_MODULATE ); |
|
qglColor4f (1,1,1,0.5); |
|
qglDisable (GL_DEPTH_TEST); |
|
#endif |
|
if (skyrotate) |
|
{ // check for no sky at all |
|
for (i=0 ; i<6 ; i++) |
|
if (skymins[0][i] < skymaxs[0][i] |
|
&& skymins[1][i] < skymaxs[1][i]) |
|
break; |
|
if (i == 6) |
|
return; // nothing visible |
|
} |
|
|
|
qglPushMatrix (); |
|
qglTranslatef (r_origin[0], r_origin[1], r_origin[2]); |
|
qglRotatef (r_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]); |
|
|
|
for (i=0 ; i<6 ; i++) |
|
{ |
|
if (skyrotate) |
|
{ // hack, forces full sky to draw when rotating |
|
skymins[0][i] = -1; |
|
skymins[1][i] = -1; |
|
skymaxs[0][i] = 1; |
|
skymaxs[1][i] = 1; |
|
} |
|
|
|
if (skymins[0][i] >= skymaxs[0][i] |
|
|| skymins[1][i] >= skymaxs[1][i]) |
|
continue; |
|
|
|
GL_Bind (sky_images[skytexorder[i]]->texnum); |
|
|
|
qglBegin (GL_QUADS); |
|
MakeSkyVec (skymins[0][i], skymins[1][i], i); |
|
MakeSkyVec (skymins[0][i], skymaxs[1][i], i); |
|
MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i); |
|
MakeSkyVec (skymaxs[0][i], skymins[1][i], i); |
|
qglEnd (); |
|
} |
|
qglPopMatrix (); |
|
#if 0 |
|
glDisable (GL_BLEND); |
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
|
glColor4f (1,1,1,0.5); |
|
glEnable (GL_DEPTH_TEST); |
|
#endif |
|
} |
|
|
|
|
|
/* |
|
============ |
|
R_SetSky |
|
============ |
|
*/ |
|
// 3dstudio environment map names |
|
char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; |
|
void R_SetSky (char *name, float rotate, vec3_t axis) |
|
{ |
|
int i; |
|
char pathname[MAX_QPATH]; |
|
|
|
strncpy (skyname, name, sizeof(skyname)-1); |
|
skyrotate = rotate; |
|
VectorCopy (axis, skyaxis); |
|
|
|
for (i=0 ; i<6 ; i++) |
|
{ |
|
// chop down rotating skies for less memory |
|
if (gl_skymip->value || skyrotate) |
|
gl_picmip->value++; |
|
|
|
if ( qglColorTableEXT && gl_ext_palettedtexture->value ) |
|
Com_sprintf (pathname, sizeof(pathname), "env/%s%s.pcx", skyname, suf[i]); |
|
else |
|
Com_sprintf (pathname, sizeof(pathname), "env/%s%s.tga", skyname, suf[i]); |
|
|
|
sky_images[i] = GL_FindImage (pathname, it_sky); |
|
if (!sky_images[i]) |
|
sky_images[i] = r_notexture; |
|
|
|
if (gl_skymip->value || skyrotate) |
|
{ // take less memory |
|
gl_picmip->value--; |
|
sky_min = 1.0/256; |
|
sky_max = 255.0/256; |
|
} |
|
else |
|
{ |
|
sky_min = 1.0/512; |
|
sky_max = 511.0/512; |
|
} |
|
} |
|
}
|
|
|