12#include "SDL_haptic.h"
33#include "nlua_shader.h"
36#define SPFX_XML_ID "spfx"
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
48#define TRAIL_UPDATE_DT 0.05
80static TrailSpec* trailSpec_getRaw(
const char* name );
86static void spfx_updateDamage(
double dt );
93typedef struct SPFX_Base_ {
119typedef struct SPFX_ {
161 return strcmp( s1->
name, s2->
name );
173 xmlNodePtr node, cur, uniforms;
174 char *shadervert, *shaderfrag;
186 node = doc->xmlChildrenNode;
188 ERR( _(
"Malformed '%s' file: missing root element '%s'"), filename,
SPFX_XML_ID);
199 xmlr_attr_strd( node,
"name", temp->
name );
202 node = node->xmlChildrenNode;
205 xmlr_float(node,
"anim", temp->
anim);
206 xmlr_float(node,
"ttl", temp->
ttl);
207 if (xml_isNode(node,
"gfx")) {
209 SPFX_GFX_PATH
"%s", 6, 5, 0 );
213 if (xml_isNode(node,
"shader")) {
214 cur = node->xmlChildrenNode;
217 xmlr_strd(cur,
"vert", shadervert);
218 xmlr_strd(cur,
"frag", shaderfrag);
219 xmlr_float(cur,
"size", temp->
size);
220 if (xml_isNode(cur,
"uniforms")) {
224 }
while (xml_nextNode(cur));
227 WARN(_(
"SPFX '%s' has unknown node '%s'."), temp->
name, node->name);
228 }
while (xml_nextNode(node));
235 if (shadervert != NULL && shaderfrag != NULL) {
236 temp->
shader = gl_program_vert_frag( shadervert, shaderfrag, NULL );
237 temp->vertex = glGetAttribLocation( temp->
shader,
"vertex");
238 temp->projection = glGetUniformLocation( temp->
shader,
"projection");
239 temp->
u_r = glGetUniformLocation( temp->
shader,
"u_r" );
240 temp->
u_time = glGetUniformLocation( temp->
shader,
"u_time" );
241 temp->
u_size = glGetUniformLocation( temp->
shader,
"u_size" );
242 if (uniforms != NULL) {
243 glUseProgram( temp->
shader );
244 node = uniforms->xmlChildrenNode;
247 name = (
char*)node->name;
248 loc = glGetUniformLocation( temp->
shader, name );
250 WARN(_(
"SPFX '%s' is trying to set uniform '%s' not in shader!"), temp->
name, name );
253 xmlr_attr_int_def(node,
"int",isint,0);
255 if (xmlHasProp(node,(xmlChar*)
"w")) dim = 4;
256 else if (xmlHasProp(node,(xmlChar*)
"z")) dim = 3;
257 else if (xmlHasProp(node,(xmlChar*)
"y")) dim = 2;
262 xmlr_attr_int(node,
"x", ix );
263 xmlr_attr_int(node,
"y", iy );
264 xmlr_attr_int(node,
"z", iz );
265 xmlr_attr_int(node,
"w", iw );
268 glUniform1i( loc, ix );
271 glUniform2i( loc, ix, iy );
274 glUniform3i( loc, ix, iy, iz );
277 glUniform4i( loc, ix, iy, iz, iw );
283 xmlr_attr_float(node,
"x", x );
284 xmlr_attr_float(node,
"y", y );
285 xmlr_attr_float(node,
"z", z );
286 xmlr_attr_float(node,
"w", w );
289 glUniform1f( loc, x );
292 glUniform2f( loc, x, y );
295 glUniform3f( loc, x, y, z );
298 glUniform4f( loc, x, y, z, w );
302 }
while (xml_nextNode(node));
308#define MELEMENT(o,s) \
309 if (o) WARN( _("SPFX '%s' missing/invalid '%s' element"), temp->name, s)
310 MELEMENT(temp->
anim==0.,
"anim");
311 MELEMENT(temp->
ttl==0.,
"ttl");
312 MELEMENT(temp->
gfx==NULL && (shadervert==NULL || shaderfrag==NULL),
"gfx or shader");
313 MELEMENT(temp->
shader>=0 && temp->
size<=0.,
"shader/size");
344 const SPFX_Base sq = { .name = (
char*)name };
363 Uint32 time = SDL_GetTicks();
368 for (
int i=0; i<
array_size(spfx_files); i++) {
375 free( spfx_files[i] );
391 shake_shader.VertexPosition= shaders.shake.VertexPosition;
392 shake_shader.ClipSpaceFromLocal = shaders.shake.ClipSpaceFromLocal;
403 damage_shader.ClipSpaceFromLocal = shaders.damage.ClipSpaceFromLocal;
412 time = SDL_GetTicks() - time;
476 const double px,
const double py,
477 const double vx,
const double vy,
484 WARN(_(
"Trying to add spfx with invalid effect!"));
491 if (layer == SPFX_LAYER_FRONT)
493 else if (layer == SPFX_LAYER_MIDDLE)
495 else if (layer == SPFX_LAYER_BACK)
498 WARN(_(
"Invalid SPFX layer."));
503 cur_spfx->
effect = effect;
504 vec2_csetmin( &cur_spfx->
pos, px, py );
505 vec2_csetmin( &cur_spfx->
vel, vx, vy );
510 cur_spfx->
timer = ttl + RNGF()*anim;
512 cur_spfx->
timer = ttl;
515 cur_spfx->
unique = RNGF();
516 cur_spfx->
time = 0.0;
565 spfx_updateDamage( dt );
580 layer[i].timer -= dt;
583 if (layer[i].timer < 0.) {
591 vec2_cadd( &layer[i].pos, dt*VX(layer[i].vel), dt*VY(layer[i].vel) );
600 double mod, vmod, angle;
601 double force_x, force_y;
624 if (!forced && (mod < 0.01) && (vmod < 0.01)) {
645 vec2_cadd( &
shake_vel, (1./SHAKE_MASS) * force_x * dt, (1./SHAKE_MASS) * force_y * dt );
651 glUseProgram( shaders.shake.program );
660static void spfx_updateDamage(
double dt )
676 glUseProgram( shaders.damage.program );
714 for (
int i=0; i<n; i++) {
717 if (!trail->
refcount && !trail_size(trail) ) {
734 GLfloat rel_dt = dt/ trail->spec->
ttl;
736 while (trail->
iread < trail->
iwrite && trail_front(trail).t < rel_dt)
740 for (
size_t i = trail->
iread; i < trail->iwrite; i++)
741 trail_at( trail, i ).t -= rel_dt;
760 if (!force && trail->spec->
style[mode].
col.a <= 0.)
769 trail_back( trail ) = p;
776 if (trail_size(trail) == trail->
capacity) {
784 trail_at( trail, trail->
iwrite++ ) = p;
818 n = trail_size(trail);
821 styles = trail->spec->
style;
824 glUseProgram( shaders.trail.program );
825 if (gl_has( OPENGL_SUBROUTINES ))
826 glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &trail->spec->
type );
827 glEnableVertexAttribArray( shaders.trail.vertex );
829 glUniform1f( shaders.trail.dt, trail->
dt );
830 glUniform1f( shaders.trail.r, trail->
r );
834 for (
size_t i=trail->
iread + 1; i < trail->iwrite; i++) {
837 double x1, y1, x2, y2, s;
842 if (tp->
mode == MODE_NONE || tpp->
mode == MODE_NONE)
848 s = hypot( x2-x1, y2-y1 );
853 if ((
MAX(x1,x2) < 0.) || (
MIN(x1,x2) > (
double)SCREEN_W) ||
854 (
MAX(y1,y2) < 0.) || (
MIN(y1,y2) > (
double)SCREEN_H)) {
859 sp = &styles[tp->
mode];
860 spp = &styles[tpp->
mode];
863 projection = gl_view_matrix;
870 gl_uniformMat4(shaders.trail.projection, &projection);
871 gl_uniformColour(shaders.trail.c1, &sp->
col);
872 gl_uniformColour(shaders.trail.c2, &spp->
col);
873 glUniform1f(shaders.trail.t1, tp->
t);
874 glUniform1f(shaders.trail.t2, tpp->
t);
875 glUniform2f(shaders.trail.pos2, len, sp->
thick);
877 glUniform2f(shaders.trail.pos1, len, spp->
thick);
880 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
884 glDisableVertexAttribArray( shaders.trail.vertex );
934 SDL_HapticEffect *efx;
941 memset( efx, 0,
sizeof(SDL_HapticEffect) );
942 efx->type = SDL_HAPTIC_SINE;
943 efx->periodic.direction.type = SDL_HAPTIC_POLAR;
944 efx->periodic.length = 1000;
945 efx->periodic.period = 200;
946 efx->periodic.magnitude = 0x4000;
947 efx->periodic.fade_length = 1000;
948 efx->periodic.fade_level = 0;
952 WARN(_(
"Unable to upload haptic effect: %s."), SDL_GetError());
966 SDL_HapticEffect *efx;
986 efx->periodic.magnitude = (int16_t)mag;
987 efx->periodic.length = (uint32_t)len;
988 efx->periodic.fade_length =
MIN( efx->periodic.length, 1000 );
990 WARN(_(
"Failed to update haptic effect: %s."), SDL_GetError());
1009 gl_renderRect( 0., SCREEN_H*0.8, SCREEN_W, SCREEN_H, &cBlack );
1012static void spfx_renderStack(
SPFX *spfx_stack )
1014 for (
int i=
array_size(spfx_stack)-1; i>=0; i--) {
1015 SPFX *spfx = &spfx_stack[i];
1019 if (effect->
shader >= 0) {
1025 s2 = effect->
size/2.;
1028 w = h = effect->
size*z;
1031 if ((x < -w) || (x > SCREEN_W+w) ||
1032 (y < -h) || (y > SCREEN_H+h))
1036 glUseProgram( effect->
shader );
1039 projection = gl_view_matrix;
1042 glEnableVertexAttribArray( effect->vertex );
1044 0, 2, GL_FLOAT, 0 );
1047 gl_uniformMat4(effect->projection, &projection);
1053 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1056 glDisableVertexAttribArray( shaders.texture.vertex );
1069 sx = (int)effect->
gfx->
sx;
1070 sy = (int)effect->
gfx->
sy;
1073 double time = 1. - fmod(spfx_stack[i].timer,effect->
anim) / effect->
anim;
1079 VX(spfx_stack[i].pos), VY(spfx_stack[i].pos),
1080 spfx_stack[i].lastframe % sx,
1081 spfx_stack[i].lastframe / sx,
1097 case SPFX_LAYER_FRONT:
1102 case SPFX_LAYER_MIDDLE:
1107 case SPFX_LAYER_BACK:
1120 WARN(_(
"Rendering invalid SPFX layer."));
1131 static const char *mode_tags[] = MODE_TAGS;
1133 xmlNodePtr parent, node;
1142 parent = doc->xmlChildrenNode;
1143 if (parent == NULL) {
1144 WARN( _(
"Malformed '%s' file: does not contain elements"), file );
1150 for(
int i=0; i<MODE_MAX; i++)
1154 xmlr_attr_strd( parent,
"inherits", inherits );
1156 xmlr_attr_strd( parent,
"name", tc->
name );
1157 if (inherits != NULL) {
1165 if (inherits == NULL) {
1172 TrailSpec *tsparent = trailSpec_getRaw( inherits );
1173 if (tsparent == NULL)
1174 WARN(_(
"Trail '%s' that inherits from '%s' has missing reference!"), tc->
name, inherits );
1176 char *name = tc->
name;
1177 char *filename = tc->filename;
1178 memcpy( tc, tsparent,
sizeof(
TrailSpec) );
1180 tc->filename = filename;
1185 node = parent->xmlChildrenNode;
1187 xml_onlyNodes(node);
1188 if (xml_isNode(node,
"thickness"))
1190 else if (xml_isNode(node,
"ttl"))
1191 tc->
ttl = xml_getFloat( node );
1192 else if (xml_isNode(node,
"type")) {
1193 char *type = xml_get(node);
1194 if (gl_has( OPENGL_SUBROUTINES )) {
1195 tc->
type = glGetSubroutineIndex( shaders.trail.program, GL_FRAGMENT_SHADER, type );
1196 if (tc->
type == GL_INVALID_INDEX)
1197 WARN(
"Trail '%s' has unknown type '%s'", tc->
name, type );
1200 else if (xml_isNode(node,
"nebula"))
1201 tc->
nebula = xml_getInt( node );
1204 for (i=0; i<MODE_MAX; i++)
1205 if (xml_isNode(node, mode_tags[i])) {
1206 xmlr_attr_float_opt( node,
"r", tc->
style[i].
col.r );
1207 xmlr_attr_float_opt( node,
"g", tc->
style[i].
col.g );
1208 xmlr_attr_float_opt( node,
"b", tc->
style[i].
col.b );
1209 xmlr_attr_float_opt( node,
"a", tc->
style[i].
col.a );
1210 xmlr_attr_float_opt( node,
"scale", tc->
style[i].
thick );
1211 col_gammaToLinear( &tc->
style[i].
col );
1215 WARN(_(
"Trail '%s' has unknown node '%s'."), tc->
name, node->name);
1217 }
while (xml_nextNode(node));
1219#define MELEMENT(o,s) if (o) WARN(_("Trail '%s' missing '%s' element"), tc->name, s)
1220 MELEMENT( tc->
def_thick==0,
"thickness" );
1221 MELEMENT( tc->
ttl==0,
"ttl" );
1246 tc.filename = ts_files[i];
1250 free( ts_files[i] );
1260 for(
int i=0; i<MODE_MAX; i++)
1261 tc->style[i].thick *= tc->def_thick;
1268static TrailSpec* trailSpec_getRaw(
const char* name )
1274 WARN(_(
"Trail type '%s' not found in stack"), name);
1285 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.
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
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, 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.
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.
unsigned int haptic_query
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.