yquake2remaster/src/refresh/r_warp.c

731 lines
12 KiB
C
Raw Normal View History

/*
* 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.
*
* =======================================================================
*
* Warps. Used on water surfaces und for skybox rotation.
*
* =======================================================================
*/
#include "header/local.h"
2012-07-21 12:09:45 +00:00
#define TURBSCALE (256.0 / (2 * M_PI))
#define SUBDIVIDE_SIZE 64
#define ON_EPSILON 0.1 /* point on plane side epsilon */
#define MAX_CLIP_VERTS 64
extern model_t *loadmodel;
2012-07-21 12:09:45 +00:00
char skyname[MAX_QPATH];
float skyrotate;
vec3_t skyaxis;
2012-07-21 12:09:45 +00:00
image_t *sky_images[6];
msurface_t *warpface;
int skytexorder[6] = {0, 2, 1, 3, 4, 5};
/* 3dstudio environment map names */
2012-07-21 12:09:45 +00:00
char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
float r_turbsin[] = {
#include "constants/warpsin.h"
};
2012-07-21 12:09:45 +00:00
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;
2012-07-21 12:09:45 +00:00
int st_to_vec[6][3] = {
{3, -1, 2},
{-3, 1, 2},
2012-07-21 12:09:45 +00:00
{1, 3, 2},
{-1, -3, 2},
2012-07-21 12:09:45 +00:00
{-2, -1, 3}, /* 0 degrees yaw, look straight up */
{2, -1, -3} /* look straight down */
};
2012-07-21 12:09:45 +00:00
int vec_to_st[6][3] = {
{-2, 3, 1},
{2, 3, -1},
2012-07-21 12:09:45 +00:00
{1, 3, 2},
{-1, 3, -2},
2012-07-21 12:09:45 +00:00
{-2, -1, 3},
{-2, 1, -3}
};
2012-07-21 12:09:45 +00:00
float skymins[2][6], skymaxs[2][6];
float sky_min, sky_max;
void
2012-07-21 12:09:45 +00:00
R_BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
{
int i, j;
2012-07-21 12:09:45 +00:00
float *v;
2012-07-21 12:09:45 +00:00
mins[0] = mins[1] = mins[2] = 9999;
maxs[0] = maxs[1] = maxs[2] = -9999;
v = verts;
2012-07-21 12:09:45 +00:00
for (i = 0; i < numverts; i++)
{
2012-07-21 12:09:45 +00:00
for (j = 0; j < 3; j++, v++)
{
2012-07-21 12:09:45 +00:00
if (*v < mins[j])
{
2012-07-21 12:09:45 +00:00
mins[j] = *v;
}
2012-07-21 12:09:45 +00:00
if (*v > maxs[j])
{
2012-07-21 12:09:45 +00:00
maxs[j] = *v;
}
}
}
}
void
2012-07-21 12:09:45 +00:00
R_SubdividePolygon(int numverts, float *verts)
{
int i, j, k;
vec3_t mins, maxs;
float m;
2012-07-21 12:09:45 +00:00
float *v;
vec3_t front[64], back[64];
int f, b;
2012-07-21 12:09:45 +00:00
float dist[64];
float frac;
2012-07-21 12:09:45 +00:00
glpoly_t *poly;
float s, t;
vec3_t total;
float total_s, total_t;
2012-07-21 12:09:45 +00:00
if (numverts > 60)
{
2012-07-21 12:09:45 +00:00
ri.Sys_Error(ERR_DROP, "numverts = %i", numverts);
}
2012-07-21 12:09:45 +00:00
R_BoundPoly(numverts, verts, mins, maxs);
2012-07-21 12:09:45 +00:00
for (i = 0; i < 3; i++)
{
2012-07-21 12:09:45 +00:00
m = (mins[i] + maxs[i]) * 0.5;
m = SUBDIVIDE_SIZE * floor(m / SUBDIVIDE_SIZE + 0.5);
2012-07-21 12:09:45 +00:00
if (maxs[i] - m < 8)
{
continue;
}
2012-07-21 12:09:45 +00:00
if (m - mins[i] < 8)
{
continue;
}
/* cut it */
v = verts + i;
2012-07-21 12:09:45 +00:00
for (j = 0; j < numverts; j++, v += 3)
{
2012-07-21 12:09:45 +00:00
dist[j] = *v - m;
}
/* wrap cases */
2012-07-21 12:09:45 +00:00
dist[j] = dist[0];
v -= i;
2012-07-21 12:09:45 +00:00
VectorCopy(verts, v);
f = b = 0;
v = verts;
2012-07-21 12:09:45 +00:00
for (j = 0; j < numverts; j++, v += 3)
{
2012-07-21 12:09:45 +00:00
if (dist[j] >= 0)
{
2012-07-21 12:09:45 +00:00
VectorCopy(v, front[f]);
f++;
}
2012-07-21 12:09:45 +00:00
if (dist[j] <= 0)
{
2012-07-21 12:09:45 +00:00
VectorCopy(v, back[b]);
b++;
}
2012-07-21 12:09:45 +00:00
if ((dist[j] == 0) || (dist[j + 1] == 0))
{
continue;
}
2012-07-21 12:09:45 +00:00
if ((dist[j] > 0) != (dist[j + 1] > 0))
{
/* clip point */
2012-07-21 12:09:45 +00:00
frac = dist[j] / (dist[j] - dist[j + 1]);
2012-07-21 12:09:45 +00:00
for (k = 0; k < 3; k++)
{
2012-07-21 12:09:45 +00:00
front[f][k] = back[b][k] = v[k] + frac * (v[3 + k] - v[k]);
}
f++;
b++;
}
}
2012-07-21 12:09:45 +00:00
R_SubdividePolygon(f, front[0]);
R_SubdividePolygon(b, back[0]);
return;
}
/* add a point in the center to help keep warp valid */
2012-07-21 12:09:45 +00:00
poly = Hunk_Alloc(sizeof(glpoly_t) + ((numverts - 4) + 2) * VERTEXSIZE * sizeof(float));
poly->next = warpface->polys;
warpface->polys = poly;
poly->numverts = numverts + 2;
2012-07-21 12:09:45 +00:00
VectorClear(total);
total_s = 0;
total_t = 0;
2012-07-21 12:09:45 +00:00
for (i = 0; i < numverts; i++, verts += 3)
{
2012-07-21 12:09:45 +00:00
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;
2012-07-21 12:09:45 +00:00
VectorAdd(total, verts, total);
2012-07-21 12:09:45 +00:00
poly->verts[i + 1][3] = s;
poly->verts[i + 1][4] = t;
}
2012-07-21 12:09:45 +00:00
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 */
2012-07-21 12:09:45 +00:00
memcpy(poly->verts[i + 1], poly->verts[1], sizeof(poly->verts[0]));
}
/*
* Breaks a polygon up along axial 64 unit
* boundaries so that turbulent and sky warps
* can be done reasonably.
*/
void
2012-07-21 12:09:45 +00:00
R_SubdivideSurface(msurface_t *fa)
{
2012-07-21 12:09:45 +00:00
vec3_t verts[64];
int numverts;
int i;
int lindex;
2012-07-21 12:09:45 +00:00
float *vec;
warpface = fa;
/* convert edges back to a normal polygon */
numverts = 0;
2012-07-21 12:09:45 +00:00
for (i = 0; i < fa->numedges; i++)
{
2012-07-21 12:09:45 +00:00
lindex = loadmodel->surfedges[fa->firstedge + i];
2012-07-21 12:09:45 +00:00
if (lindex > 0)
{
2012-07-21 12:09:45 +00:00
vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
}
else
{
2012-07-21 12:09:45 +00:00
vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
}
2012-07-21 12:09:45 +00:00
VectorCopy(vec, verts[numverts]);
numverts++;
}
2012-07-21 12:09:45 +00:00
R_SubdividePolygon(numverts, verts[0]);
}
/*
* Does a water warp on the pre-fragmented glpoly_t chain
*/
void
2012-07-21 12:09:45 +00:00
R_EmitWaterPolys(msurface_t *fa)
{
2012-07-21 12:09:45 +00:00
glpoly_t *p, *bp;
float *v;
int i;
float s, t, os, ot;
float scroll;
float rdt = r_newrefdef.time;
2012-07-21 12:09:45 +00:00
if (fa->texinfo->flags & SURF_FLOWING)
{
2012-07-21 12:09:45 +00:00
scroll = -64 * ((r_newrefdef.time * 0.5) - (int)(r_newrefdef.time * 0.5));
}
else
{
scroll = 0;
}
2012-07-21 12:09:45 +00:00
for (bp = fa->polys; bp; bp = bp->next)
{
p = bp;
2012-07-21 12:09:45 +00:00
qglBegin(GL_TRIANGLE_FAN);
2012-07-21 12:09:45 +00:00
for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE)
{
2012-07-21 12:09:45 +00:00
os = v[3];
ot = v[4];
2012-07-21 12:09:45 +00:00
s = os + r_turbsin[(int)((ot * 0.125 + r_newrefdef.time) * TURBSCALE) & 255];
s += scroll;
2012-07-21 12:09:45 +00:00
s *= (1.0 / 64);
2012-07-21 12:09:45 +00:00
t = ot + r_turbsin[(int)((os * 0.125 + rdt) * TURBSCALE) & 255];
t *= (1.0 / 64);
2012-07-21 12:09:45 +00:00
qglTexCoord2f(s, t);
qglVertex3fv(v);
}
qglEnd();
}
}
void
2012-07-21 12:09:45 +00:00
R_DrawSkyPolygon(int nump, vec3_t vecs)
{
int i, j;
vec3_t v, av;
float s, t, dv;
int axis;
2012-07-21 12:09:45 +00:00
float *vp;
c_sky++;
2009-03-05 13:08:47 +00:00
/* decide which face it maps to */
2012-07-21 12:09:45 +00:00
VectorCopy(vec3_origin, v);
2012-07-21 12:09:45 +00:00
for (i = 0, vp = vecs; i < nump; i++, vp += 3)
{
2012-07-21 12:09:45 +00:00
VectorAdd(vp, v, v);
}
2012-07-21 12:09:45 +00:00
av[0] = fabs(v[0]);
av[1] = fabs(v[1]);
av[2] = fabs(v[2]);
2012-07-21 12:09:45 +00:00
if ((av[0] > av[1]) && (av[0] > av[2]))
{
2012-07-21 12:09:45 +00:00
if (v[0] < 0)
{
axis = 1;
}
else
{
axis = 0;
}
}
2012-07-21 12:09:45 +00:00
else if ((av[1] > av[2]) && (av[1] > av[0]))
{
2012-07-21 12:09:45 +00:00
if (v[1] < 0)
{
axis = 3;
}
else
{
axis = 2;
}
}
else
{
2012-07-21 12:09:45 +00:00
if (v[2] < 0)
{
axis = 5;
}
else
{
axis = 4;
}
}
/* project new texture coords */
2012-07-21 12:09:45 +00:00
for (i = 0; i < nump; i++, vecs += 3)
{
2012-07-21 12:09:45 +00:00
j = vec_to_st[axis][2];
2012-07-21 12:09:45 +00:00
if (j > 0)
{
2012-07-21 12:09:45 +00:00
dv = vecs[j - 1];
}
else
{
2012-07-21 12:09:45 +00:00
dv = -vecs[-j - 1];
}
2012-07-21 12:09:45 +00:00
if (dv < 0.001)
{
continue; /* don't divide by zero */
}
2012-07-21 12:09:45 +00:00
j = vec_to_st[axis][0];
2012-07-21 12:09:45 +00:00
if (j < 0)
{
2012-07-21 12:09:45 +00:00
s = -vecs[-j - 1] / dv;
}
else
{
2012-07-21 12:09:45 +00:00
s = vecs[j - 1] / dv;
}
2012-07-21 12:09:45 +00:00
j = vec_to_st[axis][1];
2012-07-21 12:09:45 +00:00
if (j < 0)
{
2012-07-21 12:09:45 +00:00
t = -vecs[-j - 1] / dv;
}
else
{
2012-07-21 12:09:45 +00:00
t = vecs[j - 1] / dv;
}
2012-07-21 12:09:45 +00:00
if (s < skymins[0][axis])
{
2012-07-21 12:09:45 +00:00
skymins[0][axis] = s;
}
2012-07-21 12:09:45 +00:00
if (t < skymins[1][axis])
{
2012-07-21 12:09:45 +00:00
skymins[1][axis] = t;
}
2012-07-21 12:09:45 +00:00
if (s > skymaxs[0][axis])
{
2012-07-21 12:09:45 +00:00
skymaxs[0][axis] = s;
}
2012-07-21 12:09:45 +00:00
if (t > skymaxs[1][axis])
{
2012-07-21 12:09:45 +00:00
skymaxs[1][axis] = t;
}
}
}
void
2012-07-21 12:09:45 +00:00
R_ClipSkyPolygon(int nump, vec3_t vecs, int stage)
{
2012-07-21 12:09:45 +00:00
float *norm;
float *v;
qboolean front, back;
float d, e;
2012-07-21 12:09:45 +00:00
float dists[MAX_CLIP_VERTS];
int sides[MAX_CLIP_VERTS];
vec3_t newv[2][MAX_CLIP_VERTS];
int newc[2];
int i, j;
2012-07-21 12:09:45 +00:00
if (nump > MAX_CLIP_VERTS - 2)
{
2012-07-21 12:09:45 +00:00
ri.Sys_Error(ERR_DROP, "R_ClipSkyPolygon: MAX_CLIP_VERTS");
}
2012-07-21 12:09:45 +00:00
if (stage == 6)
{
/* fully clipped, so draw it */
2012-07-21 12:09:45 +00:00
R_DrawSkyPolygon(nump, vecs);
return;
}
front = back = false;
2012-07-21 12:09:45 +00:00
norm = skyclip[stage];
2012-07-21 12:09:45 +00:00
for (i = 0, v = vecs; i < nump; i++, v += 3)
{
2012-07-21 12:09:45 +00:00
d = DotProduct(v, norm);
2012-07-21 12:09:45 +00:00
if (d > ON_EPSILON)
{
front = true;
2012-07-21 12:09:45 +00:00
sides[i] = SIDE_FRONT;
}
2012-07-21 12:09:45 +00:00
else if (d < -ON_EPSILON)
{
back = true;
2012-07-21 12:09:45 +00:00
sides[i] = SIDE_BACK;
}
else
{
2012-07-21 12:09:45 +00:00
sides[i] = SIDE_ON;
}
2012-07-21 12:09:45 +00:00
dists[i] = d;
}
2012-07-21 12:09:45 +00:00
if (!front || !back)
{
/* not clipped */
2012-07-21 12:09:45 +00:00
R_ClipSkyPolygon(nump, vecs, stage + 1);
return;
}
/* clip it */
2012-07-21 12:09:45 +00:00
sides[i] = sides[0];
dists[i] = dists[0];
VectorCopy(vecs, (vecs + (i * 3)));
newc[0] = newc[1] = 0;
2012-07-21 12:09:45 +00:00
for (i = 0, v = vecs; i < nump; i++, v += 3)
{
2012-07-21 12:09:45 +00:00
switch (sides[i])
{
case SIDE_FRONT:
2012-07-21 12:09:45 +00:00
VectorCopy(v, newv[0][newc[0]]);
newc[0]++;
break;
case SIDE_BACK:
2012-07-21 12:09:45 +00:00
VectorCopy(v, newv[1][newc[1]]);
newc[1]++;
break;
case SIDE_ON:
2012-07-21 12:09:45 +00:00
VectorCopy(v, newv[0][newc[0]]);
newc[0]++;
VectorCopy(v, newv[1][newc[1]]);
newc[1]++;
break;
}
2012-07-21 12:09:45 +00:00
if ((sides[i] == SIDE_ON) ||
(sides[i + 1] == SIDE_ON) ||
(sides[i + 1] == sides[i]))
{
continue;
}
2012-07-21 12:09:45 +00:00
d = dists[i] / (dists[i] - dists[i + 1]);
2012-07-21 12:09:45 +00:00
for (j = 0; j < 3; j++)
{
2012-07-21 12:09:45 +00:00
e = v[j] + d * (v[j + 3] - v[j]);
newv[0][newc[0]][j] = e;
newv[1][newc[1]][j] = e;
}
2012-07-21 12:09:45 +00:00
newc[0]++;
newc[1]++;
}
/* continue */
2012-07-21 12:09:45 +00:00
R_ClipSkyPolygon(newc[0], newv[0][0], stage + 1);
R_ClipSkyPolygon(newc[1], newv[1][0], stage + 1);
}
void
2012-07-21 12:09:45 +00:00
R_AddSkySurface(msurface_t *fa)
{
int i;
2012-07-21 12:09:45 +00:00
vec3_t verts[MAX_CLIP_VERTS];
glpoly_t *p;
/* calculate vertex values for sky box */
2012-07-21 12:09:45 +00:00
for (p = fa->polys; p; p = p->next)
{
2012-07-21 12:09:45 +00:00
for (i = 0; i < p->numverts; i++)
{
2012-07-21 12:09:45 +00:00
VectorSubtract(p->verts[i], r_origin, verts[i]);
}
2012-07-21 12:09:45 +00:00
R_ClipSkyPolygon(p->numverts, verts[0], 0);
}
}
void
2012-07-21 12:09:45 +00:00
R_ClearSkyBox(void)
{
int i;
2012-07-21 12:09:45 +00:00
for (i = 0; i < 6; i++)
{
2012-07-21 12:09:45 +00:00
skymins[0][i] = skymins[1][i] = 9999;
skymaxs[0][i] = skymaxs[1][i] = -9999;
}
}
void
2012-07-21 12:09:45 +00:00
R_MakeSkyVec(float s, float t, int axis)
{
vec3_t v, b;
int j, k;
2012-07-21 12:09:45 +00:00
if (gl_farsee->value == 0)
{
b[0] = s * 2300;
b[1] = t * 2300;
b[2] = 2300;
}
else
{
b[0] = s * 4096;
b[1] = t * 4096;
b[2] = 4096;
2012-03-08 11:24:45 +00:00
}
2012-07-21 12:09:45 +00:00
for (j = 0; j < 3; j++)
{
2012-07-21 12:09:45 +00:00
k = st_to_vec[axis][j];
2012-07-21 12:09:45 +00:00
if (k < 0)
{
2012-07-21 12:09:45 +00:00
v[j] = -b[-k - 1];
}
else
{
2012-07-21 12:09:45 +00:00
v[j] = b[k - 1];
}
}
/* avoid bilerp seam */
2012-07-21 12:09:45 +00:00
s = (s + 1) * 0.5;
t = (t + 1) * 0.5;
2012-07-21 12:09:45 +00:00
if (s < sky_min)
{
s = sky_min;
}
2012-07-21 12:09:45 +00:00
else if (s > sky_max)
{
s = sky_max;
}
2012-07-21 12:09:45 +00:00
if (t < sky_min)
{
t = sky_min;
}
2012-07-21 12:09:45 +00:00
else if (t > sky_max)
{
t = sky_max;
}
t = 1.0 - t;
2012-07-21 12:09:45 +00:00
qglTexCoord2f(s, t);
qglVertex3fv(v);
}
void
2012-07-21 12:09:45 +00:00
R_DrawSkyBox(void)
{
int i;
2012-07-21 12:09:45 +00:00
if (skyrotate)
{ /* check for no sky at all */
2012-07-21 12:09:45 +00:00
for (i = 0; i < 6; i++)
{
2012-07-21 12:09:45 +00:00
if ((skymins[0][i] < skymaxs[0][i]) &&
(skymins[1][i] < skymaxs[1][i]))
{
break;
}
}
2012-07-21 12:09:45 +00:00
if (i == 6)
{
return; /* nothing visible */
}
}
qglPushMatrix();
2012-07-21 12:09:45 +00:00
qglTranslatef(r_origin[0], r_origin[1], r_origin[2]);
qglRotatef(r_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]);
2012-07-21 12:09:45 +00:00
for (i = 0; i < 6; i++)
{
2012-07-21 12:09:45 +00:00
if (skyrotate)
{
2012-07-21 12:09:45 +00:00
skymins[0][i] = -1;
skymins[1][i] = -1;
skymaxs[0][i] = 1;
skymaxs[1][i] = 1;
}
2012-07-21 12:09:45 +00:00
if ((skymins[0][i] >= skymaxs[0][i]) ||
(skymins[1][i] >= skymaxs[1][i]))
{
continue;
}
2012-07-21 12:09:45 +00:00
R_Bind(sky_images[skytexorder[i]]->texnum);
2012-07-21 12:09:45 +00:00
qglBegin(GL_QUADS);
R_MakeSkyVec(skymins[0][i], skymins[1][i], i);
R_MakeSkyVec(skymins[0][i], skymaxs[1][i], i);
R_MakeSkyVec(skymaxs[0][i], skymaxs[1][i], i);
R_MakeSkyVec(skymaxs[0][i], skymins[1][i], i);
qglEnd();
}
qglPopMatrix();
}
void
2012-07-21 12:09:45 +00:00
R_SetSky(char *name, float rotate, vec3_t axis)
{
int i;
2012-07-21 12:09:45 +00:00
char pathname[MAX_QPATH];
2012-07-21 12:09:45 +00:00
strncpy(skyname, name, sizeof(skyname) - 1);
skyrotate = rotate;
2012-07-21 12:09:45 +00:00
VectorCopy(axis, skyaxis);
2012-07-21 12:09:45 +00:00
for (i = 0; i < 6; i++)
{
2012-07-21 12:09:45 +00:00
if (qglColorTableEXT && gl_ext_palettedtexture->value)
{
2012-07-21 12:09:45 +00:00
Com_sprintf(pathname, sizeof(pathname), "env/%s%s.pcx",
skyname, suf[i]);
}
else
{
2012-07-21 12:09:45 +00:00
Com_sprintf(pathname, sizeof(pathname), "env/%s%s.tga",
skyname, suf[i]);
}
2012-07-21 12:09:45 +00:00
sky_images[i] = R_FindImage(pathname, it_sky);
2012-07-21 12:09:45 +00:00
if (!sky_images[i])
{
2012-07-21 12:09:45 +00:00
sky_images[i] = r_notexture;
}
sky_min = 1.0 / 512;
sky_max = 511.0 / 512;
}
}
2012-07-21 12:09:45 +00:00