00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
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
00158 assert(*yMin >= 0);
00159 assert(*yMin <= *yMax);
00160 assert(*yMax <= height);
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
00227
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
00267 xviLevel = CP_GetXVILevel(xCenter, yCenter);
00268
00269 if (xviLevel < MAX_ALPHA_VALUE - INITIAL_ALPHA_VALUE)
00270 xviLevel++;
00271 radius = sqrt(factor * xviLevel);
00272
00273
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
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
00370
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
00434 CP_SetMinMaxOverlayRows(pos, innerRadius, RADAR_HEIGHT, &yMin, &yMax);
00435 CP_SetMinMaxOverlayRows(pos, outerRadius, RADAR_HEIGHT, &outeryMin, &outeryMax);
00436
00437
00438 for (y = outeryMin; y < yMin; y++) {
00439
00440 const float yLat = torad * (90.0f - y / radarHeightPerDegree);
00441 float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
00442
00443
00444 CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
00445 }
00446
00447
00448 for (y = yMin; y < yMax; y++) {
00449
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
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
00461 for (y = yMax; y < outeryMax; y++) {
00462
00463 const float yLat = torad * (90.0f - y / radarHeightPerDegree);
00464 const float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
00465
00466
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 }