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