UFO: Alien Invasion
Doxygen documentation generating
ui_node_geoscape.cpp
Go to the documentation of this file.
1 
5 /*
6 Copyright (C) 2002-2023 UFO: Alien Invasion.
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 "../../DateTime.h"
26 #include "../ui_nodes.h"
27 #include "../ui_input.h"
28 #include "../ui_parse.h"
29 #include "../ui_behaviour.h"
30 #include "../ui_actions.h"
31 #include "../ui_render.h"
32 #include "ui_node_geoscape.h"
33 #include "../../renderer/r_framebuffer.h"
34 #include "../../renderer/r_geoscape.h"
35 #include "../../cl_shared.h"
36 #include "../../cgame/cl_game.h"
37 #include "../../input/cl_input.h"
38 #include "../../input/cl_keys.h"
39 
40 #include "../../../common/scripts_lua.h"
41 
42 #define EXTRADATA_TYPE mapExtraData_t
43 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
44 #define EXTRADATACONST(node) UI_EXTRADATACONST(node, EXTRADATA_TYPE)
45 
54 };
55 
60 static int oldMousePosX = 0;
61 
66 static int oldMousePosY = 0;
67 
73 static const float ROTATE_SPEED = 0.5f;
74 static const float GLOBE_ROTATE = -90.0f;
75 static const float SMOOTHING_STEP_2D = 0.02f;
76 static const float SMOOTHACCELERATION = 0.06f;
78 static cvar_t* cl_3dmap;
82 
83 // FIXME: don't make this static
85 
88 
92 #define DAWN 0.03
93 
99 {
100  vec3_t diff;
101  const float diffZoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom - UI_MAPEXTRADATACONST(node).zoom;
102 
103  VectorSubtract(UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle, UI_MAPEXTRADATACONST(node).angles, diff);
104 
105  if (UI_MAPEXTRADATACONST(node).smoothDeltaLength > UI_MAPEXTRADATACONST(node).smoothDeltaZoom) {
106  /* when we rotate (and zoom) */
107  const float diffAngle = VectorLength(diff);
108  const float epsilon = 0.1f;
109  if (diffAngle > epsilon) {
110  float rotationSpeed;
111  /* Append the old speed to the new speed if this is the first half of a new rotation, but never exceed the max speed.
112  * This allows the globe to rotate at maximum speed when the button is held down. */
113  rotationSpeed = sin(3.05f * diffAngle / UI_MAPEXTRADATACONST(node).smoothDeltaLength) * diffAngle;
114  if (diffAngle / UI_MAPEXTRADATACONST(node).smoothDeltaLength > 0.5)
115  rotationSpeed = std::min(diffAngle, UI_MAPEXTRADATACONST(node).curRotationSpeed + rotationSpeed * 0.5f);
116 
117  UI_MAPEXTRADATA(node).curRotationSpeed = rotationSpeed;
118  VectorScale(diff, SMOOTHACCELERATION / diffAngle * rotationSpeed, diff);
119  VectorAdd(UI_MAPEXTRADATACONST(node).angles, diff, UI_MAPEXTRADATA(node).angles);
120  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).zoom + SMOOTHACCELERATION * diffZoom / diffAngle * rotationSpeed;
121  return;
122  }
123  } else {
124  const float epsilonZoom = 0.01f;
125  /* when we zoom only */
126  if (fabsf(diffZoom) > epsilonZoom) {
127  float speed;
128  /* Append the old speed to the new speed if this is the first half of a new zoom operation, but never exceed the max speed.
129  * This allows the globe to zoom at maximum speed when the button is held down. */
130  if (fabsf(diffZoom) / UI_MAPEXTRADATACONST(node).smoothDeltaZoom > 0.5f) {
131  const float maxSpeed = SMOOTHACCELERATION * 2.0f;
132  const float newSpeed = UI_MAPEXTRADATACONST(node).curZoomSpeed + sin(3.05 * (fabs(diffZoom) / UI_MAPEXTRADATACONST(node).smoothDeltaZoom)) * SMOOTHACCELERATION;
133  speed = std::min(maxSpeed, newSpeed);
134  } else {
135  speed = sin(3.05 * (fabs(diffZoom) / UI_MAPEXTRADATACONST(node).smoothDeltaZoom)) * SMOOTHACCELERATION * 2.0;
136  }
137  UI_MAPEXTRADATA(node).curZoomSpeed = speed;
138  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).zoom + diffZoom * speed;
139  return;
140  }
141  }
142 
143  /* if we reach this point, that means that movement is over */
144  VectorCopy(UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle, UI_MAPEXTRADATA(node).angles);
145  UI_MAPEXTRADATA(node).smoothRotation = false;
146  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom;
147 }
148 
155 {
156  const float dist1 = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[0] - UI_MAPEXTRADATACONST(node).center[0];
157  const float dist2 = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[1] - UI_MAPEXTRADATACONST(node).center[1];
158  const float length = sqrt(dist1 * dist1 + dist2 * dist2);
159 
160  if (length < SMOOTHING_STEP_2D) {
161  UI_MAPEXTRADATA(node).center[0] = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[0];
162  UI_MAPEXTRADATA(node).center[1] = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[1];
163  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom;
164  UI_MAPEXTRADATA(node).smoothRotation = false;
165  } else {
166  const float diffZoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom - UI_MAPEXTRADATACONST(node).zoom;
167  UI_MAPEXTRADATA(node).center[0] = UI_MAPEXTRADATACONST(node).center[0] + SMOOTHING_STEP_2D * dist1 / length;
168  UI_MAPEXTRADATA(node).center[1] = UI_MAPEXTRADATACONST(node).center[1] + SMOOTHING_STEP_2D * dist2 / length;
169  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).zoom + SMOOTHING_STEP_2D * diffZoom;
170  }
171 }
172 
179 {
180  const float dphi = (float) 2 * M_PI / DAN_WIDTH;
181  const float da = M_PI / 2 * (HIGH_LAT - LOW_LAT) / DAN_HEIGHT;
182  const float sin_q = sin(q);
183  const float cos_q = cos(q);
184  float sin_phi[DAN_WIDTH], cos_phi[DAN_WIDTH];
185  byte* px;
186 
187  for (int x = 0; x < DAN_WIDTH; x++) {
188  const float phi = x * dphi - q;
189  sin_phi[x] = sin(phi);
190  cos_phi[x] = cos(phi);
191  }
192 
193  /* calculate */
194  px = UI_MAPEXTRADATA(node).r_dayandnightAlpha;
195  for (int y = 0; y < DAN_HEIGHT; y++) {
196  const float a = sin(M_PI / 2 * HIGH_LAT - y * da);
197  const float root = sqrt(1 - a * a);
198  for (int x = 0; x < DAN_WIDTH; x++) {
199  const float pos = sin_phi[x] * root * sin_q - (a * SIN_ALPHA + cos_phi[x] * root * COS_ALPHA) * cos_q;
200 
201  if (pos >= DAWN)
202  *px++ = 255;
203  else if (pos <= -DAWN)
204  *px++ = 0;
205  else
206  *px++ = (byte) (128.0 * (pos / DAWN + 1));
207  }
208  }
209 
210  /* upload alpha map into the r_dayandnighttexture */
211  R_UploadAlpha(r_dayandnightTexture, UI_MAPEXTRADATA(node).r_dayandnightAlpha);
212 }
213 
215 {
216  vec2_t screenPos;
217 
218  geoscapeNode = node;
219  UI_MAPEXTRADATA(node).flatgeoscape = cl_3dmap->integer == 0;
220  UI_MAPEXTRADATA(node).radarOverlay = Cvar_GetValue("geo_overlay_radar");
221  UI_MAPEXTRADATA(node).nationOverlay = Cvar_GetValue("geo_overlay_nation");
222  UI_MAPEXTRADATA(node).xviOverlay = Cvar_GetValue("geo_overlay_xvi");
223  UI_MAPEXTRADATA(node).ambientLightFactor = cl_3dmapAmbient->value;
224  UI_MAPEXTRADATA(node).mapzoommin = cl_mapzoommin->value;
225  UI_MAPEXTRADATA(node).mapzoommax = cl_mapzoommax->value;
226 
227  UI_GetNodeAbsPos(node, UI_MAPEXTRADATA(node).mapPos);
228  Vector2Copy(node->box.size, UI_MAPEXTRADATA(node).mapSize);
229  if (!UI_MAPEXTRADATACONST(node).flatgeoscape) {
230  /* remove the left padding */
231  UI_MAPEXTRADATA(node).mapSize[0] -= UI_MAPEXTRADATACONST(node).paddingRight;
232  }
233 
234  /* Draw geoscape */
235  UI_GetNodeScreenPos(node, screenPos);
236  UI_PushClipRect(screenPos[0], screenPos[1], node->box.size[0], node->box.size[1]);
237 
238  if (UI_MAPEXTRADATACONST(node).smoothRotation) {
239  if (UI_MAPEXTRADATACONST(node).flatgeoscape)
240  smoothTranslate(node);
241  else
242  smoothRotate(node);
243  }
244 
245  geoscapeData_t& data = *UI_MAPEXTRADATA(node).geoscapeData;
246  data.geoscapeNode = node;
247  GAME_DrawMap(&data);
248  if (!data.active)
249  return;
250 
251  const char* map = data.map;
252 
253  /* Draw the map and markers */
254  if (UI_MAPEXTRADATACONST(node).flatgeoscape) {
255  /* the last q value for the 2d geoscape night overlay */
256  static float lastQ = 0.0f;
257 
258  /* the sun is not always in the plane of the equator on earth - calculate the angle the sun is at */
259  const float q = (data.date.getDateAsDays() % DateTime::DAYS_PER_YEAR + (float)(data.date.getTimeAsSeconds() / (DateTime::SECONDS_PER_HOUR * 6)) / 4) * 2 * M_PI / DateTime::DAYS_PER_YEAR - M_PI;
260  if (lastQ != q) {
262  lastQ = q;
263  }
264  R_DrawFlatGeoscape(UI_MAPEXTRADATACONST(node).mapPos, UI_MAPEXTRADATACONST(node).mapSize, (float) data.date.getTimeAsSeconds() / DateTime::SECONDS_PER_DAY,
265  UI_MAPEXTRADATACONST(node).center[0], UI_MAPEXTRADATACONST(node).center[1], 0.5 / UI_MAPEXTRADATACONST(node).zoom, map,
266  data.nationOverlay, data.xviOverlay, data.radarOverlay, r_dayandnightTexture, r_xviTexture, r_radarTexture);
267 
268  GAME_DrawMapMarkers(node);
269  } else {
270  bool disableSolarRender = false;
271  if (UI_MAPEXTRADATACONST(node).zoom > 3.3)
272  disableSolarRender = true;
273 
274  R_EnableRenderbuffer(true);
275 
276  R_Draw3DGlobe(UI_MAPEXTRADATACONST(node).mapPos, UI_MAPEXTRADATACONST(node).mapSize, data.date.getDateAsDays(), data.date.getTimeAsSeconds(),
277  UI_MAPEXTRADATACONST(node).angles, UI_MAPEXTRADATACONST(node).zoom, map, disableSolarRender,
278  UI_MAPEXTRADATACONST(node).ambientLightFactor, UI_MAPEXTRADATA(node).nationOverlay,
279  UI_MAPEXTRADATA(node).xviOverlay, UI_MAPEXTRADATA(node).radarOverlay, r_xviTexture, r_radarTexture,
280  true);
281 
282  GAME_DrawMapMarkers(node);
283 
284  R_DrawBloom();
285  R_EnableRenderbuffer(false);
286  }
287 
288  UI_PopClipRect();
289 }
290 
292 {
293  switch (mode) {
294  case MODE_SHIFT2DMAP:
295  {
296  const float zoom = 0.5 / UI_MAPEXTRADATACONST(node).zoom;
297  /* shift the map */
298  UI_MAPEXTRADATA(node).center[0] -= (float) (mousePosX - oldMousePosX) / (node->box.size[0] * UI_MAPEXTRADATACONST(node).zoom);
299  UI_MAPEXTRADATA(node).center[1] -= (float) (mousePosY - oldMousePosY) / (node->box.size[1] * UI_MAPEXTRADATACONST(node).zoom);
300  for (int i = 0; i < 2; i++) {
301  /* clamp to min/max values */
302  while (UI_MAPEXTRADATACONST(node).center[i] < 0.0)
303  UI_MAPEXTRADATA(node).center[i] += 1.0;
304  while (UI_MAPEXTRADATACONST(node).center[i] > 1.0)
305  UI_MAPEXTRADATA(node).center[i] -= 1.0;
306  }
307  if (UI_MAPEXTRADATACONST(node).center[1] < zoom)
308  UI_MAPEXTRADATA(node).center[1] = zoom;
309  if (UI_MAPEXTRADATACONST(node).center[1] > 1.0 - zoom)
310  UI_MAPEXTRADATA(node).center[1] = 1.0 - zoom;
311  break;
312  }
313 
314  case MODE_SHIFT3DMAP:
315  /* rotate a model */
316  UI_MAPEXTRADATA(node).angles[PITCH] += ROTATE_SPEED * (mousePosX - oldMousePosX) / UI_MAPEXTRADATACONST(node).zoom;
317  UI_MAPEXTRADATA(node).angles[YAW] -= ROTATE_SPEED * (mousePosY - oldMousePosY) / UI_MAPEXTRADATACONST(node).zoom;
318 
319  /* clamp the UI_MAPEXTRADATACONST(node).angles */
320  while (UI_MAPEXTRADATACONST(node).angles[YAW] > 0.0)
321  UI_MAPEXTRADATA(node).angles[YAW] = 0.0;
322  while (UI_MAPEXTRADATACONST(node).angles[YAW] < -180.0)
323  UI_MAPEXTRADATA(node).angles[YAW] = -180.0;
324 
325  while (UI_MAPEXTRADATACONST(node).angles[PITCH] > 180.0)
326  UI_MAPEXTRADATA(node).angles[PITCH] -= 360.0;
327  while (UI_MAPEXTRADATACONST(node).angles[PITCH] < -180.0)
328  UI_MAPEXTRADATA(node).angles[PITCH] += 360.0;
329  break;
330  case MODE_ZOOMMAP:
331  {
332  const float zoom = 0.5 / UI_MAPEXTRADATACONST(node).zoom;
333  /* zoom the map */
334  UI_MAPEXTRADATA(node).zoom *= pow(0.995, mousePosY - oldMousePosY);
335  if (UI_MAPEXTRADATACONST(node).zoom < UI_MAPEXTRADATACONST(node).mapzoommin)
336  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).mapzoommin;
337  else if (UI_MAPEXTRADATACONST(node).zoom > UI_MAPEXTRADATACONST(node).mapzoommax)
338  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).mapzoommax;
339 
340  if (UI_MAPEXTRADATACONST(node).center[1] < zoom)
341  UI_MAPEXTRADATA(node).center[1] = zoom;
342  if (UI_MAPEXTRADATACONST(node).center[1] > 1.0 - zoom)
343  UI_MAPEXTRADATA(node).center[1] = 1.0 - zoom;
344  break;
345  }
346  default:
347  assert(false);
348  break;
349  }
350  oldMousePosX = x;
351  oldMousePosY = y;
352 }
353 
355 {
356  UI_SetMouseCapture(node);
357  if (UI_MAPEXTRADATACONST(node).flatgeoscape)
359  else
361  UI_MAPEXTRADATA(node).smoothRotation = false;
362  oldMousePosX = x;
363  oldMousePosY = y;
364 }
365 
372 void uiGeoscapeNode::screenToMap (const uiNode_t* node, int x, int y, vec2_t pos)
373 {
374  pos[0] = (((UI_MAPEXTRADATACONST(node).mapPos[0] - x) / UI_MAPEXTRADATACONST(node).mapSize[0] + 0.5) / UI_MAPEXTRADATACONST(node).zoom
375  - (UI_MAPEXTRADATACONST(node).center[0] - 0.5)) * 360.0;
376  pos[1] = (((UI_MAPEXTRADATACONST(node).mapPos[1] - y) / UI_MAPEXTRADATACONST(node).mapSize[1] + 0.5) / UI_MAPEXTRADATACONST(node).zoom
377  - (UI_MAPEXTRADATACONST(node).center[1] - 0.5)) * 180.0;
378 
379  while (pos[0] > 180.0)
380  pos[0] -= 360.0;
381  while (pos[0] < -180.0)
382  pos[0] += 360.0;
383 }
384 
392 void uiGeoscapeNode::screenTo3DMap (const uiNode_t* node, int x, int y, vec2_t pos)
393 {
394  vec2_t mid;
395  vec3_t v, v1, rotationAxis;
396  float dist;
397  const float radius = GLOBE_RADIUS;
398 
399  /* set mid to the coordinates of the center of the globe */
400  Vector2Set(mid, UI_MAPEXTRADATACONST(node).mapPos[0] + UI_MAPEXTRADATACONST(node).mapSize[0] / 2.0f,
401  UI_MAPEXTRADATACONST(node).mapPos[1] + UI_MAPEXTRADATACONST(node).mapSize[1] / 2.0f);
402 
403  /* stop if we click outside the globe (distance is the distance of the point to the center of the globe) */
404  dist = sqrt((x - mid[0]) * (x - mid[0]) + (y - mid[1]) * (y - mid[1]));
405  if (dist > radius) {
406  Vector2Set(pos, -1.0, -1.0);
407  return;
408  }
409 
410  /* calculate the coordinates in the local frame
411  * this frame is the frame of the screen.
412  * v[0] is the vertical axis of the screen
413  * v[1] is the horizontal axis of the screen
414  * v[2] is the axis perpendicular to the screen - we get its value knowing that norm of v is egal to radius
415  * (because the point is on the globe) */
416  v[0] = - (y - mid[1]);
417  v[1] = - (x - mid[0]);
418  v[2] = - sqrt(radius * radius - (x - mid[0]) * (x - mid[0]) - (y - mid[1]) * (y - mid[1]));
420 
421  /* rotate the vector to switch of reference frame
422  * note the ccs.angles[ROLL] is always 0, so there is only 2 rotations and not 3
423  * and that GLOBE_ROTATE is already included in ccs.angles[YAW]
424  * first rotation is along the horizontal axis of the screen, to put north-south axis of the earth
425  * perpendicular to the screen */
426  VectorSet(rotationAxis, 0, 1, 0);
427  RotatePointAroundVector(v1, rotationAxis, v, UI_MAPEXTRADATACONST(node).angles[YAW]);
428 
429  /* second rotation is to rotate the earth around its north-south axis
430  * so that Greenwich meridian is along the vertical axis of the screen */
431  VectorSet(rotationAxis, 0, 0, 1);
432  RotatePointAroundVector(v, rotationAxis, v1, UI_MAPEXTRADATACONST(node).angles[PITCH]);
433 
434  /* we therefore got in v the coordinates of the point in the static frame of the earth
435  * that we can convert in polar coordinates to get its latitude and longitude */
436  VecToPolar(v, pos);
437 }
438 
439 void uiGeoscapeNode::onLeftClick (uiNode_t* node, int x, int y)
440 {
441  if (mode != MODE_NULL)
442  return;
443 
444  vec2_t pos;
445 
446  /* get map position */
447  if (!UI_MAPEXTRADATACONST(node).flatgeoscape)
448  screenTo3DMap(node, x, y, pos);
449  else
450  screenToMap(node, x, y, pos);
451 
452  GAME_MapClick(node, x, y, pos);
453 }
454 
455 bool uiGeoscapeNode::onStartDragging (uiNode_t* node, int startX, int startY, int x, int y, int button)
456 {
457  switch (button) {
458  case K_MOUSE1:
459  case K_MOUSE3:
461  return true;
462  case K_MOUSE2:
463  UI_SetMouseCapture(node);
464  mode = MODE_ZOOMMAP;
467  return true;
468  }
469  return false;
470 }
471 
472 void uiGeoscapeNode::onMouseUp (uiNode_t* node, int x, int y, int button)
473 {
474  if (mode != MODE_NULL) {
475  UI_MouseRelease();
476  mode = MODE_NULL;
477  }
478 }
479 
485 {
486  mode = MODE_NULL;
487 }
488 
493 void uiGeoscapeNode::zoom (uiNode_t* node, bool out)
494 {
495  UI_MAPEXTRADATA(node).zoom *= pow(0.995, (out ? 10: -10));
496  if (UI_MAPEXTRADATACONST(node).zoom < UI_MAPEXTRADATACONST(node).mapzoommin)
497  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).mapzoommin;
498  else if (UI_MAPEXTRADATACONST(node).zoom > UI_MAPEXTRADATACONST(node).mapzoommax)
499  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).mapzoommax;
500 
501  if (UI_MAPEXTRADATACONST(node).flatgeoscape) {
502  if (UI_MAPEXTRADATACONST(node).center[1] < 0.5 / UI_MAPEXTRADATACONST(node).zoom)
503  UI_MAPEXTRADATA(node).center[1] = 0.5 / UI_MAPEXTRADATACONST(node).zoom;
504  if (UI_MAPEXTRADATACONST(node).center[1] > 1.0 - 0.5 / UI_MAPEXTRADATACONST(node).zoom)
505  UI_MAPEXTRADATA(node).center[1] = 1.0 - 0.5 / UI_MAPEXTRADATACONST(node).zoom;
506  }
507  UI_MAPEXTRADATA(node).smoothRotation = false;
508 }
509 
510 bool uiGeoscapeNode::onScroll (uiNode_t* node, int deltaX, int deltaY)
511 {
512  bool down = deltaY > 0;
513  if (deltaY == 0)
514  return false;
515  zoom(node, down);
516  return true;
517 }
518 
523 {
524  Vector4Set(node->color, 1, 1, 1, 1);
525 
526  OBJZERO(EXTRADATA(node));
527  EXTRADATA(node).angles[YAW] = GLOBE_ROTATE;
528  EXTRADATA(node).center[0] = EXTRADATA(node).center[1] = 0.5;
529  EXTRADATA(node).zoom = 1.0;
530  Vector2Set(EXTRADATA(node).smoothFinal2DGeoscapeCenter, 0.5, 0.5);
531  VectorSet(EXTRADATA(node).smoothFinalGlobeAngle, 0, GLOBE_ROTATE, 0);
532 
533  /* @todo: allocate this on a per node basis - and remove the global variable geoscapeData */
534  EXTRADATA(node).geoscapeData = &geoscapeData;
535  /* EXTRADATA(node).geoscapeData = Mem_AllocType(geoscapeData_t); */
536 
538  EXTRADATA(node).r_dayandnightAlpha = Mem_AllocTypeN(byte, DAN_WIDTH * DAN_HEIGHT);
539 
540  r_dayandnightTexture = R_LoadImageData("***r_dayandnighttexture***", nullptr, DAN_WIDTH, DAN_HEIGHT, it_effect);
541  r_radarTexture = R_LoadImageData("***r_radarTexture***", nullptr, RADAR_WIDTH, RADAR_HEIGHT, it_effect);
542  r_xviTexture = R_LoadImageData("***r_xvitexture***", nullptr, XVI_WIDTH, XVI_HEIGHT, it_effect);
543 }
544 
545 static void UI_GeoscapeNodeZoomIn (uiNode_t* node, const uiCallContext_t* context)
546 {
547  uiGeoscapeNode* m = static_cast<uiGeoscapeNode*>(node->behaviour->manager.get());
548  m->zoom(node, false);
549 }
550 
551 static void UI_GeoscapeNodeZoomOut (uiNode_t* node, const uiCallContext_t* context)
552 {
553  uiGeoscapeNode* m = static_cast<uiGeoscapeNode*>(node->behaviour->manager.get());
554  m->zoom(node, true);
555 }
556 
561 static void UI_GeoscapeNodeZoom_f (void)
562 {
563  const char* cmd;
564  const float zoomAmount = 50.0f;
565 
566  if (Cmd_Argc() != 2) {
567  Com_Printf("Usage: %s <in|out>\n", Cmd_Argv(0));
568  return;
569  }
570 
571  cmd = Cmd_Argv(1);
572  uiNode_t* node = geoscapeNode;
573  if (!node)
574  return;
575 
576  switch (cmd[0]) {
577  case 'i':
578  UI_MAPEXTRADATA(node).smoothFinalZoom = UI_MAPEXTRADATACONST(node).zoom * powf(0.995, -zoomAmount);
579  break;
580  case 'o':
581  UI_MAPEXTRADATA(node).smoothFinalZoom = UI_MAPEXTRADATACONST(node).zoom * powf(0.995, zoomAmount);
582  break;
583  default:
584  Com_Printf("UI_GeoscapeNodeZoom_f: Invalid parameter: %s\n", cmd);
585  return;
586  }
587 
588  if (UI_MAPEXTRADATACONST(node).smoothFinalZoom < UI_MAPEXTRADATACONST(node).mapzoommin)
589  UI_MAPEXTRADATA(node).smoothFinalZoom = UI_MAPEXTRADATACONST(node).mapzoommin;
590  else if (UI_MAPEXTRADATACONST(node).smoothFinalZoom > UI_MAPEXTRADATACONST(node).mapzoommax)
591  UI_MAPEXTRADATA(node).smoothFinalZoom = UI_MAPEXTRADATACONST(node).mapzoommax;
592 
593  if (UI_MAPEXTRADATACONST(node).flatgeoscape) {
594  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom;
595  if (UI_MAPEXTRADATACONST(node).center[1] < 0.5 / UI_MAPEXTRADATACONST(node).zoom)
596  UI_MAPEXTRADATA(node).center[1] = 0.5 / UI_MAPEXTRADATACONST(node).zoom;
597  if (UI_MAPEXTRADATACONST(node).center[1] > 1.0 - 0.5 / UI_MAPEXTRADATACONST(node).zoom)
598  UI_MAPEXTRADATA(node).center[1] = 1.0 - 0.5 / UI_MAPEXTRADATACONST(node).zoom;
599  } else {
600  VectorCopy(UI_MAPEXTRADATACONST(node).angles, UI_MAPEXTRADATA(node).smoothFinalGlobeAngle);
601  UI_MAPEXTRADATA(node).smoothDeltaLength = 0;
602  UI_MAPEXTRADATA(node).smoothRotation = true;
603  UI_MAPEXTRADATA(node).smoothDeltaZoom = fabs(UI_MAPEXTRADATACONST(node).smoothFinalZoom - UI_MAPEXTRADATACONST(node).zoom);
604  }
605 }
606 
611 static void UI_GeoscapeNodeScroll_f (void)
612 {
613  const char* cmd;
614  float scrollX = 0.0f, scrollY = 0.0f;
615  const float scrollAmount = 80.0f;
616 
617  if (Cmd_Argc() != 2) {
618  Com_Printf("Usage: %s <up|down|left|right>\n", Cmd_Argv(0));
619  return;
620  }
621 
622  cmd = Cmd_Argv(1);
623 
624  uiNode_t* node = geoscapeNode;
625  if (!node)
626  return;
627 
628  switch (cmd[0]) {
629  case 'l':
630  scrollX = scrollAmount;
631  break;
632  case 'r':
633  scrollX = -scrollAmount;
634  break;
635  case 'u':
636  scrollY = scrollAmount;
637  break;
638  case 'd':
639  scrollY = -scrollAmount;
640  break;
641  default:
642  Com_Printf("UI_GeoscapeNodeScroll_f: Invalid parameter\n");
643  return;
644  }
645 
646  if (!UI_MAPEXTRADATACONST(node).flatgeoscape) {
647  /* case 3D geoscape */
648  vec3_t diff;
649 
650  VectorCopy(UI_MAPEXTRADATACONST(node).angles, UI_MAPEXTRADATA(node).smoothFinalGlobeAngle);
651 
652  /* rotate a model */
653  UI_MAPEXTRADATA(node).smoothFinalGlobeAngle[PITCH] += ROTATE_SPEED * (scrollX) / UI_MAPEXTRADATACONST(node).zoom;
654  UI_MAPEXTRADATA(node).smoothFinalGlobeAngle[YAW] -= ROTATE_SPEED * (scrollY) / UI_MAPEXTRADATACONST(node).zoom;
655 
656  while (UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle[YAW] < -180.0) {
657  UI_MAPEXTRADATA(node).smoothFinalGlobeAngle[YAW] = -180.0;
658  }
659  while (UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle[YAW] > 0.0) {
660  UI_MAPEXTRADATA(node).smoothFinalGlobeAngle[YAW] = 0.0;
661  }
662 
663  while (UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle[PITCH] > 180.0) {
664  UI_MAPEXTRADATA(node).smoothFinalGlobeAngle[PITCH] -= 360.0;
665  UI_MAPEXTRADATA(node).angles[PITCH] -= 360.0;
666  }
667  while (UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle[PITCH] < -180.0) {
668  UI_MAPEXTRADATA(node).smoothFinalGlobeAngle[PITCH] += 360.0;
669  UI_MAPEXTRADATA(node).angles[PITCH] += 360.0;
670  }
671  VectorSubtract(UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle, UI_MAPEXTRADATACONST(node).angles, diff);
672  UI_MAPEXTRADATA(node).smoothDeltaLength = VectorLength(diff);
673 
674  UI_MAPEXTRADATA(node).smoothFinalZoom = UI_MAPEXTRADATACONST(node).zoom;
675  UI_MAPEXTRADATA(node).smoothDeltaZoom = 0.0f;
676  UI_MAPEXTRADATA(node).smoothRotation = true;
677  } else {
678  /* shift the map */
679  UI_MAPEXTRADATA(node).center[0] -= (float) (scrollX) / (UI_MAPEXTRADATACONST(node).mapSize[0] * UI_MAPEXTRADATACONST(node).zoom);
680  UI_MAPEXTRADATA(node).center[1] -= (float) (scrollY) / (UI_MAPEXTRADATACONST(node).mapSize[1] * UI_MAPEXTRADATACONST(node).zoom);
681  for (int i = 0; i < 2; i++) {
682  while (UI_MAPEXTRADATACONST(node).center[i] < 0.0)
683  UI_MAPEXTRADATA(node).center[i] += 1.0;
684  while (UI_MAPEXTRADATACONST(node).center[i] > 1.0)
685  UI_MAPEXTRADATA(node).center[i] -= 1.0;
686  }
687  if (UI_MAPEXTRADATACONST(node).center[1] < 0.5 / UI_MAPEXTRADATACONST(node).zoom)
688  UI_MAPEXTRADATA(node).center[1] = 0.5 / UI_MAPEXTRADATACONST(node).zoom;
689  if (UI_MAPEXTRADATACONST(node).center[1] > 1.0 - 0.5 / UI_MAPEXTRADATACONST(node).zoom)
690  UI_MAPEXTRADATA(node).center[1] = 1.0 - 0.5 / UI_MAPEXTRADATACONST(node).zoom;
691  }
692 }
693 
695 {
696  behaviour->name = "geoscape";
697  behaviour->manager = UINodePtr(new uiGeoscapeNode());
698  behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
699  behaviour->lua_SWIG_typeinfo = UI_SWIG_TypeQuery("uiGeoscapeNode_t *");
700 
701  /* Use a right padding. */
702  UI_RegisterExtradataNodeProperty(behaviour, "padding-right", V_FLOAT, EXTRADATA_TYPE, paddingRight);
703  /* Call it to zoom out of the map */
704  UI_RegisterNodeMethod(behaviour, "zoomin", UI_GeoscapeNodeZoomIn);
705  /* Call it to zoom into the map */
706  UI_RegisterNodeMethod(behaviour, "zoomout", UI_GeoscapeNodeZoomOut);
707 
710 
711  cl_3dmap = Cvar_Get("cl_3dmap", "1", CVAR_ARCHIVE, "3D geoscape or flat geoscape");
712  cl_3dmapAmbient = Cvar_Get("cl_3dmapAmbient", "0", CVAR_ARCHIVE, "3D geoscape ambient lighting factor");
713  cl_mapzoommax = Cvar_Get("cl_mapzoommax", "6.0", CVAR_ARCHIVE, "Maximum geoscape zooming value");
714  cl_mapzoommin = Cvar_Get("cl_mapzoommin", "1.0", CVAR_ARCHIVE, "Minimum geoscape zooming value");
715 }
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition: mathlib.cpp:434
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
bool R_EnableRenderbuffer(bool enable)
Enable the render to the framebuffer.
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition: cmd.cpp:744
static int startX
#define VectorCopy(src, dest)
Definition: vector.h:51
#define Mem_AllocTypeN(type, n)
Definition: mem.h:38
bool onStartDragging(uiNode_t *node, int startX, int startY, int currentX, int currentY, int button) override
Send mouse event when a pressed mouse button is dragged.
void UI_PopClipRect(void)
Definition: ui_render.cpp:52
#define VectorSet(v, x, y, z)
Definition: vector.h:59
#define EXTRADATA_TYPE
static const float GLOBE_ROTATE
uiBehaviour_t * behaviour
Definition: ui_nodes.h:83
void VecToPolar(const vec3_t v, vec2_t a)
Converts vector coordinates into polar coordinates.
Definition: mathlib.cpp:922
void screenToMap(const uiNode_t *node, int x, int y, vec2_t pos)
Return longitude and latitude of a point of the screen for 2D geoscape.
image_t * r_xviTexture
#define EXTRADATA(node)
#define SIN_ALPHA
Definition: mathlib.h:65
image_t * r_radarTexture
uiBox_t box
Definition: ui_nodes.h:96
static const float SMOOTHACCELERATION
mapDragMode_t
Status of the node.
#define DAN_WIDTH
int integer
Definition: cvar.h:81
image_t * R_LoadImageData(const char *name, const byte *pic, int width, int height, imagetype_t type)
Creates a new image from RGBA data. Stores it in the gltextures array and also uploads it...
Definition: r_image.cpp:475
QGL_EXTERN GLsizei const GLvoid * data
Definition: r_gl.h:89
void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees)
Rotate a point around a given vector.
Definition: mathlib.cpp:849
bool onScroll(uiNode_t *node, int deltaX, int deltaY) override
void onCapturedMouseMove(uiNode_t *node, int x, int y) override
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
#define UI_MAPEXTRADATACONST(node)
#define DAN_HEIGHT
void smoothRotate(uiNode_t *node)
smooth rotation of the 3D geoscape
#define VectorScale(in, scale, out)
Definition: vector.h:79
#define XVI_WIDTH
Definition: cl_shared.h:50
intptr_t extraDataSize
Definition: ui_behaviour.h:54
void UI_MouseRelease(void)
Release the captured node.
Definition: ui_input.cpp:526
#define CVAR_ARCHIVE
Definition: cvar.h:40
UINodePtr manager
Definition: ui_behaviour.h:43
#define UI_RegisterExtradataNodeProperty(BEHAVIOUR, NAME, TYPE, EXTRADATATYPE, ATTRIBUTE)
Initialize a property from extradata of node.
Definition: ui_behaviour.h:109
static const float SMOOTHING_STEP_2D
PointerType get() const
Definition: sharedptr.h:197
static cvar_t * cl_mapzoommin
#define YAW
Definition: mathlib.h:55
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
static void UI_GeoscapeNodeZoomIn(uiNode_t *node, const uiCallContext_t *context)
#define DAWN
void startMouseShifting(uiNode_t *node, int x, int y)
void GAME_DrawMapMarkers(uiNode_t *node)
Definition: cl_game.cpp:938
static const int SECONDS_PER_DAY
Definition: DateTime.h:43
void onMouseUp(uiNode_t *node, int x, int y, int button) override
static cvar_t * cl_mapzoommax
void calcAndUploadDayAndNightTexture(uiNode_t *node, float q)
Applies alpha values to the night overlay image for 2d geoscape.
static int startY
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition: cvar.cpp:342
static int oldMousePosX
#define OBJZERO(obj)
Definition: shared.h:178
static cvar_t * cl_3dmap
geoscapeData_t geoscapeData
Definition: cl_game.cpp:147
float Cvar_GetValue(const char *varName)
Returns the float value of a cvar.
Definition: cvar.cpp:125
#define Vector4Set(v, r, g, b, a)
Definition: vector.h:62
int mousePosY
Definition: cl_input.cpp:80
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
#define Vector2Set(v, x, y)
Definition: vector.h:61
static const int DAYS_PER_YEAR
Definition: DateTime.h:37
#define M_PI
Definition: mathlib.h:34
#define UI_MAPEXTRADATA(node)
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition: cmd.cpp:505
SharedPtr< uiNode > UINodePtr
void R_UploadAlpha(const image_t *image, const byte *alphaData)
Definition: r_image.cpp:423
static cvar_t * cl_3dmapAmbient
#define PITCH
Definition: mathlib.h:54
void UI_SetMouseCapture(uiNode_t *node)
Captured the mouse into a node.
Definition: ui_input.cpp:516
void * UI_SWIG_TypeQuery(const char *name)
This function queries the SWIG type table for a type information structure. It is used in combination...
#define GLOBE_RADIUS
radius of the globe in screen coordinates
#define RADAR_HEIGHT
Definition: cl_shared.h:53
#define HIGH_LAT
Definition: mathlib.h:67
void UI_GetNodeAbsPos(const uiNode_t *node, vec2_t pos)
Returns the absolute position of a node.
Definition: ui_node.cpp:514
#define RADAR_WIDTH
Definition: cl_shared.h:52
Contain the context of the calling of a function.
Definition: ui_actions.h:208
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
#define VectorAdd(a, b, dest)
Definition: vector.h:47
vec4_t color
Definition: ui_nodes.h:127
void smoothTranslate(uiNode_t *node)
smooth translation of the 2D geoscape
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define COS_ALPHA
Definition: mathlib.h:66
void 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)
responsible for drawing the 3d globe on geoscape param[in] rotate the rotate angle of the globe param...
Definition: r_geoscape.cpp:430
void onCapturedMouseLost(uiNode_t *node) override
Called when the node have lost the captured node We clean cached data.
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition: mathlib.cpp:745
void onLeftClick(uiNode_t *node, int x, int y) override
void draw(uiNode_t *node) override
const char int mode
Definition: ioapi.h:41
image_t * r_dayandnightTexture
vec_t vec3_t[3]
Definition: ufotypes.h:39
#define Vector2Copy(src, dest)
Definition: vector.h:52
static uiNode_t * geoscapeNode
vec_t vec2_t[2]
Definition: ufotypes.h:38
void UI_PushClipRect(int x, int y, int width, int height)
Definition: ui_render.cpp:47
void UI_RegisterGeoscapeNode(uiBehaviour_t *behaviour)
static const short SECONDS_PER_HOUR
Definition: DateTime.h:42
#define XVI_HEIGHT
Definition: cl_shared.h:51
float value
Definition: cvar.h:80
void * lua_SWIG_typeinfo
Definition: ui_behaviour.h:57
void R_DrawBloom(void)
handle post-processing bloom
Definition: r_geoscape.cpp:762
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)
Draw the day and night images of a flat geoscape multitexture feature is used to blend the images...
Definition: r_geoscape.cpp:50
static void UI_GeoscapeNodeZoomOut(uiNode_t *node, const uiCallContext_t *context)
const char * name
Definition: ui_behaviour.h:41
node behaviour, how a node work
Definition: ui_behaviour.h:39
static const float ROTATE_SPEED
int mousePosX
Definition: cl_input.cpp:80
vec2_t size
Definition: ui_nodes.h:52
static void UI_GeoscapeNodeScroll_f(void)
Command binding for map scrolling.
void GAME_DrawMap(geoscapeData_t *data)
Definition: cl_game.cpp:931
void zoom(uiNode_t *node, bool out)
uint8_t byte
Definition: ufotypes.h:34
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
static struct mdfour * m
Definition: md4.cpp:35
void screenTo3DMap(const uiNode_t *node, int x, int y, vec2_t pos)
Return longitude and latitude of a point of the screen for 3D geoscape (globe)
#define LOW_LAT
Definition: mathlib.h:68
int down
Definition: cl_input.cpp:70
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
void onLoading(uiNode_t *node) override
Called before loading. Used to set default attribute values.
static void UI_GeoscapeNodeZoom_f(void)
Command binding for map zooming.
const struct value_s * UI_RegisterNodeMethod(uiBehaviour_t *behaviour, const char *name, uiNodeMethod_t function)
Register a node method to a behaviour.
static int oldMousePosY
void GAME_MapClick(uiNode_t *node, int x, int y, const vec2_t pos)
Definition: cl_game.cpp:945
void UI_GetNodeScreenPos(const uiNode_t *node, vec2_t pos)
Returns the absolute position of a node in the screen. Screen position is not used for the node rende...
Definition: ui_node.cpp:542