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_ {
162 xmlNodePtr node, cur, uniforms;
163 char *shadervert, *shaderfrag;
175 node = doc->xmlChildrenNode;
177 ERR( _(
"Malformed '%s' file: missing root element '%s'"), filename,
SPFX_XML_ID);
188 xmlr_attr_strd( node,
"name", temp->
name );
191 node = node->xmlChildrenNode;
194 xmlr_float(node,
"anim", temp->
anim);
195 xmlr_float(node,
"ttl", temp->
ttl);
196 if (xml_isNode(node,
"gfx")) {
198 SPFX_GFX_PATH
"%s", 6, 5, 0 );
202 if (xml_isNode(node,
"shader")) {
203 cur = node->xmlChildrenNode;
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")) {
213 }
while (xml_nextNode(cur));
216 WARN(_(
"SPFX '%s' has unknown node '%s'."), temp->
name, node->name);
217 }
while (xml_nextNode(node));
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;
238 name = (
char*)node->name;
239 loc = glGetUniformLocation( temp->
shader, name );
241 WARN(_(
"SPFX '%s' is trying to set uniform '%s' not in shader!"), temp->
name, name );
244 xmlr_attr_int_def(node,
"int",isint,0);
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;
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 );
259 glUniform1i( loc, ix );
262 glUniform2i( loc, ix, iy );
265 glUniform3i( loc, ix, iy, iz );
268 glUniform4i( loc, ix, iy, iz, iw );
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 );
280 glUniform1f( loc, x );
283 glUniform2f( loc, x, y );
286 glUniform3f( loc, x, y, z );
289 glUniform4f( loc, x, y, z, w );
293 }
while (xml_nextNode(node));
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");
351 Uint32 time = SDL_GetTicks();
356 for (
int i=0; i<
array_size(spfx_files); i++) {
363 free( spfx_files[i] );
378 shake_shader.VertexPosition= shaders.shake.VertexPosition;
379 shake_shader.ClipSpaceFromLocal = shaders.shake.ClipSpaceFromLocal;
390 damage_shader.ClipSpaceFromLocal = shaders.damage.ClipSpaceFromLocal;
399 time = SDL_GetTicks() - time;
463 const double px,
const double py,
464 const double vx,
const double vy,
471 WARN(_(
"Trying to add spfx with invalid effect!"));
478 if (layer == SPFX_LAYER_FRONT)
480 else if (layer == SPFX_LAYER_MIDDLE)
482 else if (layer == SPFX_LAYER_BACK)
485 WARN(_(
"Invalid SPFX layer."));
490 cur_spfx->
effect = effect;
491 vec2_csetmin( &cur_spfx->
pos, px, py );
492 vec2_csetmin( &cur_spfx->
vel, vx, vy );
497 cur_spfx->
timer = ttl + RNGF()*anim;
499 cur_spfx->
timer = ttl;
502 cur_spfx->
unique = RNGF();
503 cur_spfx->
time = 0.0;
552 spfx_updateDamage( dt );
567 layer[i].timer -= dt;
570 if (layer[i].timer < 0.) {
578 vec2_cadd( &layer[i].pos, dt*VX(layer[i].vel), dt*VY(layer[i].vel) );
587 double mod, vmod, angle;
588 double force_x, force_y;
611 if (!forced && (mod < 0.01) && (vmod < 0.01)) {
632 vec2_cadd( &
shake_vel, (1./SHAKE_MASS) * force_x * dt, (1./SHAKE_MASS) * force_y * dt );
638 glUseProgram( shaders.shake.program );
647static void spfx_updateDamage(
double dt )
663 glUseProgram( shaders.damage.program );
701 for (
int i=0; i<n; i++) {
704 if (!trail->
refcount && !trail_size(trail) ) {
721 GLfloat rel_dt = dt/ trail->spec->
ttl;
723 while (trail->
iread < trail->
iwrite && trail_front(trail).t < rel_dt)
727 for (
size_t i = trail->
iread; i < trail->iwrite; i++)
728 trail_at( trail, i ).t -= rel_dt;
747 if (!force && trail->spec->
style[mode].
col.a <= 0.)
756 trail_back( trail ) = p;
763 if (trail_size(trail) == trail->
capacity) {
771 trail_at( trail, trail->
iwrite++ ) = p;
805 n = trail_size(trail);
808 styles = trail->spec->
style;
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 );
816 glUniform1f( shaders.trail.dt, trail->
dt );
817 glUniform1f( shaders.trail.r, trail->
r );
821 for (
size_t i=trail->
iread + 1; i < trail->iwrite; i++) {
824 double x1, y1, x2, y2, s;
829 if (tp->
mode == MODE_NONE || tpp->
mode == MODE_NONE)
835 s = hypot( x2-x1, y2-y1 );
838 if ((
MAX(x1,x2) < 0.) || (
MIN(x1,x2) > (
double)SCREEN_W) ||
839 (
MAX(y1,y2) < 0.) || (
MIN(y1,y2) > (
double)SCREEN_H)) {
844 sp = &styles[tp->
mode];
845 spp = &styles[tpp->
mode];
848 projection = gl_view_matrix;
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);
862 glUniform2f(shaders.trail.pos1, len, spp->
thick);
865 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
869 glDisableVertexAttribArray( shaders.trail.vertex );
919 SDL_HapticEffect *efx;
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;
937 WARN(_(
"Unable to upload haptic effect: %s."), SDL_GetError());
951 SDL_HapticEffect *efx;
971 efx->periodic.magnitude = (int16_t)mag;
972 efx->periodic.length = (uint32_t)len;
973 efx->periodic.fade_length =
MIN( efx->periodic.length, 1000 );
975 WARN(_(
"Failed to update haptic effect: %s."), SDL_GetError());
994 gl_renderRect( 0., SCREEN_H*0.8, SCREEN_W, SCREEN_H, &cBlack );
1010 case SPFX_LAYER_FRONT:
1015 case SPFX_LAYER_MIDDLE:
1020 case SPFX_LAYER_BACK:
1026 WARN(_(
"Rendering invalid SPFX layer."));
1031 if (layer == SPFX_LAYER_BACK)
1039 for (
int i=
array_size(spfx_stack)-1; i>=0; i--) {
1040 SPFX *spfx = &spfx_stack[i];
1044 if (effect->
shader >= 0) {
1050 s2 = effect->
size/2.;
1053 w = h = effect->
size*z;
1056 if ((x < -w) || (x > SCREEN_W+w) ||
1057 (y < -h) || (y > SCREEN_H+h))
1061 glUseProgram( effect->
shader );
1064 projection = gl_view_matrix;
1067 glEnableVertexAttribArray( effect->vertex );
1069 0, 2, GL_FLOAT, 0 );
1072 gl_uniformMat4(effect->projection, &projection);
1078 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1081 glDisableVertexAttribArray( shaders.texture.vertex );
1092 sx = (int)effect->
gfx->
sx;
1093 sy = (
int)effect->
gfx->
sy;
1096 time = 1. - fmod(spfx_stack[i].timer,effect->
anim) / effect->
anim;
1102 VX(spfx_stack[i].pos), VY(spfx_stack[i].pos),
1103 spfx_stack[i].lastframe % sx,
1104 spfx_stack[i].lastframe / sx,
1116 static const char *mode_tags[] = MODE_TAGS;
1118 xmlNodePtr parent, node;
1127 parent = doc->xmlChildrenNode;
1128 if (parent == NULL) {
1129 WARN( _(
"Malformed '%s' file: does not contain elements"), file );
1135 for(
int i=0; i<MODE_MAX; i++)
1139 xmlr_attr_strd( parent,
"inherits", inherits );
1141 xmlr_attr_strd( parent,
"name", tc->
name );
1142 if (inherits != NULL) {
1150 if (inherits == NULL) {
1157 TrailSpec *tsparent = trailSpec_getRaw( inherits );
1158 if (tsparent == NULL)
1159 WARN(_(
"Trail '%s' that inherits from '%s' has missing reference!"), tc->
name, inherits );
1161 char *name = tc->
name;
1162 char *filename = tc->filename;
1163 memcpy( tc, tsparent,
sizeof(
TrailSpec) );
1165 tc->filename = filename;
1170 node = parent->xmlChildrenNode;
1172 xml_onlyNodes(node);
1173 if (xml_isNode(node,
"thickness"))
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 );
1185 else if (xml_isNode(node,
"nebula"))
1186 tc->
nebula = xml_getInt( node );
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 );
1200 WARN(_(
"Trail '%s' has unknown node '%s'."), tc->
name, node->name);
1202 }
while (xml_nextNode(node));
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" );
1231 tc.filename = ts_files[i];
1235 free( ts_files[i] );
1245 for(
int i=0; i<MODE_MAX; i++)
1246 tc->style[i].thick *= tc->def_thick;
1253static TrailSpec* trailSpec_getRaw(
const char* name )
1259 WARN(_(
"Trail type '%s' not found in stack"), name);
1270 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_rendermg(void)
Renders the Lua SPFX in the midground.
void spfxL_clear(void)
Clears the Lua spfx.
void spfxL_renderbg(void)
Renders the Lua SPFX on the background.
void spfxL_update(double dt)
Updates the spfx.
void spfxL_renderfg(void)
Renders the Lua SPFX in the foreground.
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
const TrailSpec * trailSpec_get(const char *name)
Gets a trail spec by name.
static SPFX_Base * spfx_effects
int spfx_get(char *name)
Gets the id of an spfx based on name.
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.
void spfx_render(int layer)
Renders the entire spfx layer.
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.
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).
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.