9#include "map_overlay.h"
26static const double OVERLAY_FADEIN = 1.0/3.0;
33typedef struct MapOverlayRadiusConstraint_ {
39typedef enum ovr_marker_type_e {
47typedef struct ovr_marker_s {
61static unsigned int mrk_idgen = 0;
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.;
72typedef struct OverlayBounds_s {
85static int autonav_pos = 0;
86static vec2 autonav_pos_v;
91static const vec2 **ovr_refresh_pos = NULL;
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,
101static void ovr_refresh_uzawa_overlap(
float *forces_x,
float *forces_y,
102 float x,
float y,
float w,
float h,
const vec2** pos,
104 float *offx,
float *offy,
float *offdx,
float *offdy );
106static int ovr_safelaneKnown(
SafeLane *sf,
vec2 *posns[2] );
107static void map_overlayToScreenPos(
double *ox,
double *oy,
double x,
double y );
109static void ovr_mrkRenderAll(
double res,
int fg );
124static void ovr_boundsUpdate (
void)
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;
140void ovr_boundsSet(
double top,
double right,
double bottom,
double left )
143 ovr_bounds.r = right;
144 ovr_bounds.b = bottom;
155void ovr_center(
double *x,
double *y )
164static void map_overlayToScreenPos(
double *ox,
double *oy,
double x,
double y )
166 *ox = ovr_bounds.x + x / ovr_res;
167 *oy = ovr_bounds.y + y / ovr_res;
173int ovr_input( SDL_Event *event )
179 if (event->type != SDL_MOUSEBUTTONDOWN)
183 if (player_isFlag(PLAYER_DESTROYED) || (
player.
p == NULL))
187 if (pilot_isFlag(
player.
p, PILOT_DEAD))
191 if (event->button.button != SDL_BUTTON_LEFT &&
192 event->button.button != SDL_BUTTON_RIGHT)
196 mx =
event->button.x;
197 my =
event->button.y;
201 x = ((double)mx - ovr_bounds.x) * ovr_res;
202 y = ((double)my - ovr_bounds.y) * ovr_res;
204 return input_clickPos( event, x, y, 1., 10. * ovr_res, 15. * ovr_res );
212void ovr_refresh (
void)
215 int n, items, jumpitems, spobitems;
218 char buf[STRMAX_SHORT];
226 free( ovr_refresh_mo );
228 free( ovr_refresh_pos );
236 pos = calloc( n,
sizeof(
vec2*) );
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;
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))
256 snprintf( buf,
sizeof(buf),
"%s%s",
jump_getSymbol(jp), sys_isKnown(jp->target) ? _(jp->target->name) : _(
"Unknown") );
257 pos[items] = &jp->pos;
268 if (!spob_isKnown(pnt))
272 pos[items] = &pnt->
pos;
273 mo[items] = &pnt->
mo;
280 for (
int i=0; i<
array_size(ovr_markers); i++) {
288 mo[items] = &mrk->
mo;
290 case OVR_MARKER_POINT:
291 pos[items] = &mrk->
pos;
294 case OVR_MARKER_CIRCLE:
295 pos[items] = &mrk->
u.circle.textpos;
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++) {
309 if (autonav_pos && (i==0))
311 else if (i<jumpitems)
313 else if (i<spobitems)
317 mo[i]->
radius =
MAX( 2.+mo[i]->radius / ovr_res, rm );
321 ovr_optimizeLayout( items, pos, mo );
324 ovr_refresh_pos = pos;
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;
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;
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)
357 for (
int iter=0; (iter<max_iters) && (
array_size(fits) > 0); iter++) {
358 float shrink_factor = 0.;
359 memset( must_shrink, 0, items );
365 shrink_factor =
MAX( shrink_factor, r - FLT_EPSILON );
366 must_shrink[fits[i].
i] = must_shrink[fits[i].j] = 1;
369 for (
int i=0; i<items; i++)
371 mo[i]->
radius *= shrink_factor;
378 for (
int i=0; i<items; i++)
379 mo[i]->radius =
MAX( mo[i]->radius, 4. );
382 off_0x = calloc( items,
sizeof(
float) );
383 off_0y = calloc( items,
sizeof(
float) );
386 for (
int i=0; i<items; i++) {
390 x = pos[i]->
x/ovr_res - ovr_text_pixbuf;
391 y = pos[i]->
y/ovr_res - ovr_text_pixbuf;
402 for (
int k=0; k<4; k++) {
406 for (
int j=0; j<items; j++) {
410 mx = pos[j]->
x/ovr_res - mw/2.;
411 my = pos[j]->
y/ovr_res - mh/2.;
413 force_collision( &fx, &fy, x+tx[k], y+ty[k], w, h, mx, my, mw, mh );
418 if (k == 0 || val < best) {
443 forces_xa = calloc( 2*items*items,
sizeof(
float) );
444 forces_ya = calloc( 2*items*items,
sizeof(
float) );
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) );
453 for (
int iter=0; iter<max_iters; iter++) {
455 for (
int i=0; i<items; i++) {
456 cx = pos[i]->
x / ovr_res;
457 cy = pos[i]->
y / ovr_res;
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,
465 pos, mo, items, i, off_0x, off_0y, off_dx, off_dy );
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];
475 old_bx = off_buffx[i];
476 old_by = off_buffy[i];
479 off_buffx[i] = kx * sx;
480 off_buffy[i] = ky * sy;
482 val =
MAX( val,
ABS(old_bx-off_buffx[i]) +
ABS(old_by-off_buffy[i]) );
486 for (
int i=0; i<items; i++) {
487 off_dx[i] = off_buffx[i];
488 off_dy[i] = off_buffy[i];
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];
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 )
521 if ((y+h < my+ovr_text_pixbuf) || (y+ovr_text_pixbuf > my+mh))
525 if (x+0.5*w < mx+0.5*mw) {
537 if ((x+w < mx+ovr_text_pixbuf) || (x+ovr_text_pixbuf > mx+mw))
541 if (y+0.5*h < my+0.5*mh) {
556static void ovr_refresh_uzawa_overlap(
float *forces_x,
float *forces_y,
557 float x,
float y,
float w,
float h,
const vec2** pos,
559 float *offx,
float *offy,
float *offdx,
float *offdy )
561 for (
int i=0; i<items; i++) {
562 float mx, my, mw, mh;
563 const float pb2 = ovr_text_pixbuf*2.;
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 );
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 );
584void ovr_initAlpha (
void)
589 if (!jp_isUsable(jp))
596 if (!spob_isKnown(pnt))
605 if (!ovr_safelaneKnown( &safelanes[i], posns ))
611 ovr_render_safelanes = safelanes;
618 il_create( &ovr_qtquery, 1 );
624 il_destroy( &ovr_qtquery );
632void ovr_setOpen(
int open )
634 if (open && !ovr_open) {
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;
656void ovr_key(
int type )
666 ovr_opened = SDL_GetTicks();
670 if (SDL_GetTicks() - ovr_opened > 300)
675static int ovr_safelaneKnown(
SafeLane *sf,
vec2 *posns[2] )
680 for (
int j=0; j<2; j++) {
684 case SAFELANE_LOC_SPOB:
686 posns[j] = &pnt->
pos;
687 if (!spob_isKnown( pnt ))
690 case SAFELANE_LOC_DEST_SYS:
693 if (!jp_isKnown( jp ))
697 ERR( _(
"Invalid vertex type.") );
708void ovr_render(
double dt )
713 double rx,ry, x2,y2, rw,rh;
720 if (player_isFlag( PLAYER_DESTROYED ) || (
player.
p == NULL))
724 glClear(GL_DEPTH_BUFFER_BIT);
742 if (!ovr_safelaneKnown( &safelanes[i], posns ))
750 if (safelanes[i].map_alpha < 1.0)
751 safelanes[i].
map_alpha =
MIN( safelanes[i].map_alpha+OVERLAY_FADEIN*dt, 1.0 );
759 col.a = 0.15 *
MIN( safelanes[i].map_alpha, 1.0 );
762 map_overlayToScreenPos( &x, &y, posns[0]->x, posns[0]->y );
763 map_overlayToScreenPos( &x2, &y2, posns[1]->x, posns[1]->y );
771 glUseProgram(shaders.safelane.program);
772 gl_renderShader( x+rx/2., y+ry/2., rw, rh, r, &shaders.safelane, &col, 1 );
775 ovr_render_safelanes = safelanes;
778 ovr_mrkRenderAll( res, 0 );
782 glColour col = cRadar_hilight;
784 map_overlayToScreenPos( &x, &y, autonav_pos_v.
x, autonav_pos_v.
y );
785 glUseProgram( shaders.selectposition.program );
804 if (jp->map_alpha < 1.0)
805 jp->map_alpha =
MIN( jp->map_alpha+OVERLAY_FADEIN*dt, 1.0 );
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++) {
831 if (pstk[i]->
id == PLAYER_ID)
840 if (pilot_isFlag(
player.
p, PILOT_STEALTH )) {
842 glColour col = {0., 0., 1., 1.};
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 );
854 map_overlayToScreenPos( &x, &y, ast->
pos.
x, ast->
pos.
y );
856 glUseProgram( shaders.astaura.program );
861 glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
862 glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE );
866 map_overlayToScreenPos( &x, &y, aexcl->
pos.
x, aexcl->
pos.
y );
868 glUseProgram( shaders.astaura.program );
872 glBlendEquation( GL_MAX );
876 if (!jp_isUsable(jp) || jp_isFlag(jp,JP_HIDDEN))
878 map_overlayToScreenPos( &x, &y, jp->pos.x, jp->pos.y );
879 r = EW_JUMP_BONUS_RANGE / res;
880 glUseProgram( shaders.astaura.program );
890 if (pilot_isDisabled(pstk[i]))
897 map_overlayToScreenPos( &x, &y, pstk[i]->solid.pos.x, pstk[i]->solid.pos.y );
900 glUseProgram( shaders.stealthaura.program );
905 glBlendEquation( GL_FUNC_ADD );
906 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
908 glClearColor( 0., 0., 0., 1. );
910 glUseProgram(shaders.stealthoverlay.program);
913 glEnableVertexAttribArray( shaders.stealthoverlay.vertex );
918 gl_uniformColour(shaders.stealthoverlay.colour, &cWhite);
921 gl_uniformMat4(shaders.stealthoverlay.projection, &ortho );
922 gl_uniformMat4(shaders.stealthoverlay.tex_mat, &I );
925 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
928 glDisableVertexAttribArray( shaders.stealthoverlay.vertex );
932 ovr_mrkRenderAll( res, 1 );
947static void ovr_mrkRenderAll(
double res,
int fg )
949 for (
int i=0; i<
array_size(ovr_markers); i++) {
952 map_overlayToScreenPos( &x, &y, mrk->
pos.
x, mrk->
pos.
y );
956 const glColour highlighted = COL_ALPHA( cRadar_hilight, 0.3 );
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 );
965 case OVR_MARKER_CIRCLE:
966 r =
MAX( mrk->
u.circle.r / res, 13. );
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 );
974 if (fg && mrk->
text != NULL) {
976 case OVR_MARKER_POINT:
980 case OVR_MARKER_CIRCLE:
981 map_overlayToScreenPos( &x, &y, mrk->
u.circle.textpos.
x, mrk->
u.circle.textpos.
y );
992void ovr_mrkFree (
void)
1001 ovr_render_safelanes = NULL;
1007void ovr_mrkClear (
void)
1009 for (
int i=0; i<
array_size(ovr_markers); i++)
1010 ovr_mrkCleanup( &ovr_markers[i] );
1038 if (ovr_markers == NULL)
1043 mrk->
id = ++mrk_idgen;
1056unsigned int ovr_mrkAddPoint(
const char *text,
double x,
double y )
1061 for (
int i=0; i<
array_size(ovr_markers); i++) {
1062 mrk = &ovr_markers[i];
1064 if (mrk->
type != OVR_MARKER_POINT)
1067 if (((text==NULL) && (mrk->
text!=NULL)) ||
1068 ((text!=NULL) && ((mrk->
text==NULL) || strcmp(text,mrk->
text)!=0)))
1071 if (hypotf( x-mrk->
pos.
x, y-mrk->
pos.
y ) > 1e-3)
1081 mrk->
type = OVR_MARKER_POINT;
1083 mrk->
text = strdup( text );
1084 vec2_cset( &mrk->
pos, x, y );
1102unsigned int ovr_mrkAddCircle(
const char *text,
double x,
double y,
double r )
1107 for (
int i=0; i<
array_size(ovr_markers); i++) {
1108 mrk = &ovr_markers[i];
1110 if (mrk->
type != OVR_MARKER_CIRCLE)
1113 if (((text==NULL) && (mrk->
text!=NULL)) ||
1114 ((text!=NULL) && ((mrk->
text==NULL) || strcmp(text,mrk->
text)!=0)))
1117 if ((mrk->
u.circle.r-r) > 1e-3 || hypotf( x-mrk->
pos.
x, y-mrk->
pos.
y ) > 1e-3)
1127 mrk->
type = OVR_MARKER_CIRCLE;
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 );
1146void ovr_mrkRm(
unsigned int id )
1148 for (
int i=0; i<
array_size(ovr_markers); i++) {
1157 ovr_mrkCleanup( m );
1167void ovr_autonavPos(
double x,
double y )
1170 vec2_cset( &autonav_pos_v, x, y );
1177void ovr_autonavClear (
void)
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
int faction_isPlayerEnemy(int f)
Gets whether or not the player is an enemy of the faction.
int faction_isPlayerFriend(int f)
Gets whether or not the player is a friend of the faction.
int areAllies(int a, int b)
Checks whether two factions are allies or not.
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
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.
void gui_renderSpob(int ind, RadarShape shape, double w, double h, double res, double alpha, int overlay)
Draws the spobs in the minimap.
void gui_renderPlayer(double res, int overlay)
Renders the player cross on the radar or whatever.
void gui_renderAsteroid(const Asteroid *a, double w, double h, double res, int overlay)
Renders an asteroid in the GUI radar.
void gui_renderJumpPoint(int ind, RadarShape shape, double w, double h, double res, double alpha, int overlay)
Renders a jump point on the minimap.
void gui_renderPilot(const Pilot *p, RadarShape shape, double w, double h, double res, int overlay)
Renders a pilot in the GUI radar.
mat4 mat4_identity(void)
Creates an identity matrix.
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Header file with generic functions and naev-specifics.
void gl_windowToScreenPos(int *sx, int *sy, int wx, int wy)
Translates the window position to screen position.
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.
int pilot_validTarget(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid target for another pilot.
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
double pilot_sensorRange(void)
Returns the default sensor range for the current system.
SafeLane * safelanes_get(int faction, int standing, const StarSystem *system)
Gets a set of safelanes for a faction and system.
StarSystem * system_getIndex(int id)
Get the system by its index.
const char * jump_getSymbol(const JumpPoint *jp)
Gets the jump point symbol.
const char * spob_getSymbol(const Spob *p)
Gets the spob symbol.
JumpPoint * jump_getTarget(const StarSystem *target, const StarSystem *sys)
Less safe version of jump_get that works with pointers.
Spob * spob_getIndex(int ind)
Gets spob by index.
const char * spob_name(const Spob *p)
Gets the translated name of a spob.
glTexture * jumppoint_gfx
Represents an asteroid field anchor.
Represents an asteroid exclusion zone.
Represents a single asteroid.
Saves the layout decisions from positioning labeled objects on the overlay.
The representation of an in-game pilot.
double map_overlay_opacity
Describes a safe lane, patrolled by a faction, within a system.
SafeLaneLocType point_type[2]
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
GLuint fbo_tex[OPENGL_NUM_FBOS]
GLuint fbo[OPENGL_NUM_FBOS]
union ovr_marker_t::@26 u