naev 0.11.5
map_overlay.c
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
5#include <float.h>
6#include "SDL.h"
9#include "map_overlay.h"
10
11#include "array.h"
12#include "conf.h"
13#include "font.h"
14#include "gui.h"
15#include "quadtree.h"
16#include "input.h"
17#include "log.h"
18#include "naev.h"
19#include "nstring.h"
20#include "opengl.h"
21#include "pilot.h"
22#include "player.h"
23#include "safelanes.h"
24#include "space.h"
25
26static const double OVERLAY_FADEIN = 1.0/3.0;
28static IntList ovr_qtquery;
33typedef struct MapOverlayRadiusConstraint_ {
34 int i;
35 int j;
36 double dist;
38
39typedef enum ovr_marker_type_e {
40 OVR_MARKER_POINT,
41 OVR_MARKER_CIRCLE,
42} ovr_marker_type_t;
43
47typedef struct ovr_marker_s {
48 unsigned int id;
49 char *text;
50 ovr_marker_type_t type;
54 union {
55 struct {
56 double r;
57 vec2 textpos;
58 } circle;
59 } u;
61static unsigned int mrk_idgen = 0;
62static ovr_marker_t *ovr_markers = NULL;
64static SafeLane* ovr_render_safelanes = NULL;
66static Uint32 ovr_opened = 0;
67static int ovr_open = 0;
68static double ovr_res = 10.;
69static double ovr_dt = 0.;
70static const double ovr_text_pixbuf = 5.;
71/* Rem: high pix_buffer ovr_text_pixbuff allows to do less iterations. */
72typedef struct OverlayBounds_s {
73 double t;
74 double r;
75 double b;
76 double l;
77 double w;
78 double h;
79 double x;
80 double y;
82static OverlayBounds_t ovr_bounds;
83
84/* For autonav. */
85static int autonav_pos = 0;
86static vec2 autonav_pos_v;
87static MapOverlayPos autonav_pos_mo;
88
89/* For optimizing overlay layout. */
90static MapOverlayPos **ovr_refresh_mo = NULL;
91static const vec2 **ovr_refresh_pos = NULL;
92
93/*
94 * Prototypes
95 */
96static void force_collision( float *ox, float *oy,
97 float x, float y, float w, float h,
98 float mx, float my, float mw, float mh );
99static void ovr_optimizeLayout( int items, const vec2** pos,
100 MapOverlayPos** mo );
101static void ovr_refresh_uzawa_overlap( float *forces_x, float *forces_y,
102 float x, float y, float w, float h, const vec2** pos,
103 MapOverlayPos** mo, int items, int self,
104 float *offx, float *offy, float *offdx, float *offdy );
105/* Render. */
106static int ovr_safelaneKnown( SafeLane *sf, vec2 *posns[2] );
107static void map_overlayToScreenPos( double *ox, double *oy, double x, double y );
108/* Markers. */
109static void ovr_mrkRenderAll( double res, int fg );
110static void ovr_mrkCleanup( ovr_marker_t *mrk );
111static ovr_marker_t *ovr_mrkNew (void);
112
116int ovr_isOpen (void)
117{
118 return !!ovr_open;
119}
120
124static void ovr_boundsUpdate (void)
125{
126 ovr_bounds.w = SCREEN_W - ovr_bounds.l - ovr_bounds.r;
127 ovr_bounds.h = SCREEN_H - ovr_bounds.t - ovr_bounds.b;
128 ovr_bounds.x = ovr_bounds.w / 2. + ovr_bounds.l;
129 ovr_bounds.y = ovr_bounds.h / 2. + ovr_bounds.b;
130}
131
140void ovr_boundsSet( double top, double right, double bottom, double left )
141{
142 ovr_bounds.t = top;
143 ovr_bounds.r = right;
144 ovr_bounds.b = bottom;
145 ovr_bounds.l = left;
146 ovr_boundsUpdate();
147}
148
155void ovr_center( double *x, double *y )
156{
157 *x = ovr_bounds.x;
158 *y = ovr_bounds.y;
159}
160
164static void map_overlayToScreenPos( double *ox, double *oy, double x, double y )
165{
166 *ox = ovr_bounds.x + x / ovr_res;
167 *oy = ovr_bounds.y + y / ovr_res;
168}
169
173int ovr_input( SDL_Event *event )
174{
175 int mx, my;
176 double x, y;
177
178 /* We only want mouse events. */
179 if (event->type != SDL_MOUSEBUTTONDOWN)
180 return 0;
181
182 /* Player must not be NULL. */
183 if (player_isFlag(PLAYER_DESTROYED) || (player.p == NULL))
184 return 0;
185
186 /* Player must not be dead. */
187 if (pilot_isFlag(player.p, PILOT_DEAD))
188 return 0;
189
190 /* Mouse targeting only uses left and right buttons. */
191 if (event->button.button != SDL_BUTTON_LEFT &&
192 event->button.button != SDL_BUTTON_RIGHT)
193 return 0;
194
195 /* Translate from window to screen. */
196 mx = event->button.x;
197 my = event->button.y;
198 gl_windowToScreenPos( &mx, &my, mx, my );
199
200 /* Translate to space coords. */
201 x = ((double)mx - ovr_bounds.x) * ovr_res;
202 y = ((double)my - ovr_bounds.y) * ovr_res;
203
204 return input_clickPos( event, x, y, 1., 10. * ovr_res, 15. * ovr_res );
205}
206
212void ovr_refresh (void)
213{
214 double max_x, max_y;
215 int n, items, jumpitems, spobitems;
216 const vec2 **pos;
217 MapOverlayPos **mo;
218 char buf[STRMAX_SHORT];
219
220 /* Must be open. */
221 if (!ovr_isOpen())
222 return;
223
224 /* Clean up leftovers. */
225 if (ovr_refresh_mo)
226 free( ovr_refresh_mo );
227 if (ovr_refresh_pos)
228 free( ovr_refresh_pos );
229
230 /* Update bounds if necessary. */
231 ovr_boundsUpdate();
232
233 /* Calculate max size. */
234 items = 0;
235 n = array_size(cur_system->jumps) + array_size(cur_system->spobs) + array_size(ovr_markers) + autonav_pos;
236 pos = calloc( n, sizeof(vec2*) );
237 mo = calloc( n, sizeof(MapOverlayPos*) );
238 max_x = 0.;
239 max_y = 0.;
240 if (autonav_pos) {
241 max_x = MAX( max_x, ABS(autonav_pos_v.x) );
242 max_y = MAX( max_y, ABS(autonav_pos_v.y) );
243 pos[items] = &autonav_pos_v;
244 mo[items] = &autonav_pos_mo;
245 mo[items]->radius = 9.; /* Gets set properly below. */
246 mo[items]->text_width = gl_printWidthRaw( &gl_smallFont, _("TARGET") );
247 items++;
248 }
249 for (int i=0; i<array_size(cur_system->jumps); i++) {
250 JumpPoint *jp = &cur_system->jumps[i];
251 max_x = MAX( max_x, ABS(jp->pos.x) );
252 max_y = MAX( max_y, ABS(jp->pos.y) );
253 if (!jp_isUsable(jp))
254 continue;
255 /* Initialize the map overlay stuff. */
256 snprintf( buf, sizeof(buf), "%s%s", jump_getSymbol(jp), sys_isKnown(jp->target) ? _(jp->target->name) : _("Unknown") );
257 pos[items] = &jp->pos;
258 mo[items] = &jp->mo;
259 mo[items]->radius = jumppoint_gfx->sw / 2.;
260 mo[items]->text_width = gl_printWidthRaw(&gl_smallFont, buf);
261 items++;
262 }
263 jumpitems = items;
264 for (int i=0; i<array_size(cur_system->spobs); i++) {
265 Spob *pnt = cur_system->spobs[i];
266 max_x = MAX( max_x, ABS(pnt->pos.x) );
267 max_y = MAX( max_y, ABS(pnt->pos.y) );
268 if (!spob_isKnown(pnt))
269 continue;
270 /* Initialize the map overlay stuff. */
271 snprintf( buf, sizeof(buf), "%s%s", spob_getSymbol(pnt), spob_name(pnt) );
272 pos[items] = &pnt->pos;
273 mo[items] = &pnt->mo;
274 mo[items]->radius = pnt->radius / 2.; /* halved since it's awkwardly large if drawn to scale relative to the player. */
275 /* +2.0 represents a margin used by the SDF shader. */
276 mo[items]->text_width = gl_printWidthRaw( &gl_smallFont, buf );
277 items++;
278 }
279 spobitems = items;
280 for (int i=0; i<array_size(ovr_markers); i++) {
281 double r;
282 ovr_marker_t *mrk = &ovr_markers[i];
283 max_x = MAX( max_x, ABS(mrk->pos.x) );
284 max_y = MAX( max_y, ABS(mrk->pos.y) );
285 if (mrk->text==NULL)
286 continue;
287 /* Initialize the map overlay stuff. */
288 mo[items] = &mrk->mo;
289 switch (mrk->type) {
290 case OVR_MARKER_POINT:
291 pos[items] = &mrk->pos;
292 r = 13.; /* Will get set approprietaly with the max. */
293 break;
294 case OVR_MARKER_CIRCLE:
295 pos[items] = &mrk->u.circle.textpos;
296 r = 13.; /* We're not using the full area. */
297 break;
298 }
299 mo[items]->radius = r;
300 mo[items]->text_width = gl_printWidthRaw( &gl_smallFont, mrk->text );
301 items++;
302 }
303
304 /* We need to calculate the radius of the rendering from the maximum radius of the system. */
305 ovr_res = 2. * 1.2 * MAX( max_x / ovr_bounds.w, max_y / ovr_bounds.h );
306 ovr_res = MAX( ovr_res, 25. );
307 for (int i=0; i<items; i++) {
308 double rm;
309 if (autonav_pos && (i==0))
310 rm = 9.;
311 else if (i<jumpitems)
312 rm = 5.;
313 else if (i<spobitems)
314 rm = 7.5;
315 else
316 rm = 13.;
317 mo[i]->radius = MAX( 2.+mo[i]->radius / ovr_res, rm );
318 }
319
320 /* Compute text overlap and try to minimize it. */
321 ovr_optimizeLayout( items, pos, mo );
322
323 /* Sove the moos. */
324 ovr_refresh_pos = pos;
325 ovr_refresh_mo = mo;
326}
327
331static void ovr_optimizeLayout( int items, const vec2** pos, MapOverlayPos** mo )
332{
333 float cx, cy, r, sx, sy;
334 float x, y, w, h, mx, my, mw, mh;
335 float fx, fy, best, bx, by;
336 float *forces_xa, *forces_ya, *off_buffx, *off_buffy, *off_0x, *off_0y, old_bx, old_by, *off_dx, *off_dy;
337
338 /* Parameters for the map overlay optimization. */
339 const int max_iters = 15;
340 const float kx = 0.015;
341 const float ky = 0.045;
342 const float eps_con = 1.3;
344 /* Nothing to do. */
345 if (items <= 0)
346 return;
347
348 /* Fix radii which fit together. */
350 uint8_t *must_shrink = malloc( items );
351 for (cur.i=0; cur.i<items; cur.i++)
352 for (cur.j=cur.i+1; cur.j<items; cur.j++) {
353 cur.dist = hypot( pos[cur.i]->x - pos[cur.j]->x, pos[cur.i]->y - pos[cur.j]->y ) / ovr_res;
354 if (cur.dist < mo[cur.i]->radius + mo[cur.j]->radius)
355 array_push_back( &fits, cur );
356 }
357 for (int iter=0; (iter<max_iters) && (array_size(fits) > 0); iter++) {
358 float shrink_factor = 0.;
359 memset( must_shrink, 0, items );
360 for (int i=0; i < array_size( fits ); i++) {
361 r = fits[i].dist / (mo[fits[i].i]->radius + mo[fits[i].j]->radius);
362 if (r >= 1)
363 array_erase( &fits, &fits[i], &fits[i+1] );
364 else {
365 shrink_factor = MAX( shrink_factor, r - FLT_EPSILON );
366 must_shrink[fits[i].i] = must_shrink[fits[i].j] = 1;
367 }
368 }
369 for (int i=0; i<items; i++)
370 if (must_shrink[i])
371 mo[i]->radius *= shrink_factor;
372
373 }
374 free( must_shrink );
375 array_free( fits );
376
377 /* Limit shrinkage. */
378 for (int i=0; i<items; i++)
379 mo[i]->radius = MAX( mo[i]->radius, 4. );
380
381 /* Initialization offset list. */
382 off_0x = calloc( items, sizeof(float) );
383 off_0y = calloc( items, sizeof(float) );
384
385 /* Initialize all items. */
386 for (int i=0; i<items; i++) {
387 /* Test to see what side is best to put the text on.
388 * We actually compute the text overlap also so hopefully it will alternate
389 * sides when stuff is clustered together. */
390 x = pos[i]->x/ovr_res - ovr_text_pixbuf;
391 y = pos[i]->y/ovr_res - ovr_text_pixbuf;
392 w = mo[i]->text_width + 2.*ovr_text_pixbuf;
393 h = gl_smallFont.h + 2.*ovr_text_pixbuf;
394
395 const float tx[4] = { mo[i]->radius+ovr_text_pixbuf+0.1, -mo[i]->radius-0.1-w, -mo[i]->text_width/2. , -mo[i]->text_width/2. };
396 const float ty[4] = { -gl_smallFont.h/2., -gl_smallFont.h/2., mo[i]->radius+ovr_text_pixbuf+0.1, -mo[i]->radius-0.1-h };
397
398 /* Check all combinations. */
399 bx = 0.;
400 by = 0.;
401 best = HUGE_VALF;
402 for (int k=0; k<4; k++) {
403 double val = 0.;
404
405 /* Test intersection with the spob indicators. */
406 for (int j=0; j<items; j++) {
407 fx = fy = 0.;
408 mw = 2.*mo[j]->radius;
409 mh = mw;
410 mx = pos[j]->x/ovr_res - mw/2.;
411 my = pos[j]->y/ovr_res - mh/2.;
412
413 force_collision( &fx, &fy, x+tx[k], y+ty[k], w, h, mx, my, mw, mh );
414
415 val += ABS(fx) + ABS(fy);
416 }
417 /* Keep best. */
418 if (k == 0 || val < best) {
419 bx = tx[k];
420 by = ty[k];
421 best = val;
422 }
423 if (val==0.)
424 break;
425 }
426
427 /* Store offsets. */
428 off_0x[i] = bx;
429 off_0y[i] = by;
430 }
431
432 /* Uzawa optimization algorithm.
433 * We minimize the (weighted) L2 norm of vector of offsets and radius changes
434 * Under the constraint of no interpenetration
435 * As the algorithm is Uzawa, this constraint won't necessary be attained.
436 * This is similar to a contact problem is mechanics. */
437
438 /* Initialize the matrix that stores the dual variables (forces applied between objects).
439 * matrix is column-major, this means it is interesting to store in each column the forces
440 * received by a given object. Then these forces are summed to obtain the total force on the object.
441 * Odd lines are forces from objects and Even lines from other texts. */
442
443 forces_xa = calloc( 2*items*items, sizeof(float) );
444 forces_ya = calloc( 2*items*items, sizeof(float) );
445
446 /* And buffer lists. */
447 off_buffx = calloc( items, sizeof(float) );
448 off_buffy = calloc( items, sizeof(float) );
449 off_dx = calloc( items, sizeof(float) );
450 off_dy = calloc( items, sizeof(float) );
451
452 /* Main Uzawa Loop. */
453 for (int iter=0; iter<max_iters; iter++) {
454 double val = 0.; /* This stores the stagnation indicator. */
455 for (int i=0; i<items; i++) {
456 cx = pos[i]->x / ovr_res;
457 cy = pos[i]->y / ovr_res;
458 /* Compute the forces. */
459 ovr_refresh_uzawa_overlap(
460 forces_xa, forces_ya,
461 cx + off_dx[i] + off_0x[i] - ovr_text_pixbuf,
462 cy + off_dy[i] + off_0y[i] - ovr_text_pixbuf,
463 mo[i]->text_width + 2*ovr_text_pixbuf,
464 gl_smallFont.h + 2*ovr_text_pixbuf,
465 pos, mo, items, i, off_0x, off_0y, off_dx, off_dy );
466
467 /* Do the sum. */
468 sx = sy = 0.;
469 for (int j=0; j<2*items; j++) {
470 sx += forces_xa[2*items*i+j];
471 sy += forces_ya[2*items*i+j];
472 }
473
474 /* Store old version of buffers. */
475 old_bx = off_buffx[i];
476 old_by = off_buffy[i];
477
478 /* Update positions (in buffer). Diagonal stiffness. */
479 off_buffx[i] = kx * sx;
480 off_buffy[i] = ky * sy;
481
482 val = MAX( val, ABS(old_bx-off_buffx[i]) + ABS(old_by-off_buffy[i]) );
483 }
484
485 /* Offsets are actually updated once the first loop is over. */
486 for (int i=0; i<items; i++) {
487 off_dx[i] = off_buffx[i];
488 off_dy[i] = off_buffy[i];
489 }
490
491 /* Test stagnation. */
492 if (val <= eps_con)
493 break;
494 }
495
496 /* Permanently add the initialization offset to total offset. */
497 for (int i=0; i<items; i++) {
498 mo[i]->text_offx = off_dx[i] + off_0x[i];
499 mo[i]->text_offy = off_dy[i] + off_0y[i];
500 }
501
502 /* Free the forces matrix and the various buffers. */
503 free( forces_xa );
504 free( forces_ya );
505 free( off_buffx );
506 free( off_buffy );
507 free( off_0x );
508 free( off_0y );
509 free( off_dx );
510 free( off_dy );
511}
512
516static void force_collision( float *ox, float *oy,
517 float x, float y, float w, float h,
518 float mx, float my, float mw, float mh )
519{
520 /* No contact because of y offset (+tolerance). */
521 if ((y+h < my+ovr_text_pixbuf) || (y+ovr_text_pixbuf > my+mh))
522 *ox = 0.;
523 else {
524 /* Case A is left of B. */
525 if (x+0.5*w < mx+0.5*mw) {
526 *ox += mx-(x+w);
527 *ox = MIN(0., *ox);
528 }
529 /* Case A is to the right of B. */
530 else {
531 *ox += (mx+mw)-x;
532 *ox = MAX(0., *ox);
533 }
534 }
535
536 /* No contact because of x offset (+tolerance). */
537 if ((x+w < mx+ovr_text_pixbuf) || (x+ovr_text_pixbuf > mx+mw))
538 *oy = 0.;
539 else {
540 /* Case A is below B. */
541 if (y+0.5*h < my+0.5*mh) {
542 *oy += my-(y+h);
543 *oy = MIN(0., *oy);
544 }
545 /* Case A is above B. */
546 else {
547 *oy += (my+mh)-y;
548 *oy = MAX(0., *oy);
549 }
550 }
551}
552
556static void ovr_refresh_uzawa_overlap( float *forces_x, float *forces_y,
557 float x, float y, float w, float h, const vec2** pos,
558 MapOverlayPos** mo, int items, int self,
559 float *offx, float *offy, float *offdx, float *offdy )
560{
561 for (int i=0; i<items; i++) {
562 float mx, my, mw, mh;
563 const float pb2 = ovr_text_pixbuf*2.;
564
565 /* Collisions with spob circles and jp triangles (odd indices). */
566 mw = 2.*mo[i]->radius;
567 mh = mw;
568 mx = pos[i]->x/ovr_res - mw/2.;
569 my = pos[i]->y/ovr_res - mh/2.;
570 force_collision( &forces_x[2*items*self+2*i+1], &forces_y[2*items*self+2*i+1], x, y, w, h, mx, my, mw, mh );
571
572 if (i == self)
573 continue;
574
575 /* Collisions with other texts (even indices) */
576 mw = mo[i]->text_width + pb2;
577 mh = gl_smallFont.h + pb2;
578 mx = pos[i]->x/ovr_res + offdx[i] + offx[i] - ovr_text_pixbuf;
579 my = pos[i]->y/ovr_res + offdy[i] + offy[i] - ovr_text_pixbuf;
580 force_collision( &forces_x[2*items*self+2*i], &forces_y[2*items*self+2*i], x, y, w, h, mx, my, mw, mh );
581 }
582}
583
584void ovr_initAlpha (void)
585{
586 SafeLane *safelanes;
587 for (int i=0; i<array_size(cur_system->jumps); i++) {
588 JumpPoint *jp = &cur_system->jumps[i];
589 if (!jp_isUsable(jp))
590 jp->map_alpha = 0.;
591 else
592 jp->map_alpha = 1.;
593 }
594 for (int i=0; i<array_size(cur_system->spobs); i++) {
595 Spob *pnt = cur_system->spobs[i];
596 if (!spob_isKnown(pnt))
597 pnt->map_alpha = 0.;
598 else
599 pnt->map_alpha = 1.;
600 }
601
602 safelanes = safelanes_get( -1, 0, cur_system );
603 for (int i=0; i<array_size(safelanes); i++) {
604 vec2 *posns[2];
605 if (!ovr_safelaneKnown( &safelanes[i], posns ))
606 safelanes[i].map_alpha = 0.;
607 else
608 safelanes[i].map_alpha = 1.;
609 }
610 array_free( ovr_render_safelanes );
611 ovr_render_safelanes = safelanes;
612
613 ovr_dt = 0.;
614}
615
616int ovr_init (void)
617{
618 il_create( &ovr_qtquery, 1 );
619 return 0;
620}
621
622void ovr_exit (void)
623{
624 il_destroy( &ovr_qtquery );
625}
626
632void ovr_setOpen( int open )
633{
634 if (open && !ovr_open) {
635 ovr_open = 1;
637 ovr_initAlpha();
638 }
639 else if (ovr_open) {
640 ovr_open = 0;
642 array_free( ovr_render_safelanes );
643 ovr_render_safelanes = NULL;
644 free( ovr_refresh_pos );
645 ovr_refresh_pos = NULL;
646 free( ovr_refresh_mo );
647 ovr_refresh_mo = NULL;
648 }
649}
650
656void ovr_key( int type )
657{
658 if (type > 0) {
659 if (ovr_open)
660 ovr_setOpen(0);
661 else {
662 ovr_setOpen(1);
663
664 /* Refresh overlay size. */
665 ovr_refresh();
666 ovr_opened = SDL_GetTicks();
667 }
668 }
669 else if (type < 0) {
670 if (SDL_GetTicks() - ovr_opened > 300)
671 ovr_setOpen(0);
672 }
673}
674
675static int ovr_safelaneKnown( SafeLane *sf, vec2 *posns[2] )
676{
677 /* This is a bit asinine, but should be easily replaceable by decent code when we have a System Objects API.
678 * Specifically, a generic pos and isKnown test would clean this up nicely. */
679 int known = 1;
680 for (int j=0; j<2; j++) {
681 Spob *pnt;
682 JumpPoint *jp;
683 switch(sf->point_type[j]) {
684 case SAFELANE_LOC_SPOB:
685 pnt = spob_getIndex( sf->point_id[j] );
686 posns[j] = &pnt->pos;
687 if (!spob_isKnown( pnt ))
688 known = 0;
689 break;
690 case SAFELANE_LOC_DEST_SYS:
692 posns[j] = &jp->pos;
693 if (!jp_isKnown( jp ))
694 known = 0;
695 break;
696 default:
697 ERR( _("Invalid vertex type.") );
698 }
699 }
700 return known;
701}
702
708void ovr_render( double dt )
709{
710 SafeLane *safelanes;
711 double w, h, res;
712 double x,y;
713 double rx,ry, x2,y2, rw,rh;
714
715 /* Must be open. */
716 if (!ovr_open)
717 return;
718
719 /* Player must be alive. */
720 if (player_isFlag( PLAYER_DESTROYED ) || (player.p == NULL))
721 return;
722
723 /* Have to clear for text. */
724 glClear(GL_DEPTH_BUFFER_BIT);
725
726 /* Default values. */
727 w = ovr_bounds.w;
728 h = ovr_bounds.h;
729 res = ovr_res;
730 ovr_dt += dt;
731
732 /* First render the background overlay. */
733 glColour c = { .r=0., .g=0., .b=0., .a= conf.map_overlay_opacity };
734 gl_renderRect( ovr_bounds.l, ovr_bounds.b, w, h, &c );
735
736 /* Render the safe lanes */
737 safelanes = safelanes_get( -1, 0, cur_system );
738 for (int i=0; i<array_size(safelanes); i++) {
739 glColour col;
740 vec2 *posns[2];
741 double r;
742 if (!ovr_safelaneKnown( &safelanes[i], posns ))
743 continue;
744
745 /* Copy over alpha. FIXME: Based on past bug reports, we aren't successfully resetting ovr_render_safelanes
746 * when the lane-set changes. We really want coherency rather than this array_size check. */
747 if (i < array_size(ovr_render_safelanes))
748 safelanes[i].map_alpha = ovr_render_safelanes[i].map_alpha;
749
750 if (safelanes[i].map_alpha < 1.0)
751 safelanes[i].map_alpha = MIN( safelanes[i].map_alpha+OVERLAY_FADEIN*dt, 1.0 );
752
753 if (faction_isPlayerFriend( safelanes[i].faction ))
754 col = cFriend;
755 else if (faction_isPlayerEnemy( safelanes[i].faction ))
756 col = cHostile;
757 else
758 col = cNeutral;
759 col.a = 0.15 * MIN( safelanes[i].map_alpha, 1.0 );
760
761 /* Get positions and stuff. */
762 map_overlayToScreenPos( &x, &y, posns[0]->x, posns[0]->y );
763 map_overlayToScreenPos( &x2, &y2, posns[1]->x, posns[1]->y );
764 rx = x2-x;
765 ry = y2-y;
766 r = atan2( ry, rx );
767 rw = MOD(rx,ry)/2.;
768 rh = 9.;
769
770 /* Render. */
771 glUseProgram(shaders.safelane.program);
772 gl_renderShader( x+rx/2., y+ry/2., rw, rh, r, &shaders.safelane, &col, 1 );
773 }
774 array_free( ovr_render_safelanes );
775 ovr_render_safelanes = safelanes;
776
777 /* Render markers background. */
778 ovr_mrkRenderAll( res, 0 );
779
780 /* Check if player has goto target. */
781 if (autonav_pos) {
782 glColour col = cRadar_hilight;
783 col.a = 0.6;
784 map_overlayToScreenPos( &x, &y, autonav_pos_v.x, autonav_pos_v.y );
785 glUseProgram( shaders.selectposition.program );
786 gl_renderShader( x, y, 9., 9., 0., &shaders.selectposition, &col, 1 );
787 gl_printMarkerRaw( &gl_smallFont, x+autonav_pos_mo.text_offx, y+autonav_pos_mo.text_offy, &cRadar_hilight, _("TARGET") );
788 }
789
790 /* Render spobs. */
791 for (int i=0; i<array_size(cur_system->spobs); i++) {
792 Spob *pnt = cur_system->spobs[i];
793 if (pnt->map_alpha < 1.0)
794 pnt->map_alpha = MIN( pnt->map_alpha+OVERLAY_FADEIN*dt, 1.0 );
795 if (i != player.p->nav_spob)
796 gui_renderSpob( i, RADAR_RECT, w, h, res, pnt->map_alpha, 1 );
797 }
798 if (player.p->nav_spob > -1)
799 gui_renderSpob( player.p->nav_spob, RADAR_RECT, w, h, res, cur_system->spobs[player.p->nav_spob]->map_alpha, 1 );
800
801 /* Render jump points. */
802 for (int i=0; i<array_size(cur_system->jumps); i++) {
803 JumpPoint *jp = &cur_system->jumps[i];
804 if (jp->map_alpha < 1.0)
805 jp->map_alpha = MIN( jp->map_alpha+OVERLAY_FADEIN*dt, 1.0 );
806 if ((i != player.p->nav_hyperspace) && !jp_isFlag(jp, JP_EXITONLY))
807 gui_renderJumpPoint( i, RADAR_RECT, w, h, res, jp->map_alpha, 1 );
808 }
809 if (player.p->nav_hyperspace > -1)
810 gui_renderJumpPoint( player.p->nav_hyperspace, RADAR_RECT, w, h, res, cur_system->jumps[player.p->nav_hyperspace].map_alpha, 1 );
811
812 /* Render the asteroids */
813 for (int i=0; i<array_size(cur_system->asteroids); i++) {
815 double range = EW_ASTEROID_DIST * player.p->stats.ew_detect; /* TODO don't hardcode. */
816 int ax, ay, r;
817 ax = round(player.p->solid.pos.x);
818 ay = round(player.p->solid.pos.y);
819 r = ceil(range);
820 asteroid_collideQueryIL( ast, &ovr_qtquery, ax-r, ay-r, ax+r, ay+r );
821 for (int j=0; j<il_size(&ovr_qtquery); j++) {
822 Asteroid *a = &ast->asteroids[ il_get( &ovr_qtquery, j, 0 ) ];
823 gui_renderAsteroid( a, w, h, res, 1 );
824 }
825 }
826
827 /* Render pilots. */
828 Pilot *const* pstk = pilot_getAll();
829 int t = 0;
830 for (int i=0; i<array_size(pstk); i++) {
831 if (pstk[i]->id == PLAYER_ID) /* Skip player. */
832 continue;
833 if (pstk[i]->id == player.p->target)
834 t = i;
835 else
836 gui_renderPilot( pstk[i], RADAR_RECT, w, h, res, 1 );
837 }
838
839 /* Stealth rendering. */
840 if (pilot_isFlag( player.p, PILOT_STEALTH )) {
841 double detect;
842 glColour col = {0., 0., 1., 1.};
843
844 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.fbo[2] );
845 glClearColor( 0., 0., 0., 0. );
846 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
847 glBlendEquation( GL_FUNC_ADD );
848 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE );
849 for (int i=0; i<array_size(cur_system->asteroids); i++) {
851 detect = vec2_dist2( &player.p->solid.pos, &ast->pos );
852 if (detect < pow2(pilot_sensorRange() * player.p->stats.ew_detect + ast->radius)) {
853 double r;
854 map_overlayToScreenPos( &x, &y, ast->pos.x, ast->pos.y );
855 r = ast->radius / res;
856 glUseProgram( shaders.astaura.program );
857 gl_renderShader( x, y, r, r, 0., &shaders.astaura, &col, 1 );
858 }
859 }
860
861 glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
862 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE );
863 for (int i=0; i<array_size(cur_system->astexclude); i++) {
864 double r;
865 AsteroidExclusion *aexcl = &cur_system->astexclude[i];
866 map_overlayToScreenPos( &x, &y, aexcl->pos.x, aexcl->pos.y );
867 r = aexcl->radius / res;
868 glUseProgram( shaders.astaura.program );
869 gl_renderShader( x, y, r, r, 0., &shaders.astaura, &col, 1 );
870 }
871
872 glBlendEquation( GL_MAX );
873 for (int i=0; i<array_size(cur_system->jumps); i++) {
874 double r;
875 JumpPoint *jp = &cur_system->jumps[i];
876 if (!jp_isUsable(jp) || jp_isFlag(jp,JP_HIDDEN))
877 continue;
878 map_overlayToScreenPos( &x, &y, jp->pos.x, jp->pos.y );
879 r = EW_JUMP_BONUS_RANGE / res;
880 glUseProgram( shaders.astaura.program );
881 gl_renderShader( x, y, r, r, 0., &shaders.astaura, &col, 1 );
882 }
883
884 detect = player.p->ew_stealth / res;
885 col.r = 1.;
886 col.g = 0.;
887 col.b = 0.;
888 for (int i=0; i<array_size(pstk); i++) {
889 double r;
890 if (pilot_isDisabled(pstk[i]))
891 continue;
892 if (areAllies( player.p->faction, pstk[i]->faction ) || pilot_isFriendly(pstk[i]))
893 continue;
894 /* Only show pilots the player can see. */
895 if (!pilot_validTarget( player.p, pstk[i] ))
896 continue;
897 map_overlayToScreenPos( &x, &y, pstk[i]->solid.pos.x, pstk[i]->solid.pos.y );
898 r = detect * pstk[i]->stats.ew_detect; /* Already divided by res */
899 if (r > 0.) {
900 glUseProgram( shaders.stealthaura.program );
901 gl_renderShader( x, y, r, r, 0., &shaders.stealthaura, &col, 1 );
902 }
903 }
904
905 glBlendEquation( GL_FUNC_ADD );
906 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
907 glBindFramebuffer(GL_FRAMEBUFFER, gl_screen.current_fbo);
908 glClearColor( 0., 0., 0., 1. );
909
910 glUseProgram(shaders.stealthoverlay.program);
911 glBindTexture( GL_TEXTURE_2D, gl_screen.fbo_tex[2] );
912
913 glEnableVertexAttribArray( shaders.stealthoverlay.vertex );
914 gl_vboActivateAttribOffset( gl_squareVBO, shaders.stealthoverlay.vertex,
915 0, 2, GL_FLOAT, 0 );
916
917 /* Set shader uniforms. */
918 gl_uniformColour(shaders.stealthoverlay.colour, &cWhite);
919 const mat4 ortho = mat4_ortho( 0., 1., 0., 1., 1., -1. );
920 const mat4 I = mat4_identity();
921 gl_uniformMat4(shaders.stealthoverlay.projection, &ortho );
922 gl_uniformMat4(shaders.stealthoverlay.tex_mat, &I );
923
924 /* Draw. */
925 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
926
927 /* Clear state. */
928 glDisableVertexAttribArray( shaders.stealthoverlay.vertex );
929 }
930
931 /* Render markers foreground. */
932 ovr_mrkRenderAll( res, 1 );
933
934 /* Render the targeted pilot */
935 if (t!=0)
936 gui_renderPilot( pstk[t], RADAR_RECT, w, h, res, 1 );
937
938 /* Render the player. */
939 gui_renderPlayer( res, 1 );
940}
941
947static void ovr_mrkRenderAll( double res, int fg )
948{
949 for (int i=0; i<array_size(ovr_markers); i++) {
950 double x, y;
951 ovr_marker_t *mrk = &ovr_markers[i];
952 map_overlayToScreenPos( &x, &y, mrk->pos.x, mrk->pos.y );
953
954 if (!fg) {
955 double r;
956 const glColour highlighted = COL_ALPHA( cRadar_hilight, 0.3 );
957
958 switch (mrk->type) {
959 case OVR_MARKER_POINT:
960 glUseProgram( shaders.hilight.program );
961 glUniform1f( shaders.hilight.dt, ovr_dt );
962 gl_renderShader( x, y, 13., 13., 0., &shaders.hilight, &highlighted, 1 );
963 break;
964
965 case OVR_MARKER_CIRCLE:
966 r = MAX( mrk->u.circle.r / res, 13. ); /* Don't allow to be smaller than a "point" */
967 glUseProgram( shaders.hilight_circle.program );
968 glUniform1f( shaders.hilight_circle.dt, ovr_dt );
969 gl_renderShader( x, y, r, r, 0., &shaders.hilight_circle, &highlighted, 1 );
970 break;
971 }
972 }
973
974 if (fg && mrk->text != NULL) {
975 switch (mrk->type) {
976 case OVR_MARKER_POINT:
977 gl_printMarkerRaw( &gl_smallFont, x+mrk->mo.text_offx, y+mrk->mo.text_offy, &cRadar_hilight, mrk->text );
978 break;
979
980 case OVR_MARKER_CIRCLE:
981 map_overlayToScreenPos( &x, &y, mrk->u.circle.textpos.x, mrk->u.circle.textpos.y );
982 gl_printMarkerRaw( &gl_smallFont, x+mrk->mo.text_offx, y+mrk->mo.text_offy, &cRadar_hilight, mrk->text );
983 break;
984 }
985 }
986 }
987}
988
992void ovr_mrkFree (void)
993{
994 /* Clear markers. */
995 ovr_mrkClear();
996
997 /* Free array. */
998 array_free( ovr_markers );
999 ovr_markers = NULL;
1000 array_free( ovr_render_safelanes );
1001 ovr_render_safelanes = NULL;
1002}
1003
1007void ovr_mrkClear (void)
1008{
1009 for (int i=0; i<array_size(ovr_markers); i++)
1010 ovr_mrkCleanup( &ovr_markers[i] );
1011 array_erase( &ovr_markers, array_begin(ovr_markers), array_end(ovr_markers) );
1012
1013 /* Refresh if necessary. */
1014 if (ovr_open)
1015 ovr_refresh();
1016}
1017
1023static void ovr_mrkCleanup( ovr_marker_t *mrk )
1024{
1025 free( mrk->text );
1026 mrk->text = NULL;
1027}
1028
1034static ovr_marker_t *ovr_mrkNew (void)
1035{
1036 ovr_marker_t *mrk;
1037
1038 if (ovr_markers == NULL)
1039 ovr_markers = array_create( ovr_marker_t );
1040
1041 mrk = &array_grow( &ovr_markers );
1042 memset( mrk, 0, sizeof( ovr_marker_t ) );
1043 mrk->id = ++mrk_idgen;
1044 mrk->refcount = 1;
1045 return mrk;
1046}
1047
1056unsigned int ovr_mrkAddPoint( const char *text, double x, double y )
1057{
1058 ovr_marker_t *mrk;
1059
1060 /* Check existing ones first. */
1061 for (int i=0; i<array_size(ovr_markers); i++) {
1062 mrk = &ovr_markers[i];
1063
1064 if (mrk->type != OVR_MARKER_POINT)
1065 continue;
1066
1067 if (((text==NULL) && (mrk->text!=NULL)) ||
1068 ((text!=NULL) && ((mrk->text==NULL) || strcmp(text,mrk->text)!=0)))
1069 continue;
1070
1071 if (hypotf( x-mrk->pos.x, y-mrk->pos.y ) > 1e-3)
1072 continue;
1073
1074 /* Found same marker already! */
1075 mrk->refcount++;
1076 return mrk->id;
1077 }
1078
1079 /* Create new one. */
1080 mrk = ovr_mrkNew();
1081 mrk->type = OVR_MARKER_POINT;
1082 if (text != NULL)
1083 mrk->text = strdup( text );
1084 vec2_cset( &mrk->pos, x, y );
1085
1086 /* Refresh if necessary. */
1087 if (ovr_open)
1088 ovr_refresh();
1089
1090 return mrk->id;
1091}
1092
1102unsigned int ovr_mrkAddCircle( const char *text, double x, double y, double r )
1103{
1104 ovr_marker_t *mrk;
1105
1106 /* Check existing ones first. */
1107 for (int i=0; i<array_size(ovr_markers); i++) {
1108 mrk = &ovr_markers[i];
1109
1110 if (mrk->type != OVR_MARKER_CIRCLE)
1111 continue;
1112
1113 if (((text==NULL) && (mrk->text!=NULL)) ||
1114 ((text!=NULL) && ((mrk->text==NULL) || strcmp(text,mrk->text)!=0)))
1115 continue;
1116
1117 if ((mrk->u.circle.r-r) > 1e-3 || hypotf( x-mrk->pos.x, y-mrk->pos.y ) > 1e-3)
1118 continue;
1119
1120 /* Found same marker already! */
1121 mrk->refcount++;
1122 return mrk->id;
1123 }
1124
1125 /* Create new one. */
1126 mrk = ovr_mrkNew();
1127 mrk->type = OVR_MARKER_CIRCLE;
1128 if (text != NULL)
1129 mrk->text = strdup( text );
1130 vec2_cset( &mrk->pos, x, y );
1131 mrk->u.circle.r = r;
1132 vec2_cset( &mrk->u.circle.textpos, x+r*M_SQRT1_2, y-r*M_SQRT1_2 );
1133
1134 /* Refresh if necessary. */
1135 if (ovr_open)
1136 ovr_refresh();
1137
1138 return mrk->id;
1139}
1140
1146void ovr_mrkRm( unsigned int id )
1147{
1148 for (int i=0; i<array_size(ovr_markers); i++) {
1149 ovr_marker_t *m = &ovr_markers[i];
1150 if (id != m->id)
1151 continue;
1152
1153 m->refcount--;
1154 if (m->refcount > 0)
1155 return;
1156
1157 ovr_mrkCleanup( m );
1158 array_erase( &ovr_markers, &m[0], &m[1] );
1159
1160 /* Refresh if necessary. */
1161 if (ovr_open)
1162 ovr_refresh();
1163 break;
1164 }
1165}
1166
1167void ovr_autonavPos( double x, double y )
1168{
1169 autonav_pos = 1;
1170 vec2_cset( &autonav_pos_v, x, y );
1171
1172 /* Refresh if necessary. */
1173 if (ovr_open)
1174 ovr_refresh();
1175}
1176
1177void ovr_autonavClear (void)
1178{
1179 autonav_pos = 0;
1180
1181 /* Refresh if necessary. */
1182 if (ovr_open)
1183 ovr_refresh();
1184}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:202
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:119
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:129
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:194
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
int faction_isPlayerEnemy(int f)
Gets whether or not the player is an enemy of the faction.
Definition faction.c:1021
int faction_isPlayerFriend(int f)
Gets whether or not the player is a friend of the faction.
Definition faction.c:1009
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition faction.c:1253
glFont gl_smallFont
Definition font.c:154
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition font.c:961
void gl_printMarkerRaw(const glFont *ft_font, double x, double y, const glColour *c, const char *text)
Wrapper for gl_printRaw for map overlay markers.
Definition font.c:673
void gui_renderSpob(int ind, RadarShape shape, double w, double h, double res, double alpha, int overlay)
Draws the spobs in the minimap.
Definition gui.c:1372
void gui_renderPlayer(double res, int overlay)
Renders the player cross on the radar or whatever.
Definition gui.c:1267
void gui_renderAsteroid(const Asteroid *a, double w, double h, double res, int overlay)
Renders an asteroid in the GUI radar.
Definition gui.c:1200
void gui_renderJumpPoint(int ind, RadarShape shape, double w, double h, double res, double alpha, int overlay)
Renders a jump point on the minimap.
Definition gui.c:1477
void gui_renderPilot(const Pilot *p, RadarShape shape, double w, double h, double res, int overlay)
Renders a pilot in the GUI radar.
Definition gui.c:1107
void input_mouseShow(void)
Shows the mouse.
Definition input.c:368
int input_clickPos(SDL_Event *event, double x, double y, double zoom, double minpr, double minr)
Handles a click at a position in the current system.
Definition input.c:1234
void input_mouseHide(void)
Hides the mouse.
Definition input.c:377
mat4 mat4_identity(void)
Creates an identity matrix.
Definition mat4.c:195
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Definition mat4.c:209
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
#define ABS(x)
Definition naev.h:36
#define pow2(x)
Definition naev.h:46
#define MAX(x, y)
Definition naev.h:39
void gl_windowToScreenPos(int *sx, int *sy, int wx, int wy)
Translates the window position to screen position.
Definition opengl.c:616
glInfo gl_screen
Definition opengl.c:51
void gl_renderShader(double x, double y, double w, double h, double r, const SimpleShader *shd, const glColour *c, int center)
Renders a simple shader.
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition opengl_vbo.c:226
int pilot_validTarget(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid target for another pilot.
Definition pilot.c:237
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition pilot.c:708
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:94
double pilot_sensorRange(void)
Returns the default sensor range for the current system.
Definition pilot_ew.c:208
Player_t player
Definition player.c:74
static const double c[]
Definition rng.c:264
SafeLane * safelanes_get(int faction, int standing, const StarSystem *system)
Gets a set of safelanes for a faction and system.
Definition safelanes.c:190
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition space.c:989
const char * jump_getSymbol(const JumpPoint *jp)
Gets the jump point symbol.
Definition space.c:1260
const char * spob_getSymbol(const Spob *p)
Gets the spob symbol.
Definition space.c:1900
JumpPoint * jump_getTarget(const StarSystem *target, const StarSystem *sys)
Less safe version of jump_get that works with pointers.
Definition space.c:1246
StarSystem * cur_system
Definition space.c:106
Spob * spob_getIndex(int ind)
Gets spob by index.
Definition space.c:1082
const char * spob_name(const Spob *p)
Gets the translated name of a spob.
Definition space.c:1752
glTexture * jumppoint_gfx
Definition space.c:107
Represents an asteroid field anchor.
Definition asteroid.h:100
double radius
Definition asteroid.h:107
Asteroid * asteroids
Definition asteroid.h:105
Represents an asteroid exclusion zone.
Definition asteroid.h:124
Represents a single asteroid.
Definition asteroid.h:77
Saves the layout decisions from positioning labeled objects on the overlay.
Definition space.h:57
float text_offx
Definition space.h:59
float text_width
Definition space.h:61
float radius
Definition space.h:58
float text_offy
Definition space.h:60
The representation of an in-game pilot.
Definition pilot.h:217
ShipStats stats
Definition pilot.h:294
double ew_stealth
Definition pilot.h:273
int nav_hyperspace
Definition pilot.h:345
int faction
Definition pilot.h:222
Solid solid
Definition pilot.h:227
unsigned int target
Definition pilot.h:342
int nav_spob
Definition pilot.h:344
double map_overlay_opacity
Definition conf.h:124
Pilot * p
Definition player.h:101
Describes a safe lane, patrolled by a faction, within a system.
Definition safelanes.h:24
int point_id[2]
Definition safelanes.h:27
SafeLaneLocType point_type[2]
Definition safelanes.h:26
double map_alpha
Definition safelanes.h:28
double ew_detect
Definition shipstats.h:244
vec2 pos
Definition physics.h:49
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:89
double radius
Definition space.h:95
double map_alpha
Definition space.h:133
vec2 pos
Definition space.h:94
MapOverlayPos mo
Definition space.h:132
int h
Definition font.h:18
GLuint fbo_tex[OPENGL_NUM_FBOS]
Definition opengl.h:72
GLuint fbo[OPENGL_NUM_FBOS]
Definition opengl.h:71
GLuint current_fbo
Definition opengl.h:70
double sw
Definition opengl_tex.h:46
Definition mat4.h:10
An overlay map marker.
Definition map_overlay.c:47
ovr_marker_type_t type
Definition map_overlay.c:50
MapOverlayPos mo
Definition map_overlay.c:53
union ovr_marker_t::@26 u
unsigned int id
Definition map_overlay.c:48
Represents a 2d vector.
Definition vec2.h:32
double y
Definition vec2.h:34
double x
Definition vec2.h:33