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