10#include "SDL_haptic.h"
23#include "nlua_shader.h"
34#define SPFX_XML_ID "spfx"
39#define SHAKE_MASS ( 1. / 400. )
40#define SHAKE_K ( 1. / 50. )
42 ( 3. * sqrt( SHAKE_K * SHAKE_MASS ) )
45#define HAPTIC_UPDATE_INTERVAL 0.1
48#define TRAIL_UPDATE_DT \
84static TrailSpec *trailSpec_getRaw(
const char *name );
90static void spfx_updateDamage(
double dt );
97typedef struct SPFX_Base_ {
123typedef struct SPFX_ {
165 return strcmp( s1->
name, s2->
name );
177 xmlNodePtr node, cur, uniforms;
178 char *shadervert, *shaderfrag;
187 node = doc->xmlChildrenNode;
189 WARN( _(
"Malformed '%s' file: missing root element '%s'" ), filename,
201 xmlr_attr_strd( node,
"name", temp->
name );
204 node = node->xmlChildrenNode;
206 xml_onlyNodes( node );
207 xmlr_float( node,
"anim", temp->
anim );
208 xmlr_float( node,
"ttl", temp->
ttl );
209 if ( xml_isNode( node,
"gfx" ) ) {
214 if ( xml_isNode( node,
"shader" ) ) {
215 cur = node->xmlChildrenNode;
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" ) ) {
225 }
while ( xml_nextNode( cur ) );
230 WARN( _(
"SPFX '%s' has unknown node '%s'." ), temp->
name, node->name );
231 }
while ( xml_nextNode( node ) );
234 if ( temp->
ttl == 0. )
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;
249 xml_onlyNodes( node );
252 const char *name = (
char *)node->name;
253 GLint loc = glGetUniformLocation( temp->
shader, name );
256 _(
"SPFX '%s' is trying to set uniform '%s' not in shader!" ),
260 xmlr_attr_int_def( node,
"int", isint, 0 );
262 if ( xmlHasProp( node, (xmlChar *)
"w" ) )
264 else if ( xmlHasProp( node, (xmlChar *)
"z" ) )
266 else if ( xmlHasProp( node, (xmlChar *)
"y" ) )
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 );
279 glUniform1i( loc, ix );
282 glUniform2i( loc, ix, iy );
285 glUniform3i( loc, ix, iy, iz );
288 glUniform4i( loc, ix, iy, iz, iw );
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 );
299 glUniform1f( loc, x );
302 glUniform2f( loc, x, y );
305 glUniform3f( loc, x, y, z );
308 glUniform4f( loc, x, y, z, w );
312 }
while ( xml_nextNode( node ) );
318#define MELEMENT( o, s ) \
320 WARN( _( "SPFX '%s' missing/invalid '%s' element" ), temp->name, \
322 MELEMENT( temp->
anim == 0.,
"anim" );
323 MELEMENT( temp->
ttl == 0.,
"ttl" );
324 MELEMENT( temp->
gfx == NULL && ( shadervert == NULL || shaderfrag == NULL ),
326 MELEMENT( temp->
shader >= 0 && temp->
size <= 0.,
"shader/size" );
345 free( effect->
name );
357 const SPFX_Base sq = { .name = (
char *)name };
361 if ( sout == NULL ) {
378 Uint32 time = SDL_GetTicks();
385 for (
int i = 0; i <
array_size( spfx_files ); i++ ) {
392 free( spfx_files[i] );
409 shake_shader.VertexPosition = shaders.shake.VertexPosition;
410 shake_shader.ClipSpaceFromLocal = shaders.shake.ClipSpaceFromLocal;
420 damage_shader.VertexPosition = shaders.damage.VertexPosition;
421 damage_shader.ClipSpaceFromLocal = shaders.damage.ClipSpaceFromLocal;
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",
437 DEBUG( n_(
"Loaded %d Special Effect",
"Loaded %d Special Effects",
481 free( ts->filename );
483 glDeleteProgram( ts->shader.program );
504void spfx_add(
int effect,
const double px,
const double py,
const double vx,
505 const double vy,
int layer )
511 WARN( _(
"Trying to add spfx with invalid effect!" ) );
518 if ( layer == SPFX_LAYER_FRONT )
520 else if ( layer == SPFX_LAYER_MIDDLE )
522 else if ( layer == SPFX_LAYER_BACK )
525 WARN( _(
"Invalid SPFX layer." ) );
530 cur_spfx->
effect = effect;
531 vec2_csetmin( &cur_spfx->
pos, px, py );
532 vec2_csetmin( &cur_spfx->
vel, vx, vy );
537 cur_spfx->
timer = ttl + RNGF() * anim;
539 cur_spfx->
timer = ttl;
542 cur_spfx->
unique = RNGF();
543 cur_spfx->
time = 0.0;
551 NTracingZone( _ctx, 1 );
573 NTracingZoneEnd( _ctx );
584 NTracingZone( _ctx, 1 );
603 spfx_updateDamage( dt );
608 NTracingZoneEnd( _ctx );
619 for (
int i = 0; i <
array_size( layer ); i++ ) {
620 layer[i].timer -= dt;
623 if ( layer[i].timer < 0. ) {
631 vec2_cadd( &layer[i].pos, dt * VX( layer[i].vel ),
632 dt * VY( layer[i].vel ) );
642 double force_x, force_y;
666 if ( !forced && ( mod < 0.01 ) && ( vmod < 0.01 ) ) {
688 vec2_cadd( &
shake_vel, ( 1. / SHAKE_MASS ) * force_x * dt,
689 ( 1. / SHAKE_MASS ) * force_y * dt );
695 glUseProgram( shaders.shake.program );
696 glUniform2f( shaders.shake.shake_pos,
shake_pos.x / SCREEN_W,
698 glUniform2f( shaders.shake.shake_vel,
shake_vel.x / SCREEN_W,
706static void spfx_updateDamage(
double dt )
722 glUseProgram( shaders.damage.program );
761 for (
int i = 0; i < n; i++ ) {
764 if ( !trail->
refcount && !trail_size( trail ) ) {
781 GLfloat rel_dt = dt / trail->spec->
ttl;
783 while ( trail->
iread < trail->
iwrite && trail_front( trail ).t < rel_dt )
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;
812 double dx,
double dy, TrailMode mode,
int force )
816 if ( !force && trail->spec->
style[mode].
col.a <= 0. )
828 trail_back( trail ) = p;
832 if ( !force && trail_size( trail ) > 1 &&
838 if ( trail_size( trail ) == trail->
capacity ) {
849 trail_at( trail, trail->
iwrite++ ) = p;
885 size_t n = trail_size( trail );
889 styles = spec->
style;
892 glUseProgram( spec->shader.program );
893 glEnableVertexAttribArray( spec->shader.vertex );
896 glUniform1f( spec->shader.dt, trail->
dt );
897 glUniform1f( spec->shader.r, trail->
r );
902 for (
size_t i = trail->
iread + 1; i < trail->iwrite; i++ ) {
905 double x1, y1, x2, y2, s;
910 if ( tp->
mode == MODE_NONE || tpp->
mode == MODE_NONE )
916 s = hypot( x2 - x1, y2 - y1 );
921 if ( (
MAX( x1, x2 ) < 0. ) || (
MIN( x1, x2 ) > (
double)SCREEN_W ) ||
922 (
MAX( y1, y2 ) < 0. ) || (
MIN( y1, y2 ) > (
double)SCREEN_H ) ) {
927 sp = &styles[tp->
mode];
928 spp = &styles[tpp->
mode];
931 projection = gl_view_matrix;
932 mat4_translate_xy( &projection, x1, y1 );
934 mat4_scale_xy( &projection, s, z * ( sp->
thick + spp->
thick ) );
935 mat4_translate_xy( &projection, 0., -0.5 );
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 );
945 glUniform2f( spec->shader.pos1, len, spp->
thick );
948 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
952 glDisableVertexAttribArray( spec->shader.vertex );
978 render_postprocessAdd( &
shake_shader, PP_LAYER_GAME, 99, 0 );
996 render_postprocessAdd( &
damage_shader, PP_LAYER_GUI, 98, 0 );
1006 glUseProgram( ts->shader.program );
1007 glUniform3f( ts->shader.nebu_col, r, g, b );
1019 SDL_HapticEffect *efx;
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;
1037 WARN( _(
"Unable to upload haptic effect: %s." ), SDL_GetError() );
1051 SDL_HapticEffect *efx;
1060 ( mod > SPFX_SHAKE_MAX / 3. ) )
1072 efx->periodic.magnitude = (int16_t)mag;
1073 efx->periodic.length = (uint32_t)len;
1074 efx->periodic.fade_length =
MIN( efx->periodic.length, 1000 );
1077 WARN( _(
"Failed to update haptic effect: %s." ), SDL_GetError() );
1096 gl_renderRect( 0., SCREEN_H * 0.8, SCREEN_W, SCREEN_H, &cBlack );
1099static void spfx_renderStack(
SPFX *spfx_stack )
1101 for (
int i =
array_size( spfx_stack ) - 1; i >= 0; i-- ) {
1102 SPFX *spfx = &spfx_stack[i];
1106 if ( effect->
shader >= 0 ) {
1112 s2 = effect->
size / 2.;
1115 w = h = effect->
size * z;
1118 if ( ( x < -w ) || ( x > SCREEN_W + w ) || ( y < -h ) ||
1119 ( y > SCREEN_H + h ) )
1123 glUseProgram( effect->
shader );
1126 projection = gl_view_matrix;
1127 mat4_translate_scale_xy( &projection, x, y, w, h );
1128 glEnableVertexAttribArray( effect->vertex );
1133 gl_uniformMat4( effect->projection, &projection );
1135 glUniform1f( effect->
u_r, spfx->
unique );
1139 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1142 glDisableVertexAttribArray( shaders.texture.vertex );
1155 sx = (int)effect->
gfx->
sx;
1156 sy = (int)effect->
gfx->
sy;
1160 1. - fmod( spfx_stack[i].timer, effect->
anim ) / effect->
anim;
1166 VY( spfx_stack[i].pos ), spfx_stack[i].lastframe % sx,
1167 spfx_stack[i].lastframe / sx, NULL );
1180 NTracingZone( _ctx, 1 );
1184 case SPFX_LAYER_FRONT:
1189 case SPFX_LAYER_MIDDLE:
1194 case SPFX_LAYER_BACK:
1198 NTracingZoneName( _ctx_trails,
"spfx_render[trails]", 1 );
1202 if ( !trail->
ontop )
1205 NTracingZoneEnd( _ctx_trails );
1209 WARN( _(
"Rendering invalid SPFX layer." ) );
1213 NTracingZoneEnd( _ctx );
1222 static const char *mode_tags[] = MODE_TAGS;
1224 xmlNodePtr parent, node;
1233 parent = doc->xmlChildrenNode;
1234 if ( parent == NULL ) {
1235 WARN( _(
"Malformed '%s' file: does not contain elements" ), file );
1241 for (
int i = 0; i < MODE_MAX; i++ )
1245 xmlr_attr_strd( parent,
"inherits", inherits );
1247 xmlr_attr_strd( parent,
"name", tc->
name );
1248 if ( inherits != NULL ) {
1255 if ( inherits == NULL ) {
1261 const TrailSpec *tsparent = trailSpec_getRaw( inherits );
1262 if ( tsparent == NULL )
1264 _(
"Trail '%s' that inherits from '%s' has missing reference!" ),
1265 tc->
name, inherits );
1267 char *name = tc->
name;
1268 char *filename = tc->filename;
1269 memcpy( tc, tsparent,
sizeof(
TrailSpec ) );
1271 tc->filename = filename;
1277 node = parent->xmlChildrenNode;
1279 xml_onlyNodes( node );
1283 if ( xml_isNode( node,
"thickness" ) )
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" ) )
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 );
1303 if ( i == MODE_MAX )
1304 WARN( _(
"Trail '%s' has unknown node '%s'." ), tc->
name,
1307 }
while ( xml_nextNode( node ) );
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" );
1329#define MELEMENT( o, s ) \
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" );
1355 for (
int i = 0; i <
array_size( ts_files ); i++ ) {
1359 tc.filename = ts_files[i];
1362 free( ts_files[i] );
1373 for (
int i = 0; i < MODE_MAX; i++ )
1374 tc->style[i].thick *= tc->def_thick;
1381static TrailSpec *trailSpec_getRaw(
const char *name )
1387 WARN( _(
"Trail type '%s' not found in stack" ), name );
1398 return trailSpec_getRaw( name );
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
double cam_getZoom(void)
Gets the camera zoom.
void debris_cleanup(void)
Cleans up after the debris.
unsigned int haptic_query
void mat4_rotate2dv(mat4 *m, double c, double s)
Rotates the +x axis to the given vector.
Header file with generic functions and naev-specifics.
int ndata_matchExt(const char *path, const char *ext)
Sees if a file matches an extension.
char ** ndata_listRecursive(const char *path)
Lists all the visible files in a directory, at any depth.
void spfxL_renderbg(double dt)
Renders the Lua SPFX on the background.
void spfxL_rendermg(double dt)
Renders the Lua SPFX in the midground.
void spfxL_renderfg(double dt)
Renders the Lua SPFX in the foreground.
void spfxL_clear(void)
Clears the Lua spfx.
void spfxL_update(double dt)
Updates the spfx.
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.
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
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.
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
perlin_data_t * noise_new(void)
Creates a new perlin noise generator.
void noise_delete(perlin_data_t *pdata)
Frees some noise data.
float noise_simplex1(perlin_data_t *pdata, float f[1])
Gets 1D simplex noise for a position.
static double damage_strength
static SPFX * spfx_stack_front
static TrailSpec * trail_spec_stack
void spfx_free(void)
Frees the spfx stack.
static LuaShader_t shake_shader
int spfx_load(void)
Loads the spfx stack.
static double shake_force_mod
static Trail_spfx ** trail_spfx_stack
void spfx_render(int layer, double dt)
Renders the entire spfx layer.
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
static SPFX_Base * spfx_effects
static void spfx_update_layer(SPFX *layer, const double dt)
Updates an individual spfx.
static int trailSpec_load(void)
Loads the trail colour sets.
static void spfx_base_free(SPFX_Base *effect)
Frees a SPFX_Base.
void spfx_cinematic(void)
Sets the cinematic mode.
static int spfx_base_parse(SPFX_Base *temp, const char *filename)
Parses an xml node containing a SPFX.
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.
static SPFX * spfx_stack_middle
void spfx_shake(double mod)
Increases the current rumble level.
static unsigned int damage_shader_pp_id
static LuaShader_t damage_shader
static int trailSpec_parse(TrailSpec *tc, const char *file, int firstpass)
Parses raw values out of a "trail" element.
static void spfx_hapticRumble(double mod)
Runs a rumble effect.
static double haptic_lastUpdate
static float shake_force_ang
static unsigned int shake_shader_pp_id
void spfx_clear(void)
Clears all the currently running effects.
void spfx_setNebulaColour(double r, double g, double b)
Sets the nebula colour where applicable.
int spfx_get(const char *name)
Gets the id of an spfx based on name.
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
static void spfx_trail_update(Trail_spfx *trail, double dt)
Updates a trail.
static perlin_data_t * shake_noise
static void spfx_updateShake(double dt)
Updates the shake position.
void spfx_add(int effect, const double px, const double py, const double vx, const double vy, int layer)
Creates a new special effect.
static void spfx_update_trails(double dt)
Updates all trails (handling dispersal/fadeout).
static int spfx_base_cmp(const void *p1, const void *p2)
For sorting and stuff.
void spfx_damage(double mod)
Increases the current damage level.
static double shake_force_mean
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
static SPFX * spfx_stack_back
#define HAPTIC_UPDATE_INTERVAL
static void spfx_trail_free(Trail_spfx *trail)
Deallocates an unreferenced, expired trail.
void spfx_update(const double dt, const double real_dt)
Updates all the spfx.
static int spfx_hapticInit(void)
Initializes the rumble effect.
void spfx_trail_draw(const Trail_spfx *trail)
Draws a trail on screen.
static SDL_HapticEffect haptic_rumbleEffect
An actual in-game active special effect.
represents a set of styles for trails.
TrailStyle style[MODE_MAX]
Represents the appearance characteristics for a given trail mode.
A trail generated by a ship or an ammo.
TrailPoint * point_ringbuf
Abstraction for rendering sprite sheets.