| /* |
| 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; |
| |
| //int skytexturenum; |
| |
| int solidskytexture; |
| int alphaskytexture; |
| float speedscale; // for top sky and bottom sky |
| |
| msurface_t *warpface; |
| |
| extern cvar_t gl_subdivide_size; |
| |
| 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; |
| |
| if (numverts > 60) |
| Sys_Error ("numverts = %i", numverts); |
| |
| BoundPoly (numverts, verts, mins, maxs); |
| |
| for (i=0 ; i<3 ; i++) |
| { |
| m = (mins[i] + maxs[i]) * 0.5; |
| m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 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; |
| } |
| |
| poly = (glpoly_t*) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); |
| poly->next = warpface->polys; |
| warpface->polys = poly; |
| poly->numverts = numverts; |
| for (i=0 ; i<numverts ; i++, verts+= 3) |
| { |
| VectorCopy (verts, poly->verts[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; |
| int i; |
| int lindex; |
| float *vec; |
| texture_t *t; |
| |
| 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 turbsin[] = |
| { |
| #include "gl_warp_sin.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; |
| float *v; |
| int i; |
| float s, t, os, ot; |
| |
| |
| for (p=fa->polys ; p ; p=p->next) |
| { |
| #ifdef USE_OPENGLES |
| { |
| float* pUV = gTexCoordBuffer; |
| for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE) |
| { |
| os = v[3]; |
| ot = v[4]; |
| |
| s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; |
| s *= (1.0/64); |
| |
| t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; |
| t *= (1.0/64); |
| |
| *pUV++ = s; |
| *pUV++ = t; |
| } |
| } |
| |
| glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]); |
| glTexCoordPointer(2, GL_FLOAT, 0, gTexCoordBuffer); |
| glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts); |
| #else |
| glBegin (GL_POLYGON); |
| for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE) |
| { |
| os = v[3]; |
| ot = v[4]; |
| |
| s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; |
| s *= (1.0/64); |
| |
| t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; |
| t *= (1.0/64); |
| |
| glTexCoord2f (s, t); |
| glVertex3fv (v); |
| } |
| glEnd (); |
| #endif |
| } |
| } |
| |
| |
| |
| |
| /* |
| ============= |
| EmitSkyPolys |
| ============= |
| */ |
| void EmitSkyPolys (msurface_t *fa) |
| { |
| glpoly_t *p; |
| float *v; |
| int i; |
| float s, t; |
| vec3_t dir; |
| float length; |
| |
| for (p=fa->polys ; p ; p=p->next) |
| { |
| #ifdef USE_OPENGLES |
| { |
| float* pUV = gTexCoordBuffer; |
| for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE) |
| { |
| VectorSubtract (v, r_origin, dir); |
| dir[2] *= 3; // flatten the sphere |
| |
| length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; |
| length = sqrt (length); |
| length = 6*63/length; |
| |
| dir[0] *= length; |
| dir[1] *= length; |
| |
| s = (speedscale + dir[0]) * (1.0/128); |
| t = (speedscale + dir[1]) * (1.0/128); |
| |
| *pUV++ = s; |
| *pUV++ = t; |
| } |
| } |
| |
| glVertexPointer(3, GL_FLOAT, VERTEXSIZE*sizeof(float), &p->verts[0][0]); |
| glTexCoordPointer(2, GL_FLOAT, 0, gTexCoordBuffer); |
| glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts); |
| #else |
| glBegin (GL_POLYGON); |
| for (i=0,v=p->verts[0] ; i<p->numverts ; i++, v+=VERTEXSIZE) |
| { |
| VectorSubtract (v, r_origin, dir); |
| dir[2] *= 3; // flatten the sphere |
| |
| length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; |
| length = sqrt (length); |
| length = 6*63/length; |
| |
| dir[0] *= length; |
| dir[1] *= length; |
| |
| s = (speedscale + dir[0]) * (1.0/128); |
| t = (speedscale + dir[1]) * (1.0/128); |
| |
| glTexCoord2f (s, t); |
| glVertex3fv (v); |
| } |
| glEnd (); |
| #endif |
| } |
| } |
| |
| /* |
| =============== |
| EmitBothSkyLayers |
| |
| Does a sky warp on the pre-fragmented glpoly_t chain |
| This will be called for brushmodels, the world |
| will have them chained together. |
| =============== |
| */ |
| void EmitBothSkyLayers (msurface_t *fa) |
| { |
| int i; |
| int lindex; |
| float *vec; |
| |
| GL_DisableMultitexture(); |
| |
| GL_Bind (solidskytexture); |
| speedscale = realtime*8; |
| speedscale -= (int)speedscale & ~127 ; |
| |
| EmitSkyPolys (fa); |
| |
| glEnable (GL_BLEND); |
| GL_Bind (alphaskytexture); |
| speedscale = realtime*16; |
| speedscale -= (int)speedscale & ~127 ; |
| |
| EmitSkyPolys (fa); |
| |
| glDisable (GL_BLEND); |
| } |
| |
| #ifndef QUAKE2 |
| /* |
| ================= |
| R_DrawSkyChain |
| ================= |
| */ |
| void R_DrawSkyChain (msurface_t *s) |
| { |
| msurface_t *fa; |
| |
| GL_DisableMultitexture(); |
| |
| // used when gl_texsort is on |
| GL_Bind(solidskytexture); |
| speedscale = realtime*8; |
| speedscale -= (int)speedscale & ~127 ; |
| |
| for (fa=s ; fa ; fa=fa->texturechain) |
| EmitSkyPolys (fa); |
| |
| glEnable (GL_BLEND); |
| GL_Bind (alphaskytexture); |
| speedscale = realtime*16; |
| speedscale -= (int)speedscale & ~127 ; |
| |
| for (fa=s ; fa ; fa=fa->texturechain) |
| EmitSkyPolys (fa); |
| |
| glDisable (GL_BLEND); |
| } |
| |
| #endif |
| |
| /* |
| ================================================================= |
| |
| Quake 2 environment sky |
| |
| ================================================================= |
| */ |
| |
| #ifdef QUAKE2 |
| |
| |
| #define SKY_TEX 2000 |
| |
| /* |
| ================================================================= |
| |
| PCX Loading |
| |
| ================================================================= |
| */ |
| |
| typedef struct |
| { |
| char manufacturer; |
| char version; |
| char encoding; |
| char bits_per_pixel; |
| unsigned short xmin,ymin,xmax,ymax; |
| unsigned short hres,vres; |
| unsigned char palette[48]; |
| char reserved; |
| char color_planes; |
| unsigned short bytes_per_line; |
| unsigned short palette_type; |
| char filler[58]; |
| unsigned data; // unbounded |
| } pcx_t; |
| |
| byte *pcx_rgb; |
| |
| /* |
| ============ |
| LoadPCX |
| ============ |
| */ |
| void LoadPCX (FILE *f) |
| { |
| pcx_t *pcx, pcxbuf; |
| byte palette[768]; |
| byte *pix; |
| int x, y; |
| int dataByte, runLength; |
| int count; |
| |
| // |
| // parse the PCX file |
| // |
| fread (&pcxbuf, 1, sizeof(pcxbuf), f); |
| |
| pcx = &pcxbuf; |
| |
| if (pcx->manufacturer != 0x0a |
| || pcx->version != 5 |
| || pcx->encoding != 1 |
| || pcx->bits_per_pixel != 8 |
| || pcx->xmax >= 320 |
| || pcx->ymax >= 256) |
| { |
| Con_Printf ("Bad pcx file\n"); |
| return; |
| } |
| |
| // seek to palette |
| fseek (f, -768, SEEK_END); |
| fread (palette, 1, 768, f); |
| |
| fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); |
| |
| count = (pcx->xmax+1) * (pcx->ymax+1); |
| pcx_rgb = malloc( count * 4); |
| |
| for (y=0 ; y<=pcx->ymax ; y++) |
| { |
| pix = pcx_rgb + 4*y*(pcx->xmax+1); |
| for (x=0 ; x<=pcx->ymax ; ) |
| { |
| dataByte = fgetc(f); |
| |
| if((dataByte & 0xC0) == 0xC0) |
| { |
| runLength = dataByte & 0x3F; |
| dataByte = fgetc(f); |
| } |
| else |
| runLength = 1; |
| |
| while(runLength-- > 0) |
| { |
| pix[0] = palette[dataByte*3]; |
| pix[1] = palette[dataByte*3+1]; |
| pix[2] = palette[dataByte*3+2]; |
| pix[3] = 255; |
| pix += 4; |
| x++; |
| } |
| } |
| } |
| } |
| |
| /* |
| ========================================================= |
| |
| TARGA LOADING |
| |
| ========================================================= |
| */ |
| |
| typedef struct _TargaHeader { |
| unsigned char id_length, colormap_type, image_type; |
| unsigned short colormap_index, colormap_length; |
| unsigned char colormap_size; |
| unsigned short x_origin, y_origin, width, height; |
| unsigned char pixel_size, attributes; |
| } TargaHeader; |
| |
| |
| TargaHeader targa_header; |
| byte *targa_rgba; |
| |
| int fgetLittleShort (FILE *f) |
| { |
| byte b1, b2; |
| |
| b1 = fgetc(f); |
| b2 = fgetc(f); |
| |
| return (short)(b1 + b2*256); |
| } |
| |
| int fgetLittleLong (FILE *f) |
| { |
| byte b1, b2, b3, b4; |
| |
| b1 = fgetc(f); |
| b2 = fgetc(f); |
| b3 = fgetc(f); |
| b4 = fgetc(f); |
| |
| return b1 + (b2<<8) + (b3<<16) + (b4<<24); |
| } |
| |
| |
| /* |
| ============= |
| LoadTGA |
| ============= |
| */ |
| void LoadTGA (FILE *fin) |
| { |
| int columns, rows, numPixels; |
| byte *pixbuf; |
| int row, column; |
| |
| targa_header.id_length = fgetc(fin); |
| targa_header.colormap_type = fgetc(fin); |
| targa_header.image_type = fgetc(fin); |
| |
| targa_header.colormap_index = fgetLittleShort(fin); |
| targa_header.colormap_length = fgetLittleShort(fin); |
| targa_header.colormap_size = fgetc(fin); |
| targa_header.x_origin = fgetLittleShort(fin); |
| targa_header.y_origin = fgetLittleShort(fin); |
| targa_header.width = fgetLittleShort(fin); |
| targa_header.height = fgetLittleShort(fin); |
| targa_header.pixel_size = fgetc(fin); |
| targa_header.attributes = fgetc(fin); |
| |
| if (targa_header.image_type!=2 |
| && targa_header.image_type!=10) |
| Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); |
| |
| if (targa_header.colormap_type !=0 |
| || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) |
| Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); |
| |
| columns = targa_header.width; |
| rows = targa_header.height; |
| numPixels = columns * rows; |
| |
| targa_rgba = malloc (numPixels*4); |
| |
| if (targa_header.id_length != 0) |
| fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment |
| |
| if (targa_header.image_type==2) { // Uncompressed, RGB images |
| for(row=rows-1; row>=0; row--) { |
| pixbuf = targa_rgba + row*columns*4; |
| for(column=0; column<columns; column++) { |
| unsigned char red,green,blue,alphabyte; |
| switch (targa_header.pixel_size) { |
| case 24: |
| |
| blue = getc(fin); |
| green = getc(fin); |
| red = getc(fin); |
| *pixbuf++ = red; |
| *pixbuf++ = green; |
| *pixbuf++ = blue; |
| *pixbuf++ = 255; |
| break; |
| case 32: |
| blue = getc(fin); |
| green = getc(fin); |
| red = getc(fin); |
| alphabyte = getc(fin); |
| *pixbuf++ = red; |
| *pixbuf++ = green; |
| *pixbuf++ = blue; |
| *pixbuf++ = alphabyte; |
| break; |
| } |
| } |
| } |
| } |
| else if (targa_header.image_type==10) { // Runlength encoded RGB images |
| unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j; |
| for(row=rows-1; row>=0; row--) { |
| pixbuf = targa_rgba + row*columns*4; |
| for(column=0; column<columns; ) { |
| packetHeader=getc(fin); |
| packetSize = 1 + (packetHeader & 0x7f); |
| if (packetHeader & 0x80) { // run-length packet |
| switch (targa_header.pixel_size) { |
| case 24: |
| blue = getc(fin); |
| green = getc(fin); |
| red = getc(fin); |
| alphabyte = 255; |
| break; |
| case 32: |
| blue = getc(fin); |
| green = getc(fin); |
| red = getc(fin); |
| alphabyte = getc(fin); |
| break; |
| } |
| |
| for(j=0;j<packetSize;j++) { |
| *pixbuf++=red; |
| *pixbuf++=green; |
| *pixbuf++=blue; |
| *pixbuf++=alphabyte; |
| column++; |
| if (column==columns) { // run spans across rows |
| column=0; |
| if (row>0) |
| row--; |
| else |
| goto breakOut; |
| pixbuf = targa_rgba + row*columns*4; |
| } |
| } |
| } |
| else { // non run-length packet |
| for(j=0;j<packetSize;j++) { |
| switch (targa_header.pixel_size) { |
| case 24: |
| blue = getc(fin); |
| green = getc(fin); |
| red = getc(fin); |
| *pixbuf++ = red; |
| *pixbuf++ = green; |
| *pixbuf++ = blue; |
| *pixbuf++ = 255; |
| break; |
| case 32: |
| blue = getc(fin); |
| green = getc(fin); |
| red = getc(fin); |
| alphabyte = getc(fin); |
| *pixbuf++ = red; |
| *pixbuf++ = green; |
| *pixbuf++ = blue; |
| *pixbuf++ = alphabyte; |
| break; |
| } |
| column++; |
| if (column==columns) { // pixel packet run spans across rows |
| column=0; |
| if (row>0) |
| row--; |
| else |
| goto breakOut; |
| pixbuf = targa_rgba + row*columns*4; |
| } |
| } |
| } |
| } |
| breakOut:; |
| } |
| } |
| |
| fclose(fin); |
| } |
| |
| /* |
| ================== |
| R_LoadSkys |
| ================== |
| */ |
| char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; |
| void R_LoadSkys (void) |
| { |
| int i; |
| FILE *f; |
| char name[64]; |
| |
| for (i=0 ; i<6 ; i++) |
| { |
| GL_Bind (SKY_TEX + i); |
| sprintf (name, "gfx/env/bkgtst%s.tga", suf[i]); |
| COM_FOpenFile (name, &f); |
| if (!f) |
| { |
| Con_Printf ("Couldn't load %s\n", name); |
| continue; |
| } |
| LoadTGA (f); |
| // LoadPCX (f); |
| |
| glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, targa_rgba); |
| // glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pcx_rgb); |
| |
| free (targa_rgba); |
| // free (pcx_rgb); |
| |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| } |
| } |
| |
| |
| 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]; |
| |
| 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); |
| glVertex3fv (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]; |
| |
| 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 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) |
| Sys_Error ("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_DrawSkyChain |
| ================= |
| */ |
| void R_DrawSkyChain (msurface_t *s) |
| { |
| msurface_t *fa; |
| |
| int i; |
| vec3_t verts[MAX_CLIP_VERTS]; |
| glpoly_t *p; |
| |
| c_sky = 0; |
| GL_Bind(solidskytexture); |
| |
| // calculate vertex values for sky box |
| |
| for (fa=s ; fa ; fa=fa->texturechain) |
| { |
| 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*2048; |
| b[1] = t*2048; |
| b[2] = 2048; |
| |
| 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]; |
| 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 |
| ============== |
| */ |
| int skytexorder[6] = {0,2,1,3,4,5}; |
| void R_DrawSkyBox (void) |
| { |
| int i, j, k; |
| vec3_t v; |
| float s, t; |
| |
| #if 0 |
| glEnable (GL_BLEND); |
| glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); |
| glColor4f (1,1,1,0.5); |
| glDisable (GL_DEPTH_TEST); |
| #endif |
| for (i=0 ; i<6 ; i++) |
| { |
| if (skymins[0][i] >= skymaxs[0][i] |
| || skymins[1][i] >= skymaxs[1][i]) |
| continue; |
| |
| GL_Bind (SKY_TEX+skytexorder[i]); |
| #if 0 |
| skymins[0][i] = -1; |
| skymins[1][i] = -1; |
| skymaxs[0][i] = 1; |
| skymaxs[1][i] = 1; |
| #endif |
| 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 (); |
| } |
| #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 |
| } |
| |
| |
| #endif |
| |
| //=============================================================== |
| |
| static texture_t* current_sky_mt; |
| |
| /* |
| ============= |
| R_InitSky |
| |
| A sky texture is 256*128, with the right side being a masked overlay |
| ============== |
| */ |
| void R_InitSky (texture_t *mt) |
| { |
| int i, j, p; |
| byte *src; |
| unsigned trans[128*128]; |
| unsigned transpix; |
| int r, g, b; |
| unsigned *rgba; |
| // extern int skytexturenum; |
| |
| current_sky_mt = mt; |
| |
| 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++; |
| GL_Bind (solidskytexture ); |
| glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| |
| for (i=0 ; i<128 ; i++) |
| for (j=0 ; j<128 ; j++) |
| { |
| p = src[i*256 + j]; |
| if (p == 0) |
| trans[(i*128) + j] = transpix; |
| else |
| trans[(i*128) + j] = d_8to24table[p]; |
| } |
| |
| if (!alphaskytexture) |
| alphaskytexture = texture_extension_number++; |
| GL_Bind(alphaskytexture); |
| glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| } |
| |
| void R_ReloadSky() { |
| if (current_sky_mt) { |
| R_InitSky(current_sky_mt); |
| } |
| } |