naev 0.12.5
spfx.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "SDL_haptic.h"
11#include "SDL_timer.h"
13
14#include "spfx.h"
15
16#include "array.h"
17#include "camera.h"
18#include "conf.h"
19#include "debris.h"
20#include "log.h"
21#include "naev.h"
22#include "ndata.h"
23#include "nlua_shader.h"
24#include "nlua_spfx.h"
25#include "ntracing.h"
26#include "nxml.h"
27#include "opengl.h"
28#include "pause.h"
29#include "perlin.h"
30#include "render.h"
31#include "rng.h"
32#include "vec2.h"
33
34#define SPFX_XML_ID "spfx"
35
36/*
37 * Effect parameters.
38 */
39#define SHAKE_MASS ( 1. / 400. )
40#define SHAKE_K ( 1. / 50. )
41#define SHAKE_B \
42 ( 3. * sqrt( SHAKE_K * SHAKE_MASS ) )
43
44
45#define HAPTIC_UPDATE_INTERVAL 0.1
46
47/* Trail stuff. */
48#define TRAIL_UPDATE_DT \
49 0.05
52
53/*
54 * Special hard-coded special effects
55 */
56/* shake aka rumble */
57static unsigned int shake_shader_pp_id =
58 0;
60static vec2 shake_pos = { .x = 0., .y = 0. };
61static vec2 shake_vel = { .x = 0., .y = 0. };
62static double shake_force_mod = 0.;
63static double shake_force_mean = 0.;
64static float shake_force_ang = 0.;
65static perlin_data_t *shake_noise = NULL;
66/* Haptic stuff. */
67extern SDL_Haptic *haptic;
68extern unsigned int haptic_query;
69static int haptic_rumble = -1;
70static SDL_HapticEffect haptic_rumbleEffect;
71static double haptic_lastUpdate =
72 0.;
73/* damage effect */
74static unsigned int damage_shader_pp_id =
75 0;
77static double damage_strength = 0.;
78
79/*
80 * Trail colours handling.
81 */
82static int trailSpec_load( void );
83static int trailSpec_parse( TrailSpec *tc, const char *file, int firstpass );
84static TrailSpec *trailSpec_getRaw( const char *name );
85
86/*
87 * Misc functions.
88 */
89static void spfx_updateShake( double dt );
90static void spfx_updateDamage( double dt );
91
97typedef struct SPFX_Base_ {
98 char *name;
99
100 double ttl;
101 double anim;
102
103 /* Use texture when not using shaders. */
105
106 /* Shaders! */
107 double size;
108 GLint shader;
109 GLint vertex;
110 GLint projection;
111 GLint u_time;
112 GLint u_r;
113 GLint u_size;
114} SPFX_Base;
115
116static SPFX_Base *spfx_effects = NULL;
117
123typedef struct SPFX_ {
126
128 int effect;
129
130 double timer;
131
132 /* For shaders. */
133 GLfloat time;
134 GLfloat unique;
135} SPFX;
136
137/* front stack is for effects on player, back is for the rest */
138static SPFX *spfx_stack_front = NULL;
139static SPFX *spfx_stack_middle = NULL;
140static SPFX *spfx_stack_back = NULL;
141
142/*
143 * prototypes
144 */
145/* General. */
146static int spfx_base_cmp( const void *p1, const void *p2 );
147static int spfx_base_parse( SPFX_Base *temp, const char *filename );
148static void spfx_base_free( SPFX_Base *effect );
149static void spfx_update_layer( SPFX *layer, const double dt );
150/* Haptic. */
151static int spfx_hapticInit( void );
152static void spfx_hapticRumble( double mod );
153/* Trail. */
154static void spfx_update_trails( double dt );
155static void spfx_trail_update( Trail_spfx *trail, double dt );
156static void spfx_trail_free( Trail_spfx *trail );
157
161static int spfx_base_cmp( const void *p1, const void *p2 )
162{
163 const SPFX_Base *s1 = p1;
164 const SPFX_Base *s2 = p2;
165 return strcmp( s1->name, s2->name );
166}
167
175static int spfx_base_parse( SPFX_Base *temp, const char *filename )
176{
177 xmlNodePtr node, cur, uniforms;
178 char *shadervert, *shaderfrag;
179 xmlDocPtr doc;
180
181 /* Load and read the data. */
182 doc = xml_parsePhysFS( filename );
183 if ( doc == NULL )
184 return -1;
185
186 /* Check to see if document exists. */
187 node = doc->xmlChildrenNode;
188 if ( !xml_isNode( node, SPFX_XML_ID ) ) {
189 WARN( _( "Malformed '%s' file: missing root element '%s'" ), filename,
190 SPFX_XML_ID );
191 return -1;
192 }
193
194 /* Clear data. */
195 memset( temp, 0, sizeof( SPFX_Base ) );
196 temp->shader = -1;
197 shadervert = NULL;
198 shaderfrag = NULL;
199 uniforms = NULL;
200
201 xmlr_attr_strd( node, "name", temp->name );
202
203 /* Extract the data. */
204 node = node->xmlChildrenNode;
205 do {
206 xml_onlyNodes( node );
207 xmlr_float( node, "anim", temp->anim );
208 xmlr_float( node, "ttl", temp->ttl );
209 if ( xml_isNode( node, "gfx" ) ) {
210 temp->gfx = xml_parseTexture( node, SPFX_GFX_PATH "%s", 6, 5, 0 );
211 continue;
212 }
213
214 if ( xml_isNode( node, "shader" ) ) {
215 cur = node->xmlChildrenNode;
216 do {
217 xml_onlyNodes( cur );
218 xmlr_strd( cur, "vert", shadervert );
219 xmlr_strd( cur, "frag", shaderfrag );
220 xmlr_float( cur, "size", temp->size );
221 if ( xml_isNode( cur, "uniforms" ) ) {
222 uniforms = cur;
223 continue;
224 }
225 } while ( xml_nextNode( cur ) );
226 continue;
227 }
228
229 // cppcheck-suppress nullPointerRedundantCheck
230 WARN( _( "SPFX '%s' has unknown node '%s'." ), temp->name, node->name );
231 } while ( xml_nextNode( node ) );
232
233 /* Convert from ms to s. */
234 if ( temp->ttl == 0. )
235 temp->ttl = temp->anim;
236
237 /* Has shaders. */
238 if ( shadervert != NULL && shaderfrag != NULL ) {
239 temp->shader = gl_program_vert_frag( shadervert, shaderfrag );
240 temp->vertex = glGetAttribLocation( temp->shader, "vertex" );
241 temp->projection = glGetUniformLocation( temp->shader, "projection" );
242 temp->u_r = glGetUniformLocation( temp->shader, "u_r" );
243 temp->u_time = glGetUniformLocation( temp->shader, "u_time" );
244 temp->u_size = glGetUniformLocation( temp->shader, "u_size" );
245 if ( uniforms != NULL ) {
246 glUseProgram( temp->shader );
247 node = uniforms->xmlChildrenNode;
248 do {
249 xml_onlyNodes( node );
250 int isint;
251 GLint dim;
252 const char *name = (char *)node->name;
253 GLint loc = glGetUniformLocation( temp->shader, name );
254 if ( loc < 0 ) {
255 WARN(
256 _( "SPFX '%s' is trying to set uniform '%s' not in shader!" ),
257 temp->name, name );
258 continue;
259 }
260 xmlr_attr_int_def( node, "int", isint, 0 );
261 /* Get dimension */
262 if ( xmlHasProp( node, (xmlChar *)"w" ) )
263 dim = 4;
264 else if ( xmlHasProp( node, (xmlChar *)"z" ) )
265 dim = 3;
266 else if ( xmlHasProp( node, (xmlChar *)"y" ) )
267 dim = 2;
268 else
269 dim = 1;
270 /* Values default to 0. */
271 if ( isint ) {
272 int ix, iy, iz, iw;
273 xmlr_attr_int( node, "x", ix );
274 xmlr_attr_int( node, "y", iy );
275 xmlr_attr_int( node, "z", iz );
276 xmlr_attr_int( node, "w", iw );
277 switch ( dim ) {
278 case 1:
279 glUniform1i( loc, ix );
280 break;
281 case 2:
282 glUniform2i( loc, ix, iy );
283 break;
284 case 3:
285 glUniform3i( loc, ix, iy, iz );
286 break;
287 case 4:
288 glUniform4i( loc, ix, iy, iz, iw );
289 break;
290 }
291 } else {
292 double x, y, z, w;
293 xmlr_attr_float( node, "x", x );
294 xmlr_attr_float( node, "y", y );
295 xmlr_attr_float( node, "z", z );
296 xmlr_attr_float( node, "w", w );
297 switch ( dim ) {
298 case 1:
299 glUniform1f( loc, x );
300 break;
301 case 2:
302 glUniform2f( loc, x, y );
303 break;
304 case 3:
305 glUniform3f( loc, x, y, z );
306 break;
307 case 4:
308 glUniform4f( loc, x, y, z, w );
309 break;
310 }
311 }
312 } while ( xml_nextNode( node ) );
313 glUseProgram( 0 );
314 }
315 gl_checkErr();
316 }
317
318#define MELEMENT( o, s ) \
319 if ( o ) \
320 WARN( _( "SPFX '%s' missing/invalid '%s' element" ), temp->name, \
321 s )
322 MELEMENT( temp->anim == 0., "anim" );
323 MELEMENT( temp->ttl == 0., "ttl" );
324 MELEMENT( temp->gfx == NULL && ( shadervert == NULL || shaderfrag == NULL ),
325 "gfx or shader" );
326 MELEMENT( temp->shader >= 0 && temp->size <= 0., "shader/size" );
327#undef MELEMENT
328
329 free( shadervert );
330 free( shaderfrag );
331
332 /* Clean up. */
333 xmlFreeDoc( doc );
334
335 return 0;
336}
337
343static void spfx_base_free( SPFX_Base *effect )
344{
345 free( effect->name );
346 gl_freeTexture( effect->gfx );
347}
348
355int spfx_get( const char *name )
356{
357 const SPFX_Base sq = { .name = (char *)name };
358 const SPFX_Base *sout =
359 bsearch( &sq, spfx_effects, array_size( spfx_effects ),
360 sizeof( SPFX_Base ), spfx_base_cmp );
361 if ( sout == NULL ) {
362 // WARN(_("SPFX '%s' not found!"),name);
363 return -1;
364 }
365 return sout - spfx_effects;
366}
367
375int spfx_load( void )
376{
377#if DEBUGGING
378 Uint32 time = SDL_GetTicks();
379#endif /* DEBUGGING */
380 char **spfx_files;
381
383
384 spfx_files = ndata_listRecursive( SPFX_DATA_PATH );
385 for ( int i = 0; i < array_size( spfx_files ); i++ ) {
386 if ( ndata_matchExt( spfx_files[i], "xml" ) ) {
387 SPFX_Base spfx;
388 int ret = spfx_base_parse( &spfx, spfx_files[i] );
389 if ( ret == 0 )
391 }
392 free( spfx_files[i] );
393 }
394 array_free( spfx_files );
395
396 /* Reduce size. */
397 qsort( spfx_effects, array_size( spfx_effects ), sizeof( SPFX_Base ),
400
401 /* Trail colour sets. */
403
404 /*
405 * Now initialize force feedback.
406 */
407 memset( &shake_shader, 0, sizeof( LuaShader_t ) );
408 shake_shader.program = shaders.shake.program;
409 shake_shader.VertexPosition = shaders.shake.VertexPosition;
410 shake_shader.ClipSpaceFromLocal = shaders.shake.ClipSpaceFromLocal;
411 shake_shader.MainTex = shaders.shake.MainTex;
414
415 /*
416 * Misc shaders.
417 */
418 memset( &damage_shader, 0, sizeof( LuaShader_t ) );
419 damage_shader.program = shaders.damage.program;
420 damage_shader.VertexPosition = shaders.damage.VertexPosition;
421 damage_shader.ClipSpaceFromLocal = shaders.damage.ClipSpaceFromLocal;
422 damage_shader.MainTex = shaders.damage.MainTex;
423
424 /* Stacks. */
428
429#if DEBUGGING
430 if ( conf.devmode ) {
431 time = SDL_GetTicks() - time;
432 DEBUG( n_( "Loaded %d Special Effect in %.3f s",
433 "Loaded %d Special Effects in %.3f s",
435 array_size( spfx_effects ), time / 1000. );
436 } else
437 DEBUG( n_( "Loaded %d Special Effect", "Loaded %d Special Effects",
440#endif /* DEBUGGING */
441
442 return 0;
443}
444
448void spfx_free( void )
449{
450 /* Clean up the debris. */
452
453 /* get rid of all the particles and free the stacks */
454 spfx_clear();
456 spfx_stack_front = NULL;
458 spfx_stack_middle = NULL;
460 spfx_stack_back = NULL;
461
462 /* now clear the effects */
463 for ( int i = 0; i < array_size( spfx_effects ); i++ )
466 spfx_effects = NULL;
467
468 /* Free the noise. */
470
471 /* Free the trails. */
472 for ( int i = 0; i < array_size( trail_spfx_stack ); i++ )
475 trail_spfx_stack = NULL;
476
477 /* Free the trail styles. */
478 for ( int i = 0; i < array_size( trail_spec_stack ); i++ ) {
479 TrailSpec *ts = &trail_spec_stack[i];
480 free( ts->name );
481 free( ts->filename );
482 if ( ts->shader_path != NULL ) {
483 glDeleteProgram( ts->shader.program );
484 free( ts->shader_path );
485 }
486 }
488 trail_spec_stack = NULL;
489
490 /* Get rid of Lua effects. */
491 spfxL_exit();
492}
493
504void spfx_add( int effect, const double px, const double py, const double vx,
505 const double vy, int layer )
506{
507 SPFX *cur_spfx;
508 double ttl, anim;
509
510 if ( ( effect < 0 ) || ( effect > array_size( spfx_effects ) ) ) {
511 WARN( _( "Trying to add spfx with invalid effect!" ) );
512 return;
513 }
514
515 /*
516 * Select the Layer
517 */
518 if ( layer == SPFX_LAYER_FRONT ) /* front layer */
519 cur_spfx = &array_grow( &spfx_stack_front );
520 else if ( layer == SPFX_LAYER_MIDDLE ) /* middle layer */
521 cur_spfx = &array_grow( &spfx_stack_middle );
522 else if ( layer == SPFX_LAYER_BACK ) /* back layer */
523 cur_spfx = &array_grow( &spfx_stack_back );
524 else {
525 WARN( _( "Invalid SPFX layer." ) );
526 return;
527 }
528
529 /* The actual adding of the spfx */
530 cur_spfx->effect = effect;
531 vec2_csetmin( &cur_spfx->pos, px, py );
532 vec2_csetmin( &cur_spfx->vel, vx, vy );
533 /* Timer magic if ttl != anim */
534 ttl = spfx_effects[effect].ttl;
535 anim = spfx_effects[effect].anim;
536 if ( ttl != anim )
537 cur_spfx->timer = ttl + RNGF() * anim;
538 else
539 cur_spfx->timer = ttl;
540
541 /* Shader magic. */
542 cur_spfx->unique = RNGF();
543 cur_spfx->time = 0.0;
544}
545
549void spfx_clear( void )
550{
551 NTracingZone( _ctx, 1 );
552
553 /* Clear rumble */
554 shake_force_mod = 0.;
555 shake_force_mean = 0.;
556 vectnull( &shake_pos );
557 vectnull( &shake_vel );
558 if ( shake_shader_pp_id > 0 )
559 render_postprocessRm( shake_shader_pp_id );
561 if ( damage_shader_pp_id > 0 )
562 render_postprocessRm( damage_shader_pp_id );
564
565 for ( int i = 0; i < array_size( trail_spfx_stack ); i++ )
569
570 /* Clear the Lua spfx. */
571 spfxL_clear();
572
573 NTracingZoneEnd( _ctx );
574}
575
582void spfx_update( const double dt, const double real_dt )
583{
584 NTracingZone( _ctx, 1 );
585 NTracingPlotI( "spfx", array_size( spfx_stack_front ) +
588 NTracingPlotI( "trails", array_size( trail_spfx_stack ) );
589
593 spfx_update_trails( dt );
594
595 /* Decrement the haptic timer. */
596 if ( haptic_lastUpdate > 0. )
597 haptic_lastUpdate -= real_dt; /* Based on real delta-tick. */
598
599 /* Shake. */
600 spfx_updateShake( dt );
601
602 /* Damage. */
603 spfx_updateDamage( dt );
604
605 /* Update Lua ones. */
606 spfxL_update( dt );
607
608 NTracingZoneEnd( _ctx );
609}
610
617static void spfx_update_layer( SPFX *layer, const double dt )
618{
619 for ( int i = 0; i < array_size( layer ); i++ ) {
620 layer[i].timer -= dt; /* less time to live */
621
622 /* time to die! */
623 if ( layer[i].timer < 0. ) {
624 array_erase( &layer, &layer[i], &layer[i + 1] );
625 i--;
626 continue;
627 }
628 layer[i].time += dt; /* Shader timer. */
629
630 /* actually update it */
631 vec2_cadd( &layer[i].pos, dt * VX( layer[i].vel ),
632 dt * VY( layer[i].vel ) );
633 }
634}
635
639static void spfx_updateShake( double dt )
640{
641 double mod, vmod;
642 double force_x, force_y;
643 double dupdate;
644 int forced;
645
646 /* Must still be on. */
647 if ( shake_shader_pp_id == 0 )
648 return;
649
650 /* The shake decays over time */
651 forced = 0;
652 if ( shake_force_mod > 0. ) {
653 shake_force_mod -= SPFX_SHAKE_DECAY * dt;
654 if ( shake_force_mod < 0. )
655 shake_force_mod = 0.;
656 else
657 forced = 1;
658 }
659 dupdate = dt * 2.0;
661 dupdate * shake_force_mod + ( 1.0 - dupdate ) * shake_force_mean;
662
663 /* See if it's settled down. */
664 mod = VMOD( shake_pos );
665 vmod = VMOD( shake_vel );
666 if ( !forced && ( mod < 0.01 ) && ( vmod < 0.01 ) ) {
667 render_postprocessRm( shake_shader_pp_id );
669 if ( fabs( shake_force_ang ) > 1e3 )
670 shake_force_ang = RNGF();
671 return;
672 }
673
674 /* Calculate force. */
675 force_x = -SHAKE_K * shake_pos.x + -SHAKE_B * shake_vel.x;
676 force_y = -SHAKE_K * shake_pos.y + -SHAKE_B * shake_vel.y;
677
678 /* Apply force if necessary. */
679 if ( forced ) {
680 double angle;
681 shake_force_ang += dt;
682 angle = noise_simplex1( shake_noise, &shake_force_ang ) * 5. * M_PI;
683 force_x += shake_force_mod * cos( angle );
684 force_y += shake_force_mod * sin( angle );
685 }
686
687 /* Update velocity. */
688 vec2_cadd( &shake_vel, ( 1. / SHAKE_MASS ) * force_x * dt,
689 ( 1. / SHAKE_MASS ) * force_y * dt );
690
691 /* Update position. */
692 vec2_cadd( &shake_pos, shake_vel.x * dt, shake_vel.y * dt );
693
694 /* Set the uniform. */
695 glUseProgram( shaders.shake.program );
696 glUniform2f( shaders.shake.shake_pos, shake_pos.x / SCREEN_W,
697 shake_pos.y / SCREEN_H );
698 glUniform2f( shaders.shake.shake_vel, shake_vel.x / SCREEN_W,
699 shake_vel.y / SCREEN_H );
700 glUniform1f( shaders.shake.shake_force, shake_force_mean );
701 glUseProgram( 0 );
702
703 gl_checkErr();
704}
705
706static void spfx_updateDamage( double dt )
707{
708 /* Must still be on. */
709 if ( damage_shader_pp_id == 0 )
710 return;
711
712 /* Decrement and turn off if necessary. */
713 damage_strength -= SPFX_DAMAGE_DECAY * dt;
714 if ( damage_strength < 0. ) {
715 damage_strength = 0.;
716 render_postprocessRm( damage_shader_pp_id );
718 return;
719 }
720
721 /* Set the uniform. */
722 glUseProgram( shaders.damage.program );
723 glUniform1f( shaders.damage.damage_strength, damage_strength );
724 glUseProgram( 0 );
725
726 gl_checkErr();
727}
728
736{
737 Trail_spfx *trail = calloc( 1, sizeof( Trail_spfx ) );
738 trail->spec = spec;
739 trail->capacity = 1;
740 trail->iread = trail->iwrite = 0;
741 trail->point_ringbuf = calloc( trail->capacity, sizeof( TrailPoint ) );
742 trail->refcount = 1;
743 trail->r = RNGF();
744 trail->ontop = 0;
745
746 if ( trail_spfx_stack == NULL )
749
750 return trail;
751}
752
758void spfx_update_trails( double dt )
759{
760 int n = array_size( trail_spfx_stack );
761 for ( int i = 0; i < n; i++ ) {
762 Trail_spfx *trail = trail_spfx_stack[i];
763 spfx_trail_update( trail, dt );
764 if ( !trail->refcount && !trail_size( trail ) ) {
765 spfx_trail_free( trail );
767 }
768 }
769 if ( n < array_size( trail_spfx_stack ) )
771}
772
779static void spfx_trail_update( Trail_spfx *trail, double dt )
780{
781 GLfloat rel_dt = dt / trail->spec->ttl;
782 /* Remove outdated elements. */
783 while ( trail->iread < trail->iwrite && trail_front( trail ).t < rel_dt )
784 trail->iread++;
785
786 /* Update the other trail point's properties. */
787 for ( size_t i = trail->iread; i < trail->iwrite; i++ ) {
788 TrailPoint *trail_point = &trail_at( trail, i );
789 double mod = dt * trail_point->t * trail->spec->accel_mod;
790 trail_point->x += trail_point->dx * mod;
791 trail_point->y += trail_point->dy * mod;
792 trail_point->t -= rel_dt;
793 }
794
795 /* Update timer. */
796 trail->dt += dt;
797}
798
811void spfx_trail_sample( Trail_spfx *trail, double x, double y, double z,
812 double dx, double dy, TrailMode mode, int force )
813{
814 TrailPoint p;
815
816 if ( !force && trail->spec->style[mode].col.a <= 0. )
817 return;
818
819 p.x = x;
820 p.y = y;
821 p.z = z;
822 p.dx = dx;
823 p.dy = dy;
824 p.t = 1.;
825 p.mode = mode;
826
827 /* The "back" of the trail should always reflect our most recent state. */
828 trail_back( trail ) = p;
829
830 /* We may need to insert a control point, but not if our last sample was
831 * recent enough. */
832 if ( !force && trail_size( trail ) > 1 &&
833 trail_at( trail, trail->iwrite - 2 ).t >= 1. - TRAIL_UPDATE_DT )
834 return;
835
836 /* If the last time we inserted a control point was recent enough, we don't
837 * need a new one. */
838 if ( trail_size( trail ) == trail->capacity ) {
839 /* Full! Double capacity, and make the elements contiguous. (We've made
840 * space to grow rightward.) */
841 trail->point_ringbuf = realloc(
842 trail->point_ringbuf, 2 * trail->capacity * sizeof( TrailPoint ) );
843 trail->iread %= trail->capacity;
844 trail->iwrite = trail->iread + trail->capacity;
845 memmove( &trail->point_ringbuf[trail->capacity], trail->point_ringbuf,
846 trail->iread * sizeof( TrailPoint ) );
847 trail->capacity *= 2;
848 }
849 trail_at( trail, trail->iwrite++ ) = p;
850}
851
858{
859 if ( trail != NULL )
860 trail->refcount--;
861}
862
866static void spfx_trail_free( Trail_spfx *trail )
867{
868 assert( trail->refcount == 0 );
869 free( trail->point_ringbuf );
870 free( trail );
871}
872
878void spfx_trail_draw( const Trail_spfx *trail )
879{
880 const TrailSpec *spec;
881 const TrailStyle *styles;
882 GLfloat len;
883 double z;
884
885 size_t n = trail_size( trail );
886 if ( n == 0 )
887 return;
888 spec = trail->spec;
889 styles = spec->style;
890
891 /* Stuff that doesn't change for the entire trail. */
892 glUseProgram( spec->shader.program );
893 glEnableVertexAttribArray( spec->shader.vertex );
894 gl_vboActivateAttribOffset( gl_squareVBO, spec->shader.vertex, 0, 2,
895 GL_FLOAT, 0 );
896 glUniform1f( spec->shader.dt, trail->dt );
897 glUniform1f( spec->shader.r, trail->r );
898
899 /* Start drawing from head to tail. */
900 z = cam_getZoom();
901 len = 0.;
902 for ( size_t i = trail->iread + 1; i < trail->iwrite; i++ ) {
903 mat4 projection;
904 const TrailStyle *sp, *spp;
905 double x1, y1, x2, y2, s;
906 TrailPoint *tp = &trail_at( trail, i );
907 TrailPoint *tpp = &trail_at( trail, i - 1 );
908
909 /* Ignore none modes. */
910 if ( tp->mode == MODE_NONE || tpp->mode == MODE_NONE )
911 continue;
912
913 gl_gameToScreenCoords( &x1, &y1, tp->x, tp->y );
914 gl_gameToScreenCoords( &x2, &y2, tpp->x, tpp->y );
915
916 s = hypot( x2 - x1, y2 - y1 );
917 if ( s <= 0. )
918 continue;
919
920 /* Make sure in bounds. */
921 if ( ( MAX( x1, x2 ) < 0. ) || ( MIN( x1, x2 ) > (double)SCREEN_W ) ||
922 ( MAX( y1, y2 ) < 0. ) || ( MIN( y1, y2 ) > (double)SCREEN_H ) ) {
923 len += s;
924 continue;
925 }
926
927 sp = &styles[tp->mode];
928 spp = &styles[tpp->mode];
929
930 /* Set vertex. */
931 projection = gl_view_matrix;
932 mat4_translate_xy( &projection, x1, y1 );
933 mat4_rotate2dv( &projection, ( x2 - x1 ) / s, ( y2 - y1 ) / s );
934 mat4_scale_xy( &projection, s, z * ( sp->thick + spp->thick ) );
935 mat4_translate_xy( &projection, 0., -0.5 );
936
937 /* Set uniforms. */
938 gl_uniformMat4( spec->shader.projection, &projection );
939 gl_uniformColour( spec->shader.c1, &sp->col );
940 gl_uniformColour( spec->shader.c2, &spp->col );
941 glUniform2f( spec->shader.t, tp->t, tpp->t );
942 glUniform2f( spec->shader.z, tp->z, tpp->z );
943 glUniform2f( spec->shader.pos2, len, sp->thick );
944 len += s;
945 glUniform2f( spec->shader.pos1, len, spp->thick );
946
947 /* Draw. */
948 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
949 }
950
951 /* Clear state. */
952 glDisableVertexAttribArray( spec->shader.vertex );
953 glUseProgram( 0 );
954
955 /* Check errors. */
956 gl_checkErr();
957}
958
966void spfx_shake( double mod )
967{
968 /* Add the modifier. */
970 MIN( SPFX_SHAKE_MAX, shake_force_mod + SPFX_SHAKE_MOD * mod );
971
972 /* Rumble if it wasn't rumbling before. */
973 spfx_hapticRumble( mod );
974
975 /* Create the shake. */
976 if ( shake_shader_pp_id == 0 )
978 render_postprocessAdd( &shake_shader, PP_LAYER_GAME, 99, 0 );
979}
980
988void spfx_damage( double mod )
989{
991 MIN( SPFX_DAMAGE_MAX, damage_strength + SPFX_DAMAGE_MOD * mod );
992
993 /* Create the damage. */
994 if ( damage_shader_pp_id == 0 )
996 render_postprocessAdd( &damage_shader, PP_LAYER_GUI, 98, 0 );
997}
998
1002void spfx_setNebulaColour( double r, double g, double b )
1003{
1004 for ( int i = 0; i < array_size( trail_spec_stack ); i++ ) {
1005 TrailSpec *ts = &trail_spec_stack[i];
1006 glUseProgram( ts->shader.program );
1007 glUniform3f( ts->shader.nebu_col, r, g, b );
1008 }
1009 glUseProgram( 0 );
1010}
1011
1017static int spfx_hapticInit( void )
1018{
1019 SDL_HapticEffect *efx;
1020
1021 /* Haptic must be enabled. */
1022 if ( haptic == NULL )
1023 return 0;
1024
1025 efx = &haptic_rumbleEffect;
1026 memset( efx, 0, sizeof( SDL_HapticEffect ) );
1027 efx->type = SDL_HAPTIC_SINE;
1028 efx->periodic.direction.type = SDL_HAPTIC_POLAR;
1029 efx->periodic.length = 1000;
1030 efx->periodic.period = 200;
1031 efx->periodic.magnitude = 0x4000;
1032 efx->periodic.fade_length = 1000;
1033 efx->periodic.fade_level = 0;
1034
1035 haptic_rumble = SDL_HapticNewEffect( haptic, efx );
1036 if ( haptic_rumble < 0 ) {
1037 WARN( _( "Unable to upload haptic effect: %s." ), SDL_GetError() );
1038 return -1;
1039 }
1040
1041 return 0;
1042}
1043
1049static void spfx_hapticRumble( double mod )
1050{
1051 SDL_HapticEffect *efx;
1052 double len, mag;
1053
1054 /* Not active. */
1055 if ( haptic_rumble < 0 )
1056 return;
1057
1058 /* Not time to update yet. */
1059 if ( ( haptic_lastUpdate > 0. ) || ( shake_shader_pp_id == 0 ) ||
1060 ( mod > SPFX_SHAKE_MAX / 3. ) )
1061 return;
1062
1063 /* Stop the effect if it was playing. */
1064 SDL_HapticStopEffect( haptic, haptic_rumble );
1065
1066 /* Get length and magnitude. */
1067 len = 1000. * shake_force_mod / SPFX_SHAKE_DECAY;
1068 mag = 32767. * ( shake_force_mod / SPFX_SHAKE_MAX );
1069
1070 /* Update the effect. */
1071 efx = &haptic_rumbleEffect;
1072 efx->periodic.magnitude = (int16_t)mag;
1073 efx->periodic.length = (uint32_t)len;
1074 efx->periodic.fade_length = MIN( efx->periodic.length, 1000 );
1075 if ( SDL_HapticUpdateEffect( haptic, haptic_rumble, &haptic_rumbleEffect ) <
1076 0 ) {
1077 WARN( _( "Failed to update haptic effect: %s." ), SDL_GetError() );
1078 return;
1079 }
1080
1081 /* Run the new effect. */
1082 SDL_HapticRunEffect( haptic, haptic_rumble, 1 );
1083
1084 /* Set timer again. */
1086}
1087
1093void spfx_cinematic( void )
1094{
1095 gl_renderRect( 0., 0., SCREEN_W, SCREEN_H * 0.2, &cBlack );
1096 gl_renderRect( 0., SCREEN_H * 0.8, SCREEN_W, SCREEN_H, &cBlack );
1097}
1098
1099static void spfx_renderStack( SPFX *spfx_stack )
1100{
1101 for ( int i = array_size( spfx_stack ) - 1; i >= 0; i-- ) {
1102 SPFX *spfx = &spfx_stack[i];
1103 SPFX_Base *effect = &spfx_effects[spfx->effect];
1104
1105 /* Render shader. */
1106 if ( effect->shader >= 0 ) {
1107 double x, y, z, s2;
1108 double w, h;
1109 mat4 projection;
1110
1111 /* Translate coords. */
1112 s2 = effect->size / 2.;
1113 z = cam_getZoom();
1114 gl_gameToScreenCoords( &x, &y, spfx->pos.x - s2, spfx->pos.y - s2 );
1115 w = h = effect->size * z;
1116
1117 /* Check if inbounds. */
1118 if ( ( x < -w ) || ( x > SCREEN_W + w ) || ( y < -h ) ||
1119 ( y > SCREEN_H + h ) )
1120 continue;
1121
1122 /* Let's get to business. */
1123 glUseProgram( effect->shader );
1124
1125 /* Set up the vertex. */
1126 projection = gl_view_matrix;
1127 mat4_translate_scale_xy( &projection, x, y, w, h );
1128 glEnableVertexAttribArray( effect->vertex );
1129 gl_vboActivateAttribOffset( gl_squareVBO, effect->vertex, 0, 2,
1130 GL_FLOAT, 0 );
1131
1132 /* Set shader uniforms. */
1133 gl_uniformMat4( effect->projection, &projection );
1134 glUniform1f( effect->u_time, spfx->time );
1135 glUniform1f( effect->u_r, spfx->unique );
1136 glUniform1f( effect->u_size, effect->size );
1137
1138 /* Draw. */
1139 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1140
1141 /* Clear state. */
1142 glDisableVertexAttribArray( shaders.texture.vertex );
1143
1144 /* anything failed? */
1145 gl_checkErr();
1146
1147 glUseProgram( 0 );
1148
1149 }
1150 /* No shader. */
1151 else {
1152 int sx, sy;
1153
1154 /* Simplifies */
1155 sx = (int)effect->gfx->sx;
1156 sy = (int)effect->gfx->sy;
1157
1158 if ( !paused ) { /* don't calculate frame if paused */
1159 double time =
1160 1. - fmod( spfx_stack[i].timer, effect->anim ) / effect->anim;
1161 spfx_stack[i].lastframe = sx * sy * MIN( time, 1. );
1162 }
1163
1164 /* Renders */
1165 gl_renderSprite( effect->gfx, VX( spfx_stack[i].pos ),
1166 VY( spfx_stack[i].pos ), spfx_stack[i].lastframe % sx,
1167 spfx_stack[i].lastframe / sx, NULL );
1168 }
1169 }
1170}
1171
1178void spfx_render( int layer, double dt )
1179{
1180 NTracingZone( _ctx, 1 );
1181
1182 /* get the appropriate layer */
1183 switch ( layer ) {
1184 case SPFX_LAYER_FRONT:
1185 spfx_renderStack( spfx_stack_front );
1186 spfxL_renderfg( dt );
1187 break;
1188
1189 case SPFX_LAYER_MIDDLE:
1190 spfx_renderStack( spfx_stack_middle );
1191 spfxL_rendermg( dt );
1192 break;
1193
1194 case SPFX_LAYER_BACK:
1195 spfx_renderStack( spfx_stack_back );
1196 spfxL_renderbg( dt );
1197
1198 NTracingZoneName( _ctx_trails, "spfx_render[trails]", 1 );
1199 /* Trails are special (for now?). */
1200 for ( int i = 0; i < array_size( trail_spfx_stack ); i++ ) {
1201 const Trail_spfx *trail = trail_spfx_stack[i];
1202 if ( !trail->ontop )
1203 spfx_trail_draw( trail );
1204 }
1205 NTracingZoneEnd( _ctx_trails );
1206 break;
1207
1208 default:
1209 WARN( _( "Rendering invalid SPFX layer." ) );
1210 break;
1211 }
1212
1213 NTracingZoneEnd( _ctx );
1214}
1215
1220static int trailSpec_parse( TrailSpec *tc, const char *file, int firstpass )
1221{
1222 static const char *mode_tags[] = MODE_TAGS;
1223 char *inherits;
1224 xmlNodePtr parent, node;
1225 xmlDocPtr doc;
1226
1227 /* Load the data. */
1228 doc = xml_parsePhysFS( file );
1229 if ( doc == NULL )
1230 return -1;
1231
1232 /* Get the first node. */
1233 parent = doc->xmlChildrenNode; /* first event node */
1234 if ( parent == NULL ) {
1235 WARN( _( "Malformed '%s' file: does not contain elements" ), file );
1236 return -1;
1237 }
1238
1239 if ( firstpass ) {
1240 memset( tc, 0, sizeof( TrailSpec ) );
1241 for ( int i = 0; i < MODE_MAX; i++ )
1242 tc->style[i].thick = 1.;
1243 }
1244
1245 xmlr_attr_strd( parent, "inherits", inherits );
1246 if ( firstpass ) {
1247 xmlr_attr_strd( parent, "name", tc->name );
1248 if ( inherits != NULL ) {
1249 /* Skip this pass. */
1250 free( inherits );
1251 xmlFreeDoc( doc );
1252 return 0;
1253 }
1254 } else {
1255 if ( inherits == NULL ) {
1256 /* Already done here. */
1257 free( inherits );
1258 xmlFreeDoc( doc );
1259 return 0;
1260 } else {
1261 const TrailSpec *tsparent = trailSpec_getRaw( inherits );
1262 if ( tsparent == NULL )
1263 WARN(
1264 _( "Trail '%s' that inherits from '%s' has missing reference!" ),
1265 tc->name, inherits );
1266 else {
1267 char *name = tc->name;
1268 char *filename = tc->filename;
1269 memcpy( tc, tsparent, sizeof( TrailSpec ) );
1270 tc->name = name;
1271 tc->filename = filename;
1272 tc->shader_path = NULL;
1273 }
1274 }
1275 }
1276
1277 node = parent->xmlChildrenNode;
1278 do {
1279 xml_onlyNodes( node );
1280
1281 xmlr_strd( node, "shader", tc->shader_path );
1282
1283 if ( xml_isNode( node, "thickness" ) )
1284 tc->def_thick = xml_getFloat( node );
1285 else if ( xml_isNode( node, "ttl" ) )
1286 tc->ttl = xml_getFloat( node );
1287 else if ( xml_isNode( node, "nebula" ) )
1288 tc->nebula = xml_getInt( node );
1289 else if ( xml_isNode( node, "accel_mod" ) )
1290 tc->accel_mod = xml_getFloat( node );
1291 else {
1292 int i;
1293 for ( i = 0; i < MODE_MAX; i++ )
1294 if ( xml_isNode( node, mode_tags[i] ) ) {
1295 xmlr_attr_float_opt( node, "r", tc->style[i].col.r );
1296 xmlr_attr_float_opt( node, "g", tc->style[i].col.g );
1297 xmlr_attr_float_opt( node, "b", tc->style[i].col.b );
1298 xmlr_attr_float_opt( node, "a", tc->style[i].col.a );
1299 xmlr_attr_float_opt( node, "scale", tc->style[i].thick );
1300 col_gammaToLinear( &tc->style[i].col );
1301 break;
1302 }
1303 if ( i == MODE_MAX )
1304 WARN( _( "Trail '%s' has unknown node '%s'." ), tc->name,
1305 node->name );
1306 }
1307 } while ( xml_nextNode( node ) );
1308
1309 /* Load shader. */
1310 if ( firstpass && ( tc->shader_path != NULL ) ) {
1311 tc->shader.program =
1312 gl_program_vert_frag( "trail.vert", tc->shader_path );
1313 tc->shader.vertex = glGetAttribLocation( tc->shader.program, "vertex" );
1314 tc->shader.projection =
1315 glGetUniformLocation( tc->shader.program, "projection" );
1316 tc->shader.r = glGetUniformLocation( tc->shader.program, "r" );
1317 tc->shader.dt = glGetUniformLocation( tc->shader.program, "dt" );
1318 tc->shader.c1 = glGetUniformLocation( tc->shader.program, "c1" );
1319 tc->shader.c2 = glGetUniformLocation( tc->shader.program, "c2" );
1320 tc->shader.t = glGetUniformLocation( tc->shader.program, "t" );
1321 tc->shader.z = glGetUniformLocation( tc->shader.program, "z" );
1322 tc->shader.pos1 = glGetUniformLocation( tc->shader.program, "pos1" );
1323 tc->shader.pos2 = glGetUniformLocation( tc->shader.program, "pos2" );
1324 tc->shader.nebu_col =
1325 glGetUniformLocation( tc->shader.program, "nebu_col" );
1326 gl_checkErr();
1327 }
1328
1329#define MELEMENT( o, s ) \
1330 if ( o ) \
1331 WARN( _( "Trail '%s' missing '%s' element" ), tc->name, s )
1332 MELEMENT( tc->shader.program == 0, "shader" );
1333 MELEMENT( tc->def_thick == 0, "thickness" );
1334 MELEMENT( tc->ttl == 0, "ttl" );
1335#undef MELEMENT
1336
1337 /* Clean up. */
1338 free( inherits );
1339 xmlFreeDoc( doc );
1340 return 0;
1341}
1342
1348static int trailSpec_load( void )
1349{
1350 char **ts_files = ndata_listRecursive( TRAIL_DATA_PATH );
1351
1353
1354 /* First pass sets up and prepares inheritance. */
1355 for ( int i = 0; i < array_size( ts_files ); i++ ) {
1356 TrailSpec tc;
1357 int ret = trailSpec_parse( &tc, ts_files[i], 1 );
1358 if ( ret == 0 ) {
1359 tc.filename = ts_files[i];
1361 } else
1362 free( ts_files[i] );
1363 }
1364
1365 /* Second pass to complete inheritance. */
1366 for ( int i = 0; i < array_size( trail_spec_stack ); i++ )
1367 trailSpec_parse( &trail_spec_stack[i], trail_spec_stack[i].filename, 0 );
1368 array_free( ts_files );
1369
1370 /* Set up thickness. */
1371 for ( TrailSpec *tc = array_begin( trail_spec_stack );
1372 tc != array_end( trail_spec_stack ); tc++ ) {
1373 for ( int i = 0; i < MODE_MAX; i++ )
1374 tc->style[i].thick *= tc->def_thick;
1375 }
1377
1378 return 0;
1379}
1380
1381static TrailSpec *trailSpec_getRaw( const char *name )
1382{
1383 for ( int i = 0; i < array_size( trail_spec_stack ); i++ ) {
1384 if ( strcmp( trail_spec_stack[i].name, name ) == 0 )
1385 return &trail_spec_stack[i];
1386 }
1387 WARN( _( "Trail type '%s' not found in stack" ), name );
1388 return NULL;
1389}
1390
1396const TrailSpec *trailSpec_get( const char *name )
1397{
1398 return trailSpec_getRaw( name );
1399}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:214
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition array.h:113
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition array.h:160
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:206
#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:101
void debris_cleanup(void)
Cleans up after the debris.
Definition debris.c:28
unsigned int haptic_query
Definition joystick.c:22
SDL_Haptic * haptic
Definition joystick.c:21
void mat4_rotate2dv(mat4 *m, double c, double s)
Rotates the +x axis to the given vector.
Definition mat4.c:191
static double real_dt
Definition naev.c:111
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define MAX(x, y)
Definition naev.h:37
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
Definition ndata.c:420
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition ndata.c:286
void spfxL_renderbg(double dt)
Renders the Lua SPFX on the background.
Definition nlua_spfx.c:778
void spfxL_rendermg(double dt)
Renders the Lua SPFX in the midground.
Definition nlua_spfx.c:788
void spfxL_renderfg(double dt)
Renders the Lua SPFX in the foreground.
Definition nlua_spfx.c:798
void spfxL_clear(void)
Clears the Lua spfx.
Definition nlua_spfx.c:636
void spfxL_update(double dt)
Updates the spfx.
Definition nlua_spfx.c:661
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:25
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:70
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:835
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:224
int paused
Definition pause.c:18
perlin_data_t * noise_new(void)
Creates a new perlin noise generator.
Definition perlin.c:71
void noise_delete(perlin_data_t *pdata)
Frees some noise data.
Definition perlin.c:135
float noise_simplex1(perlin_data_t *pdata, float f[1])
Gets 1D simplex noise for a position.
Definition perlin.c:108
#define SPFX_XML_ID
Definition spfx.c:34
static double damage_strength
Definition spfx.c:77
static SPFX * spfx_stack_front
Definition spfx.c:138
static TrailSpec * trail_spec_stack
Definition spfx.c:50
void spfx_free(void)
Frees the spfx stack.
Definition spfx.c:448
static LuaShader_t shake_shader
Definition spfx.c:59
int spfx_load(void)
Loads the spfx stack.
Definition spfx.c:375
static double shake_force_mod
Definition spfx.c:62
static Trail_spfx ** trail_spfx_stack
Definition spfx.c:51
void spfx_render(int layer, double dt)
Renders the entire spfx layer.
Definition spfx.c:1178
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
Definition spfx.c:1396
static SPFX_Base * spfx_effects
Definition spfx.c:116
#define TRAIL_UPDATE_DT
Definition spfx.c:48
#define SHAKE_B
Definition spfx.c:41
static void spfx_update_layer(SPFX *layer, const double dt)
Updates an individual spfx.
Definition spfx.c:617
static int trailSpec_load(void)
Loads the trail colour sets.
Definition spfx.c:1348
static void spfx_base_free(SPFX_Base *effect)
Frees a SPFX_Base.
Definition spfx.c:343
void spfx_cinematic(void)
Sets the cinematic mode.
Definition spfx.c:1093
static int haptic_rumble
Definition spfx.c:69
static int spfx_base_parse(SPFX_Base *temp, const char *filename)
Parses an xml node containing a SPFX.
Definition spfx.c:175
void spfx_trail_sample(Trail_spfx *trail, double x, double y, double z, double dx, double dy, TrailMode mode, int force)
Makes a trail grow.
Definition spfx.c:811
static SPFX * spfx_stack_middle
Definition spfx.c:139
void spfx_shake(double mod)
Increases the current rumble level.
Definition spfx.c:966
static unsigned int damage_shader_pp_id
Definition spfx.c:74
static LuaShader_t damage_shader
Definition spfx.c:76
static int trailSpec_parse(TrailSpec *tc, const char *file, int firstpass)
Parses raw values out of a "trail" element.
Definition spfx.c:1220
static void spfx_hapticRumble(double mod)
Runs a rumble effect.
Definition spfx.c:1049
static double haptic_lastUpdate
Definition spfx.c:71
static float shake_force_ang
Definition spfx.c:64
static unsigned int shake_shader_pp_id
Definition spfx.c:57
void spfx_clear(void)
Clears all the currently running effects.
Definition spfx.c:549
void spfx_setNebulaColour(double r, double g, double b)
Sets the nebula colour where applicable.
Definition spfx.c:1002
static vec2 shake_pos
Definition spfx.c:60
int spfx_get(const char *name)
Gets the id of an spfx based on name.
Definition spfx.c:355
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition spfx.c:857
static void spfx_trail_update(Trail_spfx *trail, double dt)
Updates a trail.
Definition spfx.c:779
static perlin_data_t * shake_noise
Definition spfx.c:65
static void spfx_updateShake(double dt)
Updates the shake position.
Definition spfx.c:639
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:504
static void spfx_update_trails(double dt)
Updates all trails (handling dispersal/fadeout).
Definition spfx.c:758
static int spfx_base_cmp(const void *p1, const void *p2)
For sorting and stuff.
Definition spfx.c:161
void spfx_damage(double mod)
Increases the current damage level.
Definition spfx.c:988
static double shake_force_mean
Definition spfx.c:63
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition spfx.c:735
static SPFX * spfx_stack_back
Definition spfx.c:140
#define HAPTIC_UPDATE_INTERVAL
Definition spfx.c:45
static void spfx_trail_free(Trail_spfx *trail)
Deallocates an unreferenced, expired trail.
Definition spfx.c:866
void spfx_update(const double dt, const double real_dt)
Updates all the spfx.
Definition spfx.c:582
static int spfx_hapticInit(void)
Initializes the rumble effect.
Definition spfx.c:1017
void spfx_trail_draw(const Trail_spfx *trail)
Draws a trail on screen.
Definition spfx.c:878
#define SHAKE_K
Definition spfx.c:40
static vec2 shake_vel
Definition spfx.c:61
static SDL_HapticEffect haptic_rumbleEffect
Definition spfx.c:70
Generic special effect.
Definition spfx.c:97
char * name
Definition spfx.c:98
GLint u_time
Definition spfx.c:111
double anim
Definition spfx.c:101
GLint shader
Definition spfx.c:108
GLint u_size
Definition spfx.c:113
double size
Definition spfx.c:107
GLint u_r
Definition spfx.c:112
glTexture * gfx
Definition spfx.c:104
double ttl
Definition spfx.c:100
An actual in-game active special effect.
Definition spfx.c:123
double timer
Definition spfx.c:130
int lastframe
Definition spfx.c:127
vec2 vel
Definition spfx.c:125
GLfloat unique
Definition spfx.c:134
GLfloat time
Definition spfx.c:133
int effect
Definition spfx.c:128
vec2 pos
Definition spfx.c:124
GLfloat dy
Definition spfx.h:84
TrailMode mode
Definition spfx.h:85
GLfloat z
Definition spfx.h:81
GLfloat t
Definition spfx.h:82
represents a set of styles for trails.
Definition spfx.h:52
int nebula
Definition spfx.h:60
double ttl
Definition spfx.h:55
float def_thick
Definition spfx.h:57
TrailStyle style[MODE_MAX]
Definition spfx.h:59
double accel_mod
Definition spfx.h:56
char * shader_path
Definition spfx.h:63
char * name
Definition spfx.h:53
Represents the appearance characteristics for a given trail mode.
Definition spfx.h:26
glColour col
Definition spfx.h:27
float thick
Definition spfx.h:28
A trail generated by a ship or an ammo.
Definition spfx.h:93
int refcount
Definition spfx.h:100
double dt
Definition spfx.h:101
TrailPoint * point_ringbuf
Definition spfx.h:96
GLfloat r
Definition spfx.h:102
size_t iread
Definition spfx.h:98
size_t iwrite
Definition spfx.h:99
unsigned int ontop
Definition spfx.h:103
size_t capacity
Definition spfx.h:97
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
double sx
Definition opengl_tex.h:51
double sy
Definition opengl_tex.h:52
Definition mat4.h:12
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46