UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ui_node_geoscape.cpp
Go to the documentation of this file.
1 
5 /*
6 Copyright (C) 2002-2020 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 "../ui_nodes.h"
26 #include "../ui_input.h"
27 #include "../ui_parse.h"
28 #include "../ui_behaviour.h"
29 #include "../ui_actions.h"
30 #include "../ui_render.h"
31 #include "ui_node_geoscape.h"
32 #include "../../renderer/r_framebuffer.h"
33 #include "../../renderer/r_geoscape.h"
34 #include "../../cl_shared.h"
35 #include "../../cgame/cl_game.h"
36 #include "../../input/cl_input.h"
37 #include "../../input/cl_keys.h"
38 
39 #include "../../../common/scripts_lua.h"
40 
41 #define EXTRADATA_TYPE mapExtraData_t
42 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
43 #define EXTRADATACONST(node) UI_EXTRADATACONST(node, EXTRADATA_TYPE)
44 
53 };
54 
59 static int oldMousePosX = 0;
60 
65 static int oldMousePosY = 0;
66 
72 static const float ROTATE_SPEED = 0.5f;
73 static const float GLOBE_ROTATE = -90.0f;
74 static const float SMOOTHING_STEP_2D = 0.02f;
75 static const float SMOOTHACCELERATION = 0.06f;
77 static cvar_t* cl_3dmap;
81 
82 // FIXME: don't make this static
84 
87 
91 #define DAWN 0.03
92 
98 {
99  vec3_t diff;
100  const float diffZoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom - UI_MAPEXTRADATACONST(node).zoom;
101 
102  VectorSubtract(UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle, UI_MAPEXTRADATACONST(node).angles, diff);
103 
104  if (UI_MAPEXTRADATACONST(node).smoothDeltaLength > UI_MAPEXTRADATACONST(node).smoothDeltaZoom) {
105  /* when we rotate (and zoom) */
106  const float diffAngle = VectorLength(diff);
107  const float epsilon = 0.1f;
108  if (diffAngle > epsilon) {
109  float rotationSpeed;
110  /* Append the old speed to the new speed if this is the first half of a new rotation, but never exceed the max speed.
111  * This allows the globe to rotate at maximum speed when the button is held down. */
112  rotationSpeed = sin(3.05f * diffAngle / UI_MAPEXTRADATACONST(node).smoothDeltaLength) * diffAngle;
113  if (diffAngle / UI_MAPEXTRADATACONST(node).smoothDeltaLength > 0.5)
114  rotationSpeed = std::min(diffAngle, UI_MAPEXTRADATACONST(node).curRotationSpeed + rotationSpeed * 0.5f);
115 
116  UI_MAPEXTRADATA(node).curRotationSpeed = rotationSpeed;
117  VectorScale(diff, SMOOTHACCELERATION / diffAngle * rotationSpeed, diff);
118  VectorAdd(UI_MAPEXTRADATACONST(node).angles, diff, UI_MAPEXTRADATA(node).angles);
119  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).zoom + SMOOTHACCELERATION * diffZoom / diffAngle * rotationSpeed;
120  return;
121  }
122  } else {
123  const float epsilonZoom = 0.01f;
124  /* when we zoom only */
125  if (fabsf(diffZoom) > epsilonZoom) {
126  float speed;
127  /* 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.
128  * This allows the globe to zoom at maximum speed when the button is held down. */
129  if (fabsf(diffZoom) / UI_MAPEXTRADATACONST(node).smoothDeltaZoom > 0.5f) {
130  const float maxSpeed = SMOOTHACCELERATION * 2.0f;
131  const float newSpeed = UI_MAPEXTRADATACONST(node).curZoomSpeed + sin(3.05 * (fabs(diffZoom) / UI_MAPEXTRADATACONST(node).smoothDeltaZoom)) * SMOOTHACCELERATION;
132  speed = std::min(maxSpeed, newSpeed);
133  } else {
134  speed = sin(3.05 * (fabs(diffZoom) / UI_MAPEXTRADATACONST(node).smoothDeltaZoom)) * SMOOTHACCELERATION * 2.0;
135  }
136  UI_MAPEXTRADATA(node).curZoomSpeed = speed;
137  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).zoom + diffZoom * speed;
138  return;
139  }
140  }
141 
142  /* if we reach this point, that means that movement is over */
143  VectorCopy(UI_MAPEXTRADATACONST(node).smoothFinalGlobeAngle, UI_MAPEXTRADATA(node).angles);
144  UI_MAPEXTRADATA(node).smoothRotation = false;
145  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom;
146 }
147 
154 {
155  const float dist1 = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[0] - UI_MAPEXTRADATACONST(node).center[0];
156  const float dist2 = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[1] - UI_MAPEXTRADATACONST(node).center[1];
157  const float length = sqrt(dist1 * dist1 + dist2 * dist2);
158 
159  if (length < SMOOTHING_STEP_2D) {
160  UI_MAPEXTRADATA(node).center[0] = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[0];
161  UI_MAPEXTRADATA(node).center[1] = UI_MAPEXTRADATACONST(node).smoothFinal2DGeoscapeCenter[1];
162  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom;
163  UI_MAPEXTRADATA(node).smoothRotation = false;
164  } else {
165  const float diffZoom = UI_MAPEXTRADATACONST(node).smoothFinalZoom - UI_MAPEXTRADATACONST(node).zoom;
166  UI_MAPEXTRADATA(node).center[0] = UI_MAPEXTRADATACONST(node).center[0] + SMOOTHING_STEP_2D * dist1 / length;
167  UI_MAPEXTRADATA(node).center[1] = UI_MAPEXTRADATACONST(node).center[1] + SMOOTHING_STEP_2D * dist2 / length;
168  UI_MAPEXTRADATA(node).zoom = UI_MAPEXTRADATACONST(node).zoom + SMOOTHING_STEP_2D * diffZoom;
169  }
170 }
171 
178 {
179  const float dphi = (float) 2 * M_PI / DAN_WIDTH;
180  const float da = M_PI / 2 * (HIGH_LAT - LOW_LAT) / DAN_HEIGHT;
181  const float sin_q = sin(q);
182  const float cos_q = cos(q);
183  float sin_phi[DAN_WIDTH], cos_phi[DAN_WIDTH];
184  byte* px;
185 
186  for (int x = 0; x < DAN_WIDTH; x++) {
187  const float phi = x * dphi - q;
188  sin_phi[x] = sin(phi);
189  cos_phi[x] = cos(phi);
190  }
191 
192  /* calculate */
193  px = UI_MAPEXTRADATA(node).r_dayandnightAlpha;
194  for (int y = 0; y < DAN_HEIGHT; y++) {
195  const float a = sin(M_PI / 2 * HIGH_LAT - y * da);
196  const float root = sqrt(1 - a * a);
197  for (int x = 0; x < DAN_WIDTH; x++) {
198  const float pos = sin_phi[x] * root * sin_q - (a * SIN_ALPHA + cos_phi[x] * root * COS_ALPHA) * cos_q;
199 
200  if (pos >= DAWN)
201  *px++ = 255;
202  else if (pos <= -DAWN)
203  *px++ = 0;
204  else
205  *px++ = (byte) (128.0 * (pos / DAWN + 1));
206  }
207  }
208 
209  /* upload alpha map into the r_dayandnighttexture */
210  R_UploadAlpha(r_dayandnightTexture, UI_MAPEXTRADATA(node).r_dayandnightAlpha);
211 }
212 
214 {
215  vec2_t screenPos;
216 
217  geoscapeNode = node;
218  UI_MAPEXTRADATA(node).flatgeoscape = cl_3dmap->integer == 0;
219  UI_MAPEXTRADATA(node).radarOverlay = Cvar_GetValue("geo_overlay_radar");
220  UI_MAPEXTRADATA(node).nationOverlay = Cvar_GetValue("geo_overlay_nation");
221  UI_MAPEXTRADATA(node).xviOverlay = Cvar_GetValue("geo_overlay_xvi");
222  UI_MAPEXTRADATA(node).ambientLightFactor = cl_3dmapAmbient->value;
223  UI_MAPEXTRADATA(node).mapzoommin = cl_mapzoommin->value;
224  UI_MAPEXTRADATA(node).mapzoommax = cl_mapzoommax->value;
225 
226  UI_GetNodeAbsPos(node, UI_MAPEXTRADATA(node).mapPos);
227  Vector2Copy(node->box.size, UI_MAPEXTRADATA(node).mapSize);
228  if (!UI_MAPEXTRADATACONST(node).flatgeoscape) {
229  /* remove the left padding */
230  UI_MAPEXTRADATA(node).mapSize[0] -= UI_MAPEXTRADATACONST(node).paddingRight;
231  }
232 
233  /* Draw geoscape */
234  UI_GetNodeScreenPos(node, screenPos);
235  UI_PushClipRect(screenPos[0], screenPos[1], node->box.size[0], node->box.size[1]);
236 
237  if (UI_MAPEXTRADATACONST(node).smoothRotation) {
238  if (UI_MAPEXTRADATACONST(node).flatgeoscape)
239  smoothTranslate(node);
240  else
241  smoothRotate(node);
242  }
243 
244  geoscapeData_t& data = *UI_MAPEXTRADATA(node).geoscapeData;
245  data.geoscapeNode = node;
246  GAME_DrawMap(&data);
247  if (!data.active)
248  return;
249 
250  const char* map = data.map;
251  date_t& date = data.date;
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 = (date.day % DAYS_PER_YEAR + (float)(date.sec / (SECONDS_PER_HOUR * 6)) / 4) * 2 * M_PI / 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) date.sec / 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, date.day, date.sec,
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]));
419  VectorNormalize(v);
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:
460  startMouseShifting(node, startX, startY);
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
vec2_t size
Definition: ui_nodes.h:52
static int startX
#define VectorCopy(src, dest)
Definition: vector.h:51
#define Mem_AllocTypeN(type, n)
Definition: mem.h:38
void UI_PopClipRect(void)
Definition: ui_render.cpp:52
#define VectorSet(v, x, y, z)
Definition: vector.h:59
void calcAndUploadDayAndNightTexture(uiNode_t *node, float q)
Applies alpha values to the night overlay image for 2d geoscape.
#define EXTRADATA_TYPE
void smoothRotate(uiNode_t *node)
smooth rotation of the 3D geoscape
static const float GLOBE_ROTATE
void smoothTranslate(uiNode_t *node)
smooth translation of the 2D geoscape
const char * name
Definition: ui_behaviour.h:41
void VecToPolar(const vec3_t v, vec2_t a)
Converts vector coordinates into polar coordinates.
Definition: mathlib.cpp:922
image_t * r_xviTexture
#define EXTRADATA(node)
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
#define SIN_ALPHA
Definition: mathlib.h:65
image_t * r_radarTexture
static const float SMOOTHACCELERATION
bool onScroll(uiNode_t *node, int deltaX, int deltaY) override
mapDragMode_t
Status of the node.
uiBehaviour_t * behaviour
Definition: ui_nodes.h:83
#define DAN_WIDTH
UINodePtr manager
Definition: ui_behaviour.h:43
int day
Definition: common.h:291
float value
Definition: cvar.h:80
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
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
void startMouseShifting(uiNode_t *node, int x, int y)
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
#define UI_MAPEXTRADATACONST(node)
#define SECONDS_PER_HOUR
Definition: common.h:302
#define DAN_HEIGHT
int integer
Definition: cvar.h:81
#define DAYS_PER_YEAR
Definition: common.h:296
#define VectorScale(in, scale, out)
Definition: vector.h:79
#define XVI_WIDTH
Definition: cl_shared.h:49
void UI_MouseRelease(void)
Release the captured node.
Definition: ui_input.cpp:526
#define CVAR_ARCHIVE
Definition: cvar.h:40
#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
static cvar_t * cl_mapzoommin
#define YAW
Definition: mathlib.h:55
void onLoading(uiNode_t *node) override
Called before loading. Used to set default attribute values.
static void UI_GeoscapeNodeZoomIn(uiNode_t *node, const uiCallContext_t *context)
void zoom(uiNode_t *node, bool out)
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.
#define DAWN
void GAME_DrawMapMarkers(uiNode_t *node)
Definition: cl_game.cpp:938
void onCapturedMouseMove(uiNode_t *node, int x, int y) override
static cvar_t * cl_mapzoommax
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:146
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
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 Vector2Set(v, x, y)
Definition: vector.h:61
date_t date
Definition: cl_shared.h:60
#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
const char * map
Definition: cl_shared.h:59
void R_UploadAlpha(const image_t *image, const byte *alphaData)
Definition: r_image.cpp:423
void onCapturedMouseLost(uiNode_t *node) override
Called when the node have lost the captured node We clean cached data.
static cvar_t * cl_3dmapAmbient
bool radarOverlay
Definition: cl_shared.h:58
void * lua_SWIG_typeinfo
Definition: ui_behaviour.h:57
#define PITCH
Definition: mathlib.h:54
void UI_SetMouseCapture(uiNode_t *node)
Captured the mouse into a node.
Definition: ui_input.cpp:516
void draw(uiNode_t *node) override
Engine-side time information in the game.
Definition: common.h:290
PointerType get() const
Definition: sharedptr.h:197
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
void * UI_SWIG_TypeQuery(const char *name)
This function queries the SWIG type table for a type information structure. It is used in combination...
void * geoscapeNode
Definition: cl_shared.h:71
#define GLOBE_RADIUS
radius of the globe in screen coordinates
#define RADAR_HEIGHT
Definition: cl_shared.h:52
void onLeftClick(uiNode_t *node, int x, int y) override
int sec
Definition: common.h:292
#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:51
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
intptr_t extraDataSize
Definition: ui_behaviour.h:54
QGL_EXTERN GLint i
Definition: r_gl.h:113
node behaviour, how a node work
Definition: ui_behaviour.h:39
#define COS_ALPHA
Definition: mathlib.h:66
void onMouseUp(uiNode_t *node, int x, int y, int button) override
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:428
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition: mathlib.cpp:745
vec4_t color
Definition: ui_nodes.h:127
const char int mode
Definition: ioapi.h:41
#define SECONDS_PER_DAY
Definition: common.h:301
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
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_PushClipRect(int x, int y, int width, int height)
Definition: ui_render.cpp:47
void UI_RegisterGeoscapeNode(uiBehaviour_t *behaviour)
#define XVI_HEIGHT
Definition: cl_shared.h:50
void R_DrawBloom(void)
handle post-processing bloom
Definition: r_geoscape.cpp:760
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:48
GLsizei const GLvoid * data
Definition: r_gl.h:152
static void UI_GeoscapeNodeZoomOut(uiNode_t *node, const uiCallContext_t *context)
uiBox_t box
Definition: ui_nodes.h:96
static const float ROTATE_SPEED
int mousePosX
Definition: cl_input.cpp:80
static void UI_GeoscapeNodeScroll_f(void)
Command binding for map scrolling.
void GAME_DrawMap(geoscapeData_t *data)
Definition: cl_game.cpp:931
uint8_t byte
Definition: ufotypes.h:34
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
static struct mdfour * m
Definition: md4.cpp:35
bool xviOverlay
Definition: cl_shared.h:57
bool nationOverlay
Definition: cl_shared.h:56
#define LOW_LAT
Definition: mathlib.h:68
int down
Definition: cl_input.cpp:70
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
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