1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file background.c
7  *
8  * @brief Handles displaying backgrounds.
9  */
10 
11 #include "background.h"
12 
13 #include "naev.h"
14 
15 #include "nxml.h"
16 
17 #include "opengl.h"
18 #include "log.h"
19 #include "player.h"
20 #include "conf.h"
21 #include "rng.h"
22 #include "pause.h"
23 #include "array.h"
24 #include "ndata.h"
25 #include "nlua.h"
26 #include "nluadef.h"
27 #include "nlua_tex.h"
28 #include "nlua_col.h"
29 #include "nlua_bkg.h"
30 #include "camera.h"
31 #include "nebula.h"
32 #include "nstring.h"
33 
34 
35 /**
36  * @brief Represents a background image like say a Nebula.
37  */
38 typedef struct background_image_s {
39    unsigned int id; /**< Background id. */
40    glTexture *image; /**< Image to display. */
41    double x; /**< X center of the image. */
42    double y; /**< Y center of the image. */
43    double move; /**< How many pixels it moves for each pixel the player moves. */
44    double scale; /**< How the image should be scaled. */
45    glColour col; /**< Colour to use. */
46 } background_image_t;
47 static background_image_t *bkg_image_arr_bk = NULL; /**< Background image array to display (behind stars). */
48 static background_image_t *bkg_image_arr_ft = NULL; /**< Background image array to display (in front of stars). */
49 
50 
51 static unsigned int bkg_idgen = 0; /**< ID generator for backgrounds. */
52 
53 
54 /**
55  * @brief Backgrounds.
56  */
57 static nlua_env bkg_cur_env = LUA_NOREF; /**< Current Lua state. */
58 static nlua_env bkg_def_env = LUA_NOREF; /**< Default Lua state. */
59 
60 
61 /*
62  * Background stars.
63  */
64 #define STAR_BUF     250 /**< Area to leave around screen for stars, more = less repetition */
65 static gl_vbo *star_vertexVBO = NULL; /**< Star Vertex VBO. */
66 static gl_vbo *star_colourVBO = NULL; /**< Star Colour VBO. */
67 static GLfloat *star_vertex = NULL; /**< Vertex of the stars. */
68 static GLfloat *star_colour = NULL; /**< Brightness of the stars. */
69 static unsigned int nstars = 0; /**< Total stars. */
70 static unsigned int mstars = 0; /**< Memory stars are taking. */
71 static GLfloat star_x = 0.; /**< Star X movement. */
72 static GLfloat star_y = 0.; /**< Star Y movement. */
73 
74 
75 /*
76  * Prototypes.
77  */
78 static void background_renderImages( background_image_t *bkg_arr );
79 static nlua_env background_create( const char *path );
80 static void background_clearCurrent (void);
81 static void background_clearImgArr( background_image_t **arr );
82 /* Sorting. */
83 static int bkg_compare( const void *p1, const void *p2 );
84 static void bkg_sort( background_image_t *arr );
85 
86 
87 /**
88  * @brief Initializes background stars.
89  *
90  *    @param n Number of stars to add (stars per 800x640 screen).
91  */
background_initStars(int n)92 void background_initStars( int n )
93 {
94    unsigned int i;
95    GLfloat w, h, hw, hh;
96    double size;
97 
98    /* Calculate size. */
99    size  = SCREEN_W*SCREEN_H+STAR_BUF*STAR_BUF;
100    size /= pow2(conf.zoom_far);
101 
102    /* Calculate star buffer. */
103    w  = (SCREEN_W + 2.*STAR_BUF);
104    w += conf.zoom_stars * (w / conf.zoom_far - 1.);
105    h  = (SCREEN_H + 2.*STAR_BUF);
106    h += conf.zoom_stars * (h / conf.zoom_far - 1.);
107    hw = w / 2.;
108    hh = h / 2.;
109 
110    /* Calculate stars. */
111    size  *= n;
112    nstars = (unsigned int)(size/(800.*600.));
113 
114    if (mstars < nstars) {
115       /* Create data. */
116       star_vertex = realloc( star_vertex, nstars * sizeof(GLfloat) * 4 );
117       star_colour = realloc( star_colour, nstars * sizeof(GLfloat) * 8 );
118       mstars = nstars;
119    }
120    for (i=0; i < nstars; i++) {
121       /* Set the position. */
122       star_vertex[4*i+0] = RNGF()*w - hw;
123       star_vertex[4*i+1] = RNGF()*h - hh;
124       star_vertex[4*i+2] = 0.;
125       star_vertex[4*i+3] = 0.;
126       /* Set the colour. */
127       star_colour[8*i+0] = 1.;
128       star_colour[8*i+1] = 1.;
129       star_colour[8*i+2] = 1.;
130       star_colour[8*i+3] = RNGF()*0.6 + 0.2;
131       star_colour[8*i+4] = 1.;
132       star_colour[8*i+5] = 1.;
133       star_colour[8*i+6] = 1.;
134       star_colour[8*i+7] = 0.;
135    }
136 
137    /* Destroy old VBO. */
138    if (star_vertexVBO != NULL) {
139       gl_vboDestroy( star_vertexVBO );
140       star_vertexVBO = NULL;
141    }
142    if (star_colourVBO != NULL) {
143       gl_vboDestroy( star_colourVBO );
144       star_colourVBO = NULL;
145    }
146 
147    /* Create now VBO. */
148    star_vertexVBO = gl_vboCreateStream(
149          nstars * sizeof(GLfloat) * 4, star_vertex );
150    star_colourVBO = gl_vboCreateStatic(
151          nstars * sizeof(GLfloat) * 8, star_colour );
152 }
153 
154 
155 /**
156  * @brief Displaces the stars, useful with camera.
157  */
background_moveStars(double x,double y)158 void background_moveStars( double x, double y )
159 {
160    star_x += (GLfloat) x;
161    star_y += (GLfloat) y;
162 
163    /* Puffs also need moving. */
164    nebu_movePuffs( x, y );
165 }
166 
167 
168 /**
169  * @brief Renders the starry background.
170  *
171  * This could really benefit from OpenCL directly. It would probably give a great
172  *  speed up, although we'll consider it when we get a runtime linking OpenCL
173  *  framework someday.
174  *
175  *    @param dt Current delta tick.
176  */
background_renderStars(const double dt)177 void background_renderStars( const double dt )
178 {
179    (void) dt;
180    unsigned int i;
181    GLfloat hh, hw, h, w;
182    GLfloat x, y, m, b;
183    GLfloat brightness;
184    double z;
185    double sx, sy;
186    int shade_mode;
187    int j, n;
188 
189 
190    /*
191     * gprof claims it's the slowest thing in the game!
192     */
193 
194    /* Do some scaling for now. */
195    z = cam_getZoom();
196    z = 1. * (1. - conf.zoom_stars) + z * conf.zoom_stars;
197    gl_matrixPush();
198       gl_matrixTranslate( SCREEN_W/2., SCREEN_H/2. );
199       gl_matrixScale( z, z );
200 
201    if (!paused && (player.p != NULL) && !player_isFlag(PLAYER_DESTROYED) &&
202          !player_isFlag(PLAYER_CREATING)) { /* update position */
203 
204       /* Calculate some dimensions. */
205       w  = (SCREEN_W + 2.*STAR_BUF);
206       w += conf.zoom_stars * (w / conf.zoom_far - 1.);
207       h  = (SCREEN_H + 2.*STAR_BUF);
208       h += conf.zoom_stars * (h / conf.zoom_far - 1.);
209       hw = w/2.;
210       hh = h/2.;
211 
212       /* Calculate multiple updates in the case the ship is moving really ridiculously fast. */
213       if ((star_x > SCREEN_W) || (star_y > SCREEN_H)) {
214          sx = ceil( star_x / SCREEN_W );
215          sy = ceil( star_y / SCREEN_H );
216          n  = MAX( sx, sy );
217          star_x /= (double)n;
218          star_y /= (double)n;
219       }
220       else
221          n = 1;
222 
223       /* Calculate new star positions. */
224       for (j=0; j < n; j++) {
225          for (i=0; i < nstars; i++) {
226 
227             /* Calculate new position */
228             b = 1./(9. - 10.*star_colour[8*i+3]);
229             star_vertex[4*i+0] = star_vertex[4*i+0] + star_x*b;
230             star_vertex[4*i+1] = star_vertex[4*i+1] + star_y*b;
231 
232             /* check boundaries */
233             if (star_vertex[4*i+0] > hw)
234                star_vertex[4*i+0] -= w;
235             else if (star_vertex[4*i+0] < -hw)
236                star_vertex[4*i+0] += w;
237             if (star_vertex[4*i+1] > hh)
238                star_vertex[4*i+1] -= h;
239             else if (star_vertex[4*i+1] < -hh)
240                star_vertex[4*i+1] += h;
241          }
242       }
243 
244       /* Upload the data. */
245       gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex );
246    }
247 
248    /* Decide on shade mode. */
249    shade_mode = 0;
250    if ((player.p != NULL) && !player_isFlag(PLAYER_DESTROYED) &&
251          !player_isFlag(PLAYER_CREATING)) {
252 
253       if (pilot_isFlag(player.p,PILOT_HYPERSPACE)) { /* hyperspace fancy effects */
254 
255          glShadeModel(GL_SMOOTH);
256          shade_mode = 1;
257 
258          /* lines get longer the closer we are to finishing the jump */
259          m  = MAX( 0, HYPERSPACE_STARS_BLUR-player.p->ptimer );
260          m /= HYPERSPACE_STARS_BLUR;
261          m *= HYPERSPACE_STARS_LENGTH;
262          x = m*cos(VANGLE(player.p->solid->vel));
263          y = m*sin(VANGLE(player.p->solid->vel));
264       }
265       else if (dt_mod * VMOD(player.p->solid->vel) > 500. ){
266 
267          glShadeModel(GL_SMOOTH);
268          shade_mode = 1;
269 
270          /* Very short lines tend to flicker horribly. A stock Llama at 2x
271           * speed just so happens to make very short lines. A 5px minimum
272           * is long enough to (mostly) alleviate the flickering.
273           */
274          m = MAX( 5, dt_mod*VMOD(player.p->solid->vel)/25. - 20 );
275          x = m*cos(VANGLE(player.p->solid->vel));
276          y = m*sin(VANGLE(player.p->solid->vel));
277       }
278 
279       if (shade_mode) {
280          /* Generate lines. */
281          for (i=0; i < nstars; i++) {
282             brightness = star_colour[8*i+3];
283             star_vertex[4*i+2] = star_vertex[4*i+0] + x*brightness;
284             star_vertex[4*i+3] = star_vertex[4*i+1] + y*brightness;
285          }
286 
287          /* Upload new data. */
288          gl_vboSubData( star_vertexVBO, 0, nstars * 4 * sizeof(GLfloat), star_vertex );
289       }
290    }
291 
292    /* Render. */
293    gl_vboActivate( star_vertexVBO, GL_VERTEX_ARRAY, 2, GL_FLOAT, 2 * sizeof(GLfloat) );
294    gl_vboActivate( star_colourVBO, GL_COLOR_ARRAY,  4, GL_FLOAT, 4 * sizeof(GLfloat) );
295    if (shade_mode) {
296       glDrawArrays( GL_LINES, 0, nstars );
297       glDrawArrays( GL_POINTS, 0, nstars ); /* This second pass is when the lines are very short that they "lose" intensity. */
298       glShadeModel(GL_FLAT);
299    }
300    else
301       glDrawArrays( GL_POINTS, 0, nstars );
302 
303    /* Clear star movement. */
304    star_x = 0.;
305    star_y = 0.;
306 
307    /* Disable vertex array. */
308    gl_vboDeactivate();
309 
310    /* Pop matrix. */
311    gl_matrixPop();
312 
313    /* Check for errors. */
314    gl_checkErr();
315 }
316 
317 
318 /**
319  * @brief Render the background.
320  */
background_render(double dt)321 void background_render( double dt )
322 {
323    background_renderImages( bkg_image_arr_bk );
324    background_renderStars(dt);
325    background_renderImages( bkg_image_arr_ft );
326 }
327 
328 
329 /**
330  * @brief Compares two different backgrounds and sorts them.
331  */
bkg_compare(const void * p1,const void * p2)332 static int bkg_compare( const void *p1, const void *p2 )
333 {
334    background_image_t *bkg1, *bkg2;
335 
336    bkg1 = (background_image_t*) p1;
337    bkg2 = (background_image_t*) p2;
338 
339    if (bkg1->move < bkg2->move)
340       return -1;
341    else if (bkg1->move > bkg2->move)
342       return +1;
343    return  0;
344 }
345 
346 
347 /**
348  * @brief Sorts the backgrounds by movement.
349  */
bkg_sort(background_image_t * arr)350 static void bkg_sort( background_image_t *arr )
351 {
352    qsort( arr, array_size(arr), sizeof(background_image_t), bkg_compare );
353 }
354 
355 
356 /**
357  * @brief Adds a new background image.
358  */
background_addImage(glTexture * image,double x,double y,double move,double scale,const glColour * col,int foreground)359 unsigned int background_addImage( glTexture *image, double x, double y,
360       double move, double scale, const glColour *col, int foreground )
361 {
362    background_image_t *bkg, **arr;
363 
364    if (foreground)
365       arr = &bkg_image_arr_ft;
366    else
367       arr = &bkg_image_arr_bk;
368 
369    /* See if must create. */
370    if (*arr == NULL)
371       *arr = array_create( background_image_t );
372 
373    /* Create image. */
374    bkg         = &array_grow( arr );
375    bkg->id     = ++bkg_idgen;
376    bkg->image  = gl_dupTexture(image);
377    bkg->x      = x;
378    bkg->y      = y;
379    bkg->move   = move;
380    bkg->scale  = scale;
381    bkg->col    = (col!=NULL) ? *col : cWhite;
382 
383    /* Sort if necessary. */
384    bkg_sort( *arr );
385 
386    return bkg_idgen;
387 }
388 
389 
390 /**
391  * @brief Renders the background images.
392  */
background_renderImages(background_image_t * bkg_arr)393 static void background_renderImages( background_image_t *bkg_arr )
394 {
395    int i;
396    background_image_t *bkg;
397    double px,py, x,y, xs,ys, z;
398 
399    /* Must have an image array created. */
400    if (bkg_arr == NULL)
401       return;
402 
403    /* Render images in order. */
404    for (i=0; i<array_size(bkg_arr); i++) {
405       bkg = &bkg_arr[i];
406 
407       cam_getPos( &px, &py );
408       x  = px + (bkg->x - px) * bkg->move - bkg->scale*bkg->image->sw/2.;
409       y  = py + (bkg->y - py) * bkg->move - bkg->scale*bkg->image->sh/2.;
410       gl_gameToScreenCoords( &xs, &ys, x, y );
411       z = cam_getZoom();
412       z *= bkg->scale;
413       gl_blitScale( bkg->image, xs, ys,
414             z*bkg->image->sw, z*bkg->image->sh, &bkg->col );
415    }
416 }
417 
418 
419 /**
420  * @brief Creates a background Lua state from a script.
421  */
background_create(const char * name)422 static nlua_env background_create( const char *name )
423 {
424    uint32_t bufsize;
425    char path[PATH_MAX];
426    char *buf;
427    nlua_env env;
428 
429    /* Create file name. */
430    nsnprintf( path, sizeof(path), "dat/bkg/%s.lua", name );
431 
432    /* Create the Lua env. */
433    env = nlua_newEnv(1);
434    nlua_loadStandard(env);
435    nlua_loadTex(env);
436    nlua_loadCol(env);
437    nlua_loadBackground(env);
438 
439    /* Open file. */
440    buf = ndata_read( path, &bufsize );
441    if (buf == NULL) {
442       WARN("Default background script '%s' not found.", path);
443       nlua_freeEnv(env);
444       return LUA_NOREF;
445    }
446 
447    /* Load file. */
448    if (nlua_dobufenv(env, buf, bufsize, path) != 0) {
449       WARN("Error loading background file: %s\n"
450             "%s\n"
451             "Most likely Lua file has improper syntax, please check",
452             path, lua_tostring(naevL,-1));
453       free(buf);
454       nlua_freeEnv(env);
455       return LUA_NOREF;
456    }
457    free(buf);
458 
459    return env;
460 }
461 
462 
463 /**
464  * @brief Initializes the background system.
465  */
background_init(void)466 int background_init (void)
467 {
468    /* Load Lua. */
469    bkg_def_env = background_create( "default" );
470    return 0;
471 }
472 
473 
474 /**
475  * @brief Loads a background script by name.
476  */
background_load(const char * name)477 int background_load( const char *name )
478 {
479    int ret;
480    nlua_env env;
481    const char *err;
482 
483    /* Free if exists. */
484    background_clearCurrent();
485 
486    /* Load default. */
487    if (name == NULL)
488       bkg_cur_env = bkg_def_env;
489    /* Load new script. */
490    else
491       bkg_cur_env = background_create( name );
492 
493    /* Comfort. */
494    env = bkg_cur_env;
495    if (env == LUA_NOREF)
496       return -1;
497 
498    /* Run Lua. */
499    nlua_getenv(env,"background");
500    ret = nlua_pcall(env, 0, 0);
501    if (ret != 0) { /* error has occurred */
502       err = (lua_isstring(naevL,-1)) ? lua_tostring(naevL,-1) : NULL;
503       WARN("Background -> 'background' : %s",
504             (err) ? err : "unknown error");
505       lua_pop(naevL, 1);
506    }
507    return ret;
508 }
509 
510 
511 /**
512  * @brief Destroys the current running background script.
513  */
background_clearCurrent(void)514 static void background_clearCurrent (void)
515 {
516    if (bkg_cur_env != bkg_def_env) {
517       if (bkg_cur_env != LUA_NOREF)
518          nlua_freeEnv( bkg_cur_env );
519    }
520    bkg_cur_env = LUA_NOREF;
521 }
522 
523 
524 /**
525  * @brief Cleans up the background stuff.
526  */
background_clear(void)527 void background_clear (void)
528 {
529    /* Destroy current background script. */
530    background_clearCurrent();
531 
532    /* Clear the backgrounds. */
533    background_clearImgArr( &bkg_image_arr_bk );
534    background_clearImgArr( &bkg_image_arr_ft );
535 }
536 
537 
538 /**
539  * @brief Clears a background image array.
540  *
541  *    @param arr Array to clear.
542  */
background_clearImgArr(background_image_t ** arr)543 static void background_clearImgArr( background_image_t **arr )
544 {
545    int i;
546    background_image_t *bkg;
547 
548    /* Must have an image array created. */
549    if (*arr == NULL)
550       return;
551 
552    for (i=0; i<array_size(*arr); i++) {
553       bkg = &((*arr)[i]);
554       gl_freeTexture( bkg->image );
555    }
556 
557    /* Erase it all. */
558    array_erase( arr, &(*arr)[0], &(*arr)[ array_size(*arr) ] );
559 }
560 
561 
562 /**
563  * @brief Cleans up and frees memory after the backgrounds.
564  */
background_free(void)565 void background_free (void)
566 {
567    /* Free the Lua. */
568    background_clear();
569    if (bkg_def_env != LUA_NOREF)
570       nlua_freeEnv( bkg_def_env );
571    bkg_def_env = LUA_NOREF;
572 
573    /* Free the images. */
574    if (bkg_image_arr_ft != NULL) {
575       array_free( bkg_image_arr_ft );
576       bkg_image_arr_ft = NULL;
577    }
578    if (bkg_image_arr_bk != NULL) {
579       array_free( bkg_image_arr_bk );
580       bkg_image_arr_bk = NULL;
581    }
582 
583    /* Free the Lua. */
584    if (bkg_cur_env != LUA_NOREF)
585       nlua_freeEnv( bkg_cur_env );
586    bkg_cur_env = LUA_NOREF;
587 
588    /* Destroy VBOs. */
589    if (star_vertexVBO != NULL) {
590       gl_vboDestroy( star_vertexVBO );
591       star_vertexVBO = NULL;
592    }
593    if (star_colourVBO != NULL) {
594       gl_vboDestroy( star_colourVBO );
595       star_colourVBO = NULL;
596    }
597 
598    /* Free the stars. */
599    if (star_vertex != NULL) {
600       free(star_vertex);
601       star_vertex = NULL;
602    }
603    if (star_colour != NULL) {
604       free(star_colour);
605       star_colour = NULL;
606    }
607    nstars = 0;
608    mstars = 0;
609 }
610 
611