/* Copyright (C) 1996-1997 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 "quakedef.h" extern model_t *loadmodel; extern msurface_t *skychain; extern msurface_t **skychain_tail; extern glpoly_t *caustics_polys; extern glpoly_t *detail_polys; extern glpoly_t *chrome_polys; extern glpoly_t *glass_polys; static int solidskytexture, alphaskytexture; static float speedscale, speedscale2; // for top sky and bottom sky static msurface_t *warpface; static float sky_fog[4]; static int spherelist = -1; qboolean skyboxloaded; 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 maxs[j]) maxs[j] = *v; } } void SubdividePolygon (int numverts, float *verts) { int i, j, k, f, b; vec3_t mins, maxs, front[64], back[64]; float m, *v, dist[64], frac, s, t, subdivide_size; glpoly_t *poly; if (numverts > 60) Sys_Error ("numverts = %i", numverts); subdivide_size = max(1, gl_subdivide_size.value); 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= 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; } poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); poly->next = warpface->polys; warpface->polys = poly; poly->numverts = numverts; for (i=0 ; iverts[i]); s = DotProduct (verts, warpface->texinfo->vecs[0]); t = DotProduct (verts, warpface->texinfo->vecs[1]); poly->verts[i][3] = s; poly->verts[i][4] = t; } } /* ================ 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, i, lindex; float *vec; warpface = fa; // convert edges back to a normal polygon numverts = 0; for (i=0 ; inumedges ; 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]); } //========================================================= double EWP_1D64 = 0.015625; // speed up sin calculations - Ed float turbsin[] = { #include "gl_warp_sin.h" }; #define TURBSINSIZE (sizeof(turbsin) / sizeof(turbsin[0])) #define TURBSCALE ((float)TURBSINSIZE / (2 * M_PI)) void EmitFlatPoly (msurface_t *fa) { glpoly_t *p; float *v; int i; for (p = fa->polys ; p ; p = p->next) { glBegin (GL_TRIANGLE_FAN); for (i=0, v = p->verts[0] ; i < p->numverts ; i++, v += VERTEXSIZE) { glVertex3fv (v); } glEnd (); } } /* ============= EmitWaterPolys Does a water warp on the pre-fragmented glpoly_t chain ============= */ void EmitWaterPolys (msurface_t *fa) { glpoly_t *p; float *v, s, t, os, ot; vec3_t nv; int i; for (p=fa->polys ; p ; p = p->next) { glBegin (GL_TRIANGLE_FAN); for (i=0, v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) { os = v[3]; ot = v[4]; s = os + turbsin[(int)((ot * 0.125 + realtime) * TURBSCALE) & (TURBSINSIZE-1)]; s *= EWP_1D64; t = ot + turbsin[(int)((os * 0.125 + realtime) * TURBSCALE) & (TURBSINSIZE-1)]; t *= EWP_1D64; nv[0] = v[0]; nv[1] = v[1]; nv[2] = v[2]; // keep this clamped or else !!! if (r_waterripple.value) { nv[2] = v[2] + bound(0, r_waterripple.value, 3)*sin(v[0]*0.02+realtime)*sin(v[1]*0.02+realtime)*sin(v[2]*0.02+realtime); } if (gl_mtexable) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, s, t); qglMultiTexCoord2f (GL_TEXTURE1_ARB, s, t); } else { glTexCoord2f (s, t); } glVertex3fv (nv); } glEnd (); } } /* ============= EmitSkyPolys ============= */ void EmitSkyPolys (msurface_t *fa, qboolean mtex) { glpoly_t *p; float *v, s, t, ss, tt, length; int i; vec3_t dir; for (p = fa->polys ; p ; p = p->next) { glBegin (GL_TRIANGLE_FAN); for (i=0, v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) { VectorSubtract (v, r_origin, dir); dir[2] *= 3; // flatten the sphere length = VectorLength (dir); length = 6 * 63 / length; dir[0] *= length; dir[1] *= length; if (mtex) { s = (speedscale + dir[0]) * (1.0 / 128); t = (speedscale + dir[1]) * (1.0 / 128); ss = (speedscale2 + dir[0]) * (1.0 / 128); tt = (speedscale2 + dir[1]) * (1.0 / 128); } else { s = (speedscale + dir[0]) * (1.0 / 128); t = (speedscale + dir[1]) * (1.0 / 128); } if (mtex) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, s, t); qglMultiTexCoord2f (GL_TEXTURE1_ARB, ss, tt); } else { glTexCoord2f (s, t); } glVertex3fv (v); } glEnd (); } } /* ================= R_DrawSkyChain Hot damn Jethro, this sho' is purty!!! ================= */ void R_DrawSkyChain (void) { // sky scaling float fullscale = r_farclip.value / 4096; float halfscale = r_farclip.value / 8192; float reducedfull = (r_farclip.value / 4096) * 0.9; float reducedhalf = (r_farclip.value / 8192) * 0.9; // rotation float rotateBack = anglemod (realtime * 12); float rotateFore = anglemod (realtime * 20); if (!skychain) return; skychain = NULL; // JoeQuake specific!!! skychain_tail = &skychain; // turn on fogging. fog looks good on the skies - it gives them a more // "airy" far-away look, and has the knock-on effect of preventing the // old "texture distortion at the poles" problem. glFogi (GL_FOG_MODE, GL_LINEAR); glFogfv (GL_FOG_COLOR, sky_fog); glFogf (GL_FOG_START, 5); // must tweak the fog end too!!! glFogf (GL_FOG_END, r_farclip.value * 0.75); glEnable (GL_FOG); // sky texture scaling // previous releases made a tiled version of the sky texture. here i just shrink it using the // texture matrix, for much the same effect glMatrixMode (GL_TEXTURE); glLoadIdentity (); glScalef (2, 1, 1); glMatrixMode (GL_MODELVIEW); // background // ========== // go to a new matrix glPushMatrix (); // center it on the players position glTranslatef (r_origin[0], r_origin[1], r_origin[2]); // flatten the sphere glScalef (fullscale, fullscale, halfscale); // orient it so that the poles are unobtrusive glRotatef (-90, 1, 0, 0); // make it not always at right angles to the player glRotatef (-22, 0 ,1, 0); // rotate it around the poles glRotatef (-rotateBack, 0, 0, 1); // solid sky texture glBindTexture (GL_TEXTURE_2D, solidskytexture); // draw the sphere glCallList (spherelist); // restore the previous matrix glPopMatrix (); // foreground // ========== // go to a new matrix glPushMatrix (); // center it on the players position glTranslatef (r_origin[0], r_origin[1], r_origin[2]); // flatten the sphere and shrink it a little - the reduced scale prevents artefacts appearing when // corners on the skysphere may potentially overlap glScalef (reducedfull, reducedfull, reducedhalf); // orient it so that the poles are unobtrusive glRotatef (-90, 1, 0, 0); // make it not always at right angles to the player glRotatef (-22, 0 ,1, 0); // rotate it around the poles glRotatef (-rotateFore, 0, 0, 1); // blend mode glEnable (GL_BLEND); // alpha sky texture glBindTexture (GL_TEXTURE_2D, alphaskytexture); // draw the sphere glCallList (spherelist); // back to normal mode glDisable (GL_BLEND); // restore the previous matrix glPopMatrix (); // turn off fog glDisable (GL_FOG); glMatrixMode (GL_TEXTURE); glLoadIdentity (); glMatrixMode (GL_MODELVIEW); } //=============================================================== /* ============== R_DrawSphere Draw a sphere!!! The verts and texcoords are precalculated for extra efficiency. The sphere is put into a display list to reduce overhead even further. ============== */ float skytexes[440]; float skyverts[660]; void R_DrawSphere (void) { if (spherelist == -1) { int i; int j; int vertspos = 0; int texespos = 0; // build the sphere display list spherelist = glGenLists (1); glNewList (spherelist, GL_COMPILE); for (i = 0; i < 10; i++) { glBegin (GL_TRIANGLE_STRIP); for (j = 0; j <= 10; j++) { glTexCoord2fv (&skytexes[texespos]); glVertex3fv (&skyverts[vertspos]); texespos += 2; vertspos += 3; glTexCoord2fv (&skytexes[texespos]); glVertex3fv (&skyverts[vertspos]); texespos += 2; vertspos += 3; } glEnd (); } glEndList (); } } /* ============== R_InitSkyChain Initialize the sky chain arrays ============== */ void R_InitSkyChain (void) { float drho = 0.3141592653589; float dtheta = 0.6283185307180; float ds = 0.1; float dt = 0.1; float t = 1.0f; float s = 0.0f; int i; int j; int vertspos = 0; int texespos = 0; for (i = 0; i < 10; i++) { float rho = (float) i * drho; float srho = (float) (sin (rho)); float crho = (float) (cos (rho)); float srhodrho = (float) (sin (rho + drho)); float crhodrho = (float) (cos (rho + drho)); s = 0.0f; for (j = 0; j <= 10; j++) { float theta = (j == 10) ? 0.0f : j * dtheta; float stheta = (float) (-sin( theta)); float ctheta = (float) (cos (theta)); skytexes[texespos++] = s * 2; skytexes[texespos++] = t * 2; skyverts[vertspos++] = stheta * srho * 4096.0; skyverts[vertspos++] = ctheta * srho * 4096.0; skyverts[vertspos++] = crho * 4096.0; skytexes[texespos++] = s * 2; skytexes[texespos++] = (t - dt) * 2; skyverts[vertspos++] = stheta * srhodrho * 4096.0; skyverts[vertspos++] = ctheta * srhodrho * 4096.0; skyverts[vertspos++] = crhodrho * 4096.0; s += ds; } t -= dt; } } /* ============= R_InitSky A sky texture is 256*128, with the right side being a masked overlay ============== */ void R_InitSky (miptex_t *mt) { int i, j, p; byte *src; unsigned transpix; int r, g, b; unsigned *rgba; unsigned topalpha; int div; unsigned *trans; // initialize our pointers trans = malloc (128 * 128 * 4); // ----------------- // solid sky texture // ----------------- src = (byte *)mt + mt->offsets[0]; // make an average value for the back to avoid // a fringe on the top level r = g = b = 0; for (i=0 ; i<128 ; i++) { for (j=0 ; j<128 ; j++) { p = src[i*256 + j + 128]; rgba = &d_8to24table[p]; trans[(i*128) + j] = *rgba; r += ((byte *)rgba)[0]; g += ((byte *)rgba)[1]; b += ((byte *)rgba)[2]; } } ((byte *)&transpix)[0] = r/(128*128); ((byte *)&transpix)[1] = g/(128*128); ((byte *)&transpix)[2] = b/(128*128); ((byte *)&transpix)[3] = 0; if (!solidskytexture) solidskytexture = texture_extension_number++; glBindTexture (GL_TEXTURE_2D, solidskytexture); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // ----------------- // alpha sky texture // ----------------- // get another average cos the bottom layer average can be too dark div = r = g = b = 0; for (i=0 ; i<128 ; i++) { for (j=0 ; j<128 ; j++) { p = src[i*256 + j]; if (p == 0) topalpha = transpix; else { rgba = &d_8to24table[p]; g += ((byte *)rgba)[1]; r += ((byte *)rgba)[0]; b += ((byte *)rgba)[2]; div++; topalpha = d_8to24table[p]; } ((byte *)&topalpha)[3] = ((byte *)&topalpha)[3] / 2; trans[(i*128) + j] = topalpha; } } if (!alphaskytexture) alphaskytexture = texture_extension_number++; glBindTexture (GL_TEXTURE_2D, alphaskytexture); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // get fog colours for the sky - we halve these sky_fog[0] = ((float)r / (float)div) / 512.0; sky_fog[1] = ((float)g / (float)div) / 512.0; sky_fog[2] = ((float)b / (float)div) / 512.0; sky_fog[3] = 0.1; // free the used memory free (trans); // recalc the sphere display list - delete the original one first!!! if (spherelist != -1) glDeleteLists (spherelist, 1); spherelist = -1; R_InitSkyChain (); R_DrawSphere (); } /* ================== R_SetSky ================== */ char *skybox_ext[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; int R_SetSky (char *skyname) { int i, error = 0; byte *data[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; for (i=0 ; i<6 ; i++) { if (!(data[i] = GL_LoadImagePixels(va("env/%s%s", skyname, skybox_ext[i]), 0, 0, 0)) && !(data[i] = GL_LoadImagePixels(va("gfx/env/%s%s", skyname, skybox_ext[i]), 0, 0, 0)) && !(data[i] = GL_LoadImagePixels(va("env/%s_%s", skyname, skybox_ext[i]), 0, 0, 0)) && !(data[i] = GL_LoadImagePixels(va("gfx/env/%s_%s", skyname, skybox_ext[i]), 0, 0, 0))) { Con_Printf ("Couldn't load skybox \"%s\"\n", skyname); error = 1; goto cleanup; } } for (i=0 ; i<6 ; i++) { GL_Bind (skyboxtextures + i); GL_Upload32 ((unsigned int *)data[i], image_width, image_height, 0); } skyboxloaded = true; cleanup: for (i=0 ; i<6 ; i++) { if (data[i]) { free (data[i]); } if (!data[i]) break; } return error; } qboolean OnChange_r_skybox (cvar_t *var, char *string) { if (!string[0]) { skyboxloaded = false; return false; } if (nehahra) { Cvar_Set ("r_oldsky", "0"); strcpy (prev_skybox, string); } return R_SetSky (string); } static vec3_t skyclip[6] = { {1, 1, 0}, {1, -1, 0}, {0, -1, 1}, {0, 1, 1}, {1, 0, 1}, {-1, 0, 1} }; // 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 }; // 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} }; static float skymins[2][6], skymaxs[2][6]; void DrawSkyPolygon (int nump, vec3_t vecs) { int i, j, axis; vec3_t v, av; float s, t, dv, *vp; // decide which face it maps to VectorClear (v); for (i=0, vp=vecs ; i av[1] && av[0] > av[2]) axis = (v[0] < 0) ? 1 : 0; else if (av[1] > av[2] && av[1] > av[0]) axis = (v[1] < 0) ? 3 : 2; else axis = (v[2] < 0) ? 5 : 4; // project new texture coords for (i=0 ; i 0) ? vecs[j - 1] : -vecs[-j - 1]; j = vec_to_st[axis][0]; s = (j < 0) ? -vecs[-j -1] / dv : vecs[j-1] / dv; j = vec_to_st[axis][1]; t = (j < 0) ? -vecs[-j -1] / dv : 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 MAX_CLIP_VERTS 64 void ClipSkyPolygon (int nump, vec3_t vecs, int stage) { float *norm, *v, d, e, dists[MAX_CLIP_VERTS]; qboolean front, back; int sides[MAX_CLIP_VERTS], newc[2], i, j; vec3_t newv[2][MAX_CLIP_VERTS]; if (nump > MAX_CLIP_VERTS-2) Sys_Error ("ClipSkyPolygon: nump > MAX_CLIP_VERTS - 2"); if (stage == 6) { // fully clipped, so draw it DrawSkyPolygon (nump, vecs); return; } front = back = false; norm = skyclip[stage]; for (i=0, v=vecs ; i 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 ; ipolys ; p ; p = p->next) { for (i=0 ; inumverts ; 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, farclip; farclip = max((int)r_farclip.value, 4096); b[0] = s * (farclip >> 1); b[1] = t * (farclip >> 1); b[2] = (farclip >> 1); for (j=0 ; j<3 ; j++) { k = st_to_vec[axis][j]; v[j] = (k < 0) ? -b[-k-1] : b[k-1]; v[j] += r_origin[j]; } // avoid bilerp seam s = (s+1) * 0.5; t = (t+1) * 0.5; if (s < 1.0/512) s = 1.0 / 512; else if (s > 511.0/512) s = 511.0 / 512; if (t < 1.0/512) t = 1.0 / 512; else if (t > 511.0/512) t = 511.0 / 512; t = 1.0 - t; glTexCoord2f (s, t); glVertex3fv (v); } /* ============== R_DrawSkyBox ============== */ static int skytexorder[6] = { 0, 2, 1, 3, 4, 5 }; void R_DrawSkyBox (void) { int i; msurface_t *fa; if (!skychain) return; R_ClearSkyBox (); for (fa = skychain ; fa ; fa = fa->texturechain) { R_AddSkyBoxSurface (fa); } GL_DisableMultitexture (); for (i=0 ; i<6 ; i++) { if (skymins[0][i] >= skymaxs[0][i] || skymins[1][i] >= skymaxs[1][i]) continue; GL_Bind (skyboxtextures + skytexorder[i]); skymins[0][i] = -1; skymins[1][i] = -1; skymaxs[0][i] = 1; skymaxs[1][i] = 1; glBegin (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); glEnd (); } glDisable (GL_TEXTURE_2D); glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_ZERO, GL_ONE); for (fa = skychain ; fa ; fa = fa->texturechain) { EmitFlatPoly (fa); } glEnable (GL_TEXTURE_2D); glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); skychain = NULL; skychain_tail = &skychain; } __inline void CalcCausticTexCoords (float *v, float *s, float *t) { float os, ot; os = v[3]; ot = v[4]; *s = os + turbsin[(unsigned int)((0.465 * (realtime + ot)) * TURBSCALE) & (TURBSINSIZE - 1)]; *s *= -3 * (0.5 / 64); *t = ot + turbsin[(unsigned int)((0.465 * (realtime + os)) * TURBSCALE) & (TURBSINSIZE - 1)]; *t *= -3 * (0.5 / 64); } void EmitCausticsPolys (void) { glpoly_t *p; int i; float s, t, *v; if (!caustics_polys) return; GL_Bind (underwatertexture); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glBlendFunc(GL_DST_COLOR, GL_ONE); glEnable (GL_BLEND); for (p = caustics_polys ; p ; p = p->caustics_chain) { glBegin (GL_TRIANGLE_FAN); for (i=0, v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) { CalcCausticTexCoords (v, &s, &t); if (gl_mtexable) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, s, t); qglMultiTexCoord2f (GL_TEXTURE1_ARB, s, t); } else { glTexCoord2f (s, t); } glVertex3fv (v); } glEnd (); } glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); caustics_polys = NULL; } void EmitDetailPolys (void) { glpoly_t *p; int i; float *v; if (!detail_polys) return; GL_Bind (detailtexture); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glEnable (GL_BLEND); glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR); for (p = detail_polys ; p ; p = p->detail_chain) { glBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+=VERTEXSIZE) { if (gl_mtexable) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, v[7]*18, v[8]*18); qglMultiTexCoord2f (GL_TEXTURE1_ARB, v[7]*18, v[8]*18); } else { glTexCoord2f (v[7]*18, v[8]*18); } glVertex3fv (v); } glEnd(); } glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); detail_polys = NULL; } void EmitChromePolys (void) { glpoly_t *p; int i; float *v; if (!chrome_polys) return; GL_Bind (chrometexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glEnable (GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); for (p = chrome_polys ; p ; p = p->chrome_chain) { glBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; inumverts ; i++, v+=VERTEXSIZE) { if (gl_mtexable) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, v[7], v[8]); qglMultiTexCoord2f (GL_TEXTURE1_ARB, v[7], v[8]); } else { glTexCoord2f(v[7], v[8]); } glVertex3fv (v); } glEnd (); } glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glDisable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); chrome_polys = NULL; } void EmitGlassPolys (void) { glpoly_t *p; int i; float *v; if (!glass_polys) return; GL_Bind (glasstexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glEnable (GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); for (p = glass_polys ; p ; p = p->glass_chain) { glBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; inumverts ; i++, v+=VERTEXSIZE) { if (gl_mtexable) { qglMultiTexCoord2f (GL_TEXTURE0_ARB, v[7], v[8]); qglMultiTexCoord2f (GL_TEXTURE1_ARB, v[7], v[8]); } else { glTexCoord2f(v[7], v[8]); } glVertex3fv (v); } glEnd (); } glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glDisable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glass_polys = NULL; }