Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

tr_flares.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // tr_flares.c
00023 
00024 #include "tr_local.h"
00025 
00026 /*
00027 =============================================================================
00028 
00029 LIGHT FLARES
00030 
00031 A light flare is an effect that takes place inside the eye when bright light
00032 sources are visible.  The size of the flare reletive to the screen is nearly
00033 constant, irrespective of distance, but the intensity should be proportional to the
00034 projected area of the light source.
00035 
00036 A surface that has been flagged as having a light flare will calculate the depth
00037 buffer value that it's midpoint should have when the surface is added.
00038 
00039 After all opaque surfaces have been rendered, the depth buffer is read back for
00040 each flare in view.  If the point has not been obscured by a closer surface, the
00041 flare should be drawn.
00042 
00043 Surfaces that have a repeated texture should never be flagged as flaring, because
00044 there will only be a single flare added at the midpoint of the polygon.
00045 
00046 To prevent abrupt popping, the intensity of the flare is interpolated up and
00047 down as it changes visibility.  This involves scene to scene state, unlike almost
00048 all other aspects of the renderer, and is complicated by the fact that a single
00049 frame may have multiple scenes.
00050 
00051 RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially
00052 up to five or more times in a frame with 3D status bar icons).
00053 
00054 =============================================================================
00055 */
00056 
00057 
00058 // flare states maintain visibility over multiple frames for fading
00059 // layers: view, mirror, menu
00060 typedef struct flare_s {
00061     struct      flare_s *next;      // for active chain
00062 
00063     int         addedFrame;
00064 
00065     qboolean    inPortal;               // true if in a portal view of the scene
00066     int         frameSceneNum;
00067     void        *surface;
00068     int         fogNum;
00069 
00070     int         fadeTime;
00071 
00072     qboolean    visible;            // state of last test
00073     float       drawIntensity;      // may be non 0 even if !visible due to fading
00074 
00075     int         windowX, windowY;
00076     float       eyeZ;
00077 
00078     vec3_t      color;
00079 } flare_t;
00080 
00081 #define     MAX_FLARES      128
00082 
00083 flare_t     r_flareStructs[MAX_FLARES];
00084 flare_t     *r_activeFlares, *r_inactiveFlares;
00085 
00086 /*
00087 ==================
00088 R_ClearFlares
00089 ==================
00090 */
00091 void R_ClearFlares( void ) {
00092     int     i;
00093 
00094     Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) );
00095     r_activeFlares = NULL;
00096     r_inactiveFlares = NULL;
00097 
00098     for ( i = 0 ; i < MAX_FLARES ; i++ ) {
00099         r_flareStructs[i].next = r_inactiveFlares;
00100         r_inactiveFlares = &r_flareStructs[i];
00101     }
00102 }
00103 
00104 
00105 /*
00106 ==================
00107 RB_AddFlare
00108 
00109 This is called at surface tesselation time
00110 ==================
00111 */
00112 void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) {
00113     int             i;
00114     flare_t         *f, *oldest;
00115     vec3_t          local;
00116     float           d;
00117     vec4_t          eye, clip, normalized, window;
00118 
00119     backEnd.pc.c_flareAdds++;
00120 
00121     // if the point is off the screen, don't bother adding it
00122     // calculate screen coordinates and depth
00123     R_TransformModelToClip( point, backEnd.or.modelMatrix, 
00124         backEnd.viewParms.projectionMatrix, eye, clip );
00125 
00126     // check to see if the point is completely off screen
00127     for ( i = 0 ; i < 3 ; i++ ) {
00128         if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) {
00129             return;
00130         }
00131     }
00132 
00133     R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window );
00134 
00135     if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth
00136         || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) {
00137         return; // shouldn't happen, since we check the clip[] above, except for FP rounding
00138     }
00139 
00140     // see if a flare with a matching surface, scene, and view exists
00141     oldest = r_flareStructs;
00142     for ( f = r_activeFlares ; f ; f = f->next ) {
00143         if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum
00144             && f->inPortal == backEnd.viewParms.isPortal ) {
00145             break;
00146         }
00147     }
00148 
00149     // allocate a new one
00150     if (!f ) {
00151         if ( !r_inactiveFlares ) {
00152             // the list is completely full
00153             return;
00154         }
00155         f = r_inactiveFlares;
00156         r_inactiveFlares = r_inactiveFlares->next;
00157         f->next = r_activeFlares;
00158         r_activeFlares = f;
00159 
00160         f->surface = surface;
00161         f->frameSceneNum = backEnd.viewParms.frameSceneNum;
00162         f->inPortal = backEnd.viewParms.isPortal;
00163         f->addedFrame = -1;
00164     }
00165 
00166     if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) {
00167         f->visible = qfalse;
00168         f->fadeTime = backEnd.refdef.time - 2000;
00169     }
00170 
00171     f->addedFrame = backEnd.viewParms.frameCount;
00172     f->fogNum = fogNum;
00173 
00174     VectorCopy( color, f->color );
00175 
00176     // fade the intensity of the flare down as the
00177     // light surface turns away from the viewer
00178     if ( normal ) {
00179         VectorSubtract( backEnd.viewParms.or.origin, point, local );
00180         VectorNormalizeFast( local );
00181         d = DotProduct( local, normal );
00182         VectorScale( f->color, d, f->color ); 
00183     }
00184 
00185     // save info needed to test
00186     f->windowX = backEnd.viewParms.viewportX + window[0];
00187     f->windowY = backEnd.viewParms.viewportY + window[1];
00188 
00189     f->eyeZ = eye[2];
00190 }
00191 
00192 /*
00193 ==================
00194 RB_AddDlightFlares
00195 ==================
00196 */
00197 void RB_AddDlightFlares( void ) {
00198     dlight_t        *l;
00199     int             i, j, k;
00200     fog_t           *fog;
00201 
00202     if ( !r_flares->integer ) {
00203         return;
00204     }
00205 
00206     l = backEnd.refdef.dlights;
00207     fog = tr.world->fogs;
00208     for (i=0 ; i<backEnd.refdef.num_dlights ; i++, l++) {
00209 
00210         // find which fog volume the light is in 
00211         for ( j = 1 ; j < tr.world->numfogs ; j++ ) {
00212             fog = &tr.world->fogs[j];
00213             for ( k = 0 ; k < 3 ; k++ ) {
00214                 if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) {
00215                     break;
00216                 }
00217             }
00218             if ( k == 3 ) {
00219                 break;
00220             }
00221         }
00222         if ( j == tr.world->numfogs ) {
00223             j = 0;
00224         }
00225 
00226         RB_AddFlare( (void *)l, j, l->origin, l->color, NULL );
00227     }
00228 }
00229 
00230 /*
00231 ===============================================================================
00232 
00233 FLARE BACK END
00234 
00235 ===============================================================================
00236 */
00237 
00238 /*
00239 ==================
00240 RB_TestFlare
00241 ==================
00242 */
00243 void RB_TestFlare( flare_t *f ) {
00244     float           depth;
00245     qboolean        visible;
00246     float           fade;
00247     float           screenZ;
00248 
00249     backEnd.pc.c_flareTests++;
00250 
00251     // doing a readpixels is as good as doing a glFinish(), so
00252     // don't bother with another sync
00253     glState.finishCalled = qfalse;
00254 
00255     // read back the z buffer contents
00256     qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
00257 
00258     screenZ = backEnd.viewParms.projectionMatrix[14] / 
00259         ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] );
00260 
00261     visible = ( -f->eyeZ - -screenZ ) < 24;
00262 
00263     if ( visible ) {
00264         if ( !f->visible ) {
00265             f->visible = qtrue;
00266             f->fadeTime = backEnd.refdef.time - 1;
00267         }
00268         fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value;
00269     } else {
00270         if ( f->visible ) {
00271             f->visible = qfalse;
00272             f->fadeTime = backEnd.refdef.time - 1;
00273         }
00274         fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value;
00275     }
00276 
00277     if ( fade < 0 ) {
00278         fade = 0;
00279     }
00280     if ( fade > 1 ) {
00281         fade = 1;
00282     }
00283 
00284     f->drawIntensity = fade;
00285 }
00286 
00287 
00288 /*
00289 ==================
00290 RB_RenderFlare
00291 ==================
00292 */
00293 void RB_RenderFlare( flare_t *f ) {
00294     float           size;
00295     vec3_t          color;
00296     int             iColor[3];
00297 
00298     backEnd.pc.c_flareRenders++;
00299 
00300     VectorScale( f->color, f->drawIntensity*tr.identityLight, color );
00301     iColor[0] = color[0] * 255;
00302     iColor[1] = color[1] * 255;
00303     iColor[2] = color[2] * 255;
00304 
00305     size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / -f->eyeZ );
00306 
00307     RB_BeginSurface( tr.flareShader, f->fogNum );
00308 
00309     // FIXME: use quadstamp?
00310     tess.xyz[tess.numVertexes][0] = f->windowX - size;
00311     tess.xyz[tess.numVertexes][1] = f->windowY - size;
00312     tess.texCoords[tess.numVertexes][0][0] = 0;
00313     tess.texCoords[tess.numVertexes][0][1] = 0;
00314     tess.vertexColors[tess.numVertexes][0] = iColor[0];
00315     tess.vertexColors[tess.numVertexes][1] = iColor[1];
00316     tess.vertexColors[tess.numVertexes][2] = iColor[2];
00317     tess.vertexColors[tess.numVertexes][3] = 255;
00318     tess.numVertexes++;
00319 
00320     tess.xyz[tess.numVertexes][0] = f->windowX - size;
00321     tess.xyz[tess.numVertexes][1] = f->windowY + size;
00322     tess.texCoords[tess.numVertexes][0][0] = 0;
00323     tess.texCoords[tess.numVertexes][0][1] = 1;
00324     tess.vertexColors[tess.numVertexes][0] = iColor[0];
00325     tess.vertexColors[tess.numVertexes][1] = iColor[1];
00326     tess.vertexColors[tess.numVertexes][2] = iColor[2];
00327     tess.vertexColors[tess.numVertexes][3] = 255;
00328     tess.numVertexes++;
00329 
00330     tess.xyz[tess.numVertexes][0] = f->windowX + size;
00331     tess.xyz[tess.numVertexes][1] = f->windowY + size;
00332     tess.texCoords[tess.numVertexes][0][0] = 1;
00333     tess.texCoords[tess.numVertexes][0][1] = 1;
00334     tess.vertexColors[tess.numVertexes][0] = iColor[0];
00335     tess.vertexColors[tess.numVertexes][1] = iColor[1];
00336     tess.vertexColors[tess.numVertexes][2] = iColor[2];
00337     tess.vertexColors[tess.numVertexes][3] = 255;
00338     tess.numVertexes++;
00339 
00340     tess.xyz[tess.numVertexes][0] = f->windowX + size;
00341     tess.xyz[tess.numVertexes][1] = f->windowY - size;
00342     tess.texCoords[tess.numVertexes][0][0] = 1;
00343     tess.texCoords[tess.numVertexes][0][1] = 0;
00344     tess.vertexColors[tess.numVertexes][0] = iColor[0];
00345     tess.vertexColors[tess.numVertexes][1] = iColor[1];
00346     tess.vertexColors[tess.numVertexes][2] = iColor[2];
00347     tess.vertexColors[tess.numVertexes][3] = 255;
00348     tess.numVertexes++;
00349 
00350     tess.indexes[tess.numIndexes++] = 0;
00351     tess.indexes[tess.numIndexes++] = 1;
00352     tess.indexes[tess.numIndexes++] = 2;
00353     tess.indexes[tess.numIndexes++] = 0;
00354     tess.indexes[tess.numIndexes++] = 2;
00355     tess.indexes[tess.numIndexes++] = 3;
00356 
00357     RB_EndSurface();
00358 }
00359 
00360 /*
00361 ==================
00362 RB_RenderFlares
00363 
00364 Because flares are simulating an occular effect, they should be drawn after
00365 everything (all views) in the entire frame has been drawn.
00366 
00367 Because of the way portals use the depth buffer to mark off areas, the
00368 needed information would be lost after each view, so we are forced to draw
00369 flares after each view.
00370 
00371 The resulting artifact is that flares in mirrors or portals don't dim properly
00372 when occluded by something in the main view, and portal flares that should
00373 extend past the portal edge will be overwritten.
00374 ==================
00375 */
00376 void RB_RenderFlares (void) {
00377     flare_t     *f;
00378     flare_t     **prev;
00379     qboolean    draw;
00380 
00381     if ( !r_flares->integer ) {
00382         return;
00383     }
00384 
00385 //  RB_AddDlightFlares();
00386 
00387     // perform z buffer readback on each flare in this view
00388     draw = qfalse;
00389     prev = &r_activeFlares;
00390     while ( ( f = *prev ) != NULL ) {
00391         // throw out any flares that weren't added last frame
00392         if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) {
00393             *prev = f->next;
00394             f->next = r_inactiveFlares;
00395             r_inactiveFlares = f;
00396             continue;
00397         }
00398 
00399         // don't draw any here that aren't from this scene / portal
00400         f->drawIntensity = 0;
00401         if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum
00402             && f->inPortal == backEnd.viewParms.isPortal ) {
00403             RB_TestFlare( f );
00404             if ( f->drawIntensity ) {
00405                 draw = qtrue;
00406             } else {
00407                 // this flare has completely faded out, so remove it from the chain
00408                 *prev = f->next;
00409                 f->next = r_inactiveFlares;
00410                 r_inactiveFlares = f;
00411                 continue;
00412             }
00413         }
00414 
00415         prev = &f->next;
00416     }
00417 
00418     if ( !draw ) {
00419         return;     // none visible
00420     }
00421 
00422     if ( backEnd.viewParms.isPortal ) {
00423         qglDisable (GL_CLIP_PLANE0);
00424     }
00425 
00426     qglPushMatrix();
00427     qglLoadIdentity();
00428     qglMatrixMode( GL_PROJECTION );
00429     qglPushMatrix();
00430     qglLoadIdentity();
00431     qglOrtho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth,
00432               backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight,
00433               -99999, 99999 );
00434 
00435     for ( f = r_activeFlares ; f ; f = f->next ) {
00436         if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum
00437             && f->inPortal == backEnd.viewParms.isPortal
00438             && f->drawIntensity ) {
00439             RB_RenderFlare( f );
00440         }
00441     }
00442 
00443     qglPopMatrix();
00444     qglMatrixMode( GL_MODELVIEW );
00445     qglPopMatrix();
00446 }
00447 

Generated on Thu Aug 25 12:37:51 2005 for Quake III Arena by  doxygen 1.3.9.1