naev 0.11.5
spfx.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <inttypes.h>
11#include "SDL.h"
12#include "SDL_haptic.h"
13
14#include "naev.h"
17#include "spfx.h"
18
19#include "conf.h"
20#include "array.h"
21#include "camera.h"
22#include "debris.h"
23#include "log.h"
24#include "ndata.h"
25#include "nxml.h"
26#include "opengl.h"
27#include "pause.h"
28#include "physics.h"
29#include "perlin.h"
30#include "render.h"
31#include "rng.h"
32#include "space.h"
33#include "nlua_shader.h"
34#include "nlua_spfx.h"
35
36#define SPFX_XML_ID "spfx"
38/*
39 * Effect parameters.
40 */
41#define SHAKE_MASS (1./400.)
42#define SHAKE_K (1./50.)
43#define SHAKE_B (3.*sqrt(SHAKE_K*SHAKE_MASS))
45#define HAPTIC_UPDATE_INTERVAL 0.1
47/* Trail stuff. */
48#define TRAIL_UPDATE_DT 0.05
52/*
53 * Special hard-coded special effects
54 */
55/* shake aka rumble */
56static unsigned int shake_shader_pp_id = 0;
58static vec2 shake_pos = { .x = 0., .y = 0. };
59static vec2 shake_vel = { .x = 0., .y = 0. };
60static double shake_force_mod = 0.;
61static double shake_force_mean = 0.;
62static float shake_force_ang = 0.;
63static perlin_data_t *shake_noise = NULL;
64/* Haptic stuff. */
65extern SDL_Haptic *haptic;
66extern unsigned int haptic_query;
67static int haptic_rumble = -1;
68static SDL_HapticEffect haptic_rumbleEffect;
69static double haptic_lastUpdate = 0.;
70/* damage effect */
71static unsigned int damage_shader_pp_id = 0;
73static double damage_strength = 0.;
75/*
76 * Trail colours handling.
77 */
78static int trailSpec_load (void);
79static int trailSpec_parse( TrailSpec *tc, const char *file, int firstpass );
80static TrailSpec* trailSpec_getRaw( const char* name );
81
82/*
83 * Misc functions.
84 */
85static void spfx_updateShake( double dt );
86static void spfx_updateDamage( double dt );
87
93typedef struct SPFX_Base_ {
94 char* name;
96 double ttl;
97 double anim;
99 /* Use texture when not using shaders. */
102 /* Shaders! */
103 double size;
104 GLint shader;
105 GLint vertex;
106 GLint projection;
107 GLint u_time;
108 GLint u_r;
109 GLint u_size;
110} SPFX_Base;
111
112static SPFX_Base *spfx_effects = NULL;
119typedef struct SPFX_ {
124 int effect;
126 double timer;
128 /* For shaders. */
129 GLfloat time;
130 GLfloat unique;
131} SPFX;
132
133/* front stack is for effects on player, back is for the rest */
134static SPFX *spfx_stack_front = NULL;
135static SPFX *spfx_stack_middle = NULL;
136static SPFX *spfx_stack_back = NULL;
138/*
139 * prototypes
140 */
141/* General. */
142static int spfx_base_cmp( const void *p1, const void *p2 );
143static int spfx_base_parse( SPFX_Base *temp, const char *filename );
144static void spfx_base_free( SPFX_Base *effect );
145static void spfx_update_layer( SPFX *layer, const double dt );
146/* Haptic. */
147static int spfx_hapticInit (void);
148static void spfx_hapticRumble( double mod );
149/* Trail. */
150static void spfx_update_trails( double dt );
151static void spfx_trail_update( Trail_spfx* trail, double dt );
152static void spfx_trail_free( Trail_spfx* trail );
153
157static int spfx_base_cmp( const void *p1, const void *p2 )
158{
159 const SPFX_Base *s1 = (const SPFX_Base*) p1;
160 const SPFX_Base *s2 = (const SPFX_Base*) p2;
161 return strcmp( s1->name, s2->name );
162}
163
171static int spfx_base_parse( SPFX_Base *temp, const char *filename )
172{
173 xmlNodePtr node, cur, uniforms;
174 char *shadervert, *shaderfrag;
175 const char *name;
176 int isint;
177 GLint loc, dim;
178 xmlDocPtr doc;
179
180 /* Load and read the data. */
181 doc = xml_parsePhysFS( filename );
182 if (doc == NULL)
183 return -1;
184
185 /* Check to see if document exists. */
186 node = doc->xmlChildrenNode;
187 if (!xml_isNode(node,SPFX_XML_ID)) {
188 ERR( _("Malformed '%s' file: missing root element '%s'"), filename, SPFX_XML_ID);
189 return -1;
190 }
191
192 /* Clear data. */
193 memset( temp, 0, sizeof(SPFX_Base) );
194 temp->shader = -1;
195 shadervert = NULL;
196 shaderfrag = NULL;
197 uniforms = NULL;
198
199 xmlr_attr_strd( node, "name", temp->name );
200
201 /* Extract the data. */
202 node = node->xmlChildrenNode;
203 do {
204 xml_onlyNodes(node);
205 xmlr_float(node, "anim", temp->anim);
206 xmlr_float(node, "ttl", temp->ttl);
207 if (xml_isNode(node,"gfx")) {
208 temp->gfx = xml_parseTexture( node,
209 SPFX_GFX_PATH"%s", 6, 5, 0 );
210 continue;
211 }
212
213 if (xml_isNode(node,"shader")) {
214 cur = node->xmlChildrenNode;
215 do {
216 xml_onlyNodes(cur);
217 xmlr_strd(cur, "vert", shadervert);
218 xmlr_strd(cur, "frag", shaderfrag);
219 xmlr_float(cur, "size", temp->size);
220 if (xml_isNode(cur,"uniforms")) {
221 uniforms = cur;
222 continue;
223 }
224 } while (xml_nextNode(cur));
225 continue;
226 }
227 WARN(_("SPFX '%s' has unknown node '%s'."), temp->name, node->name);
228 } while (xml_nextNode(node));
229
230 /* Convert from ms to s. */
231 if (temp->ttl == 0.)
232 temp->ttl = temp->anim;
233
234 /* Has shaders. */
235 if (shadervert != NULL && shaderfrag != NULL) {
236 temp->shader = gl_program_vert_frag( shadervert, shaderfrag, NULL );
237 temp->vertex = glGetAttribLocation( temp->shader, "vertex");
238 temp->projection = glGetUniformLocation( temp->shader, "projection");
239 temp->u_r = glGetUniformLocation( temp->shader, "u_r" );
240 temp->u_time = glGetUniformLocation( temp->shader, "u_time" );
241 temp->u_size = glGetUniformLocation( temp->shader, "u_size" );
242 if (uniforms != NULL) {
243 glUseProgram( temp->shader );
244 node = uniforms->xmlChildrenNode;
245 do {
246 xml_onlyNodes(node);
247 name = (char*)node->name;
248 loc = glGetUniformLocation( temp->shader, name );
249 if (loc < 0) {
250 WARN(_("SPFX '%s' is trying to set uniform '%s' not in shader!"), temp->name, name );
251 continue;
252 }
253 xmlr_attr_int_def(node,"int",isint,0);
254 /* Get dimension */
255 if (xmlHasProp(node,(xmlChar*)"w")) dim = 4;
256 else if (xmlHasProp(node,(xmlChar*)"z")) dim = 3;
257 else if (xmlHasProp(node,(xmlChar*)"y")) dim = 2;
258 else dim = 1;
259 /* Values default to 0. */
260 if (isint) {
261 int ix, iy, iz, iw;
262 xmlr_attr_int(node, "x", ix );
263 xmlr_attr_int(node, "y", iy );
264 xmlr_attr_int(node, "z", iz );
265 xmlr_attr_int(node, "w", iw );
266 switch (dim) {
267 case 1:
268 glUniform1i( loc, ix );
269 break;
270 case 2:
271 glUniform2i( loc, ix, iy );
272 break;
273 case 3:
274 glUniform3i( loc, ix, iy, iz );
275 break;
276 case 4:
277 glUniform4i( loc, ix, iy, iz, iw );
278 break;
279 }
280 }
281 else {
282 double x, y, z, w;
283 xmlr_attr_float(node, "x", x );
284 xmlr_attr_float(node, "y", y );
285 xmlr_attr_float(node, "z", z );
286 xmlr_attr_float(node, "w", w );
287 switch (dim) {
288 case 1:
289 glUniform1f( loc, x );
290 break;
291 case 2:
292 glUniform2f( loc, x, y );
293 break;
294 case 3:
295 glUniform3f( loc, x, y, z );
296 break;
297 case 4:
298 glUniform4f( loc, x, y, z, w );
299 break;
300 }
301 }
302 } while (xml_nextNode(node));
303 glUseProgram( 0 );
304 }
305 gl_checkErr();
306 }
307
308#define MELEMENT(o,s) \
309 if (o) WARN( _("SPFX '%s' missing/invalid '%s' element"), temp->name, s)
310 MELEMENT(temp->anim==0.,"anim");
311 MELEMENT(temp->ttl==0.,"ttl");
312 MELEMENT(temp->gfx==NULL && (shadervert==NULL || shaderfrag==NULL),"gfx or shader");
313 MELEMENT(temp->shader>=0 && temp->size<=0., "shader/size");
314#undef MELEMENT
315
316 free(shadervert);
317 free(shaderfrag);
318
319 /* Clean up. */
320 xmlFreeDoc(doc);
321
322 return 0;
323}
324
330static void spfx_base_free( SPFX_Base *effect )
331{
332 free(effect->name);
333 gl_freeTexture(effect->gfx);
334}
335
342int spfx_get( const char *name )
343{
344 const SPFX_Base sq = { .name = (char*)name };
345 const SPFX_Base *sout = bsearch( &sq, spfx_effects, array_size(spfx_effects), sizeof(SPFX_Base), spfx_base_cmp );
346 if (sout==NULL) {
347 //WARN(_("SPFX '%s' not found!"),name);
348 return -1;
349 }
350 return sout-spfx_effects;
351}
352
360int spfx_load (void)
361{
362 char **spfx_files;
363 Uint32 time = SDL_GetTicks();
364
366
367 spfx_files = ndata_listRecursive( SPFX_DATA_PATH );
368 for (int i=0; i<array_size(spfx_files); i++) {
369 if (ndata_matchExt( spfx_files[i], "xml" )) {
370 SPFX_Base spfx;
371 int ret = spfx_base_parse( &spfx, spfx_files[i] );
372 if (ret == 0)
374 }
375 free( spfx_files[i] );
376 }
377 array_free( spfx_files );
378
379 /* Reduce size. */
382
383 /* Trail colour sets. */
385
386 /*
387 * Now initialize force feedback.
388 */
389 memset( &shake_shader, 0, sizeof(LuaShader_t) );
390 shake_shader.program = shaders.shake.program;
391 shake_shader.VertexPosition= shaders.shake.VertexPosition;
392 shake_shader.ClipSpaceFromLocal = shaders.shake.ClipSpaceFromLocal;
393 shake_shader.MainTex = shaders.shake.MainTex;
396
397 /*
398 * Misc shaders.
399 */
400 memset( &damage_shader, 0, sizeof(LuaShader_t) );
401 damage_shader.program = shaders.damage.program;
402 damage_shader.VertexPosition= shaders.damage.VertexPosition;
403 damage_shader.ClipSpaceFromLocal = shaders.damage.ClipSpaceFromLocal;
404 damage_shader.MainTex = shaders.damage.MainTex;
405
406 /* Stacks. */
410
411 if (conf.devmode) {
412 time = SDL_GetTicks() - time;
413 DEBUG( n_( "Loaded %d Special Effect in %.3f s", "Loaded %d Special Effects in %.3f s", array_size(spfx_effects) ), array_size(spfx_effects), time/1000. );
414 }
415 else
416 DEBUG( n_( "Loaded %d Special Effect", "Loaded %d Special Effects", array_size(spfx_effects) ), array_size(spfx_effects) );
417
418 return 0;
419}
420
424void spfx_free (void)
425{
426 /* Clean up the debris. */
428
429 /* get rid of all the particles and free the stacks */
430 spfx_clear();
432 spfx_stack_front = NULL;
434 spfx_stack_middle = NULL;
436 spfx_stack_back = NULL;
437
438 /* now clear the effects */
439 for (int i=0; i<array_size(spfx_effects); i++)
442 spfx_effects = NULL;
443
444 /* Free the noise. */
446
447 /* Free the trails. */
448 for (int i=0; i<array_size(trail_spfx_stack); i++)
451 trail_spfx_stack = NULL;
452
453 /* Free the trail styles. */
454 for (int i=0; i<array_size(trail_spec_stack); i++) {
455 free( trail_spec_stack[i].name );
456 free( trail_spec_stack[i].filename );
457 }
459 trail_spec_stack = NULL;
460
461 /* Get rid of Lua effects. */
462 spfxL_exit();
463}
464
475void spfx_add( int effect,
476 const double px, const double py,
477 const double vx, const double vy,
478 int layer )
479{
480 SPFX *cur_spfx;
481 double ttl, anim;
482
483 if ((effect < 0) || (effect > array_size(spfx_effects))) {
484 WARN(_("Trying to add spfx with invalid effect!"));
485 return;
486 }
487
488 /*
489 * Select the Layer
490 */
491 if (layer == SPFX_LAYER_FRONT) /* front layer */
492 cur_spfx = &array_grow( &spfx_stack_front );
493 else if (layer == SPFX_LAYER_MIDDLE) /* middle layer */
494 cur_spfx = &array_grow( &spfx_stack_middle );
495 else if (layer == SPFX_LAYER_BACK) /* back layer */
496 cur_spfx = &array_grow( &spfx_stack_back );
497 else {
498 WARN(_("Invalid SPFX layer."));
499 return;
500 }
501
502 /* The actual adding of the spfx */
503 cur_spfx->effect = effect;
504 vec2_csetmin( &cur_spfx->pos, px, py );
505 vec2_csetmin( &cur_spfx->vel, vx, vy );
506 /* Timer magic if ttl != anim */
507 ttl = spfx_effects[effect].ttl;
508 anim = spfx_effects[effect].anim;
509 if (ttl != anim)
510 cur_spfx->timer = ttl + RNGF()*anim;
511 else
512 cur_spfx->timer = ttl;
513
514 /* Shader magic. */
515 cur_spfx->unique = RNGF();
516 cur_spfx->time = 0.0;
517}
518
522void spfx_clear (void)
523{
524 /* Clear rumble */
525 shake_force_mod = 0.;
526 shake_force_mean = 0.;
527 vectnull( &shake_pos );
528 vectnull( &shake_vel );
529 if (shake_shader_pp_id > 0)
530 render_postprocessRm( shake_shader_pp_id );
532 if (damage_shader_pp_id > 0)
533 render_postprocessRm( damage_shader_pp_id );
535
536 for (int i=0; i<array_size(trail_spfx_stack); i++)
539
540 /* Clear the Lua spfx. */
541 spfxL_clear();
542}
543
550void spfx_update( const double dt, const double real_dt )
551{
555 spfx_update_trails( dt );
556
557 /* Decrement the haptic timer. */
558 if (haptic_lastUpdate > 0.)
559 haptic_lastUpdate -= real_dt; /* Based on real delta-tick. */
560
561 /* Shake. */
562 spfx_updateShake( dt );
563
564 /* Damage. */
565 spfx_updateDamage( dt );
566
567 /* Update Lua ones. */
568 spfxL_update( dt );
569}
570
577static void spfx_update_layer( SPFX *layer, const double dt )
578{
579 for (int i=0; i<array_size(layer); i++) {
580 layer[i].timer -= dt; /* less time to live */
581
582 /* time to die! */
583 if (layer[i].timer < 0.) {
584 array_erase( &layer, &layer[i], &layer[i+1] );
585 i--;
586 continue;
587 }
588 layer[i].time += dt; /* Shader timer. */
589
590 /* actually update it */
591 vec2_cadd( &layer[i].pos, dt*VX(layer[i].vel), dt*VY(layer[i].vel) );
592 }
593}
594
598static void spfx_updateShake( double dt )
599{
600 double mod, vmod, angle;
601 double force_x, force_y;
602 double dupdate;
603 int forced;
604
605 /* Must still be on. */
606 if (shake_shader_pp_id == 0)
607 return;
608
609 /* The shake decays over time */
610 forced = 0;
611 if (shake_force_mod > 0.) {
612 shake_force_mod -= SPFX_SHAKE_DECAY*dt;
613 if (shake_force_mod < 0.)
614 shake_force_mod = 0.;
615 else
616 forced = 1;
617 }
618 dupdate = dt*2.0;
619 shake_force_mean = dupdate*shake_force_mod + (1.0-dupdate)*shake_force_mean;
620
621 /* See if it's settled down. */
622 mod = VMOD( shake_pos );
623 vmod = VMOD( shake_vel );
624 if (!forced && (mod < 0.01) && (vmod < 0.01)) {
625 render_postprocessRm( shake_shader_pp_id );
627 if (fabs(shake_force_ang) > 1e3)
628 shake_force_ang = RNGF();
629 return;
630 }
631
632 /* Calculate force. */
633 force_x = -SHAKE_K*shake_pos.x + -SHAKE_B*shake_vel.x;
634 force_y = -SHAKE_K*shake_pos.y + -SHAKE_B*shake_vel.y;
635
636 /* Apply force if necessary. */
637 if (forced) {
638 shake_force_ang += dt;
639 angle = noise_simplex1( shake_noise, &shake_force_ang ) * 5.*M_PI;
640 force_x += shake_force_mod * cos(angle);
641 force_y += shake_force_mod * sin(angle);
642 }
643
644 /* Update velocity. */
645 vec2_cadd( &shake_vel, (1./SHAKE_MASS) * force_x * dt, (1./SHAKE_MASS) * force_y * dt );
646
647 /* Update position. */
648 vec2_cadd( &shake_pos, shake_vel.x * dt, shake_vel.y * dt );
649
650 /* Set the uniform. */
651 glUseProgram( shaders.shake.program );
652 glUniform2f( shaders.shake.shake_pos, shake_pos.x / SCREEN_W, shake_pos.y / SCREEN_H );
653 glUniform2f( shaders.shake.shake_vel, shake_vel.x / SCREEN_W, shake_vel.y / SCREEN_H );
654 glUniform1f( shaders.shake.shake_force, shake_force_mean );
655 glUseProgram( 0 );
656
657 gl_checkErr();
658}
659
660static void spfx_updateDamage( double dt )
661{
662 /* Must still be on. */
663 if (damage_shader_pp_id == 0)
664 return;
665
666 /* Decrement and turn off if necessary. */
667 damage_strength -= SPFX_DAMAGE_DECAY * dt;
668 if (damage_strength < 0.) {
669 damage_strength = 0.;
670 render_postprocessRm( damage_shader_pp_id );
672 return;
673 }
674
675 /* Set the uniform. */
676 glUseProgram( shaders.damage.program );
677 glUniform1f( shaders.damage.damage_strength, damage_strength );
678 glUseProgram( 0 );
679
680 gl_checkErr();
681}
682
689{
690 Trail_spfx *trail = calloc( 1, sizeof(Trail_spfx) );
691 trail->spec = spec;
692 trail->capacity = 1;
693 trail->iread = trail->iwrite = 0;
694 trail->point_ringbuf = calloc( trail->capacity, sizeof(TrailPoint) );
695 trail->refcount = 1;
696 trail->r = RNGF();
697 trail->ontop = 0;
698
699 if ( trail_spfx_stack == NULL )
702
703 return trail;
704}
705
711void spfx_update_trails( double dt )
712{
713 int n = array_size( trail_spfx_stack );
714 for (int i=0; i<n; i++) {
715 Trail_spfx *trail = trail_spfx_stack[i];
716 spfx_trail_update( trail, dt );
717 if (!trail->refcount && !trail_size(trail) ) {
718 spfx_trail_free( trail );
720 }
721 }
722 if (n < array_size( trail_spfx_stack ) )
724}
725
732static void spfx_trail_update( Trail_spfx* trail, double dt )
733{
734 GLfloat rel_dt = dt/ trail->spec->ttl;
735 /* Remove outdated elements. */
736 while (trail->iread < trail->iwrite && trail_front(trail).t < rel_dt)
737 trail->iread++;
738
739 /* Update others' timestamps. */
740 for (size_t i = trail->iread; i < trail->iwrite; i++)
741 trail_at( trail, i ).t -= rel_dt;
742
743 /* Update timer. */
744 trail->dt += dt;
745}
746
756void spfx_trail_sample( Trail_spfx* trail, double x, double y, TrailMode mode, int force )
757{
758 TrailPoint p;
759
760 if (!force && trail->spec->style[mode].col.a <= 0.)
761 return;
762
763 p.x = x;
764 p.y = y;
765 p.t = 1.;
766 p.mode = mode;
767
768 /* The "back" of the trail should always reflect our most recent state. */
769 trail_back( trail ) = p;
770
771 /* We may need to insert a control point, but not if our last sample was recent enough. */
772 if (!force && trail_size(trail) > 1 && trail_at( trail, trail->iwrite-2 ).t >= 1.-TRAIL_UPDATE_DT)
773 return;
774
775 /* If the last time we inserted a control point was recent enough, we don't need a new one. */
776 if (trail_size(trail) == trail->capacity) {
777 /* Full! Double capacity, and make the elements contiguous. (We've made space to grow rightward.) */
778 trail->point_ringbuf = realloc( trail->point_ringbuf, 2 * trail->capacity * sizeof(TrailPoint) );
779 trail->iread %= trail->capacity;
780 trail->iwrite = trail->iread + trail->capacity;
781 memmove( &trail->point_ringbuf[trail->capacity], trail->point_ringbuf, trail->iread * sizeof(TrailPoint) );
782 trail->capacity *= 2;
783 }
784 trail_at( trail, trail->iwrite++ ) = p;
785}
786
793{
794 if (trail != NULL)
795 trail->refcount--;
796}
797
801static void spfx_trail_free( Trail_spfx* trail )
802{
803 assert(trail->refcount == 0);
804 free(trail->point_ringbuf);
805 free(trail);
806}
807
811void spfx_trail_draw( const Trail_spfx* trail )
812{
813 const TrailStyle *styles;
814 size_t n;
815 GLfloat len;
816 double z;
817
818 n = trail_size(trail);
819 if (n==0)
820 return;
821 styles = trail->spec->style;
822
823 /* Stuff that doesn't change for the entire trail. */
824 glUseProgram( shaders.trail.program );
825 if (gl_has( OPENGL_SUBROUTINES ))
826 glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &trail->spec->type );
827 glEnableVertexAttribArray( shaders.trail.vertex );
828 gl_vboActivateAttribOffset( gl_squareVBO, shaders.trail.vertex, 0, 2, GL_FLOAT, 0 );
829 glUniform1f( shaders.trail.dt, trail->dt );
830 glUniform1f( shaders.trail.r, trail->r );
831
832 z = cam_getZoom();
833 len = 0.;
834 for (size_t i=trail->iread + 1; i < trail->iwrite; i++) {
835 mat4 projection;
836 const TrailStyle *sp, *spp;
837 double x1, y1, x2, y2, s;
838 TrailPoint *tp = &trail_at( trail, i );
839 TrailPoint *tpp = &trail_at( trail, i-1 );
840
841 /* Ignore none modes. */
842 if (tp->mode == MODE_NONE || tpp->mode == MODE_NONE)
843 continue;
844
845 gl_gameToScreenCoords( &x1, &y1, tp->x, tp->y );
846 gl_gameToScreenCoords( &x2, &y2, tpp->x, tpp->y );
847
848 s = hypot( x2-x1, y2-y1 );
849 if (s <= 0.)
850 continue;
851
852 /* Make sure in bounds. */
853 if ((MAX(x1,x2) < 0.) || (MIN(x1,x2) > (double)SCREEN_W) ||
854 (MAX(y1,y2) < 0.) || (MIN(y1,y2) > (double)SCREEN_H)) {
855 len += s;
856 continue;
857 }
858
859 sp = &styles[tp->mode];
860 spp = &styles[tpp->mode];
861
862 /* Set vertex. */
863 projection = gl_view_matrix;
864 mat4_translate( &projection, x1, y1, 0. );
865 mat4_rotate2dv( &projection, (x2-x1)/s, (y2-y1)/s );
866 mat4_scale( &projection, s, z*(sp->thick+spp->thick), 1. );
867 mat4_translate( &projection, 0., -0.5, 0. );
868
869 /* Set uniforms. */
870 gl_uniformMat4(shaders.trail.projection, &projection);
871 gl_uniformColour(shaders.trail.c1, &sp->col);
872 gl_uniformColour(shaders.trail.c2, &spp->col);
873 glUniform1f(shaders.trail.t1, tp->t);
874 glUniform1f(shaders.trail.t2, tpp->t);
875 glUniform2f(shaders.trail.pos2, len, sp->thick);
876 len += s;
877 glUniform2f(shaders.trail.pos1, len, spp->thick);
878
879 /* Draw. */
880 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
881 }
882
883 /* Clear state. */
884 glDisableVertexAttribArray( shaders.trail.vertex );
885 glUseProgram(0);
886
887 /* Check errors. */
888 gl_checkErr();
889}
890
898void spfx_shake( double mod )
899{
900 /* Add the modifier. */
901 shake_force_mod = MIN( SPFX_SHAKE_MAX, shake_force_mod + SPFX_SHAKE_MOD*mod );
902
903 /* Rumble if it wasn't rumbling before. */
905
906 /* Create the shake. */
907 if (shake_shader_pp_id==0)
908 shake_shader_pp_id = render_postprocessAdd( &shake_shader, PP_LAYER_GAME, 99, 0 );
909}
910
918void spfx_damage( double mod )
919{
920 damage_strength = MIN( SPFX_DAMAGE_MAX, damage_strength + SPFX_DAMAGE_MOD*mod );
921
922 /* Create the damage. */
923 if (damage_shader_pp_id==0)
924 damage_shader_pp_id = render_postprocessAdd( &damage_shader, PP_LAYER_GUI, 98, 0 );
925}
926
932static int spfx_hapticInit (void)
933{
934 SDL_HapticEffect *efx;
935
936 /* Haptic must be enabled. */
937 if (haptic == NULL)
938 return 0;
939
940 efx = &haptic_rumbleEffect;
941 memset( efx, 0, sizeof(SDL_HapticEffect) );
942 efx->type = SDL_HAPTIC_SINE;
943 efx->periodic.direction.type = SDL_HAPTIC_POLAR;
944 efx->periodic.length = 1000;
945 efx->periodic.period = 200;
946 efx->periodic.magnitude = 0x4000;
947 efx->periodic.fade_length = 1000;
948 efx->periodic.fade_level = 0;
949
950 haptic_rumble = SDL_HapticNewEffect( haptic, efx );
951 if (haptic_rumble < 0) {
952 WARN(_("Unable to upload haptic effect: %s."), SDL_GetError());
953 return -1;
954 }
955
956 return 0;
957}
958
964static void spfx_hapticRumble( double mod )
965{
966 SDL_HapticEffect *efx;
967 double len, mag;
968
969 /* Not active. */
970 if (haptic_rumble < 0)
971 return;
972
973 /* Not time to update yet. */
974 if ((haptic_lastUpdate > 0.) || (shake_shader_pp_id==0) || (mod > SPFX_SHAKE_MAX/3.))
975 return;
976
977 /* Stop the effect if it was playing. */
978 SDL_HapticStopEffect( haptic, haptic_rumble );
979
980 /* Get length and magnitude. */
981 len = 1000. * shake_force_mod / SPFX_SHAKE_DECAY;
982 mag = 32767. * (shake_force_mod / SPFX_SHAKE_MAX);
983
984 /* Update the effect. */
985 efx = &haptic_rumbleEffect;
986 efx->periodic.magnitude = (int16_t)mag;
987 efx->periodic.length = (uint32_t)len;
988 efx->periodic.fade_length = MIN( efx->periodic.length, 1000 );
989 if (SDL_HapticUpdateEffect( haptic, haptic_rumble, &haptic_rumbleEffect ) < 0) {
990 WARN(_("Failed to update haptic effect: %s."), SDL_GetError());
991 return;
992 }
993
994 /* Run the new effect. */
995 SDL_HapticRunEffect( haptic, haptic_rumble, 1 );
996
997 /* Set timer again. */
999}
1000
1007{
1008 gl_renderRect( 0., 0., SCREEN_W, SCREEN_H*0.2, &cBlack );
1009 gl_renderRect( 0., SCREEN_H*0.8, SCREEN_W, SCREEN_H, &cBlack );
1010}
1011
1012static void spfx_renderStack( SPFX *spfx_stack )
1013{
1014 for (int i=array_size(spfx_stack)-1; i>=0; i--) {
1015 SPFX *spfx = &spfx_stack[i];
1016 SPFX_Base *effect = &spfx_effects[ spfx->effect ];
1017
1018 /* Render shader. */
1019 if (effect->shader >= 0) {
1020 double x, y, z, s2;
1021 double w, h;
1022 mat4 projection;
1023
1024 /* Translate coords. */
1025 s2 = effect->size/2.;
1026 z = cam_getZoom();
1027 gl_gameToScreenCoords( &x, &y, spfx->pos.x-s2, spfx->pos.y-s2 );
1028 w = h = effect->size*z;
1029
1030 /* Check if inbounds. */
1031 if ((x < -w) || (x > SCREEN_W+w) ||
1032 (y < -h) || (y > SCREEN_H+h))
1033 continue;
1034
1035 /* Let's get to business. */
1036 glUseProgram( effect->shader );
1037
1038 /* Set up the vertex. */
1039 projection = gl_view_matrix;
1040 mat4_translate( &projection, x, y, 0. );
1041 mat4_scale( &projection, w, h, 1. );
1042 glEnableVertexAttribArray( effect->vertex );
1043 gl_vboActivateAttribOffset( gl_squareVBO, effect->vertex,
1044 0, 2, GL_FLOAT, 0 );
1045
1046 /* Set shader uniforms. */
1047 gl_uniformMat4(effect->projection, &projection);
1048 glUniform1f(effect->u_time, spfx->time);
1049 glUniform1f(effect->u_r, spfx->unique);
1050 glUniform1f(effect->u_size, effect->size);
1051
1052 /* Draw. */
1053 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1054
1055 /* Clear state. */
1056 glDisableVertexAttribArray( shaders.texture.vertex );
1057
1058 /* anything failed? */
1059 gl_checkErr();
1060
1061 glUseProgram(0);
1062
1063 }
1064 /* No shader. */
1065 else {
1066 int sx, sy;
1067
1068 /* Simplifies */
1069 sx = (int)effect->gfx->sx;
1070 sy = (int)effect->gfx->sy;
1071
1072 if (!paused) { /* don't calculate frame if paused */
1073 double time = 1. - fmod(spfx_stack[i].timer,effect->anim) / effect->anim;
1074 spfx_stack[i].lastframe = sx * sy * MIN(time, 1.);
1075 }
1076
1077 /* Renders */
1078 gl_renderSprite( effect->gfx,
1079 VX(spfx_stack[i].pos), VY(spfx_stack[i].pos),
1080 spfx_stack[i].lastframe % sx,
1081 spfx_stack[i].lastframe / sx,
1082 NULL );
1083 }
1084 }
1085}
1086
1093void spfx_render( int layer, double dt )
1094{
1095 /* get the appropriate layer */
1096 switch (layer) {
1097 case SPFX_LAYER_FRONT:
1098 spfx_renderStack( spfx_stack_front );
1099 spfxL_renderfg( dt );
1100 break;
1101
1102 case SPFX_LAYER_MIDDLE:
1103 spfx_renderStack( spfx_stack_middle );
1104 spfxL_rendermg( dt );
1105 break;
1106
1107 case SPFX_LAYER_BACK:
1108 spfx_renderStack( spfx_stack_back );
1109 spfxL_renderbg( dt );
1110
1111 /* Trails are special (for now?). */
1112 for (int i=0; i<array_size(trail_spfx_stack); i++) {
1113 Trail_spfx *trail = trail_spfx_stack[i];
1114 if (!trail->ontop)
1115 spfx_trail_draw( trail );
1116 }
1117 break;
1118
1119 default:
1120 WARN(_("Rendering invalid SPFX layer."));
1121 return;
1122 }
1123}
1124
1129static int trailSpec_parse( TrailSpec *tc, const char *file, int firstpass )
1130{
1131 static const char *mode_tags[] = MODE_TAGS;
1132 char *inherits;
1133 xmlNodePtr parent, node;
1134 xmlDocPtr doc;
1135
1136 /* Load the data. */
1137 doc = xml_parsePhysFS( file );
1138 if (doc == NULL)
1139 return -1;
1140
1141 /* Get the first node. */
1142 parent = doc->xmlChildrenNode; /* first event node */
1143 if (parent == NULL) {
1144 WARN( _("Malformed '%s' file: does not contain elements"), file );
1145 return -1;
1146 }
1147
1148 if (firstpass) {
1149 memset( tc, 0, sizeof(TrailSpec) );
1150 for(int i=0; i<MODE_MAX; i++)
1151 tc->style[i].thick = 1.;
1152 }
1153
1154 xmlr_attr_strd( parent, "inherits", inherits );
1155 if (firstpass) {
1156 xmlr_attr_strd( parent, "name", tc->name );
1157 if (inherits != NULL) {
1158 /* Skip this pass. */
1159 free( inherits );
1160 xmlFreeDoc(doc);
1161 return 0;
1162 }
1163 }
1164 else {
1165 if (inherits == NULL) {
1166 /* Already done here. */
1167 free( inherits );
1168 xmlFreeDoc(doc);
1169 return 0;
1170 }
1171 else {
1172 TrailSpec *tsparent = trailSpec_getRaw( inherits );
1173 if (tsparent == NULL)
1174 WARN(_("Trail '%s' that inherits from '%s' has missing reference!"), tc->name, inherits );
1175 else {
1176 char *name = tc->name;
1177 char *filename = tc->filename;
1178 memcpy( tc, tsparent, sizeof(TrailSpec) );
1179 tc->name = name;
1180 tc->filename = filename;
1181 }
1182 }
1183 }
1184
1185 node = parent->xmlChildrenNode;
1186 do {
1187 xml_onlyNodes(node);
1188 if (xml_isNode(node,"thickness"))
1189 tc->def_thick = xml_getFloat( node );
1190 else if (xml_isNode(node, "ttl"))
1191 tc->ttl = xml_getFloat( node );
1192 else if (xml_isNode(node, "type")) {
1193 char *type = xml_get(node);
1194 if (gl_has( OPENGL_SUBROUTINES )) {
1195 tc->type = glGetSubroutineIndex( shaders.trail.program, GL_FRAGMENT_SHADER, type );
1196 if (tc->type == GL_INVALID_INDEX)
1197 WARN("Trail '%s' has unknown type '%s'", tc->name, type );
1198 }
1199 }
1200 else if (xml_isNode(node, "nebula"))
1201 tc->nebula = xml_getInt( node );
1202 else {
1203 int i;
1204 for (i=0; i<MODE_MAX; i++)
1205 if (xml_isNode(node, mode_tags[i])) {
1206 xmlr_attr_float_opt( node, "r", tc->style[i].col.r );
1207 xmlr_attr_float_opt( node, "g", tc->style[i].col.g );
1208 xmlr_attr_float_opt( node, "b", tc->style[i].col.b );
1209 xmlr_attr_float_opt( node, "a", tc->style[i].col.a );
1210 xmlr_attr_float_opt( node, "scale", tc->style[i].thick );
1211 col_gammaToLinear( &tc->style[i].col );
1212 break;
1213 }
1214 if (i == MODE_MAX)
1215 WARN(_("Trail '%s' has unknown node '%s'."), tc->name, node->name);
1216 }
1217 } while (xml_nextNode(node));
1218
1219#define MELEMENT(o,s) if (o) WARN(_("Trail '%s' missing '%s' element"), tc->name, s)
1220 MELEMENT( tc->def_thick==0, "thickness" );
1221 MELEMENT( tc->ttl==0, "ttl" );
1222#undef MELEMENT
1223
1224 /* Clean up. */
1225 free( inherits );
1226 xmlFreeDoc(doc);
1227 return 0;
1228}
1229
1235static int trailSpec_load (void)
1236{
1237 char **ts_files = ndata_listRecursive( TRAIL_DATA_PATH );
1238
1240
1241 /* First pass sets up and prepares inheritance. */
1242 for (int i=0; i<array_size(ts_files); i++) {
1243 TrailSpec tc;
1244 int ret = trailSpec_parse( &tc, ts_files[i], 1 );
1245 if (ret == 0) {
1246 tc.filename = ts_files[i];
1248 }
1249 else
1250 free( ts_files[i] );
1251 }
1252
1253 /* Second pass to complete inheritance. */
1254 for (int i=0; i<array_size( trail_spec_stack ); i++)
1255 trailSpec_parse( &trail_spec_stack[i], trail_spec_stack[i].filename, 0 );
1256 array_free( ts_files );
1257
1258 /* Set up thickness. */
1260 for(int i=0; i<MODE_MAX; i++)
1261 tc->style[i].thick *= tc->def_thick;
1262 }
1264
1265 return 0;
1266}
1267
1268static TrailSpec* trailSpec_getRaw( const char* name )
1269{
1270 for (int i=0; i<array_size(trail_spec_stack); i++) {
1271 if ( strcmp(trail_spec_stack[i].name, name)==0 )
1272 return &trail_spec_stack[i];
1273 }
1274 WARN(_("Trail type '%s' not found in stack"), name);
1275 return NULL;
1276}
1277
1283const TrailSpec* trailSpec_get( const char* name )
1284{
1285 return trailSpec_getRaw( name );
1286}
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_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition array.h:112
#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_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition array.h:149
#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
double cam_getZoom(void)
Gets the camera zoom.
Definition camera.c:97
void debris_cleanup(void)
Cleans up after the debris.
Definition debris.c:28
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
Definition mat4.c:99
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition mat4.c:82
void mat4_rotate2dv(mat4 *m, double c, double s)
Rotates the +x axis to the given vector.
Definition mat4.c:135
static double real_dt
Definition naev.c:113
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
#define MAX(x, y)
Definition naev.h:39
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:365
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:231
void spfxL_renderbg(double dt)
Renders the Lua SPFX on the background.
Definition nlua_spfx.c:738
void spfxL_rendermg(double dt)
Renders the Lua SPFX in the midground.
Definition nlua_spfx.c:746
void spfxL_renderfg(double dt)
Renders the Lua SPFX in the foreground.
Definition nlua_spfx.c:754
void spfxL_clear(void)
Clears the Lua spfx.
Definition nlua_spfx.c:608
void spfxL_update(double dt)
Updates the spfx.
Definition nlua_spfx.c:632
glTexture * xml_parseTexture(xmlNodePtr node, const char *path, int defsx, int defsy, const unsigned int flags)
Parses a texture handling the sx and sy elements.
Definition nxml.c:29
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:75
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
void gl_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_renderSprite(const glTexture *sprite, double bx, double by, int sx, int sy, const glColour *c)
Blits a sprite, position is relative to the player.
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition opengl_tex.c:862
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:226
int paused
Definition pause.c:21
perlin_data_t * noise_new(void)
Creates a new perlin noise generator.
Definition perlin.c:76
void noise_delete(perlin_data_t *pdata)
Frees some noise data.
Definition perlin.c:132
float noise_simplex1(perlin_data_t *pdata, float f[1])
Gets 1D simplex noise for a position.
Definition perlin.c:105
#define SPFX_XML_ID
Definition spfx.c:36
static double damage_strength
Definition spfx.c:73
static SPFX * spfx_stack_front
Definition spfx.c:134
static TrailSpec * trail_spec_stack
Definition spfx.c:49
void spfx_free(void)
Frees the spfx stack.
Definition spfx.c:424
static LuaShader_t shake_shader
Definition spfx.c:57
int spfx_load(void)
Loads the spfx stack.
Definition spfx.c:360
static double shake_force_mod
Definition spfx.c:60
static Trail_spfx ** trail_spfx_stack
Definition spfx.c:50
void spfx_render(int layer, double dt)
Renders the entire spfx layer.
Definition spfx.c:1093
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
Definition spfx.c:1283
static SPFX_Base * spfx_effects
Definition spfx.c:112
#define TRAIL_UPDATE_DT
Definition spfx.c:48
#define SHAKE_B
Definition spfx.c:43
static void spfx_update_layer(SPFX *layer, const double dt)
Updates an individual spfx.
Definition spfx.c:577
static int trailSpec_load(void)
Loads the trail colour sets.
Definition spfx.c:1235
static void spfx_base_free(SPFX_Base *effect)
Frees a SPFX_Base.
Definition spfx.c:330
void spfx_cinematic(void)
Sets the cinematic mode.
Definition spfx.c:1006
static int haptic_rumble
Definition spfx.c:67
static int spfx_base_parse(SPFX_Base *temp, const char *filename)
Parses an xml node containing a SPFX.
Definition spfx.c:171
void spfx_trail_sample(Trail_spfx *trail, double x, double y, TrailMode mode, int force)
Makes a trail grow.
Definition spfx.c:756
static SPFX * spfx_stack_middle
Definition spfx.c:135
void spfx_shake(double mod)
Increases the current rumble level.
Definition spfx.c:898
static unsigned int damage_shader_pp_id
Definition spfx.c:71
static LuaShader_t damage_shader
Definition spfx.c:72
static int trailSpec_parse(TrailSpec *tc, const char *file, int firstpass)
Parses raw values out of a "trail" element.
Definition spfx.c:1129
static void spfx_hapticRumble(double mod)
Runs a rumble effect.
Definition spfx.c:964
static double haptic_lastUpdate
Definition spfx.c:69
static float shake_force_ang
Definition spfx.c:62
static unsigned int shake_shader_pp_id
Definition spfx.c:56
void spfx_clear(void)
Clears all the currently running effects.
Definition spfx.c:522
static vec2 shake_pos
Definition spfx.c:58
int spfx_get(const char *name)
Gets the id of an spfx based on name.
Definition spfx.c:342
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition spfx.c:792
unsigned int haptic_query
Definition joystick.c:25
static void spfx_trail_update(Trail_spfx *trail, double dt)
Updates a trail.
Definition spfx.c:732
static perlin_data_t * shake_noise
Definition spfx.c:63
static void spfx_updateShake(double dt)
Updates the shake position.
Definition spfx.c:598
void spfx_add(int effect, const double px, const double py, const double vx, const double vy, int layer)
Creates a new special effect.
Definition spfx.c:475
static void spfx_update_trails(double dt)
Updates all trails (handling dispersal/fadeout).
Definition spfx.c:711
static int spfx_base_cmp(const void *p1, const void *p2)
For sorting and stuff.
Definition spfx.c:157
void spfx_damage(double mod)
Increases the current damage level.
Definition spfx.c:918
static double shake_force_mean
Definition spfx.c:61
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition spfx.c:688
static SPFX * spfx_stack_back
Definition spfx.c:136
#define HAPTIC_UPDATE_INTERVAL
Definition spfx.c:45
static void spfx_trail_free(Trail_spfx *trail)
Deallocates an unreferenced, expired trail.
Definition spfx.c:801
void spfx_update(const double dt, const double real_dt)
Updates all the spfx.
Definition spfx.c:550
SDL_Haptic * haptic
Definition joystick.c:24
static int spfx_hapticInit(void)
Initializes the rumble effect.
Definition spfx.c:932
void spfx_trail_draw(const Trail_spfx *trail)
Draws a trail on screen.
Definition spfx.c:811
#define SHAKE_K
Definition spfx.c:42
static vec2 shake_vel
Definition spfx.c:59
static SDL_HapticEffect haptic_rumbleEffect
Definition spfx.c:68
int devmode
Definition conf.h:157
Generic special effect.
Definition spfx.c:93
char * name
Definition spfx.c:94
GLint u_time
Definition spfx.c:107
double anim
Definition spfx.c:97
GLint shader
Definition spfx.c:104
GLint u_size
Definition spfx.c:109
double size
Definition spfx.c:103
GLint u_r
Definition spfx.c:108
glTexture * gfx
Definition spfx.c:100
double ttl
Definition spfx.c:96
An actual in-game active special effect.
Definition spfx.c:119
double timer
Definition spfx.c:126
int lastframe
Definition spfx.c:123
vec2 vel
Definition spfx.c:121
GLfloat unique
Definition spfx.c:130
GLfloat time
Definition spfx.c:129
int effect
Definition spfx.c:124
vec2 pos
Definition spfx.c:120
TrailMode mode
Definition spfx.h:56
GLfloat y
Definition spfx.h:54
GLfloat t
Definition spfx.h:55
represents a set of styles for trails.
Definition spfx.h:43
int nebula
Definition spfx.h:50
GLuint type
Definition spfx.h:48
double ttl
Definition spfx.h:46
float def_thick
Definition spfx.h:47
TrailStyle style[MODE_MAX]
Definition spfx.h:49
char * name
Definition spfx.h:44
Represents the appearance characteristics for a given trail mode.
Definition spfx.h:25
glColour col
Definition spfx.h:26
float thick
Definition spfx.h:27
A trail generated by a ship or an ammo.
Definition spfx.h:64
int refcount
Definition spfx.h:70
double dt
Definition spfx.h:71
TrailPoint * point_ringbuf
Definition spfx.h:66
GLfloat r
Definition spfx.h:72
size_t iread
Definition spfx.h:68
size_t iwrite
Definition spfx.h:69
unsigned int ontop
Definition spfx.h:73
size_t capacity
Definition spfx.h:67
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:36
double sx
Definition opengl_tex.h:44
double sy
Definition opengl_tex.h:45
Definition mat4.h:10
Represents a 2d vector.
Definition vec2.h:32
double y
Definition vec2.h:34
double x
Definition vec2.h:33