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