naev 0.10.4
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 "input.h"
16#include "log.h"
17#include "naev.h"
18#include "nstring.h"
19#include "opengl.h"
20#include "pilot.h"
21#include "player.h"
22#include "safelanes.h"
23#include "space.h"
24
25const double OVERLAY_FADEIN = 1.0/3.0;
30typedef struct MapOverlayRadiusConstraint_ {
31 int i;
32 int j;
33 double dist;
35
36typedef enum ovr_marker_type_e {
37 OVR_MARKER_POINT,
38 OVR_MARKER_CIRCLE,
39} ovr_marker_type_t;
40
44typedef struct ovr_marker_s {
45 unsigned int id;
46 char *text;
47 ovr_marker_type_t type;
49 double x;
50 double y;
51 union {
52 struct {
53 double r;
54 } circle;
55 } u;
57static unsigned int mrk_idgen = 0;
58static ovr_marker_t *ovr_markers = NULL;
60static SafeLane* ovr_render_safelanes = NULL;
62static Uint32 ovr_opened = 0;
63static int ovr_open = 0;
64static double ovr_res = 10.;
65static double ovr_dt = 0.;
66static const double ovr_text_pixbuf = 5.;
67/* Rem: high pix_buffer ovr_text_pixbuff allows to do less iterations. */
68typedef struct OverlayBounds_s {
69 double t;
70 double r;
71 double b;
72 double l;
73 double w;
74 double h;
75 double x;
76 double y;
78static OverlayBounds_t ovr_bounds;
79
80/*
81 * Prototypes
82 */
83static void force_collision( float *ox, float *oy,
84 float x, float y, float w, float h,
85 float mx, float my, float mw, float mh );
86static void ovr_optimizeLayout( int items, const vec2** pos,
87 MapOverlayPos** mo );
88static void ovr_refresh_uzawa_overlap( float *forces_x, float *forces_y,
89 float x, float y, float w, float h, const vec2** pos,
90 MapOverlayPos** mo, int items, int self,
91 float *offx, float *offy, float *offdx, float *offdy );
92/* Render. */
93static int ovr_safelaneKnown( SafeLane *sf, vec2 *posns[2] );
94static void map_overlayToScreenPos( double *ox, double *oy, double x, double y );
95/* Markers. */
96static void ovr_mrkRenderAll( double res, int fg );
97static void ovr_mrkCleanup( ovr_marker_t *mrk );
98static ovr_marker_t *ovr_mrkNew (void);
99
103int ovr_isOpen (void)
104{
105 return !!ovr_open;
106}
107
111static void ovr_boundsUpdate (void)
112{
113 ovr_bounds.w = SCREEN_W - ovr_bounds.l - ovr_bounds.r;
114 ovr_bounds.h = SCREEN_H - ovr_bounds.t - ovr_bounds.b;
115 ovr_bounds.x = ovr_bounds.w / 2. + ovr_bounds.l;
116 ovr_bounds.y = ovr_bounds.h / 2. + ovr_bounds.b;
117}
118
127void ovr_boundsSet( double top, double right, double bottom, double left )
128{
129 ovr_bounds.t = top;
130 ovr_bounds.r = right;
131 ovr_bounds.b = bottom;
132 ovr_bounds.l = left;
133 ovr_boundsUpdate();
134}
135
142void ovr_center( double *x, double *y )
143{
144 *x = ovr_bounds.x;
145 *y = ovr_bounds.y;
146}
147
151static void map_overlayToScreenPos( double *ox, double *oy, double x, double y )
152{
153 *ox = ovr_bounds.x + x / ovr_res;
154 *oy = ovr_bounds.y + y / ovr_res;
155}
156
160int ovr_input( SDL_Event *event )
161{
162 int mx, my;
163 double x, y;
164
165 /* We only want mouse events. */
166 if (event->type != SDL_MOUSEBUTTONDOWN)
167 return 0;
168
169 /* Player must not be NULL. */
170 if (player_isFlag(PLAYER_DESTROYED) || (player.p == NULL))
171 return 0;
172
173 /* Player must not be dead. */
174 if (pilot_isFlag(player.p, PILOT_DEAD))
175 return 0;
176
177 /* Mouse targeting only uses left and right buttons. */
178 if (event->button.button != SDL_BUTTON_LEFT &&
179 event->button.button != SDL_BUTTON_RIGHT)
180 return 0;
181
182 /* Translate from window to screen. */
183 mx = event->button.x;
184 my = event->button.y;
185 gl_windowToScreenPos( &mx, &my, mx, my );
186
187 /* Translate to space coords. */
188 x = ((double)mx - ovr_bounds.x) * ovr_res;
189 y = ((double)my - ovr_bounds.y) * ovr_res;
190
191 return input_clickPos( event, x, y, 1., 10. * ovr_res, 15. * ovr_res );
192}
193
199void ovr_refresh (void)
200{
201 double max_x, max_y;
202 int items, jumpitems;
203 const vec2 **pos;
204 MapOverlayPos **mo;
205 char buf[STRMAX_SHORT];
206
207 /* Must be open. */
208 if (!ovr_isOpen())
209 return;
210
211 /* Update bounds if necessary. */
212 ovr_boundsUpdate();
213
214 /* Calculate max size. */
215 items = 0;
216 pos = calloc(array_size(cur_system->jumps) + array_size(cur_system->spobs), sizeof(vec2*));
217 mo = calloc(array_size(cur_system->jumps) + array_size(cur_system->spobs), sizeof(MapOverlayPos*));
218 max_x = 0.;
219 max_y = 0.;
220 for (int i=0; i<array_size(cur_system->jumps); i++) {
221 JumpPoint *jp = &cur_system->jumps[i];
222 max_x = MAX( max_x, ABS(jp->pos.x) );
223 max_y = MAX( max_y, ABS(jp->pos.y) );
224 if (!jp_isUsable(jp))
225 continue;
226 /* Initialize the map overlay stuff. */
227 snprintf( buf, sizeof(buf), "%s%s", jump_getSymbol(jp), sys_isKnown(jp->target) ? _(jp->target->name) : _("Unknown") );
228 pos[items] = &jp->pos;
229 mo[items] = &jp->mo;
230 mo[items]->radius = jumppoint_gfx->sw / 2.;
231 mo[items]->text_width = gl_printWidthRaw(&gl_smallFont, buf);
232 items++;
233 }
234 jumpitems = items;
235 for (int i=0; i<array_size(cur_system->spobs); i++) {
236 Spob *pnt = cur_system->spobs[i];
237 max_x = MAX( max_x, ABS(pnt->pos.x) );
238 max_y = MAX( max_y, ABS(pnt->pos.y) );
239 if (!spob_isKnown(pnt))
240 continue;
241 /* Initialize the map overlay stuff. */
242 snprintf( buf, sizeof(buf), "%s%s", spob_getSymbol(pnt), spob_name(pnt) );
243 pos[items] = &pnt->pos;
244 mo[items] = &pnt->mo;
245 mo[items]->radius = pnt->radius / 2.; /* halved since it's awkwardly large if drawn to scale relative to the player. */
246 /* +2.0 represents a margin used by the SDF shader. */
247 mo[items]->text_width = gl_printWidthRaw( &gl_smallFont, buf );
248 items++;
249 }
250
251 /* We need to calculate the radius of the rendering from the maximum radius of the system. */
252 ovr_res = 2. * 1.2 * MAX( max_x / ovr_bounds.w, max_y / ovr_bounds.h );
253 ovr_res = MAX( ovr_res, 25. );
254 for (int i=0; i<items; i++)
255 mo[i]->radius = MAX( 2.+mo[i]->radius / ovr_res, i<jumpitems ? 5. : 7.5 );
256
257 /* Compute text overlap and try to minimize it. */
258 ovr_optimizeLayout( items, pos, mo );
259
260 /* Free the moos. */
261 free( mo );
262 free( pos );
263}
264
268static void ovr_optimizeLayout( int items, const vec2** pos, MapOverlayPos** mo )
269{
270 float cx, cy, r, sx, sy;
271 float x, y, w, h, mx, my, mw, mh;
272 float fx, fy, best, bx, by;
273 float *forces_xa, *forces_ya, *off_buffx, *off_buffy, *off_0x, *off_0y, old_bx, old_by, *off_dx, *off_dy;
274
275 /* Parameters for the map overlay optimization. */
276 const int max_iters = 15;
277 const float kx = 0.015;
278 const float ky = 0.045;
279 const float eps_con = 1.3;
281 if (items <= 0)
282 return;
283
284 /* Fix radii which fit together. */
286 uint8_t *must_shrink = malloc( items );
287 for (cur.i=0; cur.i<items; cur.i++)
288 for (cur.j=cur.i+1; cur.j<items; cur.j++) {
289 cur.dist = hypot( pos[cur.i]->x - pos[cur.j]->x, pos[cur.i]->y - pos[cur.j]->y ) / ovr_res;
290 if (cur.dist < mo[cur.i]->radius + mo[cur.j]->radius)
291 array_push_back( &fits, cur );
292 }
293 while (array_size( fits ) > 0) {
294 float shrink_factor = 0.;
295 memset( must_shrink, 0, items );
296 for (int i=0; i < array_size( fits ); i++) {
297 r = fits[i].dist / (mo[fits[i].i]->radius + mo[fits[i].j]->radius);
298 if (r >= 1)
299 array_erase( &fits, &fits[i], &fits[i+1] );
300 else {
301 shrink_factor = MAX( shrink_factor, r - FLT_EPSILON );
302 must_shrink[fits[i].i] = must_shrink[fits[i].j] = 1;
303 }
304 }
305 for (int i=0; i<items; i++)
306 if (must_shrink[i])
307 mo[i]->radius *= shrink_factor;
308 }
309 free( must_shrink );
310 array_free( fits );
311
312 /* Limit shrinkage. */
313 for (int i=0; i<items; i++)
314 mo[i]->radius = MAX( mo[i]->radius, 4. );
315
316 /* Initialization offset list. */
317 off_0x = calloc( items, sizeof(float) );
318 off_0y = calloc( items, sizeof(float) );
319
320 /* Initialize all items. */
321 for (int i=0; i<items; i++) {
322 /* Test to see what side is best to put the text on.
323 * We actually compute the text overlap also so hopefully it will alternate
324 * sides when stuff is clustered together. */
325
326 x = pos[i]->x/ovr_res - ovr_text_pixbuf;
327 y = pos[i]->y/ovr_res - ovr_text_pixbuf;
328 w = mo[i]->text_width + 2.*ovr_text_pixbuf;
329 h = gl_smallFont.h + 2.*ovr_text_pixbuf;
330
331 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. };
332 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 };
333
334 /* Check all combinations. */
335 bx = 0.;
336 by = 0.;
337 best = HUGE_VALF;
338 for (int k=0; k<4; k++) {
339 double val = 0.;
340
341 /* Test intersection with the spob indicators. */
342 for (int j=0; j<items; j++) {
343 fx = fy = 0.;
344 mw = 2.*mo[j]->radius;
345 mh = mw;
346 mx = pos[j]->x/ovr_res - mw/2.;
347 my = pos[j]->y/ovr_res - mh/2.;
348
349 force_collision( &fx, &fy, x+tx[k], y+ty[k], w, h, mx, my, mw, mh );
350
351 val += ABS(fx) + ABS(fy);
352 }
353 /* Keep best. */
354 if (k == 0 || val < best) {
355 bx = tx[k];
356 by = ty[k];
357 best = val;
358 }
359 if (val==0.)
360 break;
361 }
362
363 /* Store offsets. */
364 off_0x[i] = bx;
365 off_0y[i] = by;
366 }
367
368 /* Uzawa optimization algorithm.
369 * We minimize the (weighted) L2 norm of vector of offsets and radius changes
370 * Under the constraint of no interpenetration
371 * As the algorithm is Uzawa, this constraint won't necessary be attained.
372 * This is similar to a contact problem is mechanics. */
373
374 /* Initialize the matrix that stores the dual variables (forces applied between objects).
375 * matrix is column-major, this means it is interesting to store in each column the forces
376 * received by a given object. Then these forces are summed to obtain the total force on the object.
377 * Odd lines are forces from objects and Even lines from other texts. */
378
379 forces_xa = calloc( 2*items*items, sizeof(float) );
380 forces_ya = calloc( 2*items*items, sizeof(float) );
381
382 /* And buffer lists. */
383 off_buffx = calloc( items, sizeof(float) );
384 off_buffy = calloc( items, sizeof(float) );
385 off_dx = calloc( items, sizeof(float) );
386 off_dy = calloc( items, sizeof(float) );
387
388 /* Main Uzawa Loop. */
389 for (int iter=0; iter<max_iters; iter++) {
390 double val = 0.; /* This stores the stagnation indicator. */
391 for (int i=0; i<items; i++) {
392 cx = pos[i]->x / ovr_res;
393 cy = pos[i]->y / ovr_res;
394 /* Compute the forces. */
395 ovr_refresh_uzawa_overlap(
396 forces_xa, forces_ya,
397 cx + off_dx[i] + off_0x[i] - ovr_text_pixbuf,
398 cy + off_dy[i] + off_0y[i] - ovr_text_pixbuf,
399 mo[i]->text_width + 2*ovr_text_pixbuf,
400 gl_smallFont.h + 2*ovr_text_pixbuf,
401 pos, mo, items, i, off_0x, off_0y, off_dx, off_dy );
402
403 /* Do the sum. */
404 sx = sy = 0.;
405 for (int j=0; j<2*items; j++) {
406 sx += forces_xa[2*items*i+j];
407 sy += forces_ya[2*items*i+j];
408 }
409
410 /* Store old version of buffers. */
411 old_bx = off_buffx[i];
412 old_by = off_buffy[i];
413
414 /* Update positions (in buffer). Diagonal stiffness. */
415 off_buffx[i] = kx * sx;
416 off_buffy[i] = ky * sy;
417
418 val = MAX( val, ABS(old_bx-off_buffx[i]) + ABS(old_by-off_buffy[i]) );
419 }
420
421 /* Offsets are actually updated once the first loop is over. */
422 for (int i=0; i<items; i++) {
423 off_dx[i] = off_buffx[i];
424 off_dy[i] = off_buffy[i];
425 }
426
427 /* Test stagnation. */
428 if (val <= eps_con)
429 break;
430 }
431
432 /* Permanently add the initialization offset to total offset. */
433 for (int i=0; i<items; i++) {
434 mo[i]->text_offx = off_dx[i] + off_0x[i];
435 mo[i]->text_offy = off_dy[i] + off_0y[i];
436 }
437
438 /* Free the forces matrix and the various buffers. */
439 free( forces_xa );
440 free( forces_ya );
441 free( off_buffx );
442 free( off_buffy );
443 free( off_0x );
444 free( off_0y );
445 free( off_dx );
446 free( off_dy );
447}
448
452static void force_collision( float *ox, float *oy,
453 float x, float y, float w, float h,
454 float mx, float my, float mw, float mh )
455{
456 /* No contact because of y offset (+tolerance). */
457 if ((y+h < my+ovr_text_pixbuf) || (y+ovr_text_pixbuf > my+mh))
458 *ox = 0.;
459 else {
460 /* Case A is left of B. */
461 if (x+0.5*w < mx+0.5*mw) {
462 *ox += mx-(x+w);
463 *ox = MIN(0., *ox);
464 }
465 /* Case A is to the right of B. */
466 else {
467 *ox += (mx+mw)-x;
468 *ox = MAX(0., *ox);
469 }
470 }
471
472 /* No contact because of x offset (+tolerance). */
473 if ((x+w < mx+ovr_text_pixbuf) || (x+ovr_text_pixbuf > mx+mw))
474 *oy = 0.;
475 else {
476 /* Case A is below B. */
477 if (y+0.5*h < my+0.5*mh) {
478 *oy += my-(y+h);
479 *oy = MIN(0., *oy);
480 }
481 /* Case A is above B. */
482 else {
483 *oy += (my+mh)-y;
484 *oy = MAX(0., *oy);
485 }
486 }
487}
488
492static void ovr_refresh_uzawa_overlap( float *forces_x, float *forces_y,
493 float x, float y, float w, float h, const vec2** pos,
494 MapOverlayPos** mo, int items, int self,
495 float *offx, float *offy, float *offdx, float *offdy )
496{
497 for (int i=0; i<items; i++) {
498 float mx, my, mw, mh;
499 const float pb2 = ovr_text_pixbuf*2.;
500
501 /* Collisions with spob circles and jp triangles (odd indices). */
502 mw = 2.*mo[i]->radius;
503 mh = mw;
504 mx = pos[i]->x/ovr_res - mw/2.;
505 my = pos[i]->y/ovr_res - mh/2.;
506 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 );
507
508 if (i == self)
509 continue;
510
511 /* Collisions with other texts (even indices) */
512 mw = mo[i]->text_width + pb2;
513 mh = gl_smallFont.h + pb2;
514 mx = pos[i]->x/ovr_res + offdx[i] + offx[i] - ovr_text_pixbuf;
515 my = pos[i]->y/ovr_res + offdy[i] + offy[i] - ovr_text_pixbuf;
516 force_collision( &forces_x[2*items*self+2*i], &forces_y[2*items*self+2*i], x, y, w, h, mx, my, mw, mh );
517 }
518}
519
520void ovr_initAlpha (void)
521{
522 SafeLane *safelanes;
523 for (int i=0; i<array_size(cur_system->jumps); i++) {
524 JumpPoint *jp = &cur_system->jumps[i];
525 if (!jp_isUsable(jp))
526 jp->map_alpha = 0.;
527 else
528 jp->map_alpha = 1.;
529 }
530 for (int i=0; i<array_size(cur_system->spobs); i++) {
531 Spob *pnt = cur_system->spobs[i];
532 if (!spob_isKnown(pnt))
533 pnt->map_alpha = 0.;
534 else
535 pnt->map_alpha = 1.;
536 }
537
538 safelanes = safelanes_get( -1, 0, cur_system );
539 for (int i=0; i<array_size(safelanes); i++) {
540 vec2 *posns[2];
541 if (!ovr_safelaneKnown( &safelanes[i], posns ))
542 safelanes[i].map_alpha = 0.;
543 else
544 safelanes[i].map_alpha = 1.;
545 }
546 array_free( ovr_render_safelanes );
547 ovr_render_safelanes = safelanes;
548
549 ovr_dt = 0.;
550}
551
557void ovr_setOpen( int open )
558{
559 if (open && !ovr_open) {
560 ovr_open = 1;
562 ovr_initAlpha();
563 }
564 else if (ovr_open) {
565 ovr_open = 0;
567 array_free( ovr_render_safelanes );
568 ovr_render_safelanes = NULL;
569 }
570}
571
577void ovr_key( int type )
578{
579 if (type > 0) {
580 if (ovr_open)
581 ovr_setOpen(0);
582 else {
583 ovr_setOpen(1);
584
585 /* Refresh overlay size. */
586 ovr_refresh();
587 ovr_opened = SDL_GetTicks();
588 }
589 }
590 else if (type < 0) {
591 if (SDL_GetTicks() - ovr_opened > 300)
592 ovr_setOpen(0);
593 }
594}
595
596static int ovr_safelaneKnown( SafeLane *sf, vec2 *posns[2] )
597{
598 /* This is a bit asinine, but should be easily replaceable by decent code when we have a System Objects API.
599 * Specifically, a generic pos and isKnown test would clean this up nicely. */
600 int known = 1;
601 for (int j=0; j<2; j++) {
602 Spob *pnt;
603 JumpPoint *jp;
604 switch(sf->point_type[j]) {
605 case SAFELANE_LOC_SPOB:
606 pnt = spob_getIndex( sf->point_id[j] );
607 posns[j] = &pnt->pos;
608 if (!spob_isKnown( pnt ))
609 known = 0;
610 break;
611 case SAFELANE_LOC_DEST_SYS:
613 posns[j] = &jp->pos;
614 if (!jp_isKnown( jp ))
615 known = 0;
616 break;
617 default:
618 ERR( _("Invalid vertex type.") );
619 }
620 }
621 return known;
622}
623
629void ovr_render( double dt )
630{
631 SafeLane *safelanes;
632 double w, h, res;
633 double x,y;
634 double rx,ry, x2,y2, rw,rh;
635
636 /* Must be open. */
637 if (!ovr_open)
638 return;
639
640 /* Player must be alive. */
641 if (player_isFlag( PLAYER_DESTROYED ) || (player.p == NULL))
642 return;
643
644 /* Have to clear for text. */
645 glClear(GL_DEPTH_BUFFER_BIT);
646
647 /* Default values. */
648 w = ovr_bounds.w;
649 h = ovr_bounds.h;
650 res = ovr_res;
651 ovr_dt += dt;
652
653 /* First render the background overlay. */
654 glColour c = { .r=0., .g=0., .b=0., .a= conf.map_overlay_opacity };
655 gl_renderRect( ovr_bounds.l, ovr_bounds.b, w, h, &c );
656
657 /* Render the safe lanes */
658 safelanes = safelanes_get( -1, 0, cur_system );
659 for (int i=0; i<array_size(safelanes); i++) {
660 glColour col;
661 vec2 *posns[2];
662 double r;
663 if (!ovr_safelaneKnown( &safelanes[i], posns ))
664 continue;
665
666 /* Copy over alpha. FIXME: Based on past bug reports, we aren't successfully resetting ovr_render_safelanes
667 * when the lane-set changes. We really want coherency rather than this array_size check. */
668 if (i < array_size(ovr_render_safelanes))
669 safelanes[i].map_alpha = ovr_render_safelanes[i].map_alpha;
670
671 if (safelanes[i].map_alpha < 1.0)
672 safelanes[i].map_alpha = MIN( safelanes[i].map_alpha+OVERLAY_FADEIN*dt, 1.0 );
673
674 if (faction_isPlayerFriend( safelanes[i].faction ))
675 col = cFriend;
676 else if (faction_isPlayerEnemy( safelanes[i].faction ))
677 col = cHostile;
678 else
679 col = cNeutral;
680 col.a = 0.15 * MIN( safelanes[i].map_alpha, 1.0 );
681
682 /* Get positions and stuff. */
683 map_overlayToScreenPos( &x, &y, posns[0]->x, posns[0]->y );
684 map_overlayToScreenPos( &x2, &y2, posns[1]->x, posns[1]->y );
685 rx = x2-x;
686 ry = y2-y;
687 r = atan2( ry, rx );
688 rw = MOD(rx,ry)/2.;
689 rh = 9.;
690
691 /* Render. */
692 glUseProgram(shaders.safelane.program);
693 gl_renderShader( x+rx/2., y+ry/2., rw, rh, r, &shaders.safelane, &col, 1 );
694 }
695 array_free( ovr_render_safelanes );
696 ovr_render_safelanes = safelanes;
697
698 /* Render markers background. */
699 ovr_mrkRenderAll( res, 0 );
700
701 /* Check if player has goto target. */
702 if (player_isFlag(PLAYER_AUTONAV) && (player.autonav == AUTONAV_POS_APPROACH)) {
703 glColour col = cRadar_hilight;
704 col.a = 0.6;
705 map_overlayToScreenPos( &x, &y, player.autonav_pos.x, player.autonav_pos.y );
706 glUseProgram( shaders.selectposition.program );
707 gl_renderShader( x, y, 9., 9., 0., &shaders.selectposition, &col, 1 );
708 gl_printMarkerRaw( &gl_smallFont, x+10., y-gl_smallFont.h/2., &cRadar_hilight, _("TARGET") );
709 }
710
711 /* Render spobs. */
712 for (int i=0; i<array_size(cur_system->spobs); i++) {
713 Spob *pnt = cur_system->spobs[i];
714 if (pnt->map_alpha < 1.0)
715 pnt->map_alpha = MIN( pnt->map_alpha+OVERLAY_FADEIN*dt, 1.0 );
716 if (i != player.p->nav_spob)
717 gui_renderSpob( i, RADAR_RECT, w, h, res, pnt->map_alpha, 1 );
718 }
719 if (player.p->nav_spob > -1)
720 gui_renderSpob( player.p->nav_spob, RADAR_RECT, w, h, res, cur_system->spobs[player.p->nav_spob]->map_alpha, 1 );
721
722 /* Render jump points. */
723 for (int i=0; i<array_size(cur_system->jumps); i++) {
724 JumpPoint *jp = &cur_system->jumps[i];
725 if (jp->map_alpha < 1.0)
726 jp->map_alpha = MIN( jp->map_alpha+OVERLAY_FADEIN*dt, 1.0 );
727 if ((i != player.p->nav_hyperspace) && !jp_isFlag(jp, JP_EXITONLY))
728 gui_renderJumpPoint( i, RADAR_RECT, w, h, res, jp->map_alpha, 1 );
729 }
730 if (player.p->nav_hyperspace > -1)
731 gui_renderJumpPoint( player.p->nav_hyperspace, RADAR_RECT, w, h, res, cur_system->jumps[player.p->nav_hyperspace].map_alpha, 1 );
732
733 /* Render the asteroids */
734 for (int i=0; i<array_size(cur_system->asteroids); i++) {
735 AsteroidAnchor *ast = &cur_system->asteroids[i];
736 for (int j=0; j<ast->nb; j++)
737 gui_renderAsteroid( &ast->asteroids[j], w, h, res, 1 );
738 }
739
740 /* Render pilots. */
741 Pilot *const* pstk = pilot_getAll();
742 int t = 0;
743 for (int i=0; i<array_size(pstk); i++) {
744 if (pstk[i]->id == PLAYER_ID) /* Skip player. */
745 continue;
746 if (pstk[i]->id == player.p->target)
747 t = i;
748 else
749 gui_renderPilot( pstk[i], RADAR_RECT, w, h, res, 1 );
750 }
751
752 /* Stealth rendering. */
753 if (pilot_isFlag( player.p, PILOT_STEALTH )) {
754 double detect;
755 glColour col = {0., 0., 1., 1.};
756
757 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.fbo[2] );
758 glClearColor( 0., 0., 0., 0. );
759 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
760 glBlendEquation( GL_FUNC_ADD );
761 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE );
762 for (int i=0; i<array_size(cur_system->asteroids); i++) {
763 AsteroidAnchor *ast = &cur_system->asteroids[i];
764 detect = vec2_dist2( &player.p->solid->pos, &ast->pos );
765 if (detect < pow2(pilot_sensorRange() * player.p->stats.ew_detect + ast->radius)) {
766 double r;
767 map_overlayToScreenPos( &x, &y, ast->pos.x, ast->pos.y );
768 r = ast->radius / res;
769 glUseProgram( shaders.astaura.program );
770 gl_renderShader( x, y, r, r, 0., &shaders.astaura, &col, 1 );
771 }
772 }
773
774 glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
775 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE );
776 for (int i=0; i<array_size(cur_system->astexclude); i++) {
777 double r;
778 AsteroidExclusion *aexcl = &cur_system->astexclude[i];
779 map_overlayToScreenPos( &x, &y, aexcl->pos.x, aexcl->pos.y );
780 r = aexcl->radius / res;
781 glUseProgram( shaders.astaura.program );
782 gl_renderShader( x, y, r, r, 0., &shaders.astaura, &col, 1 );
783 }
784
785 glBlendEquation( GL_MAX );
786 for (int i=0; i<array_size(cur_system->jumps); i++) {
787 double r;
788 JumpPoint *jp = &cur_system->jumps[i];
789 if (!jp_isUsable(jp) || jp_isFlag(jp,JP_HIDDEN))
790 continue;
791 map_overlayToScreenPos( &x, &y, jp->pos.x, jp->pos.y );
792 r = EW_JUMP_BONUS_RANGE / res;
793 glUseProgram( shaders.astaura.program );
794 gl_renderShader( x, y, r, r, 0., &shaders.astaura, &col, 1 );
795 }
796
797 detect = player.p->ew_stealth / res;
798 col.r = 1.;
799 col.g = 0.;
800 col.b = 0.;
801 for (int i=0; i<array_size(pstk); i++) {
802 double r;
803 if (pilot_isDisabled(pstk[i]))
804 continue;
805 if (areAllies( player.p->faction, pstk[i]->faction ) || pilot_isFriendly(pstk[i]))
806 continue;
807 /* Only show pilots the player can see. */
808 if (!pilot_validTarget( player.p, pstk[i] ))
809 continue;
810 map_overlayToScreenPos( &x, &y, pstk[i]->solid->pos.x, pstk[i]->solid->pos.y );
811 r = detect * pstk[i]->stats.ew_detect; /* Already divided by res */
812 if (r > 0.) {
813 glUseProgram( shaders.stealthaura.program );
814 gl_renderShader( x, y, r, r, 0., &shaders.stealthaura, &col, 1 );
815 }
816 }
817
818 glBlendEquation( GL_FUNC_ADD );
819 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
820 glBindFramebuffer(GL_FRAMEBUFFER, gl_screen.current_fbo);
821 glClearColor( 0., 0., 0., 1. );
822
823 glUseProgram(shaders.stealthoverlay.program);
824 glBindTexture( GL_TEXTURE_2D, gl_screen.fbo_tex[2] );
825
826 glEnableVertexAttribArray( shaders.stealthoverlay.vertex );
827 gl_vboActivateAttribOffset( gl_squareVBO, shaders.stealthoverlay.vertex,
828 0, 2, GL_FLOAT, 0 );
829
830 /* Set shader uniforms. */
831 gl_uniformColor(shaders.stealthoverlay.color, &cWhite);
832 const mat4 ortho = mat4_ortho( 0., 1., 0., 1., 1., -1. );
833 const mat4 I = mat4_identity();
834 gl_uniformMat4(shaders.stealthoverlay.projection, &ortho );
835 gl_uniformMat4(shaders.stealthoverlay.tex_mat, &I );
836
837 /* Draw. */
838 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
839
840 /* Clear state. */
841 glDisableVertexAttribArray( shaders.stealthoverlay.vertex );
842 }
843
844 /* Render markers foreground. */
845 ovr_mrkRenderAll( res, 1 );
846
847 /* Render the targeted pilot */
848 if (t!=0)
849 gui_renderPilot( pstk[t], RADAR_RECT, w, h, res, 1 );
850
851 /* Render the player. */
852 gui_renderPlayer( res, 1 );
853}
854
860static void ovr_mrkRenderAll( double res, int fg )
861{
862 for (int i=0; i<array_size(ovr_markers); i++) {
863 double x, y;
864 ovr_marker_t *mrk = &ovr_markers[i];
865 map_overlayToScreenPos( &x, &y, mrk->x, mrk->y );
866
867 if (!fg) {
868 double r;
869 const glColour highlighted = COL_ALPHA( cRadar_hilight, 0.3 );
870
871 switch (mrk->type) {
872 case OVR_MARKER_POINT:
873 glUseProgram( shaders.hilight.program );
874 glUniform1f( shaders.hilight.dt, ovr_dt );
875 gl_renderShader( x, y, 13., 13., 0., &shaders.hilight, &highlighted, 1 );
876 break;
877
878 case OVR_MARKER_CIRCLE:
879 r = MAX( mrk->u.circle.r / res, 13. ); /* Don't allow to be smaller than a "point" */
880 glUseProgram( shaders.hilight_circle.program );
881 glUniform1f( shaders.hilight_circle.dt, ovr_dt );
882 gl_renderShader( x, y, r, r, 0., &shaders.hilight_circle, &highlighted, 1 );
883 break;
884 }
885 }
886
887 if (fg && mrk->text != NULL) {
888 double r;
889 switch (mrk->type) {
890 case OVR_MARKER_POINT:
891 gl_printMarkerRaw( &gl_smallFont, x+10., y-gl_smallFont.h/2., &cRadar_hilight, mrk->text );
892 break;
893
894 case OVR_MARKER_CIRCLE:
895 r = MAX( mrk->u.circle.r / res, 13. ); /* Don't allow to be smaller than a "point" */
896 gl_printMarkerRaw( &gl_smallFont, x+r*M_SQRT1_2+7., y-r*M_SQRT1_2-gl_smallFont.h/2., &cRadar_hilight, mrk->text );
897 break;
898 }
899 }
900 }
901}
902
906void ovr_mrkFree (void)
907{
908 /* Clear markers. */
909 ovr_mrkClear();
910
911 /* Free array. */
912 array_free( ovr_markers );
913 ovr_markers = NULL;
914 array_free( ovr_render_safelanes );
915 ovr_render_safelanes = NULL;
916}
917
921void ovr_mrkClear (void)
922{
923 for (int i=0; i<array_size(ovr_markers); i++)
924 ovr_mrkCleanup( &ovr_markers[i] );
925 array_erase( &ovr_markers, array_begin(ovr_markers), array_end(ovr_markers) );
926}
927
933static void ovr_mrkCleanup( ovr_marker_t *mrk )
934{
935 free( mrk->text );
936 mrk->text = NULL;
937}
938
944static ovr_marker_t *ovr_mrkNew (void)
945{
946 ovr_marker_t *mrk;
947
948 if (ovr_markers == NULL)
949 ovr_markers = array_create( ovr_marker_t );
950
951 mrk = &array_grow( &ovr_markers );
952 memset( mrk, 0, sizeof( ovr_marker_t ) );
953 mrk->id = ++mrk_idgen;
954 mrk->refcount = 1;
955 return mrk;
956}
957
966unsigned int ovr_mrkAddPoint( const char *text, double x, double y )
967{
968 ovr_marker_t *mrk;
969
970 /* Check existing ones first. */
971 for (int i=0; i<array_size(ovr_markers); i++) {
972 mrk = &ovr_markers[i];
973
974 if (mrk->type != OVR_MARKER_POINT)
975 continue;
976
977 if (((text==NULL) && (mrk->text!=NULL)) ||
978 ((text!=NULL) && ((mrk->text==NULL) || strcmp(text,mrk->text)!=0)))
979 continue;
980
981 if (hypotf( x-mrk->x, y-mrk->y ) > 1e-3)
982 continue;
983
984 /* Found same marker already! */
985 mrk->refcount++;
986 return mrk->id;
987 }
988
989 /* Create new one. */
990 mrk = ovr_mrkNew();
991 mrk->type = OVR_MARKER_POINT;
992 if (text != NULL)
993 mrk->text = strdup( text );
994 mrk->x = x;
995 mrk->y = y;
996
997 return mrk->id;
998}
999
1009unsigned int ovr_mrkAddCircle( const char *text, double x, double y, double r )
1010{
1011 ovr_marker_t *mrk;
1012
1013 /* Check existing ones first. */
1014 for (int i=0; i<array_size(ovr_markers); i++) {
1015 mrk = &ovr_markers[i];
1016
1017 if (mrk->type != OVR_MARKER_CIRCLE)
1018 continue;
1019
1020 if (((text==NULL) && (mrk->text!=NULL)) ||
1021 ((text!=NULL) && ((mrk->text==NULL) || strcmp(text,mrk->text)!=0)))
1022 continue;
1023
1024 if ((mrk->u.circle.r-r) > 1e-3 || hypotf( x-mrk->x, y-mrk->y ) > 1e-3)
1025 continue;
1026
1027 /* Found same marker already! */
1028 mrk->refcount++;
1029 return mrk->id;
1030 }
1031
1032 /* Create new one. */
1033 mrk = ovr_mrkNew();
1034 mrk->type = OVR_MARKER_CIRCLE;
1035 if (text != NULL)
1036 mrk->text = strdup( text );
1037 mrk->x = x;
1038 mrk->y = y;
1039 mrk->u.circle.r = r;
1040
1041 return mrk->id;
1042}
1043
1049void ovr_mrkRm( unsigned int id )
1050{
1051 for (int i=0; i<array_size(ovr_markers); i++) {
1052 ovr_marker_t *m = &ovr_markers[i];
1053 if (id != m->id)
1054 continue;
1055
1056 m->refcount--;
1057 if (m->refcount > 0)
1058 return;
1059
1060 ovr_mrkCleanup( m );
1061 array_erase( &ovr_markers, &m[0], &m[1] );
1062 break;
1063 }
1064}
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:1009
int faction_isPlayerFriend(int f)
Gets whether or not the player is a friend of the faction.
Definition: faction.c:997
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition: faction.c:1222
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:960
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:672
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:1344
void gui_renderPlayer(double res, int overlay)
Renders the player cross on the radar or whatever.
Definition: gui.c:1239
void gui_renderAsteroid(const Asteroid *a, double w, double h, double res, int overlay)
Renders an asteroid in the GUI radar.
Definition: gui.c:1172
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:1446
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:1094
void input_mouseShow(void)
Shows the mouse.
Definition: input.c:373
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:1237
void input_mouseHide(void)
Hides the mouse.
Definition: input.c:382
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.
Definition: opengl_render.c:90
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:228
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:226
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition: pilot.c:681
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition: pilot.c:83
double pilot_sensorRange(void)
Returns the default sensor range for the current system.
Definition: pilot_ew.c:212
Player_t player
Definition: player.c:73
static const double c[]
Definition: rng.c:264
static const double b[]
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:187
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition: space.c:944
const char * jump_getSymbol(const JumpPoint *jp)
Gets the jump point symbol.
Definition: space.c:1212
const char * spob_getSymbol(const Spob *p)
Gets the spob symbol.
Definition: space.c:1853
JumpPoint * jump_getTarget(const StarSystem *target, const StarSystem *sys)
Less safe version of jump_get that works with pointers.
Definition: space.c:1198
StarSystem * cur_system
Definition: space.c:105
Spob * spob_getIndex(int ind)
Gets spob by index.
Definition: space.c:1038
const char * spob_name(const Spob *p)
Gets the translated name of a spob.
Definition: space.c:1705
glTexture * jumppoint_gfx
Definition: space.c:106
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:121
Saves the layout decisions from positioning labeled objects on the overlay.
Definition: space.h:56
float text_offx
Definition: space.h:58
float text_width
Definition: space.h:60
float radius
Definition: space.h:57
float text_offy
Definition: space.h:59
The representation of an in-game pilot.
Definition: pilot.h:210
Solid * solid
Definition: pilot.h:220
ShipStats stats
Definition: pilot.h:286
double ew_stealth
Definition: pilot.h:266
int nav_hyperspace
Definition: pilot.h:337
int faction
Definition: pilot.h:215
unsigned int target
Definition: pilot.h:334
int nav_spob
Definition: pilot.h:336
double map_overlay_opacity
Definition: conf.h:125
Pilot * p
Definition: player.h:101
int autonav
Definition: player.h:107
vec2 autonav_pos
Definition: player.h:108
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:22
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition: space.h:88
double radius
Definition: space.h:94
double map_alpha
Definition: space.h:129
vec2 pos
Definition: space.h:93
MapOverlayPos mo
Definition: space.h:128
int h
Definition: font.h:18
GLuint fbo_tex[OPENGL_NUM_FBOS]
Definition: opengl.h:68
GLuint fbo[OPENGL_NUM_FBOS]
Definition: opengl.h:67
GLuint current_fbo
Definition: opengl.h:66
double sw
Definition: opengl_tex.h:44
Definition: mat4.h:10
An overlay map marker.
Definition: map_overlay.c:44
ovr_marker_type_t type
Definition: map_overlay.c:47
char * text
Definition: map_overlay.c:46
union ovr_marker_t::@16 u
unsigned int id
Definition: map_overlay.c:45
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33