UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_overlay.cpp
Go to the documentation of this file.
1
6
7/*
8Copyright (C) 1997-2001 Id Software, Inc.
9
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
19See the GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25*/
26
27#include "../../cl_shared.h"
28#include "cp_campaign.h"
29#include "cp_overlay.h"
30
32static const int MAX_ALPHA_VALUE = 200;
33static const int INITIAL_ALPHA_VALUE = 60;
34
35static inline byte* CP_XVIGetAlpha (int x, int y)
36{
37 return &cgi->r_xviAlpha[y * XVI_WIDTH + x];
38}
39
40static inline byte* CP_RadarGet (int x, int y, bool source)
41{
42 if (source)
43 return &cgi->r_radarSourcePic[y * RADAR_WIDTH + x];
44 return &cgi->r_radarPic[y * RADAR_WIDTH + x];
45}
46
47static inline void CP_UploadXVI (void)
48{
49 cgi->R_UploadAlpha("***r_xvitexture***", cgi->r_xviAlpha);
50}
51
52void CP_GetXVIMapDimensions (int* width, int* height)
53{
54 *width = XVI_WIDTH;
55 *height = XVI_HEIGHT;
56}
57
58void CP_SetXVILevel (int x, int y, int value)
59{
60 assert(x >= 0);
61 assert(x < XVI_WIDTH);
62 assert(y >= 0);
63 assert(y < XVI_HEIGHT);
64
65 if (!value)
66 *CP_XVIGetAlpha(x, y) = 0;
67 else
68 *CP_XVIGetAlpha(x, y) = std::min(MAX_ALPHA_VALUE, value + INITIAL_ALPHA_VALUE);
69}
70
71int CP_GetXVILevel (int x, int y)
72{
73 assert(x >= 0);
74 assert(x < XVI_WIDTH);
75 assert(y >= 0);
76 assert(y < XVI_HEIGHT);
77
78 return std::max(0, *CP_XVIGetAlpha(x, y) - INITIAL_ALPHA_VALUE);
79}
80
91static void CP_SetMinMaxOverlayRows (const vec2_t pos, float radius, const int height, int* yMin, int* yMax)
92{
93 const float radarHeightPerDegree = height / 180.0f;
94
95 if (pos[1] + radius > 90) {
96 *yMin = 0;
97 *yMax = round((90 - pos[1] + radius) * radarHeightPerDegree);
98 } else if (pos[1] - radius < -90) {
99 *yMin = ceil((90 - pos[1] - radius) * radarHeightPerDegree);
100 *yMax = height;
101 } else {
102 *yMin = ceil((90 - pos[1] - radius) * radarHeightPerDegree);
103 *yMax = round((90 - pos[1] + radius) * radarHeightPerDegree);
104 }
105
106 /* a few assert to avoid buffer overflow */
107 assert(*yMin >= 0);
108 assert(*yMin <= *yMax);
109 assert(*yMax <= height); /* the loop will stop just BEFORE yMax, so use <= rather than < */
110}
111
122static inline float CP_GetCircleDeltaLongitude (const vec2_t centerPos, float radius, const float yLat)
123{
124 const float angle = (cos(radius * torad) - sin(centerPos[1] * torad) * sin(yLat)) / (cos(centerPos[1] * torad) * cos(yLat));
125 return fabs(angle) > 1.0f ? 180.0f : todeg * acos(angle);
126}
127
138static void CP_DrawXVIOverlayPixel (int xMin, int xMax, const vec2_t centerPos, int y, const float yLat, int xviLevel, float radius)
139{
140 int x;
141 vec2_t currentPos;
142
143 currentPos[1] = yLat;
144
145 for (x = xMin; x < xMax; x++) {
146 const int previousLevel = CP_GetXVILevel(x, y);
147 float distance;
148 int newLevel;
149
150 currentPos[0] = 180.0f - 360.0f * x / ((float) XVI_WIDTH);
151 distance = GetDistanceOnGlobe(centerPos, currentPos);
152 newLevel = ceil((xviLevel * (radius - distance)) / radius);
153 if (newLevel > previousLevel)
154 CP_SetXVILevel(x, y, xviLevel);
155 }
156}
157
168static void CP_DrawXVIOverlayRow (float latMin, float latMax, const vec2_t center, int y, float yLat, int xviLevel, float radius)
169{
170 const float xviWidthPerDegree = XVI_WIDTH / 360.0f;
171
172 assert(latMax - latMin <= 360 + EQUAL_EPSILON);
173
174 /* if the disc we draw cross the left or right edge of the picture, we need to
175 * draw 2 part of circle on each side of the overlay */
176 if (latMin < -180.0f) {
177 int xMin = 0;
178 int xMax = ceil((latMax + 180.0f) * xviWidthPerDegree);
179 CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
180 xMin = floor((latMin + 540.0f) * xviWidthPerDegree);
181 xMax = RADAR_WIDTH;
182 CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
183 } else if (latMax > 180.0f) {
184 int xMin = 0;
185 int xMax = ceil((latMax - 180.0f) * xviWidthPerDegree);
186 CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
187 xMin = floor((latMin + 180.0f) * xviWidthPerDegree);
188 xMax = RADAR_WIDTH;
189 CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
190 } else {
191 const int xMin = floor((latMin + 180.0f) * xviWidthPerDegree);
192 const int xMax = ceil((latMax + 180.0f) * xviWidthPerDegree);
193 CP_DrawXVIOverlayPixel(xMin, xMax, center, y, yLat, xviLevel, radius);
194 }
195}
196
207static void CP_IncreaseXVILevel (const vec2_t pos, int xCenter, int yCenter, float factor)
208{
209 int xviLevel;
210 int y;
211 int yMax, yMin;
212 float radius;
213
214 /* Get xvi Level infection at pos */
215 xviLevel = CP_GetXVILevel(xCenter, yCenter);
216 /* Calculate radius of new spreading */
217 if (xviLevel < MAX_ALPHA_VALUE - INITIAL_ALPHA_VALUE)
218 xviLevel++;
219 radius = sqrt(factor * xviLevel);
220
221 /* Set minimum and maximum rows value we'll have to change */
222 CP_SetMinMaxOverlayRows(pos, radius, XVI_HEIGHT, &yMin, &yMax);
223
224 for (y = yMin; y < yMax; y++) {
225 const float yLat = 90.0f - 180.0f * y / ((float) XVI_HEIGHT);
226 const float deltaLong = CP_GetCircleDeltaLongitude(pos, radius, torad * yLat);
227
228 CP_DrawXVIOverlayRow(-pos[0] - deltaLong, -pos[0] + deltaLong, pos, y, yLat, xviLevel, radius);
229 }
230
231 CP_UploadXVI();
232}
233
238{
240 for (int y = 0; y < XVI_HEIGHT; y++) {
241 for (int x = 0; x < XVI_WIDTH; x++) {
242 const int xviLevel = CP_GetXVILevel(x, y);
243 if (xviLevel > 0)
244 CP_SetXVILevel(x, y, xviLevel - 1);
245 }
246 }
247
248 CP_UploadXVI();
249}
250
257void CP_ChangeXVILevel (const vec2_t pos, float factor)
258{
259 const int xCenter = round((180 - pos[0]) * XVI_WIDTH / 360.0f);
260 const int yCenter = round((90 - pos[1]) * XVI_HEIGHT / 180.0f);
261
262 CP_IncreaseXVILevel(pos, xCenter, yCenter, factor);
263}
264
270{
271 /* data Pointer to the data containing values to store in XVI map. Can be nullptr for new games.
272 * This is only the alpha channel of the xvi map */
273 memset(cgi->r_xviAlpha, 0, XVI_WIDTH * XVI_HEIGHT);
274 CP_UploadXVI();
275}
276
277/*
278 * @brief Radar overlay code description
279 * The radar overlay is handled 2 times: bases radar range and aircraft radar range.
280 * Bases radar range needs to be updated only when a radar facility is built or destroyed.
281 * The base radar overlay is stored in r_radarSourcePic.
282 * Aircraft radar overlay needs to be updated every time an aircraft moves, because the position of the radar moves.
283 * this overlay is created by duplicating r_radarSourcePic, and then adding any aircraft radar coverage. It is stored in r_radarTexture
284 * @sa RADAR_UpdateWholeRadarOverlay
285 */
286
293{
294 /* Initialize Radar */
295 if (source)
296 memset(cgi->r_radarSourcePic, INITIAL_ALPHA_VALUE, RADAR_WIDTH * RADAR_HEIGHT);
297 else
298 memcpy(cgi->r_radarPic, cgi->r_radarSourcePic, RADAR_WIDTH * RADAR_HEIGHT);
299}
300
309static void CP_DrawRadarOverlayRow (float latMin, float latMax, int y, byte alpha, bool source)
310{
311 const float radarWidthPerDegree = RADAR_WIDTH / 360.0f;
312 int x;
313
314 assert(latMax - latMin <= 360 + EQUAL_EPSILON);
315
316 /* if the disc we draw cross the left or right edge of the picture, we need to
317 * draw 2 part of circle on each side of the overlay */
318 if (latMin < -180.0f) {
319 int xMin = 0;
320 int xMax = ceil((latMax + 180.0f) * radarWidthPerDegree);
321 for (x = xMin; x < xMax; x++) {
322 byte* dest = CP_RadarGet(x, y, source);
323 if (alpha < dest[3])
324 dest[3] = alpha;
325 }
326 xMin = floor((latMin + 540.0f) * radarWidthPerDegree);
327 xMax = RADAR_WIDTH;
328 for (x = xMin; x < xMax; x++) {
329 byte* dest = CP_RadarGet(x, y, source);
330 if (alpha < dest[3])
331 dest[3] = alpha;
332 }
333 } else if (latMax > 180.0f) {
334 int xMin = 0;
335 int xMax = ceil((latMax - 180.0f) * radarWidthPerDegree);
336 for (x = xMin; x < xMax; x++) {
337 byte* dest = CP_RadarGet(x, y, source);
338 if (alpha < dest[3])
339 dest[3] = alpha;
340 }
341 xMin = floor((latMin + 180.0f) * radarWidthPerDegree);
342 xMax = RADAR_WIDTH;
343 for (x = xMin; x < xMax; x++) {
344 byte* dest = CP_RadarGet(x, y, source);
345 if (alpha < dest[3])
346 dest[3] = alpha;
347 }
348 } else {
349 const int xMin = floor((latMin + 180.0f) * radarWidthPerDegree);
350 const int xMax = ceil((latMax + 180.0f) * radarWidthPerDegree);
351 for (x = xMin; x < xMax; x++) {
352 byte* dest = CP_RadarGet(x, y, source);
353 if (alpha < dest[3])
354 dest[3] = alpha;
355 }
356 }
357}
358
367void CP_AddRadarCoverage (const vec2_t pos, float innerRadius, float outerRadius, bool source)
368{
369 const byte innerAlpha = 0;
370 const byte outerAlpha = 60;
371 const float radarHeightPerDegree = RADAR_HEIGHT / 180.0f;
372 int y;
373 int yMax, yMin;
374 int outeryMax, outeryMin;
375
377 assert(outerRadius < 180.0f);
378 assert(outerRadius > innerRadius);
379
380 /* Set minimum and maximum rows value we'll have to change */
381 CP_SetMinMaxOverlayRows(pos, innerRadius, RADAR_HEIGHT, &yMin, &yMax);
382 CP_SetMinMaxOverlayRows(pos, outerRadius, RADAR_HEIGHT, &outeryMin, &outeryMax);
383
384 /* Draw upper part of the radar coverage */
385 for (y = outeryMin; y < yMin; y++) {
386 /* latitude of current point, in radian */
387 const float yLat = torad * (90.0f - y / radarHeightPerDegree);
388 float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
389
390 /* Only the outer radar coverage is drawn at this latitude */
391 CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
392 }
393
394 /* Draw middle part of the radar coverage */
395 for (y = yMin; y < yMax; y++) {
396 /* latitude of current point, in radian */
397 const float yLat = torad * (90.0f - y / radarHeightPerDegree);
398 const float deltaLong = CP_GetCircleDeltaLongitude(pos, innerRadius, yLat);
399 const float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
400
401 /* At this latitude, there are 3 parts to draw: left outer radar, inner radar, and right outer radar */
402 CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] - deltaLong, y, outerAlpha, source);
403 CP_DrawRadarOverlayRow(-pos[0] - deltaLong, -pos[0] + deltaLong, y, innerAlpha, source);
404 CP_DrawRadarOverlayRow(-pos[0] + deltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
405 }
406
407 /* Draw lower part of the radar coverage */
408 for (y = yMax; y < outeryMax; y++) {
409 /* latitude of current point, in radian */
410 const float yLat = torad * (90.0f - y / radarHeightPerDegree);
411 const float outerDeltaLong = CP_GetCircleDeltaLongitude(pos, outerRadius, yLat);
412
413 /* Only the outer radar coverage is drawn at this latitude */
414 CP_DrawRadarOverlayRow(-pos[0] - outerDeltaLong, -pos[0] + outerDeltaLong, y, outerAlpha, source);
415 }
416}
417
423{
424 cgi->R_SoftenTexture(cgi->r_radarPic, RADAR_WIDTH, RADAR_HEIGHT, 1);
425 cgi->R_UploadAlpha("***r_radarTexture***", cgi->r_radarPic);
426}
Share stuff between the different cgame implementations.
#define XVI_HEIGHT
Definition cl_shared.h:51
#define XVI_WIDTH
Definition cl_shared.h:50
#define RADAR_WIDTH
Definition cl_shared.h:52
#define RADAR_HEIGHT
Definition cl_shared.h:53
Header file for single player campaign control.
const cgame_import_t * cgi
void CP_InitializeRadarOverlay(bool source)
Initialize radar overlay on geoscape.
int CP_GetXVILevel(int x, int y)
void CP_SetXVILevel(int x, int y, int value)
static void CP_IncreaseXVILevel(const vec2_t pos, int xCenter, int yCenter, float factor)
Applies spreading on xvi transparency channel centered on a given pos.
static byte * CP_RadarGet(int x, int y, bool source)
void CP_InitializeXVIOverlay(void)
Initialize XVI overlay on geoscape.
void CP_UploadRadarCoverage(void)
Smooth radar coverage.
static void CP_SetMinMaxOverlayRows(const vec2_t pos, float radius, const int height, int *yMin, int *yMax)
Set lower and upper value of an overlay (radar, xvi) row that can be modified when tracing a circle.
static void CP_DrawXVIOverlayRow(float latMin, float latMax, const vec2_t center, int y, float yLat, int xviLevel, float radius)
Draw XVI overlay for a given latitude between 2 longitudes.
static void CP_UploadXVI(void)
void CP_GetXVIMapDimensions(int *width, int *height)
static float CP_GetCircleDeltaLongitude(const vec2_t centerPos, float radius, const float yLat)
Return the half longitude affected when tracing a circle at a given latitude.
static void CP_DrawRadarOverlayRow(float latMin, float latMax, int y, byte alpha, bool source)
Draw radar overlay for a given latitude between 2 longitudes.
static byte * CP_XVIGetAlpha(int x, int y)
void CP_ChangeXVILevel(const vec2_t pos, float factor)
Convert the pos into degrees and increase XVI there.
void CP_DecreaseXVILevelEverywhere(void)
static const int INITIAL_ALPHA_VALUE
static const int MAX_ALPHA_VALUE
void CP_AddRadarCoverage(const vec2_t pos, float innerRadius, float outerRadius, bool source)
Add a radar coverage (base or aircraft) to radar overlay.
static void CP_DrawXVIOverlayPixel(int xMin, int xMax, const vec2_t centerPos, int y, const float yLat, int xviLevel, float radius)
Change the value of 1 pixel in XVI overlay, the new value is higher than old one.
Functions to generate and render overlay for geoscape.
double GetDistanceOnGlobe(const vec2_t pos1, const vec2_t pos2)
Calculate distance on the geoscape.
Definition mathlib.cpp:171
#define EQUAL_EPSILON
Definition mathlib.h:40
#define torad
Definition mathlib.h:50
#define todeg
Definition mathlib.h:51
QGL_EXTERN GLenum GLuint * dest
Definition r_gl.h:101
vec_t vec2_t[2]
Definition ufotypes.h:38