naev 0.10.4
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_parse( SPFX_Base *temp, const char *filename );
143static void spfx_base_free( SPFX_Base *effect );
144static void spfx_update_layer( SPFX *layer, const double dt );
145/* Haptic. */
146static int spfx_hapticInit (void);
147static void spfx_hapticRumble( double mod );
148/* Trail. */
149static void spfx_update_trails( double dt );
150static void spfx_trail_update( Trail_spfx* trail, double dt );
151static void spfx_trail_free( Trail_spfx* trail );
152
160static int spfx_base_parse( SPFX_Base *temp, const char *filename )
161{
162 xmlNodePtr node, cur, uniforms;
163 char *shadervert, *shaderfrag;
164 const char *name;
165 int isint;
166 GLint loc, dim;
167 xmlDocPtr doc;
168
169 /* Load and read the data. */
170 doc = xml_parsePhysFS( filename );
171 if (doc == NULL)
172 return -1;
173
174 /* Check to see if document exists. */
175 node = doc->xmlChildrenNode;
176 if (!xml_isNode(node,SPFX_XML_ID)) {
177 ERR( _("Malformed '%s' file: missing root element '%s'"), filename, SPFX_XML_ID);
178 return -1;
179 }
180
181 /* Clear data. */
182 memset( temp, 0, sizeof(SPFX_Base) );
183 temp->shader = -1;
184 shadervert = NULL;
185 shaderfrag = NULL;
186 uniforms = NULL;
187
188 xmlr_attr_strd( node, "name", temp->name );
189
190 /* Extract the data. */
191 node = node->xmlChildrenNode;
192 do {
193 xml_onlyNodes(node);
194 xmlr_float(node, "anim", temp->anim);
195 xmlr_float(node, "ttl", temp->ttl);
196 if (xml_isNode(node,"gfx")) {
197 temp->gfx = xml_parseTexture( node,
198 SPFX_GFX_PATH"%s", 6, 5, 0 );
199 continue;
200 }
201
202 if (xml_isNode(node,"shader")) {
203 cur = node->xmlChildrenNode;
204 do {
205 xml_onlyNodes(cur);
206 xmlr_strd(cur, "vert", shadervert);
207 xmlr_strd(cur, "frag", shaderfrag);
208 xmlr_float(cur, "size", temp->size);
209 if (xml_isNode(cur,"uniforms")) {
210 uniforms = cur;
211 continue;
212 }
213 } while (xml_nextNode(cur));
214 continue;
215 }
216 WARN(_("SPFX '%s' has unknown node '%s'."), temp->name, node->name);
217 } while (xml_nextNode(node));
218
219 /* Convert from ms to s. */
220 temp->anim /= 1000.;
221 temp->ttl /= 1000.;
222 if (temp->ttl == 0.)
223 temp->ttl = temp->anim;
224
225 /* Has shaders. */
226 if (shadervert != NULL && shaderfrag != NULL) {
227 temp->shader = gl_program_vert_frag( shadervert, shaderfrag );
228 temp->vertex = glGetAttribLocation( temp->shader, "vertex");
229 temp->projection = glGetUniformLocation( temp->shader, "projection");
230 temp->u_r = glGetUniformLocation( temp->shader, "u_r" );
231 temp->u_time = glGetUniformLocation( temp->shader, "u_time" );
232 temp->u_size = glGetUniformLocation( temp->shader, "u_size" );
233 if (uniforms != NULL) {
234 glUseProgram( temp->shader );
235 node = uniforms->xmlChildrenNode;
236 do {
237 xml_onlyNodes(node);
238 name = (char*)node->name;
239 loc = glGetUniformLocation( temp->shader, name );
240 if (loc < 0) {
241 WARN(_("SPFX '%s' is trying to set uniform '%s' not in shader!"), temp->name, name );
242 continue;
243 }
244 xmlr_attr_int_def(node,"int",isint,0);
245 /* Get dimension */
246 if (xmlHasProp(node,(xmlChar*)"w")) dim = 4;
247 else if (xmlHasProp(node,(xmlChar*)"z")) dim = 3;
248 else if (xmlHasProp(node,(xmlChar*)"y")) dim = 2;
249 else dim = 1;
250 /* Values default to 0. */
251 if (isint) {
252 int ix, iy, iz, iw;
253 xmlr_attr_int(node, "x", ix );
254 xmlr_attr_int(node, "y", iy );
255 xmlr_attr_int(node, "z", iz );
256 xmlr_attr_int(node, "w", iw );
257 switch (dim) {
258 case 1:
259 glUniform1i( loc, ix );
260 break;
261 case 2:
262 glUniform2i( loc, ix, iy );
263 break;
264 case 3:
265 glUniform3i( loc, ix, iy, iz );
266 break;
267 case 4:
268 glUniform4i( loc, ix, iy, iz, iw );
269 break;
270 }
271 }
272 else {
273 double x, y, z, w;
274 xmlr_attr_float(node, "x", x );
275 xmlr_attr_float(node, "y", y );
276 xmlr_attr_float(node, "z", z );
277 xmlr_attr_float(node, "w", w );
278 switch (dim) {
279 case 1:
280 glUniform1f( loc, x );
281 break;
282 case 2:
283 glUniform2f( loc, x, y );
284 break;
285 case 3:
286 glUniform3f( loc, x, y, z );
287 break;
288 case 4:
289 glUniform4f( loc, x, y, z, w );
290 break;
291 }
292 }
293 } while (xml_nextNode(node));
294 glUseProgram( 0 );
295 }
296 gl_checkErr();
297 }
298
299#define MELEMENT(o,s) \
300 if (o) WARN( _("SPFX '%s' missing/invalid '%s' element"), temp->name, s)
301 MELEMENT(temp->anim==0.,"anim");
302 MELEMENT(temp->ttl==0.,"ttl");
303 MELEMENT(temp->gfx==NULL && (shadervert==NULL || shaderfrag==NULL),"gfx or shader");
304 MELEMENT(temp->shader>=0 && temp->size<=0., "shader/size");
305#undef MELEMENT
306
307 free(shadervert);
308 free(shaderfrag);
309
310 /* Clean up. */
311 xmlFreeDoc(doc);
312
313 return 0;
314}
315
321static void spfx_base_free( SPFX_Base *effect )
322{
323 free(effect->name);
324 gl_freeTexture(effect->gfx);
325}
326
333int spfx_get( char* name )
334{
335 for (int i=0; i<array_size(spfx_effects); i++)
336 if (strcmp(spfx_effects[i].name, name)==0)
337 return i;
338 return -1;
339}
340
348int spfx_load (void)
349{
350 char **spfx_files;
351 Uint32 time = SDL_GetTicks();
352
354
355 spfx_files = ndata_listRecursive( SPFX_DATA_PATH );
356 for (int i=0; i<array_size(spfx_files); i++) {
357 if (ndata_matchExt( spfx_files[i], "xml" )) {
358 SPFX_Base spfx;
359 int ret = spfx_base_parse( &spfx, spfx_files[i] );
360 if (ret == 0)
362 }
363 free( spfx_files[i] );
364 }
365 array_free( spfx_files );
366
367 /* Reduce size. */
369
370 /* Trail colour sets. */
372
373 /*
374 * Now initialize force feedback.
375 */
376 memset( &shake_shader, 0, sizeof(LuaShader_t) );
377 shake_shader.program = shaders.shake.program;
378 shake_shader.VertexPosition= shaders.shake.VertexPosition;
379 shake_shader.ClipSpaceFromLocal = shaders.shake.ClipSpaceFromLocal;
380 shake_shader.MainTex = shaders.shake.MainTex;
383
384 /*
385 * Misc shaders.
386 */
387 memset( &damage_shader, 0, sizeof(LuaShader_t) );
388 damage_shader.program = shaders.damage.program;
389 damage_shader.VertexPosition= shaders.damage.VertexPosition;
390 damage_shader.ClipSpaceFromLocal = shaders.damage.ClipSpaceFromLocal;
391 damage_shader.MainTex = shaders.damage.MainTex;
392
393 /* Stacks. */
397
398 if (conf.devmode) {
399 time = SDL_GetTicks() - time;
400 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. );
401 }
402 else
403 DEBUG( n_( "Loaded %d Special Effect", "Loaded %d Special Effects", array_size(spfx_effects) ), array_size(spfx_effects) );
404
405 return 0;
406}
407
411void spfx_free (void)
412{
413 /* Clean up the debris. */
415
416 /* get rid of all the particles and free the stacks */
417 spfx_clear();
419 spfx_stack_front = NULL;
421 spfx_stack_middle = NULL;
423 spfx_stack_back = NULL;
424
425 /* now clear the effects */
426 for (int i=0; i<array_size(spfx_effects); i++)
429 spfx_effects = NULL;
430
431 /* Free the noise. */
433
434 /* Free the trails. */
435 for (int i=0; i<array_size(trail_spfx_stack); i++)
438 trail_spfx_stack = NULL;
439
440 /* Free the trail styles. */
441 for (int i=0; i<array_size(trail_spec_stack); i++) {
442 free( trail_spec_stack[i].name );
443 free( trail_spec_stack[i].filename );
444 }
446 trail_spec_stack = NULL;
447
448 /* Get rid of Lua effects. */
449 spfxL_exit();
450}
451
462void spfx_add( int effect,
463 const double px, const double py,
464 const double vx, const double vy,
465 int layer )
466{
467 SPFX *cur_spfx;
468 double ttl, anim;
469
470 if ((effect < 0) || (effect > array_size(spfx_effects))) {
471 WARN(_("Trying to add spfx with invalid effect!"));
472 return;
473 }
474
475 /*
476 * Select the Layer
477 */
478 if (layer == SPFX_LAYER_FRONT) /* front layer */
479 cur_spfx = &array_grow( &spfx_stack_front );
480 else if (layer == SPFX_LAYER_MIDDLE) /* middle layer */
481 cur_spfx = &array_grow( &spfx_stack_middle );
482 else if (layer == SPFX_LAYER_BACK) /* back layer */
483 cur_spfx = &array_grow( &spfx_stack_back );
484 else {
485 WARN(_("Invalid SPFX layer."));
486 return;
487 }
488
489 /* The actual adding of the spfx */
490 cur_spfx->effect = effect;
491 vec2_csetmin( &cur_spfx->pos, px, py );
492 vec2_csetmin( &cur_spfx->vel, vx, vy );
493 /* Timer magic if ttl != anim */
494 ttl = spfx_effects[effect].ttl;
495 anim = spfx_effects[effect].anim;
496 if (ttl != anim)
497 cur_spfx->timer = ttl + RNGF()*anim;
498 else
499 cur_spfx->timer = ttl;
500
501 /* Shader magic. */
502 cur_spfx->unique = RNGF();
503 cur_spfx->time = 0.0;
504}
505
509void spfx_clear (void)
510{
511 /* Clear rumble */
512 shake_force_mod = 0.;
513 shake_force_mean = 0.;
514 vectnull( &shake_pos );
515 vectnull( &shake_vel );
516 if (shake_shader_pp_id > 0)
517 render_postprocessRm( shake_shader_pp_id );
519 if (damage_shader_pp_id > 0)
520 render_postprocessRm( damage_shader_pp_id );
522
523 for (int i=0; i<array_size(trail_spfx_stack); i++)
526
527 /* Clear the Lua spfx. */
528 spfxL_clear();
529}
530
537void spfx_update( const double dt, const double real_dt )
538{
542 spfx_update_trails( dt );
543
544 /* Decrement the haptic timer. */
545 if (haptic_lastUpdate > 0.)
546 haptic_lastUpdate -= real_dt; /* Based on real delta-tick. */
547
548 /* Shake. */
549 spfx_updateShake( dt );
550
551 /* Damage. */
552 spfx_updateDamage( dt );
553
554 /* Update Lua ones. */
555 spfxL_update( dt );
556}
557
564static void spfx_update_layer( SPFX *layer, const double dt )
565{
566 for (int i=0; i<array_size(layer); i++) {
567 layer[i].timer -= dt; /* less time to live */
568
569 /* time to die! */
570 if (layer[i].timer < 0.) {
571 array_erase( &layer, &layer[i], &layer[i+1] );
572 i--;
573 continue;
574 }
575 layer[i].time += dt; /* Shader timer. */
576
577 /* actually update it */
578 vec2_cadd( &layer[i].pos, dt*VX(layer[i].vel), dt*VY(layer[i].vel) );
579 }
580}
581
585static void spfx_updateShake( double dt )
586{
587 double mod, vmod, angle;
588 double force_x, force_y;
589 double dupdate;
590 int forced;
591
592 /* Must still be on. */
593 if (shake_shader_pp_id == 0)
594 return;
595
596 /* The shake decays over time */
597 forced = 0;
598 if (shake_force_mod > 0.) {
599 shake_force_mod -= SPFX_SHAKE_DECAY*dt;
600 if (shake_force_mod < 0.)
601 shake_force_mod = 0.;
602 else
603 forced = 1;
604 }
605 dupdate = dt*2.0;
606 shake_force_mean = dupdate*shake_force_mod + (1.0-dupdate)*shake_force_mean;
607
608 /* See if it's settled down. */
609 mod = VMOD( shake_pos );
610 vmod = VMOD( shake_vel );
611 if (!forced && (mod < 0.01) && (vmod < 0.01)) {
612 render_postprocessRm( shake_shader_pp_id );
614 if (fabs(shake_force_ang) > 1e3)
615 shake_force_ang = RNGF();
616 return;
617 }
618
619 /* Calculate force. */
620 force_x = -SHAKE_K*shake_pos.x + -SHAKE_B*shake_vel.x;
621 force_y = -SHAKE_K*shake_pos.y + -SHAKE_B*shake_vel.y;
622
623 /* Apply force if necessary. */
624 if (forced) {
625 shake_force_ang += dt;
626 angle = noise_simplex1( shake_noise, &shake_force_ang ) * 5.*M_PI;
627 force_x += shake_force_mod * cos(angle);
628 force_y += shake_force_mod * sin(angle);
629 }
630
631 /* Update velocity. */
632 vec2_cadd( &shake_vel, (1./SHAKE_MASS) * force_x * dt, (1./SHAKE_MASS) * force_y * dt );
633
634 /* Update position. */
635 vec2_cadd( &shake_pos, shake_vel.x * dt, shake_vel.y * dt );
636
637 /* Set the uniform. */
638 glUseProgram( shaders.shake.program );
639 glUniform2f( shaders.shake.shake_pos, shake_pos.x / SCREEN_W, shake_pos.y / SCREEN_H );
640 glUniform2f( shaders.shake.shake_vel, shake_vel.x / SCREEN_W, shake_vel.y / SCREEN_H );
641 glUniform1f( shaders.shake.shake_force, shake_force_mean );
642 glUseProgram( 0 );
643
644 gl_checkErr();
645}
646
647static void spfx_updateDamage( double dt )
648{
649 /* Must still be on. */
650 if (damage_shader_pp_id == 0)
651 return;
652
653 /* Decrement and turn off if necessary. */
654 damage_strength -= SPFX_DAMAGE_DECAY * dt;
655 if (damage_strength < 0.) {
656 damage_strength = 0.;
657 render_postprocessRm( damage_shader_pp_id );
659 return;
660 }
661
662 /* Set the uniform. */
663 glUseProgram( shaders.damage.program );
664 glUniform1f( shaders.damage.damage_strength, damage_strength );
665 glUseProgram( 0 );
666
667 gl_checkErr();
668}
669
676{
677 Trail_spfx *trail = calloc( 1, sizeof(Trail_spfx) );
678 trail->spec = spec;
679 trail->capacity = 1;
680 trail->iread = trail->iwrite = 0;
681 trail->point_ringbuf = calloc( trail->capacity, sizeof(TrailPoint) );
682 trail->refcount = 1;
683 trail->r = RNGF();
684 trail->ontop = 0;
685
686 if ( trail_spfx_stack == NULL )
689
690 return trail;
691}
692
698void spfx_update_trails( double dt )
699{
700 int n = array_size( trail_spfx_stack );
701 for (int i=0; i<n; i++) {
702 Trail_spfx *trail = trail_spfx_stack[i];
703 spfx_trail_update( trail, dt );
704 if (!trail->refcount && !trail_size(trail) ) {
705 spfx_trail_free( trail );
707 }
708 }
709 if (n < array_size( trail_spfx_stack ) )
711}
712
719static void spfx_trail_update( Trail_spfx* trail, double dt )
720{
721 GLfloat rel_dt = dt/ trail->spec->ttl;
722 /* Remove outdated elements. */
723 while (trail->iread < trail->iwrite && trail_front(trail).t < rel_dt)
724 trail->iread++;
725
726 /* Update others' timestamps. */
727 for (size_t i = trail->iread; i < trail->iwrite; i++)
728 trail_at( trail, i ).t -= rel_dt;
729
730 /* Update timer. */
731 trail->dt += dt;
732}
733
743void spfx_trail_sample( Trail_spfx* trail, double x, double y, TrailMode mode, int force )
744{
745 TrailPoint p;
746
747 if (!force && trail->spec->style[mode].col.a <= 0.)
748 return;
749
750 p.x = x;
751 p.y = y;
752 p.t = 1.;
753 p.mode = mode;
754
755 /* The "back" of the trail should always reflect our most recent state. */
756 trail_back( trail ) = p;
757
758 /* We may need to insert a control point, but not if our last sample was recent enough. */
759 if (!force && trail_size(trail) > 1 && trail_at( trail, trail->iwrite-2 ).t >= 1.-TRAIL_UPDATE_DT)
760 return;
761
762 /* If the last time we inserted a control point was recent enough, we don't need a new one. */
763 if (trail_size(trail) == trail->capacity) {
764 /* Full! Double capacity, and make the elements contiguous. (We've made space to grow rightward.) */
765 trail->point_ringbuf = realloc( trail->point_ringbuf, 2 * trail->capacity * sizeof(TrailPoint) );
766 trail->iread %= trail->capacity;
767 trail->iwrite = trail->iread + trail->capacity;
768 memmove( &trail->point_ringbuf[trail->capacity], trail->point_ringbuf, trail->iread * sizeof(TrailPoint) );
769 trail->capacity *= 2;
770 }
771 trail_at( trail, trail->iwrite++ ) = p;
772}
773
780{
781 if (trail != NULL)
782 trail->refcount--;
783}
784
788static void spfx_trail_free( Trail_spfx* trail )
789{
790 assert(trail->refcount == 0);
791 free(trail->point_ringbuf);
792 free(trail);
793}
794
798void spfx_trail_draw( const Trail_spfx* trail )
799{
800 const TrailStyle *styles;
801 size_t n;
802 GLfloat len;
803 double z;
804
805 n = trail_size(trail);
806 if (n==0)
807 return;
808 styles = trail->spec->style;
809
810 /* Stuff that doesn't change for the entire trail. */
811 glUseProgram( shaders.trail.program );
812 if (gl_has( OPENGL_SUBROUTINES ))
813 glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &trail->spec->type );
814 glEnableVertexAttribArray( shaders.trail.vertex );
815 gl_vboActivateAttribOffset( gl_squareVBO, shaders.trail.vertex, 0, 2, GL_FLOAT, 0 );
816 glUniform1f( shaders.trail.dt, trail->dt );
817 glUniform1f( shaders.trail.r, trail->r );
818
819 z = cam_getZoom();
820 len = 0.;
821 for (size_t i=trail->iread + 1; i < trail->iwrite; i++) {
822 mat4 projection;
823 const TrailStyle *sp, *spp;
824 double x1, y1, x2, y2, s;
825 TrailPoint *tp = &trail_at( trail, i );
826 TrailPoint *tpp = &trail_at( trail, i-1 );
827
828 /* Ignore none modes. */
829 if (tp->mode == MODE_NONE || tpp->mode == MODE_NONE)
830 continue;
831
832 gl_gameToScreenCoords( &x1, &y1, tp->x, tp->y );
833 gl_gameToScreenCoords( &x2, &y2, tpp->x, tpp->y );
834
835 s = hypot( x2-x1, y2-y1 );
836
837 /* Make sure in bounds. */
838 if ((MAX(x1,x2) < 0.) || (MIN(x1,x2) > (double)SCREEN_W) ||
839 (MAX(y1,y2) < 0.) || (MIN(y1,y2) > (double)SCREEN_H)) {
840 len += s;
841 continue;
842 }
843
844 sp = &styles[tp->mode];
845 spp = &styles[tpp->mode];
846
847 /* Set vertex. */
848 projection = gl_view_matrix;
849 mat4_translate( &projection, x1, y1, 0. );
850 mat4_rotate2dv( &projection, (x2-x1)/s, (y2-y1)/s );
851 mat4_scale( &projection, s, z*(sp->thick+spp->thick), 1. );
852 mat4_translate( &projection, 0., -0.5, 0. );
853
854 /* Set uniforms. */
855 gl_uniformMat4(shaders.trail.projection, &projection);
856 gl_uniformColor(shaders.trail.c1, &sp->col);
857 gl_uniformColor(shaders.trail.c2, &spp->col);
858 glUniform1f(shaders.trail.t1, tp->t);
859 glUniform1f(shaders.trail.t2, tpp->t);
860 glUniform2f(shaders.trail.pos2, len, sp->thick);
861 len += s;
862 glUniform2f(shaders.trail.pos1, len, spp->thick);
863
864 /* Draw. */
865 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
866 }
867
868 /* Clear state. */
869 glDisableVertexAttribArray( shaders.trail.vertex );
870 glUseProgram(0);
871
872 /* Check errors. */
873 gl_checkErr();
874}
875
883void spfx_shake( double mod )
884{
885 /* Add the modifier. */
886 shake_force_mod = MIN( SPFX_SHAKE_MAX, shake_force_mod + SPFX_SHAKE_MOD*mod );
887
888 /* Rumble if it wasn't rumbling before. */
890
891 /* Create the shake. */
892 if (shake_shader_pp_id==0)
893 shake_shader_pp_id = render_postprocessAdd( &shake_shader, PP_LAYER_GAME, 99, 0 );
894}
895
903void spfx_damage( double mod )
904{
905 damage_strength = MIN( SPFX_DAMAGE_MAX, damage_strength + SPFX_DAMAGE_MOD*mod );
906
907 /* Create the damage. */
908 if (damage_shader_pp_id==0)
909 damage_shader_pp_id = render_postprocessAdd( &damage_shader, PP_LAYER_GUI, 98, 0 );
910}
911
917static int spfx_hapticInit (void)
918{
919 SDL_HapticEffect *efx;
920
921 /* Haptic must be enabled. */
922 if (haptic == NULL)
923 return 0;
924
925 efx = &haptic_rumbleEffect;
926 memset( efx, 0, sizeof(SDL_HapticEffect) );
927 efx->type = SDL_HAPTIC_SINE;
928 efx->periodic.direction.type = SDL_HAPTIC_POLAR;
929 efx->periodic.length = 1000;
930 efx->periodic.period = 200;
931 efx->periodic.magnitude = 0x4000;
932 efx->periodic.fade_length = 1000;
933 efx->periodic.fade_level = 0;
934
935 haptic_rumble = SDL_HapticNewEffect( haptic, efx );
936 if (haptic_rumble < 0) {
937 WARN(_("Unable to upload haptic effect: %s."), SDL_GetError());
938 return -1;
939 }
940
941 return 0;
942}
943
949static void spfx_hapticRumble( double mod )
950{
951 SDL_HapticEffect *efx;
952 double len, mag;
953
954 /* Not active. */
955 if (haptic_rumble < 0)
956 return;
957
958 /* Not time to update yet. */
959 if ((haptic_lastUpdate > 0.) || (shake_shader_pp_id==0) || (mod > SPFX_SHAKE_MAX/3.))
960 return;
961
962 /* Stop the effect if it was playing. */
963 SDL_HapticStopEffect( haptic, haptic_rumble );
964
965 /* Get length and magnitude. */
966 len = 1000. * shake_force_mod / SPFX_SHAKE_DECAY;
967 mag = 32767. * (shake_force_mod / SPFX_SHAKE_MAX);
968
969 /* Update the effect. */
970 efx = &haptic_rumbleEffect;
971 efx->periodic.magnitude = (int16_t)mag;
972 efx->periodic.length = (uint32_t)len;
973 efx->periodic.fade_length = MIN( efx->periodic.length, 1000 );
974 if (SDL_HapticUpdateEffect( haptic, haptic_rumble, &haptic_rumbleEffect ) < 0) {
975 WARN(_("Failed to update haptic effect: %s."), SDL_GetError());
976 return;
977 }
978
979 /* Run the new effect. */
980 SDL_HapticRunEffect( haptic, haptic_rumble, 1 );
981
982 /* Set timer again. */
984}
985
991void spfx_cinematic (void)
992{
993 gl_renderRect( 0., 0., SCREEN_W, SCREEN_H*0.2, &cBlack );
994 gl_renderRect( 0., SCREEN_H*0.8, SCREEN_W, SCREEN_H, &cBlack );
995}
996
1002void spfx_render( int layer )
1003{
1004 SPFX *spfx_stack;
1005 int sx, sy;
1006 double time;
1007
1008 /* get the appropriate layer */
1009 switch (layer) {
1010 case SPFX_LAYER_FRONT:
1011 spfx_stack = spfx_stack_front;
1013 break;
1014
1015 case SPFX_LAYER_MIDDLE:
1016 spfx_stack = spfx_stack_middle;
1018 break;
1019
1020 case SPFX_LAYER_BACK:
1021 spfx_stack = spfx_stack_back;
1023 break;
1024
1025 default:
1026 WARN(_("Rendering invalid SPFX layer."));
1027 return;
1028 }
1029
1030 /* Trails are special (for now?). */
1031 if (layer == SPFX_LAYER_BACK)
1032 for (int i=0; i<array_size(trail_spfx_stack); i++) {
1033 Trail_spfx *trail = trail_spfx_stack[i];
1034 if (!trail->ontop)
1035 spfx_trail_draw( trail );
1036 }
1037
1038 /* Now render the layer */
1039 for (int i=array_size(spfx_stack)-1; i>=0; i--) {
1040 SPFX *spfx = &spfx_stack[i];
1041 SPFX_Base *effect = &spfx_effects[ spfx->effect ];
1042
1043 /* Render shader. */
1044 if (effect->shader >= 0) {
1045 double x, y, z, s2;
1046 double w, h;
1047 mat4 projection;
1048
1049 /* Translate coords. */
1050 s2 = effect->size/2.;
1051 z = cam_getZoom();
1052 gl_gameToScreenCoords( &x, &y, spfx->pos.x-s2, spfx->pos.y-s2 );
1053 w = h = effect->size*z;
1054
1055 /* Check if inbounds. */
1056 if ((x < -w) || (x > SCREEN_W+w) ||
1057 (y < -h) || (y > SCREEN_H+h))
1058 continue;
1059
1060 /* Let's get to business. */
1061 glUseProgram( effect->shader );
1062
1063 /* Set up the vertex. */
1064 projection = gl_view_matrix;
1065 mat4_translate( &projection, x, y, 0. );
1066 mat4_scale( &projection, w, h, 1. );
1067 glEnableVertexAttribArray( effect->vertex );
1068 gl_vboActivateAttribOffset( gl_squareVBO, effect->vertex,
1069 0, 2, GL_FLOAT, 0 );
1070
1071 /* Set shader uniforms. */
1072 gl_uniformMat4(effect->projection, &projection);
1073 glUniform1f(effect->u_time, spfx->time);
1074 glUniform1f(effect->u_r, spfx->unique);
1075 glUniform1f(effect->u_size, effect->size);
1076
1077 /* Draw. */
1078 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1079
1080 /* Clear state. */
1081 glDisableVertexAttribArray( shaders.texture.vertex );
1082
1083 /* anything failed? */
1084 gl_checkErr();
1085
1086 glUseProgram(0);
1087
1088 }
1089 /* No shader. */
1090 else {
1091 /* Simplifies */
1092 sx = (int)effect->gfx->sx;
1093 sy = (int)effect->gfx->sy;
1094
1095 if (!paused) { /* don't calculate frame if paused */
1096 time = 1. - fmod(spfx_stack[i].timer,effect->anim) / effect->anim;
1097 spfx_stack[i].lastframe = sx * sy * MIN(time, 1.);
1098 }
1099
1100 /* Renders */
1101 gl_renderSprite( effect->gfx,
1102 VX(spfx_stack[i].pos), VY(spfx_stack[i].pos),
1103 spfx_stack[i].lastframe % sx,
1104 spfx_stack[i].lastframe / sx,
1105 NULL );
1106 }
1107 }
1108}
1109
1114static int trailSpec_parse( TrailSpec *tc, const char *file, int firstpass )
1115{
1116 static const char *mode_tags[] = MODE_TAGS;
1117 char *inherits;
1118 xmlNodePtr parent, node;
1119 xmlDocPtr doc;
1120
1121 /* Load the data. */
1122 doc = xml_parsePhysFS( file );
1123 if (doc == NULL)
1124 return -1;
1125
1126 /* Get the first node. */
1127 parent = doc->xmlChildrenNode; /* first event node */
1128 if (parent == NULL) {
1129 WARN( _("Malformed '%s' file: does not contain elements"), file );
1130 return -1;
1131 }
1132
1133 if (firstpass) {
1134 memset( tc, 0, sizeof(TrailSpec) );
1135 for(int i=0; i<MODE_MAX; i++)
1136 tc->style[i].thick = 1.;
1137 }
1138
1139 xmlr_attr_strd( parent, "inherits", inherits );
1140 if (firstpass) {
1141 xmlr_attr_strd( parent, "name", tc->name );
1142 if (inherits != NULL) {
1143 /* Skip this pass. */
1144 free( inherits );
1145 xmlFreeDoc(doc);
1146 return 0;
1147 }
1148 }
1149 else {
1150 if (inherits == NULL) {
1151 /* Already done here. */
1152 free( inherits );
1153 xmlFreeDoc(doc);
1154 return 0;
1155 }
1156 else {
1157 TrailSpec *tsparent = trailSpec_getRaw( inherits );
1158 if (tsparent == NULL)
1159 WARN(_("Trail '%s' that inherits from '%s' has missing reference!"), tc->name, inherits );
1160 else {
1161 char *name = tc->name;
1162 char *filename = tc->filename;
1163 memcpy( tc, tsparent, sizeof(TrailSpec) );
1164 tc->name = name;
1165 tc->filename = filename;
1166 }
1167 }
1168 }
1169
1170 node = parent->xmlChildrenNode;
1171 do {
1172 xml_onlyNodes(node);
1173 if (xml_isNode(node,"thickness"))
1174 tc->def_thick = xml_getFloat( node );
1175 else if (xml_isNode(node, "ttl"))
1176 tc->ttl = xml_getFloat( node );
1177 else if (xml_isNode(node, "type")) {
1178 char *type = xml_get(node);
1179 if (gl_has( OPENGL_SUBROUTINES )) {
1180 tc->type = glGetSubroutineIndex( shaders.trail.program, GL_FRAGMENT_SHADER, type );
1181 if (tc->type == GL_INVALID_INDEX)
1182 WARN("Trail '%s' has unknown type '%s'", tc->name, type );
1183 }
1184 }
1185 else if (xml_isNode(node, "nebula"))
1186 tc->nebula = xml_getInt( node );
1187 else {
1188 int i;
1189 for (i=0; i<MODE_MAX; i++)
1190 if (xml_isNode(node, mode_tags[i])) {
1191 xmlr_attr_float_opt( node, "r", tc->style[i].col.r );
1192 xmlr_attr_float_opt( node, "g", tc->style[i].col.g );
1193 xmlr_attr_float_opt( node, "b", tc->style[i].col.b );
1194 xmlr_attr_float_opt( node, "a", tc->style[i].col.a );
1195 xmlr_attr_float_opt( node, "scale", tc->style[i].thick );
1196 col_gammaToLinear( &tc->style[i].col );
1197 break;
1198 }
1199 if (i == MODE_MAX)
1200 WARN(_("Trail '%s' has unknown node '%s'."), tc->name, node->name);
1201 }
1202 } while (xml_nextNode(node));
1203
1204#define MELEMENT(o,s) if (o) WARN(_("Trail '%s' missing '%s' element"), tc->name, s)
1205 MELEMENT( tc->def_thick==0, "thickness" );
1206 MELEMENT( tc->ttl==0, "ttl" );
1207#undef MELEMENT
1208
1209 /* Clean up. */
1210 free( inherits );
1211 xmlFreeDoc(doc);
1212 return 0;
1213}
1214
1220static int trailSpec_load (void)
1221{
1222 char **ts_files = ndata_listRecursive( TRAIL_DATA_PATH );
1223
1225
1226 /* First pass sets up and prepares inheritance. */
1227 for (int i=0; i<array_size(ts_files); i++) {
1228 TrailSpec tc;
1229 int ret = trailSpec_parse( &tc, ts_files[i], 1 );
1230 if (ret == 0) {
1231 tc.filename = ts_files[i];
1233 }
1234 else
1235 free( ts_files[i] );
1236 }
1237
1238 /* Second pass to complete inheritance. */
1239 for (int i=0; i<array_size( trail_spec_stack ); i++)
1240 trailSpec_parse( &trail_spec_stack[i], trail_spec_stack[i].filename, 0 );
1241 array_free( ts_files );
1242
1243 /* Set up thickness. */
1245 for(int i=0; i<MODE_MAX; i++)
1246 tc->style[i].thick *= tc->def_thick;
1247 }
1249
1250 return 0;
1251}
1252
1253static TrailSpec* trailSpec_getRaw( const char* name )
1254{
1255 for (int i=0; i<array_size(trail_spec_stack); i++) {
1256 if ( strcmp(trail_spec_stack[i].name, name)==0 )
1257 return &trail_spec_stack[i];
1258 }
1259 WARN(_("Trail type '%s' not found in stack"), name);
1260 return NULL;
1261}
1262
1268const TrailSpec* trailSpec_get( const char* name )
1269{
1270 return trailSpec_getRaw( name );
1271}
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:366
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
Definition: ndata.c:232
void spfxL_rendermg(void)
Renders the Lua SPFX in the midground.
Definition: nlua_spfx.c:685
void spfxL_clear(void)
Clears the Lua spfx.
Definition: nlua_spfx.c:577
void spfxL_renderbg(void)
Renders the Lua SPFX on the background.
Definition: nlua_spfx.c:652
void spfxL_update(double dt)
Updates the spfx.
Definition: nlua_spfx.c:601
void spfxL_renderfg(void)
Renders the Lua SPFX in the foreground.
Definition: nlua_spfx.c:726
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.
Definition: opengl_render.c:90
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:755
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:228
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:411
static LuaShader_t shake_shader
Definition: spfx.c:57
int spfx_load(void)
Loads the spfx stack.
Definition: spfx.c:348
static double shake_force_mod
Definition: spfx.c:60
static Trail_spfx ** trail_spfx_stack
Definition: spfx.c:50
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
Definition: spfx.c:1268
static SPFX_Base * spfx_effects
Definition: spfx.c:112
#define TRAIL_UPDATE_DT
Definition: spfx.c:48
#define SHAKE_B
Definition: spfx.c:43
int spfx_get(char *name)
Gets the id of an spfx based on name.
Definition: spfx.c:333
static void spfx_update_layer(SPFX *layer, const double dt)
Updates an individual spfx.
Definition: spfx.c:564
static int trailSpec_load(void)
Loads the trail colour sets.
Definition: spfx.c:1220
static void spfx_base_free(SPFX_Base *effect)
Frees a SPFX_Base.
Definition: spfx.c:321
void spfx_cinematic(void)
Sets the cinematic mode.
Definition: spfx.c:991
static int haptic_rumble
Definition: spfx.c:67
void spfx_render(int layer)
Renders the entire spfx layer.
Definition: spfx.c:1002
static int spfx_base_parse(SPFX_Base *temp, const char *filename)
Parses an xml node containing a SPFX.
Definition: spfx.c:160
void spfx_trail_sample(Trail_spfx *trail, double x, double y, TrailMode mode, int force)
Makes a trail grow.
Definition: spfx.c:743
static SPFX * spfx_stack_middle
Definition: spfx.c:135
void spfx_shake(double mod)
Increases the current rumble level.
Definition: spfx.c:883
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:1114
static void spfx_hapticRumble(double mod)
Runs a rumble effect.
Definition: spfx.c:949
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:509
static vec2 shake_pos
Definition: spfx.c:58
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition: spfx.c:779
unsigned int haptic_query
Definition: joystick.c:25
static void spfx_trail_update(Trail_spfx *trail, double dt)
Updates a trail.
Definition: spfx.c:719
static perlin_data_t * shake_noise
Definition: spfx.c:63
static void spfx_updateShake(double dt)
Updates the shake position.
Definition: spfx.c:585
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:462
static void spfx_update_trails(double dt)
Updates all trails (handling dispersal/fadeout).
Definition: spfx.c:698
void spfx_damage(double mod)
Increases the current damage level.
Definition: spfx.c:903
static double shake_force_mean
Definition: spfx.c:61
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition: spfx.c:675
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:788
void spfx_update(const double dt, const double real_dt)
Updates all the spfx.
Definition: spfx.c:537
SDL_Haptic * haptic
Definition: joystick.c:24
static int spfx_hapticInit(void)
Initializes the rumble effect.
Definition: spfx.c:917
void spfx_trail_draw(const Trail_spfx *trail)
Draws a trail on screen.
Definition: spfx.c:798
#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:158
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:34
double sx
Definition: opengl_tex.h:42
double sy
Definition: opengl_tex.h:43
Definition: mat4.h:10
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33