1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 1997-2001 Id Software, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "r_local.h"
26 #include "r_error.h"
27 #include "r_sphere.h"
28 #include "r_geoscape.h"
29
30 #include "r_mesh.h"
31 #include "r_draw.h"
32
33 #define MARKER_SIZE 60.0
34
35 /**
36 * @brief Draw the day and night images of a flat geoscape
37 * multitexture feature is used to blend the images
38 * @sa R_Draw3DGlobe
39 * @param[in] p The horizontal shift of the night map
40 * @param[in] cx The x texture coordinate
41 * @param[in] cy The y texture coordinate
42 * @param[in] iz The zoomlevel of the geoscape - see ccs.zoom
43 * @param[in] map The geoscape map to draw (can be changed in the campaign definition)
44 * @param[in] overlayNation,overlayXVI,overlayRadar Whether these overlays should be drawn or not
45 */
R_DrawFlatGeoscape(const vec2_t nodePos,const vec2_t nodeSize,float p,float cx,float cy,float iz,const char * map,bool overlayNation,bool overlayXVI,bool overlayRadar,image_t * r_dayandnightTexture,image_t * r_xviTexture,image_t * r_radarTexture)46 void R_DrawFlatGeoscape (const vec2_t nodePos, const vec2_t nodeSize, float p, float cx, float cy, float iz, const char* map, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t* r_dayandnightTexture, image_t* r_xviTexture, image_t* r_radarTexture)
47 {
48 image_t* gl;
49 float geoscape_texcoords[4 * 2];
50 short geoscape_verts[4 * 2];
51
52 /* normalize */
53 const float nx = nodePos[0] * viddef.rx;
54 const float ny = nodePos[1] * viddef.ry;
55 const float nw = nodeSize[0] * viddef.rx;
56 const float nh = nodeSize[1] * viddef.ry;
57
58 /* load day image */
59 gl = R_FindImage(va("pics/geoscape/%s_day", map), it_wrappic);
60 if (gl == r_noTexture)
61 Com_Error(ERR_FATAL, "Could not load geoscape day image");
62
63 /* alter the array pointers */
64 glVertexPointer(2, GL_SHORT, 0, geoscape_verts);
65 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_texcoords);
66
67 geoscape_texcoords[0] = cx - iz;
68 geoscape_texcoords[1] = cy - iz;
69 geoscape_texcoords[2] = cx + iz;
70 geoscape_texcoords[3] = cy - iz;
71 geoscape_texcoords[4] = cx + iz;
72 geoscape_texcoords[5] = cy + iz;
73 geoscape_texcoords[6] = cx - iz;
74 geoscape_texcoords[7] = cy + iz;
75
76 geoscape_verts[0] = nx;
77 geoscape_verts[1] = ny;
78 geoscape_verts[2] = nx + nw;
79 geoscape_verts[3] = ny;
80 geoscape_verts[4] = nx + nw;
81 geoscape_verts[5] = ny + nh;
82 geoscape_verts[6] = nx;
83 geoscape_verts[7] = ny + nh;
84
85 /* draw day image */
86 R_BindTexture(gl->texnum);
87 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
88
89 refdef.batchCount++;
90
91 /* draw night map */
92 gl = R_FindImage(va("pics/geoscape/%s_night", map), it_wrappic);
93 /* maybe the campaign map doesn't have a night image */
94 if (gl != r_noTexture) {
95 float geoscape_nighttexcoords[4 * 2];
96
97 R_BindTexture(gl->texnum);
98 R_EnableTexture(&texunit_lightmap, true);
99 R_SelectTexture(&texunit_lightmap);
100
101 geoscape_nighttexcoords[0] = geoscape_texcoords[0] + p;
102 geoscape_nighttexcoords[1] = geoscape_texcoords[1];
103 geoscape_nighttexcoords[2] = geoscape_texcoords[2] + p;
104 geoscape_nighttexcoords[3] = geoscape_texcoords[3];
105 geoscape_nighttexcoords[4] = geoscape_texcoords[4] + p;
106 geoscape_nighttexcoords[5] = geoscape_texcoords[5];
107 geoscape_nighttexcoords[6] = geoscape_texcoords[6] + p;
108 geoscape_nighttexcoords[7] = geoscape_texcoords[7];
109
110 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_nighttexcoords);
111
112 R_BindTexture(r_dayandnightTexture->texnum);
113
114 R_SelectTexture(&texunit_diffuse);
115 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
116
117 refdef.batchCount++;
118
119 R_SelectTexture(&texunit_lightmap);
120 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_texcoords);
121
122 R_EnableTexture(&texunit_lightmap, false);
123 }
124
125 /* draw nation overlay */
126 if (overlayNation) {
127 gl = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic);
128 if (gl == r_noTexture)
129 Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image");
130
131 /* draw day image */
132 R_BindTexture(gl->texnum);
133 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
134
135 refdef.batchCount++;
136 }
137
138 /* draw XVI image */
139 if (overlayXVI) {
140 gl = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic);
141 if (gl == r_noTexture)
142 Com_Error(ERR_FATAL, "Could not load xvi overlay image");
143
144 R_BindTexture(gl->texnum);
145
146 R_EnableTexture(&texunit_lightmap, true);
147 R_BindLightmapTexture(r_xviTexture->texnum);
148
149 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
150
151 refdef.batchCount++;
152
153 R_EnableTexture(&texunit_lightmap, false);
154 }
155
156 /* draw radar image */
157 if (overlayRadar) {
158 R_BindTexture(r_radarTexture->texnum);
159 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
160
161 refdef.batchCount++;
162 }
163
164 /* and restore them */
165 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
166 R_BindDefaultArray(GL_VERTEX_ARRAY);
167 }
168
169 /**
170 * @brief Draw 3D Marker on the 2D geoscape.
171 * @param[in] screenPos Position on screenlongitude and latitude of the model to draw.
172 * @param[in] direction angle giving the direction the model is heading toward.
173 */
R_Draw2DMapMarkers(const vec2_t screenPos,float direction,const char * model,int skin)174 void R_Draw2DMapMarkers (const vec2_t screenPos, float direction, const char* model, int skin)
175 {
176 modelInfo_t mi;
177 vec2_t size;
178 vec3_t scale, center, position, angles;
179 float zoom = 0.4f;
180
181 OBJZERO(mi);
182 VectorCopy(vec3_origin, position);
183 VectorCopy(vec3_origin, angles);
184
185 mi.model = R_FindModel(model);
186 if (!mi.model) {
187 Com_Printf("Could not find model '%s'\n", model);
188 return;
189 }
190
191 mi.name = model;
192 mi.origin = position;
193 mi.angles = angles;
194 mi.skin = skin;
195
196 size[0] = size[1] = MARKER_SIZE * zoom;
197 R_ModelAutoScale(size, &mi, scale, center);
198 /* reset the center, as we want to place the models onto the surface of the earth */
199 mi.center = nullptr;
200
201 /* go to a new matrix */
202 glPushMatrix();
203
204 /* Apply all transformation to model. Note that the transformations are applied starting
205 * from the last one and ending with the first one */
206
207 /* move model to its location */
208 glTranslatef(screenPos[0]* viddef.rx, screenPos[1]* viddef.ry, 0);
209 /* scale model to proper resolution */
210 glScalef(viddef.rx, viddef.ry, 1.0f);
211 /* rotate model to proper direction. */
212 glRotatef(-90.f + direction, 0, 0, 1);
213
214 R_DrawModelDirect(&mi, nullptr, nullptr);
215
216 /* restore previous matrix */
217 glPopMatrix();
218 }
219
220 /**
221 * @brief Draw 3D Marker on the 3D geoscape.
222 * @param[in] rotate vector giving the angles of earth rotation due to player view.
223 * @param[in] pos longitude and latitude of the model to draw.
224 * @param[in] direction angle giving the direction the model is heading toward.
225 * @param[in] earthRadius Radius of earth on screen (this include zoom).
226 */
R_Draw3DMapMarkers(const vec2_t nodePos,const vec2_t nodeSize,const vec3_t rotate,const vec2_t pos,float direction,float earthRadius,const char * model,int skin)227 void R_Draw3DMapMarkers (const vec2_t nodePos, const vec2_t nodeSize, const vec3_t rotate, const vec2_t pos, float direction, float earthRadius, const char* model, int skin)
228 {
229 /* normalize */
230 const float nx = nodePos[0] * viddef.rx;
231 const float ny = nodePos[1] * viddef.ry;
232 const float nw = nodeSize[0] * viddef.rx;
233 const float nh = nodeSize[1] * viddef.ry;
234
235 /* Earth center is in the middle of node.
236 * Due to Orthographic view, this is also camera position */
237 const vec3_t earthPos = {nx + nw / 2.0f, ny + nh / 2.0f, 0.0f};
238
239 modelInfo_t mi;
240 vec2_t size;
241 vec3_t scale, center, position, angles;
242 float zoom = 0.4f;
243
244 OBJZERO(mi);
245 VectorCopy(vec3_origin, position);
246 VectorCopy(vec3_origin, angles);
247
248 mi.model = R_FindModel(model);
249 if (!mi.model) {
250 Com_Printf("Could not find model '%s'\n", model);
251 return;
252 }
253
254 mi.name = model;
255 mi.origin = position;
256 mi.angles = angles;
257 mi.skin = skin;
258
259 size[0] = size[1] = MARKER_SIZE * zoom;
260 R_ModelAutoScale(size, &mi, scale, center);
261 /* reset the center, as we want to place the models onto the surface of the earth */
262 mi.center = nullptr;
263
264 /* go to a new matrix */
265 glPushMatrix();
266
267 /* Apply all transformation to model. Note that the transformations are applied starting
268 * from the last one and ending with the first one */
269
270 /* center model on earth. Translate also along z to avoid seeing
271 * bottom part of the model through earth (only half of earth is drawn) */
272 glTranslatef(earthPos[0], earthPos[1], 10.0f);
273 /* scale model to proper resolution */
274 glScalef(viddef.rx, viddef.ry, 1.0f);
275 /* place model on earth: make it tangent to earth surface, heading toward it if direction is used. */
276 glRotatef(-rotate[1], 1, 0, 0);
277 glRotatef(rotate[2], 0, 1, 0);
278 glRotatef(rotate[0] - pos[0], 0, 0, 1);
279 glRotatef(90.0f - pos[1], 1, 0, 0);
280 glTranslatef(0, 0, earthRadius);
281 glRotatef(90.0f + direction, 0, 0, 1);
282
283 R_DrawModelDirect(&mi, nullptr, nullptr);
284
285 /* restore previous matrix */
286 glPopMatrix();
287 }
288
289 /**
290 * @brief Half size of Skybox.
291 * @note The bigger, the less perspective default you'll have, but the more you'll
292 * zoom on the texture (and see it's default).
293 * @sa R_DrawStarfield
294 * @sa R_Setup2D
295 */
296 #define SKYBOX_HALFSIZE 800.0f
297
298 static const float starFieldVerts[] = {
299 /* face 1 */
300 -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
301 +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
302 +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
303 -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
304
305 /* face 2 */
306 -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
307 +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
308 +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
309 -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
310
311 /* face 3 */
312 +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
313 +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
314 +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
315 +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
316
317 /* face 4 */
318 -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
319 -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
320 -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
321 -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
322
323 /* face 5 */
324 -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
325 +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
326 +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
327 -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
328
329 /* face 6 */
330 -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
331 +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, +SKYBOX_HALFSIZE,
332 +SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE,
333 -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE, -SKYBOX_HALFSIZE
334 };
335
336 static const float starFieldTexCoords[] = {
337 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
338 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
339 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
340 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
341 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
342 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
343 };
344
345 /**
346 * @brief Bind and draw starfield.
347 * @param[in] texnum The texture id (already uploaded of course)
348 * @note We draw a skybox: the camera is inside a cube rotating at earth rotation speed
349 * (stars seems to rotate because we see earth as idle, but in reality stars are statics
350 * and earth rotate around itself)
351 * @sa R_Setup2D
352 * @sa R_Draw3DGlobe
353 */
R_DrawStarfield(int texnum,const vec3_t pos,const vec3_t rotate,float p)354 static void R_DrawStarfield (int texnum, const vec3_t pos, const vec3_t rotate, float p)
355 {
356 vec3_t angle; /**< Angle of rotation of starfield */
357
358 /* go to a new matrix */
359 glPushMatrix();
360
361 /* we must center the skybox on the camera border of view, and not on the earth, in order
362 * to see only the inside of the cube */
363 glTranslatef(pos[0], pos[1], -SKYBOX_DEPTH);
364
365 /* rotates starfield: only time and rotation of earth around itself causes starfield to rotate. */
366 VectorSet(angle, rotate[0] - p * todeg, rotate[1], rotate[2]);
367 glRotatef(angle[YAW], 1, 0, 0);
368 glRotatef(angle[ROLL], 0, 1, 0);
369 glRotatef(angle[PITCH], 0, 0, 1);
370
371 R_BindTexture(texnum);
372
373 /* alter the array pointers */
374 glVertexPointer(3, GL_FLOAT, 0, starFieldVerts);
375 glTexCoordPointer(2, GL_FLOAT, 0, starFieldTexCoords);
376
377 /* draw the cube */
378 #ifdef GL_VERSION_ES_CM_1_0
379 for( int ii = 0; ii < 6; ii++ )
380 glDrawArrays(GL_TRIANGLE_FAN, ii * 4, 4);
381 #else
382 glDrawArrays(GL_QUADS, 0, 24);
383 #endif
384
385 refdef.batchCount++;
386
387 /* restore previous matrix */
388 glPopMatrix();
389 }
390
391 /**
392 * @brief rotate a planet (sun or moon) with respect to the earth
393 */
R_RotateCelestialBody(const vec4_t v,vec4_t r,const vec3_t rotate,const vec3_t earthPos,const float celestialDist)394 static inline void R_RotateCelestialBody (const vec4_t v, vec4_t r, const vec3_t rotate, const vec3_t earthPos, const float celestialDist)
395 {
396 vec4_t v1;
397 vec3_t v2;
398 vec3_t rotationAxis;
399
400 VectorSet(v2, v[1], v[0], v[2]);
401 VectorSet(rotationAxis, 0, 0, 1);
402 RotatePointAroundVector(v1, rotationAxis, v2, -rotate[PITCH]);
403 VectorSet(rotationAxis, 0, 1, 0);
404 RotatePointAroundVector(v2, rotationAxis, v1, -rotate[YAW]);
405
406 Vector4Set(r, earthPos[0] + celestialDist * v2[1], earthPos[1] + celestialDist * v2[0], -celestialDist * v2[2], 0);
407 }
408
409 /**
410 * @brief responsible for drawing the 3d globe on geoscape
411 * param[in] rotate the rotate angle of the globe
412 * param[in] zoom the current globe zoon
413 * param[in] map the prefix of the map to use (image must be at base/pics/menu/\<map\>_[day|night])
414 * @sa R_DrawFlatGeoscape
415 * @sa R_SphereGenerate
416 */
R_Draw3DGlobe(const vec2_t pos,const vec2_t size,int day,int second,const vec3_t rotate,float zoom,const char * map,bool disableSolarRender,float ambient,bool overlayNation,bool overlayXVI,bool overlayRadar,image_t * r_xviTexture,image_t * r_radarTexture,bool renderNationGlow)417 void R_Draw3DGlobe (const vec2_t pos, const vec2_t size, int day, int second, const vec3_t rotate, float zoom, const char* map,
418 bool disableSolarRender, float ambient, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t* r_xviTexture,
419 image_t* r_radarTexture, bool renderNationGlow)
420 {
421 /* globe scaling */
422 const float fullscale = zoom / STANDARD_3D_ZOOM;
423
424 /* lighting colors */
425 static const vec4_t diffuseLightColor = { 1.75f, 1.75f, 1.75f, 1.0f };
426 static const vec4_t specularLightColor = { 2.0f, 1.9f, 1.7f, 1.0f };
427 static const vec4_t darknessLightColor = { 0.0f, 0.0f, 0.0f, 1.0f };
428 static const vec4_t brightDiffuseLightColor = { 5.0f, 5.0f, 5.0f, 1.0f };
429 const vec4_t ambientLightColor = { ambient + 0.2f, ambient + 0.2f, ambient + 0.2f, ambient + 0.2f };
430 /* billboard textures */
431 image_t* starfield;
432 image_t* halo;
433 image_t* sun;
434 image_t* sunOverlay;
435
436 /* set distance of the sun and moon to make them static on starfield when
437 * time is stoped. this distance should be used for any celestial body
438 * considered at infinite location (sun, moon) */
439 static const float celestialDist = 1.37f * SKYBOX_HALFSIZE;
440 static const float moonSize = 0.025f;
441 vec4_t sunPos;
442 vec4_t antiSunPos;
443 vec4_t moonLoc;
444 vec4_t sunLoc;
445
446 /* normalize */
447 const float nx = pos[0] * viddef.rx;
448 const float ny = pos[1] * viddef.ry;
449 const float nw = size[0] * viddef.rx;
450 const float nh = size[1] * viddef.ry;
451
452 /* Earth center is in the middle of node.
453 * Due to Orthographic view, this is also camera position */
454 const vec3_t earthPos = { nx + nw / 2.0f, ny + nh / 2.0f, 0.0f };
455
456 /* estimate the progress through the current season so we can do
457 * smooth transitions between textures. Currently there are 12
458 * "seasons", because we have one image per Earth-month. */
459 const float season = (float) (day % DAYS_PER_YEAR) / ((float) (DAYS_PER_YEAR) / (float) (SEASONS_PER_YEAR));
460 const int currSeason = (int) floorf(season) % SEASONS_PER_YEAR;
461 const int nextSeason = (int) ceilf(season) % SEASONS_PER_YEAR;
462 const float seasonProgress = season - (float) currSeason;
463
464 /* Compute sun position in absolute frame */
465 const float q = (day % DAYS_PER_YEAR * SECONDS_PER_DAY + second) * (2.0f * M_PI / (SECONDS_PER_DAY * DAYS_PER_YEAR)); /* sun rotation (year) */
466 const float a = cos(q) * SIN_ALPHA; /* due to earth obliquity */
467 const float sqrta = sqrt(0.5f * (1 - a * a));
468
469 /* earth rotation (day) */
470 const float p = (second - SECONDS_PER_DAY / 4) * (2.0f * M_PI / SECONDS_PER_DAY);
471 /* lunar orbit */
472 const float m = p + (((double)((10 * day % 249) / 10.0f) + ((double)second / (double)SECONDS_PER_DAY)) / 24.9f) * (2.0f * M_PI);
473
474 glPushMatrix();
475 glMatrixMode(GL_TEXTURE);
476 glLoadIdentity();
477 glMatrixMode(GL_MODELVIEW);
478 glDisable(GL_LIGHTING);
479 /* draw the starfield, rotating with the planet */
480 starfield = R_FindImage(va("pics/geoscape/%s_stars", map), it_wrappic);
481 if (starfield != r_noTexture)
482 R_DrawStarfield(starfield->texnum, earthPos, rotate, p);
483
484 glPopMatrix();
485
486 /* set up position vectors for celestial bodies */
487 Vector4Set(sunPos, cos(p) * sqrta, -sin(p) * sqrta, a, 0);
488 Vector4Set(antiSunPos, -cos(p) * sqrta, sin(p) * sqrta, -a, 0);
489
490 /* Rotate the sun in the relative frame of player view, to get sun location */
491 R_RotateCelestialBody(sunPos, sunLoc, rotate, earthPos, 1.0f);
492 /* load sun texture image */
493 sun = R_FindImage(va("pics/geoscape/%s_sun", map), it_wrappic);
494 sunOverlay = R_FindImage(va("pics/geoscape/%s_sun_overlay", map), it_pic);
495 if (sun != r_noTexture && sunOverlay != r_noTexture && sunLoc[2] > 0 && !disableSolarRender) {
496 const int sunx = earthPos[0] + viddef.rx * (-128.0f + celestialDist * (sunLoc[0] - earthPos[0]));
497 const int suny = earthPos[1] + viddef.ry * (-128.0f + celestialDist * (sunLoc[1] - earthPos[1]));
498
499 R_DrawTexture(sunOverlay->texnum, sunx, suny, 256.0f * viddef.rx, 256.0f * viddef.ry);
500 R_DrawBuffers(2);
501 R_DrawTexture(sun->texnum, sunx, suny, 256.0 * viddef.rx, 256.0 * viddef.ry);
502 R_DrawBuffers(1);
503 }
504
505 /* calculate position of the moon (it rotates around earth with a period of
506 * about 24.9 h, and we must take day into account to avoid moon to "jump"
507 * every time the day is changing) */
508 VectorSet(moonLoc, cos(m) * sqrta, -sin(m) * sqrta, a);
509 R_RotateCelestialBody(moonLoc, moonLoc, rotate, earthPos, celestialDist);
510
511 /* free last month's texture image */
512 if (r_globeEarth.season != currSeason) {
513 r_globeEarth.season = currSeason;
514 R_FreeImage(r_globeEarth.texture);
515 }
516
517 /* load diffuse texture map (with embedded night-glow map as alpha channel) */
518 r_globeEarth.texture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, currSeason), it_wrappic);
519 if (r_globeEarth.texture == r_noTexture)
520 Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, currSeason);
521
522 /* set up for advanced GLSL rendering if we have the capability */
523 if (r_programs->integer) {
524 r_globeEarth.glslProgram = r_state.geoscape_program;
525 /* load earth image for the next month so we can blend them */
526 r_globeEarth.blendTexture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, nextSeason), it_wrappic);
527 if (r_globeEarth.blendTexture == r_noTexture)
528 Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, nextSeason);
529
530 /* load normal map (with embedded gloss map as alpha channel) */
531 r_globeEarth.normalMap = R_FindImage(va("pics/geoscape/%s/%s_bump", r_config.lodDir, map), it_wrappic);
532 if (r_globeEarth.normalMap == r_noTexture)
533 r_globeEarth.normalMap = nullptr;
534
535 /* weight the blending based on how much of the month has elapsed */
536 r_globeEarth.blendScale = seasonProgress;
537 /* set up lights for nighttime city glow */
538 VectorCopy(antiSunPos, r_globeEarth.nightLightPos);
539 glLightfv(GL_LIGHT1, GL_AMBIENT, darknessLightColor);
540 glLightfv(GL_LIGHT1, GL_DIFFUSE, brightDiffuseLightColor);
541 glLightfv(GL_LIGHT1, GL_SPECULAR, darknessLightColor);
542
543 r_globeEarth.glowScale = 0.7f;
544 }
545
546 /* load moon texture image */
547 r_globeMoon.texture = R_FindImage(va("pics/geoscape/%s_moon", map), it_wrappic);
548
549 /* globe texture scaling */
550 glMatrixMode(GL_TEXTURE);
551 glLoadIdentity();
552 glScalef(2.0f, 1.0f, 1.0f);
553 glMatrixMode(GL_MODELVIEW);
554
555 /* enable the lighting */
556 glEnable(GL_LIGHTING);
557 glEnable(GL_LIGHT0);
558 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLightColor);
559 glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLightColor);
560 glLightfv(GL_LIGHT0, GL_SPECULAR, specularLightColor);
561
562 /* draw the moon */
563 if (r_globeMoon.texture != r_noTexture && moonLoc[2] > 0 && !disableSolarRender)
564 R_SphereRender(&r_globeMoon, moonLoc, rotate, moonSize, sunPos);
565
566 /* activate depth to hide 3D models behind earth */
567 glEnable(GL_DEPTH_TEST);
568
569 /* draw the earth */
570 R_DrawBuffers(2);
571 #if 0 /* old rendering code which doesn't render city lights in FFP */
572 if (r_programs->integer == 0) /* ignore alpha channel, since the city-light map is stored there */
573 glBlendFunc(GL_ONE, GL_ZERO);
574
575 R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos);
576
577 if (r_programs->integer == 0) /* restore default blend function */
578 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
579 #else /* new which does render city lights in FFP */
580 if (r_programs->integer == 0) {
581 /* set up rendering of city lights map, which is stored in alpha channel; OpenGL 1.3 required */
582 R_SelectTexture(&texunit_diffuse); /* select texture to edit texture environment for */
583 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); /* enable color combiner */
584 /* setup texture combiner to blend between daylight diffuse map stored in the RGB channels of the diffuse texture
585 * and the monochomatic emission map (which simulates city lights) stored in the alpha channel;
586 * incoming color value is the blend factor.
587 */
588 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); /* set day color as blending target*/
589 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
590 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); /* set night color as blending source */
591 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
592 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS); /* set incoming color as blending factor */
593 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
594 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); /* set blending mode to interpolation from src1 to src0 */
595 /* copy alpha from incoming color, bypassing the value read from texture, which is not a "real" alpha anyway */
596 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
597 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
598 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
599 }
600
601 R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos);
602
603 if (r_programs->integer == 0) { /* disable combiner */
604 R_SelectTexture(&texunit_diffuse);
605 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
606 }
607 #endif
608
609 r_globeEarthAtmosphere.texture = R_FindImage(va("pics/geoscape/%s_atmosphere", map), it_wrappic);
610
611 /* Draw earth atmosphere */
612 /** @todo render atmosphere glow even when postprocessing is disabled */
613 if (r_programs->integer && r_postprocess->integer) {
614 r_globeEarthAtmosphere.normalMap = r_globeEarth.normalMap;
615 r_globeEarthAtmosphere.glowScale = 1.0;
616 r_globeEarthAtmosphere.blendScale = -1.0;
617 r_globeEarthAtmosphere.glslProgram = r_state.atmosphere_program;
618 R_SphereRender(&r_globeEarthAtmosphere, earthPos, rotate, fullscale, sunPos);
619 } else {
620 halo = R_FindImage("pics/geoscape/map_earth_halo", it_pic);
621 if (halo != r_noTexture) {
622 /** @todo Replace this magic number with some speaking constant */
623 const float earthSizeX = fullscale * 20500.0f * viddef.rx;
624 const float earthSizeY = fullscale * 20500.0f * viddef.ry;
625 glMatrixMode(GL_TEXTURE);
626 glPushMatrix();
627 glLoadIdentity();
628 glDisable(GL_LIGHTING);
629
630 R_DrawTexture(halo->texnum, earthPos[0] - earthSizeX * 0.5f, earthPos[1] - earthSizeY * 0.5f, earthSizeX, earthSizeY);
631 glEnable(GL_LIGHTING);
632 glPopMatrix();
633 glMatrixMode(GL_MODELVIEW);
634 }
635 }
636
637 R_DrawBuffers(1);
638 glDisable(GL_DEPTH_TEST);
639
640 /* draw nation overlay */
641 if (overlayNation) {
642 r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic);
643 if (r_globeEarth.overlay == r_noTexture)
644 Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image");
645
646 R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos);
647
648 if (renderNationGlow) {
649 /* draw glowing borders */
650 r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay_glow", map), it_wrappic);
651 if (r_globeEarth.overlay == r_noTexture)
652 Com_Error(ERR_FATAL, "Could not load geoscape nation overlay glow image");
653
654 R_DrawBuffers(2);
655 glDisable(GL_LIGHTING);
656 R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos);
657 glEnable(GL_LIGHTING);
658 R_DrawBuffers(1);
659 }
660
661 r_globeEarth.overlay = nullptr;
662 }
663 /* draw XVI overlay */
664 if (overlayXVI) {
665 r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic);
666 r_globeEarth.overlayAlphaMask = r_xviTexture;
667 assert(r_globeEarth.overlayAlphaMask);
668 R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos);
669 r_globeEarth.overlayAlphaMask = nullptr;
670 r_globeEarth.overlay = nullptr;
671 }
672 /* draw radar overlay */
673 if (overlayRadar) {
674 r_globeEarth.overlay = r_radarTexture;
675 assert(r_globeEarth.overlay);
676 R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos);
677 r_globeEarth.overlay = nullptr;
678 }
679
680 /* disable 3d geoscape lighting */
681 glDisable(GL_LIGHTING);
682
683 /* restore the previous matrix */
684 glMatrixMode(GL_TEXTURE);
685 glLoadIdentity();
686 glMatrixMode(GL_MODELVIEW);
687 }
688
689 /**
690 * @brief Draw the current texture on a quad the size of the renderbuffer
691 */
R_DrawQuad(void)692 static inline void R_DrawQuad (void)
693 {
694 /** @todo use default_texcoords */
695 const vec2_t texcoord[] = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
696 const vec2_t verts[] = { { 0.0f, 0.0f }, Vector2FromInt(fbo_render->width, 0.0f), Vector2FromInt(fbo_render->width, fbo_render->height), Vector2FromInt(0.0f, fbo_render->height) };
697
698 glVertexPointer(2, GL_FLOAT, 0, verts);
699 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoord);
700
701 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
702
703 refdef.batchCount++;
704
705 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
706 R_BindDefaultArray(GL_VERTEX_ARRAY);
707 }
708
709 /**
710 * @brief does 1D filter convolution to blur a framebuffer texture.
711 * dir=0 for horizontal, dir=1 for vertical
712 */
R_Blur(r_framebuffer_t * source,r_framebuffer_t * dest,int tex,int dir)713 static void R_Blur (r_framebuffer_t* source, r_framebuffer_t* dest, int tex, int dir)
714 {
715 R_EnableBlur(r_state.convolve_program, true, source, dest, dir);
716
717 /* draw new texture onto a flat surface */
718 R_BindTextureForTexUnit(source->textures[tex], &texunit_0);
719 R_UseViewport(source);
720 R_DrawQuad();
721
722 R_EnableBlur(r_state.convolve_program, false, nullptr, nullptr, 0);
723 }
724
725 /**
726 * @brief blur from the source image pyramid into the dest image pyramid
727 */
R_BlurStack(int levels,r_framebuffer_t ** sources,r_framebuffer_t ** dests)728 static void R_BlurStack (int levels, r_framebuffer_t** sources, r_framebuffer_t** dests)
729 {
730 int i;
731
732 for (i = 0; i < levels; i++) {
733 const int l = levels - i - 1;
734
735 R_UseProgram(i == 0 ? default_program : r_state.combine2_program);
736 R_UseFramebuffer(dests[l]);
737 R_BindTextureForTexUnit(sources[l]->textures[0], &texunit_0);
738 if (i != 0)
739 R_BindTextureForTexUnit(dests[l + 1]->textures[0], &texunit_1);
740
741 R_UseViewport(sources[l]);
742 R_DrawQuad();
743
744 R_Blur(dests[l], sources[l], 0, 1);
745 R_Blur(sources[l], dests[l], 0, 0);
746 }
747 }
748
749 /**
750 * @brief handle post-processing bloom
751 */
R_DrawBloom(void)752 void R_DrawBloom (void)
753 {
754 int i;
755 bool renderBufferState;
756
757 if (!r_config.frameBufferObject || !r_postprocess->integer || !r_programs->integer)
758 return;
759
760 /* save state, then set up for blit-style rendering to quads */
761 renderBufferState = R_RenderbufferEnabled();
762 glMatrixMode(GL_MODELVIEW);
763 glPushMatrix();
764 glLoadIdentity();
765 glMatrixMode(GL_TEXTURE);
766 glPushMatrix();
767 glLoadIdentity();
768 glMatrixMode(GL_PROJECTION);
769 glPushMatrix();
770 glLoadIdentity();
771 #ifndef GL_VERSION_ES_CM_1_0
772 glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
773 #endif
774 glOrtho(0, viddef.context.width, viddef.context.height, 0, 9999.0f, SKYBOX_DEPTH);
775
776 glDisable(GL_LIGHTING);
777 glDisable(GL_DEPTH_TEST);
778
779 /* downsample into image pyramid */
780 R_ResolveMSAA(fbo_render);
781 R_BindTexture(fbo_render->textures[1]);
782 qglGenerateMipmapEXT(GL_TEXTURE_2D);
783
784 R_Blur(fbo_render, fbo_bloom0, 1, 0);
785 R_Blur(fbo_bloom0, fbo_bloom1, 0, 1);
786
787 R_UseFramebuffer(r_state.buffers0[0]);
788 R_BindTexture(fbo_bloom1->textures[0]);
789 qglGenerateMipmapEXT(GL_TEXTURE_2D);
790 R_UseViewport(r_state.buffers0[0]);
791 R_DrawQuad();
792
793 for (i = 1; i < DOWNSAMPLE_PASSES; i++) {
794 R_Blur(r_state.buffers0[i - 1], r_state.buffers1[i - 1], 0, 0);
795 R_Blur(r_state.buffers1[i - 1], r_state.buffers2[i - 1], 0, 1);
796 R_UseFramebuffer(r_state.buffers0[i]);
797 R_BindTexture(r_state.buffers2[i - 1]->textures[0]);
798 R_UseViewport(r_state.buffers0[i]);
799 R_DrawQuad();
800 }
801
802 /* blur and combine downsampled images */
803 R_BlurStack(DOWNSAMPLE_PASSES, r_state.buffers0, r_state.buffers1);
804
805 /* re-combine the blurred version with the original "glow" image */
806 R_UseProgram(r_state.combine2_program);
807 R_UseFramebuffer(fbo_bloom0);
808 R_BindTextureForTexUnit(fbo_render->textures[1], &texunit_0);
809 R_BindTextureForTexUnit(r_state.buffers1[0]->textures[0], &texunit_1);
810
811 R_UseViewport(fbo_screen);
812 R_DrawQuad();
813
814 /* draw final result to the screenbuffer */
815 R_UseFramebuffer(fbo_screen);
816 R_UseProgram(r_state.combine2_program);
817 R_BindTextureForTexUnit(fbo_render->textures[0], &texunit_0);
818 R_BindTextureForTexUnit(fbo_bloom0->textures[0], &texunit_1);
819
820 R_DrawQuad();
821
822 /* cleanup before returning */
823 R_UseProgram(default_program);
824
825 R_CheckError();
826
827 #ifndef GL_VERSION_ES_CM_1_0
828 glPopAttrib();
829 #endif
830 glMatrixMode(GL_PROJECTION);
831 glPopMatrix();
832 glMatrixMode(GL_TEXTURE);
833 glPopMatrix();
834 glMatrixMode(GL_MODELVIEW);
835 glPopMatrix();
836 R_CheckError();
837
838 /* reset renderbuffer state to what it was before */
839 R_EnableRenderbuffer(renderBufferState);
840 }
841