cp_overlay.c

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 1997-2001 Id Software, Inc.
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019 See the GNU General Public License for more details.
00020 
00021 You should have received a copy of the GNU General Public License
00022 along with this program; if not, write to the Free Software
00023 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00024 
00025 */
00026 
00027 #include "../cl_shared.h"
00028 #include "../renderer/r_image.h"
00029 #include "cp_campaign.h"
00030 #include "cp_overlay.h"
00031 
00033 static const int MAX_ALPHA_VALUE = 200;
00034 static const int INITIAL_ALPHA_VALUE = 60;
00035 
00036 #define XVI_WIDTH       512
00037 #define XVI_HEIGHT      256
00038 #define RADAR_WIDTH     512
00039 #define RADAR_HEIGHT    256
00040 #define DAN_WIDTH   512
00041 #define DAN_HEIGHT  256
00042 
00043 #define DAWN        0.03
00044 
00046 image_t *r_dayandnightTexture;
00047 image_t *r_radarTexture;                
00048 image_t *r_xviTexture;                  
00051 static byte r_xviAlpha[XVI_WIDTH * XVI_HEIGHT];
00052 
00054 static byte r_radarPic[RADAR_WIDTH * RADAR_HEIGHT];
00055 
00057 static byte r_radarSourcePic[RADAR_WIDTH * RADAR_HEIGHT];
00058 
00060 static byte r_dayandnightAlpha[DAN_WIDTH * DAN_HEIGHT];
00061 
00066 void CP_CalcAndUploadDayAndNightTexture (float q)
00067 {
00068     int x, y;
00069     const float dphi = (float) 2 * M_PI / DAN_WIDTH;
00070     const float da = M_PI / 2 * (HIGH_LAT - LOW_LAT) / DAN_HEIGHT;
00071     const float sin_q = sin(q);
00072     const float cos_q = cos(q);
00073     float sin_phi[DAN_WIDTH], cos_phi[DAN_WIDTH];
00074     byte *px;
00075 
00076     for (x = 0; x < DAN_WIDTH; x++) {
00077         const float phi = x * dphi - q;
00078         sin_phi[x] = sin(phi);
00079         cos_phi[x] = cos(phi);
00080     }
00081 
00082     /* calculate */
00083     px = r_dayandnightAlpha;
00084     for (y = 0; y < DAN_HEIGHT; y++) {
00085         const float a = sin(M_PI / 2 * HIGH_LAT - y * da);
00086         const float root = sqrt(1 - a * a);
00087         for (x = 0; x < DAN_WIDTH; x++) {
00088             const float pos = sin_phi[x] * root * sin_q - (a * SIN_ALPHA + cos_phi[x] * root * COS_ALPHA) * cos_q;
00089 
00090             if (pos >= DAWN)
00091                 *px++ = 255;
00092             else if (pos <= -DAWN)
00093                 *px++ = 0;
00094             else
00095                 *px++ = (byte) (128.0 * (pos / DAWN + 1));
00096         }
00097     }
00098 
00099     /* upload alpha map into the r_dayandnighttexture */
00100     R_UploadAlpha(r_dayandnightTexture, r_dayandnightAlpha);
00101 }
00102 
00103 void CP_GetXVIMapDimensions (int *width, int *height)
00104 {
00105     *width = XVI_WIDTH;
00106     *height = XVI_HEIGHT;
00107 }
00108 
00109 void CP_SetXVILevel (int x, int y, int value)
00110 {
00111     assert(x >= 0);
00112     assert(x < XVI_WIDTH);
00113     assert(y >= 0);
00114     assert(y < XVI_HEIGHT);
00115 
00116     if (!value)
00117         r_xviAlpha[y * XVI_WIDTH + x] = 0;
00118     else
00119         r_xviAlpha[y * XVI_WIDTH + x] = min(MAX_ALPHA_VALUE, value + INITIAL_ALPHA_VALUE);
00120 }
00121 
00122 int CP_GetXVILevel (int x, int y)
00123 {
00124     assert(x >= 0);
00125     assert(x < XVI_WIDTH);
00126     assert(y >= 0);
00127     assert(y < XVI_HEIGHT);
00128 
00129     return max(0, r_xviAlpha[y * XVI_WIDTH + x] - INITIAL_ALPHA_VALUE);
00130 }
00131 
00142 static void CP_SetMinMaxOverlayRows (const vec2_t pos, float radius, const int height, int *yMin, int *yMax)
00143 {
00144     const float radarHeightPerDegree = height / 180.0f;
00145 
00146     if (pos[1] + radius > 90) {
00147         *yMin = 0;
00148         *yMax = round((90 - pos[1] + radius) * radarHeightPerDegree);
00149     } else if (pos[1] - radius < -90) {
00150         *yMin = ceil((90 - pos[1] - radius) * radarHeightPerDegree);
00151         *yMax = height;
00152     } else {
00153         *yMin = ceil((90 - pos[1] - radius) * radarHeightPerDegree);
00154         *yMax = round((90 - pos[1] + radius) * radarHeightPerDegree);
00155     }
00156 
00157     /* a few assert to avoid buffer overflow */
00158     assert(*yMin >= 0);
00159     assert(*yMin <= *yMax);
00160     assert(*yMax <= height);            /* the loop will stop just BEFORE yMax, so use <= rather than < */
00161 }
00162 
00173 static inline float CP_GetCircleDeltaLongitude (const vec2_t centerPos, float radius, const float yLat)
00174 {
00175     const float angle = (cos(radius * torad) - sin(centerPos[1] * torad) * sin(yLat)) / (cos(centerPos[1] * torad) * cos(yLat));
00176     return fabs(angle) > 1.0f ? 180.0f : todeg * acos(angle);
00177 }
00178 
00189 static void CP_DrawXVIOverlayPixel (int xMin, int xMax, const vec2_t centerPos, int y, const float yLat, int xviLevel, float radius)
00190 {
00191     int x;
00192     vec2_t currentPos;
00193 
00194     currentPos[1] = yLat;
00195 
00196     for (x = xMin; x < xMax; x++) {
00197         const int previousLevel = CP_GetXVILevel(x, y);
00198         float distance;
00199         int newLevel;
00200 
00201         currentPos[0] = 180.0f - 360.0f * x / ((float) XVI_WIDTH);
00202         distance = GetDistanceOnGlobe(centerPos, currentPos);
00203         newLevel = ceil((xviLevel * (radius - distance)) / radius);
00204         if (newLevel > previousLevel)
00205             CP_SetXVILevel(x, y, xviLevel);
00206     }
00207 }
00208 
00220 static void CP_DrawXVIOverlayRow (float latMin, float latMax, const vec2_t center, int y, float yLat, int xviLevel, float radius)
00221 {
00222     const float xviWidthPerDegree = XVI_WIDTH / 360.0f;
00223 
00224     assert(latMax - latMin <= 360 + EQUAL_EPSILON);
00225 
00226     /* if the disc we draw cross the left or right edge of the picture, we need to
00227      * draw 2 part of circle on each side of the overlay */
00228     if (latMin < -180.0f) {
00229         int xMin = 0;
00230         int xMax = ceil((latMax + 180.0f) * xviWidthPerDegree);
00231         CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
00232         xMin = floor((latMin + 540.0f) * xviWidthPerDegree);
00233         xMax = RADAR_WIDTH;
00234         CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
00235     } else if (latMax > 180.0f) {
00236         int xMin = 0;
00237         int xMax = ceil((latMax - 180.0f) * xviWidthPerDegree);
00238         CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
00239         xMin = floor((latMin + 180.0f) * xviWidthPerDegree);
00240         xMax = RADAR_WIDTH;
00241         CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
00242     } else {
00243         const int xMin = floor((latMin + 180.0f) * xviWidthPerDegree);
00244         const int xMax = ceil((latMax + 180.0f) * xviWidthPerDegree);
00245         CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
00246     }
00247 }
00248 
00259 static void CP_IncreaseXVILevel (const vec2_t pos, int xCenter, int yCenter, float factor)
00260 {
00261     int xviLevel;                               
00262     int y;                                      
00263     int yMax, yMin;                             
00264     float radius;                               
00266     /* Get xvi Level infection at pos */
00267     xviLevel = CP_GetXVILevel(xCenter, yCenter);
00268     /* Calculate radius of new spreading */
00269     if (xviLevel < MAX_ALPHA_VALUE - INITIAL_ALPHA_VALUE)
00270         xviLevel++;
00271     radius = sqrt(factor * xviLevel);
00272 
00273     /* Set minimum and maximum rows value we'll have to change */
00274     CP_SetMinMaxOverlayRows(pos, radius, XVI_HEIGHT, &yMin, &yMax);
00275 
00276     for (y = yMin; y < yMax; y++) {
00277         const float yLat = 90.0f - 180.0f * y / ((float) XVI_HEIGHT);
00278         const float deltaLong = CP_GetCircleDeltaLongitude(pos, radius, torad * yLat);
00279 
00280         CP_DrawXVIOverlayRow(-pos[0] - deltaLong, -pos[0] + deltaLong, pos, y, yLat, xviLevel, radius);
00281     }
00282 
00283     R_UploadAlpha(r_xviTexture, r_xviAlpha);
00284 }
00285 
00289 void CP_DecreaseXVILevelEverywhere (void)
00290 {
00291     int x, y;                                   
00293     for (y = 0; y < XVI_HEIGHT; y++) {
00294         for (x = 0; x < XVI_WIDTH; x++) {
00295             const int xviLevel = CP_GetXVILevel(x, y);
00296             if (xviLevel > 0)
00297                 CP_SetXVILevel(x, y, xviLevel - 1);
00298         }
00299     }
00300 
00301     R_UploadAlpha(r_xviTexture, r_xviAlpha);
00302 }
00303 
00304 void CP_ChangeXVILevel (const vec2_t pos, float factor)
00305 {
00306     const int xCenter = round((180 - pos[0]) * XVI_WIDTH / 360.0f);
00307     const int yCenter = round((90 - pos[1]) * XVI_HEIGHT / 180.0f);
00308 
00309     CP_IncreaseXVILevel(pos, xCenter, yCenter, factor);
00310 }
00311 
00318 void CP_InitializeXVIOverlay (const byte *data)
00319 {
00320     assert(r_xviTexture);
00321 
00322     if (!data)
00323         memset(r_xviAlpha, 0, sizeof(r_xviAlpha));
00324     else
00325         memcpy(r_xviAlpha, data, sizeof(r_xviAlpha));
00326 
00327     R_UploadAlpha(r_xviTexture, r_xviAlpha);
00328 }
00329 
00345 void CP_InitializeRadarOverlay (qboolean source)
00346 {
00347     /* Initialize Radar */
00348     if (source)
00349         memset(r_radarSourcePic, INITIAL_ALPHA_VALUE, sizeof(r_radarSourcePic));
00350     else
00351         memcpy(r_radarPic, r_radarSourcePic, sizeof(r_radarPic));
00352 }
00353 
00362 static void CP_DrawRadarOverlayRow (float latMin, float latMax, int y, byte alpha, qboolean source)
00363 {
00364     const float radarWidthPerDegree = RADAR_WIDTH / 360.0f;
00365     int x;
00366 
00367     assert(latMax - latMin <= 360 + EQUAL_EPSILON);
00368 
00369     /* if the disc we draw cross the left or right edge of the picture, we need to
00370      * draw 2 part of circle on each side of the overlay */
00371     if (latMin < -180.0f) {
00372         int xMin = 0;
00373         int xMax = ceil((latMax + 180.0f) * radarWidthPerDegree);
00374         for (x = xMin; x < xMax; x++) {
00375             byte *dest = source ? &r_radarSourcePic[y * RADAR_WIDTH + x] : &r_radarPic[y * RADAR_WIDTH + x];
00376             if (alpha < dest[3])
00377                 dest[3] = alpha;
00378         }
00379         xMin = floor((latMin + 540.0f) * radarWidthPerDegree);
00380         xMax = RADAR_WIDTH;
00381         for (x = xMin; x < xMax; x++) {
00382             byte *dest = source ? &r_radarSourcePic[y * RADAR_WIDTH + x] : &r_radarPic[y * RADAR_WIDTH + x];
00383             if (alpha < dest[3])
00384                 dest[3] = alpha;
00385         }
00386     } else if (latMax > 180.0f) {
00387         int xMin = 0;
00388         int xMax = ceil((latMax - 180.0f) * radarWidthPerDegree);
00389         for (x = xMin; x < xMax; x++) {
00390             byte *dest = source ? &r_radarSourcePic[y * RADAR_WIDTH + x] : &r_radarPic[y * RADAR_WIDTH + x];
00391             if (alpha < dest[3])
00392                 dest[3] = alpha;
00393         }
00394         xMin = floor((latMin + 180.0f) * radarWidthPerDegree);
00395         xMax = RADAR_WIDTH;
00396         for (x = xMin; x < xMax; x++) {
00397             byte *dest = source ? &r_radarSourcePic[y * RADAR_WIDTH + x] : &r_radarPic[y * RADAR_WIDTH + x];
00398             if (alpha < dest[3])
00399                 dest[3] = alpha;
00400         }
00401     } else {
00402         const int xMin = floor((latMin + 180.0f) * radarWidthPerDegree);
00403         const int xMax = ceil((latMax + 180.0f) * radarWidthPerDegree);
00404         for (x = xMin; x < xMax; x++) {
00405             byte *dest = source ? &r_radarSourcePic[y * RADAR_WIDTH + x] : &r_radarPic[y * RADAR_WIDTH + x];
00406             if (alpha < dest[3])
00407                 dest[3] = alpha;
00408         }
00409     }
00410 }
00411 
00420 void CP_AddRadarCoverage (const vec2_t pos, float innerRadius, float outerRadius, qboolean source)
00421 {
00422     const byte innerAlpha = 0;                  
00423     const byte outerAlpha = 60;                 
00424     const float radarHeightPerDegree = RADAR_HEIGHT / 180.0f;
00425     int y;                                      
00426     int yMax, yMin;                             
00427     int outeryMax, outeryMin;                   
00430     assert(outerRadius < 180.0f);
00431     assert(outerRadius > innerRadius);
00432 
00433     /* Set minimum and maximum rows value we'll have to change */
00434     CP_SetMinMaxOverlayRows(pos, innerRadius, RADAR_HEIGHT, &yMin, &yMax);
00435     CP_SetMinMaxOverlayRows(pos, outerRadius, RADAR_HEIGHT, &outeryMin, &outeryMax);
00436 
00437     /* Draw upper part of the radar coverage */
00438     for (y = outeryMin; y < yMin; y++) {
00439         /* latitude of current point, in radian */
00440         const float yLat = torad * (90.0f - y / radarHeightPerDegree);
00441         float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
00442 
00443         /* Only the outer radar coverage is drawn at this latitude */
00444         CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
00445     }
00446 
00447     /* Draw middle part of the radar coverage */
00448     for (y = yMin; y < yMax; y++) {
00449         /* latitude of current point, in radian */
00450         const float yLat = torad * (90.0f - y / radarHeightPerDegree);
00451         const float deltaLong = CP_GetCircleDeltaLongitude(pos, innerRadius, yLat);
00452         const float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
00453 
00454         /* At this latitude, there are 3 parts to draw: left outer radar, inner radar, and right outer radar */
00455         CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] - deltaLong, y, outerAlpha, source);
00456         CP_DrawRadarOverlayRow(-pos[0] - deltaLong, -pos[0] + deltaLong, y, innerAlpha, source);
00457         CP_DrawRadarOverlayRow(-pos[0] + deltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
00458     }
00459 
00460     /* Draw lower part of the radar coverage */
00461     for (y = yMax; y < outeryMax; y++) {
00462         /* latitude of current point, in radian */
00463         const float yLat = torad * (90.0f - y / radarHeightPerDegree);
00464         const float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
00465 
00466         /* Only the outer radar coverage is drawn at this latitude */
00467         CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
00468     }
00469 }
00470 
00475 void CP_UploadRadarCoverage (void)
00476 {
00477     R_SoftenTexture(r_radarPic, RADAR_WIDTH, RADAR_HEIGHT, 1);
00478 
00479     R_UploadAlpha(r_radarTexture, r_radarPic);
00480 }
00481 
00482 void CP_ShutdownOverlay (void)
00483 {
00484     r_radarTexture = NULL;
00485     r_xviTexture = NULL;
00486 }
00487 
00488 void CP_InitOverlay (void)
00489 {
00490     r_radarTexture = R_LoadImageData("***r_radarTexture***", NULL, RADAR_WIDTH, RADAR_HEIGHT, it_effect);
00491     r_xviTexture = R_LoadImageData("***r_xvitexture***", NULL, XVI_WIDTH, XVI_HEIGHT, it_effect);
00492     r_dayandnightTexture = R_LoadImageData("***r_dayandnighttexture***", NULL, DAN_WIDTH, DAN_HEIGHT, it_effect);
00493 }

Generated by  doxygen 1.6.2