diff --git a/include/QF/GLSL/qf_particles.h b/include/QF/GLSL/qf_particles.h index a6a4111f8..1c6a2108d 100644 --- a/include/QF/GLSL/qf_particles.h +++ b/include/QF/GLSL/qf_particles.h @@ -44,4 +44,8 @@ void glsl_R_Particles_Init_Cvars (void); void glsl_R_InitParticles (void); void glsl_R_ShutdownParticles (void); +void glsl_R_InitTrails (void); +void glsl_R_ShutdownTrails (void); +void glsl_R_DrawTrails (struct psystem_s *psystem); + #endif//__QF_GLSL_qf_particles_h diff --git a/include/QF/plugin/vid_render.h b/include/QF/plugin/vid_render.h index 0d5d512b4..c1e802273 100644 --- a/include/QF/plugin/vid_render.h +++ b/include/QF/plugin/vid_render.h @@ -39,6 +39,7 @@ struct plitem_s; struct cvar_s; struct scene_s; struct skin_s; +struct particle_s; struct mod_alias_ctx_s; struct mod_sprite_ctx_s; @@ -117,6 +118,7 @@ typedef struct vid_render_funcs_s { void (*Draw_Glyph) (int x, int y, int fontid, int glyphid, int c); struct psystem_s *(*ParticleSystem) (void); + struct psystem_s *(*TrailSystem) (void); void (*R_Init) (void); void (*R_ClearState) (void); void (*R_LoadSkys) (const char *); @@ -126,6 +128,7 @@ typedef struct vid_render_funcs_s { void (*begin_frame) (void); void (*render_view) (void); void (*draw_particles) (struct psystem_s *psystem); + void (*draw_trails) (struct psystem_s *psystem); void (*draw_transparent) (void); void (*post_process) (struct framebuffer_s *src); void (*set_2d) (int scaled); diff --git a/include/QF/render.h b/include/QF/render.h index 51950eeb4..cd522ab0f 100644 --- a/include/QF/render.h +++ b/include/QF/render.h @@ -41,10 +41,8 @@ typedef enum { part_tex_smoke, } ptextype_t; -typedef struct particle_s particle_t; - // !!! if this is changed, it must be changed in d_ifacea.h too !!! -struct particle_s { +typedef struct particle_s { vec4f_t pos; vec4f_t vel; @@ -61,7 +59,24 @@ struct particle_s { float ramp; float scale; float live; -}; +} particle_t; + +static_assert (sizeof (particle_t) == 4 * sizeof(vec4f_t), + "particle_t wrong size"); + +typedef struct trailpnt_s { + vec4f_t pos; + vec4f_t vel; + vec3_t bary; + float pathoffset; + byte colora[4]; + byte colorb[4]; + uint32_t trailid; + float live; +} trailpnt_t; + +static_assert (sizeof (trailpnt_t) == 4 * sizeof(vec4f_t), + "trailprt_t wrong size"); typedef struct partparm_s { vec4f_t drag; // drag[3] is grav scale @@ -71,6 +86,9 @@ typedef struct partparm_s { float alpha_rate; } partparm_t; +static_assert (sizeof (partparm_t) == 2 * sizeof(vec4f_t), + "partparm_t wrong size"); + typedef struct psystem_s { vec4f_t gravity; uint32_t maxparticles; @@ -186,4 +204,9 @@ void Fog_StartAdditive (void); void Fog_StopAdditive (void); void Fog_Init (void); +bool R_Trail_Valid (psystem_t *system, uint32_t trailid) __attribute__((pure)); +uint32_t R_Trail_Create (psystem_t *system, int num_points, vec4f_t start); +void R_Trail_Update (psystem_t *system, uint32_t trailid, vec4f_t pos); +void R_Trail_Destroy (psystem_t *system, uint32_t trailid); + #endif//__QF_render_h diff --git a/include/QF/scene/entity.h b/include/QF/scene/entity.h index a59496759..8d279d7ab 100644 --- a/include/QF/scene/entity.h +++ b/include/QF/scene/entity.h @@ -75,6 +75,7 @@ typedef struct visibility_s { typedef struct renderer_s { struct model_s *model; // NULL = no model struct skin_s *skin; + struct trail_s *trail; unsigned fullbright:1; unsigned noshadows:1; unsigned onlyshadows:1; diff --git a/include/client/effects.h b/include/client/effects.h index 16214742a..e152bce32 100644 --- a/include/client/effects.h +++ b/include/client/effects.h @@ -36,6 +36,7 @@ enum { effect_light, // light entity id effect_muzzleflash, // light entity id + effect_trail, // trail id effect_comp_count, }; @@ -54,5 +55,6 @@ void CL_EntityEffects (struct entity_s ent, struct entity_state_s *state, double time); void CL_MuzzleFlash (struct entity_s ent, vec4f_t position, vec4f_t fv, float zoffset, double time); +void CL_Effects_Init (void); #endif//__client_effects_h diff --git a/include/d_iface.h b/include/d_iface.h index bd04da4d7..ec4ac6bf9 100644 --- a/include/d_iface.h +++ b/include/d_iface.h @@ -56,6 +56,17 @@ typedef struct float zi; } emitpoint_t; +typedef struct particle_s particle_t; + +void R_LoadParticles (void); +bool R_CompileParticlePhysics (const char *name, const char *code); +void R_RunParticlePhysics (particle_t *part); +const union pt_phys_op_s *R_ParticlePhysics (const char *type); +bool R_AddParticlePhysicsFunction (const char *name, + bool (*func) (struct particle_s *, void *), + void *data); +extern const char particle_types[]; + #define PARTICLE_Z_CLIP 8.0 typedef struct polyvert_s { diff --git a/include/r_dynamic.h b/include/r_dynamic.h index 628ae6b67..6690fc8ad 100644 --- a/include/r_dynamic.h +++ b/include/r_dynamic.h @@ -45,10 +45,13 @@ typedef enum { struct entity_s; -void R_Particles_Init_Cvars (void); -void R_InitBubble (void); +void R_Trails_Init (void); +void R_Trails_Init_Cvars (void); +void R_ClearTrails (void); +void R_RunTrails (float dT); -void R_InitParticles (void); +void R_RunParticles (float dT); +void R_Particles_Init_Cvars (void); void R_ClearParticles (void); void R_InitSprites (void); diff --git a/include/r_internal.h b/include/r_internal.h index 0436d126d..b37ae3918 100644 --- a/include/r_internal.h +++ b/include/r_internal.h @@ -42,10 +42,11 @@ void R_ViewChanged (void); // must set r_refdef first // called whenever r_refdef or vid change extern struct psystem_s r_psystem; +extern struct psystem_s r_tsystem; struct psystem_s *gl_ParticleSystem (void); struct psystem_s *glsl_ParticleSystem (void); +struct psystem_s *glsl_TrailSystem (void); struct psystem_s *sw_ParticleSystem (void); -void R_RunParticles (float dT); struct scene_s; void R_NewScene (struct scene_s *scene); diff --git a/libs/client/Makemodule.am b/libs/client/Makemodule.am index 5b33a1c20..cddd028e8 100644 --- a/libs/client/Makemodule.am +++ b/libs/client/Makemodule.am @@ -1,5 +1,13 @@ noinst_LTLIBRARIES += libs/client/libQFclient.la +particles_src= libs/client/particles.part +particles_gen= libs/client/particles.pc + +SUFFICES += .part .pc +.part.pc: + $(V_SED)sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\ + $(am__mv) $@.t $@ + libs_client_libQFclient_la_LDFLAGS= @STATIC@ libs_client_libQFclient_la_LIBADD= \ libs/ui/libQFgui.la \ @@ -24,9 +32,13 @@ libs_client_libQFclient_la_SOURCES= \ default_input_src = libs/client/default_input.plist default_input_gen = libs/client/default_input.plc -libs/client/cl_input.lo: libs/client/cl_input.c $(default_input_gen) - EXTRA_DIST += \ - libs/client/default_input.plist + $(default_input_src) \ + $(particles_src) CLEANFILES += \ - libs/client/*.plc + libs/client/*.plc \ + libs/client/*.pc + +BUILT_SOURCES += \ + $(default_input_gen) \ + $(particles_gen) diff --git a/libs/client/cl_effects.c b/libs/client/cl_effects.c index 04863e93d..b34fad885 100644 --- a/libs/client/cl_effects.c +++ b/libs/client/cl_effects.c @@ -53,6 +53,15 @@ ecs_system_t effect_system; +static psystem_t *cl_tsystem; + +static void +cl_destroy_trail (void *comp) +{ + auto trail = *(uint32_t *) comp; + R_Trail_Destroy (cl_tsystem, trail); +} + const component_t effect_components[effect_comp_count] = { [effect_light] = { .size = sizeof (uint32_t), @@ -62,9 +71,15 @@ const component_t effect_components[effect_comp_count] = { .size = sizeof (uint32_t), .name = "muzzle flash", }, + [effect_trail] = { + .size = sizeof (uint32_t), + .name = "effect trail", + .destroy = cl_destroy_trail, + }, }; #define c_light (effect_system.base + effect_light) +#define c_trail (effect_system.base + effect_trail) static bool has_light (entity_t ent) @@ -98,6 +113,27 @@ attach_light_ent (entity_t ent) return light; } +static bool +has_trail (entity_t ent) +{ + return Ent_HasComponent (ent.id, c_trail, ent.reg); +} + +static uint32_t +get_trail (entity_t ent) +{ + if (!has_trail (ent)) { + return nullent; + } + return *(uint32_t *) Ent_GetComponent (ent.id, c_trail, ent.reg); +} + +static void +set_trail (entity_t ent, uint32_t trail) +{ + Ent_SetComponent (ent.id, c_trail, ent.reg, &trail); +} + void CL_NewDlight (entity_t ent, vec4f_t org, int effects, byte glow_size, byte glow_color, double time) @@ -181,6 +217,14 @@ CL_ModelEffects (entity_t ent, int glow_color, double time) }); Light_LinkLight (cl_world.scene->lights, light); clp_funcs->RocketTrail (*old_origin, ent_origin); + if (cl_tsystem) { + uint32_t trail = get_trail (ent); + if (R_Trail_Valid (cl_tsystem, trail)) { + R_Trail_Update (cl_tsystem, trail, ent_origin); + } else { + set_trail (ent, R_Trail_Create (cl_tsystem, 30, ent_origin)); + } + } renderer->noshadows = 1; } else if (model->effects & ME_GRENADE) clp_funcs->GrenadeTrail (*old_origin, ent_origin); @@ -210,3 +254,11 @@ CL_EntityEffects (entity_t ent, entity_state_t *state, double time) CL_MuzzleFlash (ent, position, fv, 16, time); } } + +void +CL_Effects_Init (void) +{ + if (r_funcs->TrailSystem) { + cl_tsystem = r_funcs->TrailSystem (); + } +} diff --git a/libs/client/particles.part b/libs/client/particles.part new file mode 100644 index 000000000..b868ef926 --- /dev/null +++ b/libs/client/particles.part @@ -0,0 +1,87 @@ +All of QuakeForge's standard particle physics functions converted to a script +-- pt_static +add_vel + +-- pt_grav +add_vel +add_grav + +-- pt_slowgrav +add_vel +add_grav + +-- pt_fire +add_ramp 5 6 +add_vel +color_ramp3 +alpha_ramp 6 +sub_grav + +-- pt_float +add_vel +sub_grav + +-- pt_explode +add_ramp 10 8 +add_vel +color_ramp1 +explode_vel 4 +add_grav + +-- pt_explode2 +add_ramp 15 8 +add_vel +color_ramp2 +explode_vel 1 +add_grav + +-- pt_blob +add_vel +explode_vel 4 +add_grav + +-- pt_blob2 +add_vel +damp_vel 4 +add_grav + +-- pt_smoke +fade_alpha 0.4 +add_vel +grow_scale 4 +//sub_slowgrav + +-- pt_smokecloud +fade_alpha 0.55 +add_vel +grow_scale 50 +sub_slowgrav + +-- pt_bloodcloud +fade_alpha 0.25 +add_vel +grow_scale 4 +add_grav + +-- pt_fadespark +add_vel + +-- pt_fadespark2 +add_vel + +-- pt_fallfade +fade_alpha 1 +add_vel +add_fastgrav + +-- pt_fallfadespark +add_ramp 15 8 +fade_alpha 1 +color_ramp1 +add_vel +add_fastgrav + +-- pt_flame +fade_alpha 0.125 +add_vel +shrink_scale 2 diff --git a/libs/util/segtext.c b/libs/util/segtext.c index a75a7c779..347f0fe61 100644 --- a/libs/util/segtext.c +++ b/libs/util/segtext.c @@ -151,10 +151,9 @@ Segtext_new (const char *source_string) (*chunk)->text = src; (*chunk)->start_line = line; - chunk = &(*chunk)->next; - while ((src = next_chunk (src, &line))) { *src++ = 0; // terminate the previous chunk + chunk = &(*chunk)->next; *chunk = new_chunk (); (*chunk)->tag = find_tag (src); src = next_line (src, &line); @@ -163,7 +162,6 @@ Segtext_new (const char *source_string) // If tags are duplicated, the first one takes precedence if ((*chunk)->tag && !Hash_Find (text->tab, (*chunk)->tag)) Hash_Add (text->tab, *chunk); - chunk = &(*chunk)->next; } return text; } diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am index d2060b7e0..3cf3a388b 100644 --- a/libs/video/renderer/Makemodule.am +++ b/libs/video/renderer/Makemodule.am @@ -58,9 +58,10 @@ libs_video_renderer_libQFrenderer_la_SOURCES=\ libs/video/renderer/r_light.c \ libs/video/renderer/r_main.c \ libs/video/renderer/r_part.c \ + libs/video/renderer/r_progs.c \ libs/video/renderer/r_scrap.c \ libs/video/renderer/r_screen.c \ - libs/video/renderer/r_progs.c + libs/video/renderer/r_trails.c video_renderer_gl_libs= \ libs/video/renderer/librender_gl.la \ @@ -97,12 +98,18 @@ libs_video_renderer_librender_gl_la_SOURCES = \ libs/video/renderer/gl/vid_common_gl.c \ libs/video/renderer/gl/vtxarray.c -shader_src= libs/video/renderer/glsl/quakeforge.glsl -shader_gen= libs/video/renderer/glsl/quakeforge.slc +glslshaderpath = libs/video/renderer/glsl + +shader_src = \ + $(glslshaderpath)/quakeforge.glsl \ + $(glslshaderpath)/sgustavson.glsl +shader_gen = \ + $(glslshaderpath)/quakeforge.slc \ + $(glslshaderpath)/sgustavson.slc SUFFIXES += .frag .vert .spv .spvc .fc .vc .slc .glsl .glsl.slc: - $(V_SED)sed -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\ + $(V_SED)sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\ $(am__mv) $@.t $@ video_renderer_glsl_libs= \ @@ -129,6 +136,7 @@ libs_video_renderer_librender_glsl_la_SOURCES = \ libs/video/renderer/glsl/glsl_shader.c \ libs/video/renderer/glsl/glsl_sprite.c \ libs/video/renderer/glsl/glsl_textures.c \ + libs/video/renderer/glsl/glsl_trails.c \ libs/video/renderer/glsl/glsl_warp.c \ libs/video/renderer/glsl/qfglsl.c \ libs/video/renderer/glsl/quakeforge.glsl \ diff --git a/libs/video/renderer/glsl/glsl_main.c b/libs/video/renderer/glsl/glsl_main.c index 06601e743..2c8febac4 100644 --- a/libs/video/renderer/glsl/glsl_main.c +++ b/libs/video/renderer/glsl/glsl_main.c @@ -186,6 +186,7 @@ glsl_R_Init (void) glsl_R_InitIQM (); glsl_R_InitSprites (); glsl_R_InitParticles (); + glsl_R_InitTrails (); glsl_InitFisheye (); glsl_InitWarp (); Skin_Init (); diff --git a/libs/video/renderer/glsl/glsl_particles.c b/libs/video/renderer/glsl/glsl_particles.c index cf47429a4..9f8407765 100644 --- a/libs/video/renderer/glsl/glsl_particles.c +++ b/libs/video/renderer/glsl/glsl_particles.c @@ -37,6 +37,7 @@ #include +#include "QF/alloc.h" #include "QF/cmd.h" #include "QF/cvar.h" #include "QF/image.h" @@ -178,7 +179,9 @@ glsl_R_InitParticles (void) float v[2] = {0, 0}; byte data[64][64][2]; tex_t *tex; - +#if 0 + R_LoadParticles (); +#endif Cvar_AddListener (Cvar_FindVar ("r_particles"), glsl_particles_f, 0); Cvar_AddListener (Cvar_FindVar ("r_particles_max"), glsl_particles_f, 0); diff --git a/libs/video/renderer/glsl/glsl_shader.c b/libs/video/renderer/glsl/glsl_shader.c index 251a93882..2098439ec 100644 --- a/libs/video/renderer/glsl/glsl_shader.c +++ b/libs/video/renderer/glsl/glsl_shader.c @@ -152,7 +152,7 @@ GLSL_BuildShader (const char **effect_keys) chunk->start_line + 1, chunk->text + vline_len); } else { - shader->strings[num] = nva ("#line %d\n%s", chunk->start_line, + shader->strings[num] = nva ("#line %d\n%s", chunk->start_line - 1, chunk->text); } shader->src[num] = strdup (ekey->str); diff --git a/libs/video/renderer/glsl/glsl_trails.c b/libs/video/renderer/glsl/glsl_trails.c new file mode 100644 index 000000000..a44d22cf4 --- /dev/null +++ b/libs/video/renderer/glsl/glsl_trails.c @@ -0,0 +1,333 @@ +/* + glsl_trails.c + + OpenGL trail system. + + Copyright (C) 2013 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/alloc.h" +#include "QF/cmd.h" +#include "QF/cvar.h" +#include "QF/image.h" +#include "QF/mersenne.h" +#include "QF/qargs.h" +#include "QF/quakefs.h" +#include "QF/render.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "QF/scene/entity.h" + +#include "QF/GLSL/defines.h" +#include "QF/GLSL/funcs.h" +#include "QF/GLSL/qf_particles.h" +#include "QF/GLSL/qf_textures.h" +#include "QF/GLSL/qf_vid.h" + +#include "r_internal.h" +#include "r_local.h" + +static uint32_t maxparticles; +static GLushort *pVAindices; +static partvert_t *particleVertexArray; + +static const char *particle_trail_vert_effects[] = +{ + "QuakeForge.Screen.viewport", + "QuakeForge.Vertex.transform.view_projection", + "QuakeForge.Vertex.ScreenSpace.curve.width", + "QuakeForge.Vertex.particle.trail", + 0 +}; + +static const char *particle_trail_frag_effects[] = +{ + "QuakeForge.Math.InvSqrt", + "QuakeForge.Math.permute", + "QuakeForge.Noise.simplex", + "QuakeForge.Fragment.particle.trail", + 0 +}; + +static const char *particle_trail_debug_frag_effects[] = +{ + "QuakeForge.Fragment.barycentric", + 0 +}; + +typedef struct { + int program; + shaderparam_t proj; + shaderparam_t view; + shaderparam_t viewport; + shaderparam_t width; + shaderparam_t last; + shaderparam_t current; + shaderparam_t next; + shaderparam_t barycentric; + shaderparam_t texoff; + shaderparam_t colora; + shaderparam_t colorb; +} trailprog_t; + +static trailprog_t trail = { + 0, + {"projection_mat", 1}, + {"view_mat", 1}, + {"viewport", 1}, + {"width", 1}, + {"last", 0}, + {"current", 0}, + {"next", 0}, + {"barycentric", 0}, + {"texoff", 0}, + {"vcolora", 0}, + {"vcolorb", 0}, +}; +static trailprog_t trail_debug; + +typedef struct trailvtx_s { + vec4f_t vertex; + vec3_t bary; + float texoff; + byte colora[4]; + byte colorb[4]; +} trailvtx_t; + +static void +alloc_arrays (psystem_t *ps) +{ + if (ps->maxparticles > maxparticles) { + maxparticles = ps->maxparticles; + if (particleVertexArray) + free (particleVertexArray); + printf ("alloc_arrays: %d\n", ps->maxparticles); + particleVertexArray = calloc (ps->maxparticles * 4, + sizeof (partvert_t)); + + if (pVAindices) + free (pVAindices); + pVAindices = calloc (ps->maxparticles * 6, sizeof (GLushort)); + for (uint32_t i = 0; i < ps->maxparticles; i++) { + pVAindices[i * 6 + 0] = i * 4 + 0; + pVAindices[i * 6 + 1] = i * 4 + 1; + pVAindices[i * 6 + 2] = i * 4 + 2; + pVAindices[i * 6 + 3] = i * 4 + 0; + pVAindices[i * 6 + 4] = i * 4 + 2; + pVAindices[i * 6 + 5] = i * 4 + 3; + } + } +} + +void +glsl_R_ShutdownTrails (void) +{ + free (particleVertexArray); + free (pVAindices); +} + +static void +build_program (trailprog_t *prog, const char *name, int vert, int frag) +{ + prog->program = GLSL_LinkProgram (name, vert, frag); + GLSL_ResolveShaderParam (prog->program, &prog->proj); + GLSL_ResolveShaderParam (prog->program, &prog->view); + GLSL_ResolveShaderParam (prog->program, &prog->viewport); + GLSL_ResolveShaderParam (prog->program, &prog->width); + GLSL_ResolveShaderParam (prog->program, &prog->last); + GLSL_ResolveShaderParam (prog->program, &prog->current); + GLSL_ResolveShaderParam (prog->program, &prog->next); + GLSL_ResolveShaderParam (prog->program, &prog->barycentric); + GLSL_ResolveShaderParam (prog->program, &prog->texoff); + GLSL_ResolveShaderParam (prog->program, &prog->colora); + GLSL_ResolveShaderParam (prog->program, &prog->colorb); +} + +void +glsl_R_InitTrails (void) +{ + shader_t *vert_shader, *frag_shader, *debug_shader; + int vert; + int frag; + int debug; + + + vert_shader = GLSL_BuildShader (particle_trail_vert_effects); + frag_shader = GLSL_BuildShader (particle_trail_frag_effects); + debug_shader = GLSL_BuildShader (particle_trail_debug_frag_effects); + vert = GLSL_CompileShader ("trail.vert", vert_shader, GL_VERTEX_SHADER); + frag = GLSL_CompileShader ("trail.frag", frag_shader, GL_FRAGMENT_SHADER); + debug = GLSL_CompileShader ("trail.frag.debug", debug_shader, + GL_FRAGMENT_SHADER); + trail_debug = trail; + build_program (&trail, "trail", vert, frag); + build_program (&trail_debug, "trail.debug", vert, debug); + + GLSL_FreeShader (vert_shader); + GLSL_FreeShader (frag_shader); + GLSL_FreeShader (debug_shader); + + alloc_arrays (&r_psystem); +} + +static int +count_bits (uint32_t v) +{ + int c = 0; + for (; v; c++) { + v &= v - 1; + } + return c; +} + + +static int +count_points (uint32_t block_mask) +{ + int num_blocks = count_bits (block_mask); + return num_blocks * 64; +} + +static void +build_verts (trailvtx_t *verts, int *counts, + const trailpnt_t *points, uint32_t block_mask) +{ + uint32_t id = ~0u; + for (int m = 0; m < 32; m++, points += 64) { + if (!(block_mask & (1 << m))) { + if (*counts) { + counts++; + } + continue; + } + if (id != ~0u && points->trailid != id) { + counts++; + } + id = points->trailid; + *counts += 64; + for (int i = 0; i < 64; i++) { + verts[i] = (trailvtx_t) { + .vertex = points[i].pos, + .bary = {VectorExpand (points[i].bary)}, + .texoff = points[i].pathoffset, + .colora = { QuatExpand (points[i].colora)}, + .colorb = { QuatExpand (points[i].colorb)}, + }; + } + verts += 64; + } +} + +static void +draw_trails (trailpnt_t *points, uint32_t block_mask) +{ + int num_verts = count_points (block_mask); + if (!num_verts) { + return; + } + + trailvtx_t verts[num_verts]; + int counts[33] = {}; + build_verts (verts, counts, points, block_mask); + + auto prog = trail; + qfeglUseProgram (prog.program); + qfeglEnableVertexAttribArray (prog.last.location); + qfeglEnableVertexAttribArray (prog.current.location); + qfeglEnableVertexAttribArray (prog.next.location); + if (prog.barycentric.location >= 0) + qfeglEnableVertexAttribArray (prog.barycentric.location); + qfeglEnableVertexAttribArray (prog.texoff.location); + if (prog.colora.location >= 0) + qfeglEnableVertexAttribArray (prog.colora.location); + if (prog.colorb.location >= 0) + qfeglEnableVertexAttribArray (prog.colorb.location); + + qfeglUniformMatrix4fv (prog.proj.location, 1, false, + (vec_t*)&glsl_projection[0]);//FIXME + qfeglUniformMatrix4fv (prog.view.location, 1, false, + (vec_t*)&glsl_view[0]);//FIXME + qfeglUniform2f (prog.viewport.location, r_refdef.vrect.width, + r_refdef.vrect.height); + qfeglUniform1f (prog.width.location, 10); + + qfeglVertexAttribPointer (prog.last.location, 4, GL_FLOAT, + 0, sizeof (trailvtx_t), &verts[0].vertex); + qfeglVertexAttribPointer (prog.current.location, 4, GL_FLOAT, + 0, sizeof (trailvtx_t), &verts[2].vertex); + qfeglVertexAttribPointer (prog.next.location, 4, GL_FLOAT, + 0, sizeof (trailvtx_t), &verts[4].vertex); + if (prog.barycentric.location >= 0) + qfeglVertexAttribPointer (prog.barycentric.location, 3, GL_FLOAT, + 0, sizeof (trailvtx_t), &verts[2].bary); + qfeglVertexAttribPointer (prog.texoff.location, 1, GL_FLOAT, + 0, sizeof (trailvtx_t), &verts[0].texoff); + if (prog.colora.location >= 0) + qfeglVertexAttribPointer (prog.colora.location, 4, GL_UNSIGNED_BYTE, + 1, sizeof (trailvtx_t), &verts[0].colora); + if (prog.colorb.location >= 0) + qfeglVertexAttribPointer (prog.colorb.location, 4, GL_UNSIGNED_BYTE, + 1, sizeof (trailvtx_t), &verts[0].colorb); + + int first = 0; + for (auto c = counts; *c; c++) { + qfeglDrawArrays (GL_TRIANGLE_STRIP, first, *c - 4); + first += *c; + } + + qfeglDisableVertexAttribArray (prog.last.location); + qfeglDisableVertexAttribArray (prog.current.location); + qfeglDisableVertexAttribArray (prog.next.location); + if (prog.barycentric.location >= 0) + qfeglDisableVertexAttribArray (prog.barycentric.location); + qfeglDisableVertexAttribArray (prog.texoff.location); + if (prog.colora.location >= 0) + qfeglDisableVertexAttribArray (prog.colora.location); + if (prog.colorb.location >= 0) + qfeglDisableVertexAttribArray (prog.colorb.location); +} + +void +glsl_R_DrawTrails (psystem_t *psystem) +{ + //FIXME abuse of psystem_t + draw_trails ((trailpnt_t *)psystem->particles, psystem->numparticles); +} + +psystem_t * __attribute__((const))//FIXME +glsl_TrailSystem (void) +{ + return &r_tsystem; +} diff --git a/libs/video/renderer/glsl/quakeforge.glsl b/libs/video/renderer/glsl/quakeforge.glsl index b6f16ca0e..6673f3486 100644 --- a/libs/video/renderer/glsl/quakeforge.glsl +++ b/libs/video/renderer/glsl/quakeforge.glsl @@ -1,3 +1,30 @@ +quakeforge.glsl + +Builtin QuakeForge GLSL shaders. + +Copyright (C) 2013 Bill Currie + +Author: Bill Currie +Date: 2013/05/12 + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + -- Math.const const float PI = 3.14159265; @@ -23,6 +50,100 @@ dqtrans (vec4 q0, vec4 qe) return 2.0 * (Ts * qv + qs * Tv + cross (Tv, qv)); } +-- Vertex.transform.mvp +uniform mat4 mvp_mat; + +-- Vertex.transform.view_projection +uniform mat4 projection_mat, view_mat; + +-- Screen.viewport +uniform vec2 viewport; + +-- Vertex.ScreenSpace.curve.width + +vec2 +project (vec4 coord) +{ + vec3 device = coord.xyz / coord.w; + vec2 clip = (device * 0.5 + 0.5).xy; + return clip * viewport; +} + +vec4 +unproject (vec2 screen, float z, float w) +{ + vec2 clip = screen / viewport; + vec2 device = clip * 2.0 - 1.0; + return vec4 (device * w, z, w); +} + +vec2 +direction (vec2 from, vec2 to) +{ + vec2 t = to - from; + vec2 d = vec2 (0.0, 0.0); + + if (dot (t, t) > 0.001) { + d = normalize (t); + } + return d; +} + +vec2 +shared_direction (vec2 a, vec2 b) +{ + vec2 d = a + b; + + if (dot (d, d) > 0.001) { + d = normalize (d); + } else if (dot (a, a) > 0.001) { + d = normalize (a); + } else if (dot (b, b) > 0.001) { + d = normalize (b); + } else { + d = vec2 (0.0); + } + return d; +} + +float +estimateScale (vec3 position, vec2 sPosition, float width) +{ + vec4 view_pos = view_mat * vec4 (position, 1.0); + vec4 scale_pos = view_pos - vec4 (normalize (view_pos.xy) * width, + 0.0, 0.0); + vec2 screen_scale_pos = project (projection_mat * scale_pos); + return distance (sPosition, screen_scale_pos); +} + +vec4 +transform (vec4 coord) +{ + return projection_mat * view_mat * vec4 (coord.xyz, 1.0); +} + +vec4 +curve_offset_vertex (vec4 last, vec4 current, vec4 next, float width) +{ + float offset = current.w; + + vec2 sLast = project (transform (last)); + vec2 sNext = project (transform (next)); + vec4 dCurrent = transform (current); + vec2 sCurrent = project (dCurrent); + + vec2 n1 = direction (sCurrent, sLast); + vec2 n2 = direction (sNext, sCurrent); + vec2 n = shared_direction (n1, n2); + + // rotate the normal by 90 degrees and scale by the desired offset + vec2 dir = vec2(n.y, -n.x) * offset; + float scale = estimateScale (vec3(current), sCurrent, width); + vec2 pos = sCurrent + dir * scale; + + return unproject (pos, dCurrent.z, dCurrent.w); +} + -- Fragment.fog uniform vec4 fog; @@ -569,6 +690,72 @@ main (void) gl_FragColor = fogBlend (col); } +-- Vertex.particle.trail + +attribute vec4 last, current, next; +attribute vec3 barycentric; +attribute float texoff; +attribute vec4 vcolora; +attribute vec4 vcolorb; + +uniform float width; + +varying vec2 texcoord; +varying vec3 vbarycentric; +varying vec4 colora; +varying vec4 colorb; + +void +main (void) +{ + colora = vcolora; + colorb = vcolorb; + texcoord = vec2 (texoff * 0.7, current.w);// * 0.5 + 0.5); + vbarycentric = barycentric; + + gl_Position = curve_offset_vertex (last, current, next, width); +} + +-- Fragment.particle.trail + +varying vec2 texcoord; +varying vec4 colora; +varying vec4 colorb; + +void +main (void) +{ + vec3 tex3 = vec3 (texcoord, 0.5); + float n = abs(snoise(tex3)); + n += 0.5 * abs(snoise(tex3 * 2.0)); + n += 0.25 * abs(snoise(tex3 * 4.0)); + n += 0.125 * abs(snoise(tex3 * 8.0)); + vec4 c = mix (colora, colorb, n); + c.a *= exp (-4.0 * abs(texcoord.y)); + gl_FragColor = c; +} + +-- Fragment.barycentric + +varying vec3 vbarycentric; +varying vec2 texcoord; + +float +edgeFactor (void) +{ + vec3 d = fwidth (vbarycentric); + vec3 a3 = smoothstep (vec3 (0.0), d * 1.5, vbarycentric); + return min (min (a3.x, a3.y), a3.z); +} + +void +main (void) +{ + //gl_FragColor = vec4 (vec3 (edgeFactor ()), 0.5); + vec4 c = vec4 (vec3 (edgeFactor ()), 0.5); + c.a *= 1.0 - exp (-4.0 * (1.0 - texcoord.y * texcoord.y)); + gl_FragColor = c; +} -- version.130 #version 130 -- Vertex.fstri diff --git a/libs/video/renderer/glsl/sgustavson.glsl b/libs/video/renderer/glsl/sgustavson.glsl new file mode 100644 index 000000000..641df2f03 --- /dev/null +++ b/libs/video/renderer/glsl/sgustavson.glsl @@ -0,0 +1,592 @@ +Note that the GPL covers only the arrangement in this file. The actual code +is covered by the MIT licence (see LICENCE.txt below). + +This file is just all the shader code presented by Stefan Gustavson +collected into one file and split into useable sections with redundant +code removed. + +-- + +sgustavson.glsl + +GLSL noise functions + +Copyright (C) 2014 Bill Currie + +Author: Bill Currie +Date: 2014/01/29 + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +-- LICENCE.txt + +Copyright (C) 2011 by Stefan Gustavson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +-- Math.permute + +vec4 permute(vec4 x) +{ + return mod(((x*34.0)+1.0)*x, 289.0); +} + +-- Math.InvSqrt + +vec4 taylorInvSqrt(vec4 r) +{ + return 1.79284291400159 - 0.85373472095314 * r; +} + +-- Noise.simplex + +// Description : Array and textureless GLSL 3D simplex noise function. +// Author : Ian McEwan, Ashima Arts. +// Maintainer : ijm +// Lastmod : 20110409 (stegu) +// License : Copyright (C) 2011 Ashima Arts. All rights reserved. +// Distributed under the MIT License. See LICENSE file. + +float snoise(vec3 v) +{ + const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + + // First corner + vec3 i = floor(v + dot(v, C.yyy) ); + vec3 x0 = v - i + dot(i, C.xxx) ; + + // Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + + // x0 = x0 - 0. + 0.0 * C + vec3 x1 = x0 - i1 + 1.0 * C.xxx; + vec3 x2 = x0 - i2 + 2.0 * C.xxx; + vec3 x3 = x0 - 1. + 3.0 * C.xxx; + + // Permutations + i = mod(i, 289.0 ); + vec4 p = permute( permute( permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); + + // Gradients + // ( N*N points uniformly over a square, mapped onto an octahedron.) + float n_ = 1.0/7.0; // N=7 + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N) + + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) + + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + + //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec4 s0 = floor(b0)*2.0 + 1.0; + vec4 s1 = floor(b1)*2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + + vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; + vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; + + vec3 p0 = vec3(a0.xy,h.x); + vec3 p1 = vec3(a0.zw,h.y); + vec3 p2 = vec3(a1.xy,h.z); + vec3 p3 = vec3(a1.zw,h.w); + + //Normalise gradients + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), + dot(p2,x2), dot(p3,x3) ) ); +} + +-- Vertex.simplex + +uniform float time; +varying vec3 vTexCoord3D; + +void main(void) { + vTexCoord3D = gl_Vertex.xyz * 4.0 + vec3(0.0, 0.0, time); + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} + +-- Fragment.simplex + +varying vec3 vTexCoord3D; +void main( void ) +{ + float n = snoise(vTexCoord3D); + + gl_FragColor = vec4(0.5 + 0.6 * vec3(n, n, n), 1.0); +} + +-- Vertex.flame + +uniform float time; +varying vec3 vTexCoord3D; + +void main(void) { + vTexCoord3D = gl_Vertex.xyz * 2.0 + vec3(0.0, 0.0, -time); + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} + +-- Fragment.flame + +varying vec3 vTexCoord3D; +void main( void ) +{ + float n = abs(snoise(vTexCoord3D)); + n += 0.5 * abs(snoise(vTexCoord3D * 2.0)); + n += 0.25 * abs(snoise(vTexCoord3D * 4.0)); + n += 0.125 * abs(snoise(vTexCoord3D * 8.0)); + + gl_FragColor = vec4(vec3(1.5-n, 1.0-n, 0.5-n), 1.0); +} + +-- Noise.flow + +// GLSL implementation of 2D "flow noise" as presented +// by Ken Perlin and Fabrice Neyret at Siggraph 2001. +// (2D simplex noise with analytic derivatives and +// in-plane rotation of generating gradients, +// in a fractal sum where higher frequencies are +// displaced (advected) by lower frequencies in the +// direction of their gradient. For details, please +// refer to the 2001 paper "Flow Noise" by Perlin and Neyret.) +// +// Author: Stefan Gustavson (stefan.gustavson@liu.se) +// Distributed under the terms of the MIT license. +// See LICENSE file for details. +// + +// Helper constants +#define F2 0.366025403 +#define G2 0.211324865 +#define K 0.0243902439 // 1/41 + +// Gradient mapping with an extra rotation. +vec2 grad2(vec2 p, float rot) { +#if 1 +// Map from a line to a diamond such that a shift maps to a rotation. + float u = permute(permute(p.x) + p.y) * K + rot; // Rotate by shift + u = 4.0 * fract(u) - 2.0; + return vec2(abs(u)-1.0, abs(abs(u+1.0)-2.0)-1.0); +#else +#define TWOPI 6.28318530718 +// For more isotropic gradients, sin/cos can be used instead. + float u = permute(permute(p.x) + p.y) * K + rot; // Rotate by shift + u = fract(u) * TWOPI; + return vec2(cos(u), sin(u)); +#endif +} + +float srdnoise(in vec2 P, in float rot, out vec2 grad) { + + // Transform input point to the skewed simplex grid + vec2 Ps = P + dot(P, vec2(F2)); + + // Round down to simplex origin + vec2 Pi = floor(Ps); + + // Transform simplex origin back to (x,y) system + vec2 P0 = Pi - dot(Pi, vec2(G2)); + + // Find (x,y) offsets from simplex origin to first corner + vec2 v0 = P - P0; + + // Pick (+x, +y) or (+y, +x) increment sequence + vec2 i1 = (v0.x > v0.y) ? vec2(1.0, 0.0) : vec2 (0.0, 1.0); + + // Determine the offsets for the other two corners + vec2 v1 = v0 - i1 + G2; + vec2 v2 = v0 - 1.0 + 2.0 * G2; + + // Wrap coordinates at 289 to avoid float precision problems + Pi = mod(Pi, 289.0); + + // Calculate the circularly symmetric part of each noise wiggle + vec3 t = max(0.5 - vec3(dot(v0,v0), dot(v1,v1), dot(v2,v2)), 0.0); + vec3 t2 = t*t; + vec3 t4 = t2*t2; + + // Calculate the gradients for the three corners + vec2 g0 = grad2(Pi, rot); + vec2 g1 = grad2(Pi + i1, rot); + vec2 g2 = grad2(Pi + 1.0, rot); + + // Compute noise contributions from each corner + vec3 gv = vec3(dot(g0,v0), dot(g1,v1), dot(g2,v2)); // ramp: g dot v + vec3 n = t4 * gv; // Circular kernel times linear ramp + + // Compute partial derivatives in x and y + vec3 temp = t2 * t * gv; + vec3 gradx = temp * vec3(v0.x, v1.x, v2.x); + vec3 grady = temp * vec3(v0.y, v1.y, v2.y); + grad.x = -8.0 * (gradx.x + gradx.y + gradx.z); + grad.y = -8.0 * (grady.x + grady.y + grady.z); + grad.x += dot(t4, vec3(g0.x, g1.x, g2.x)); + grad.y += dot(t4, vec3(g0.y, g1.y, g2.y)); + grad *= 40.0; + + // Add contributions from the three corners and return + return 40.0 * (n.x + n.y + n.z); +} + +-- Vertex.flow + +varying vec2 vTexCoord2D; + +void main(void) { + vTexCoord2D = gl_Vertex.xy * 8.0; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} + +-- Fragment.flow + +varying vec3 vTexCoord3D; +uniform float time; + +void main(void) { + vec2 g1, g2; + vec2 p = vTexCoord2D; + float n1 = srdnoise(p*0.5, 0.2*time, g1); + float n2 = srdnoise(p*2.0 + g1*0.5, 0.51*time, g2); + float n3 = srdnoise(p*4.0 + g1*0.5 + g2*0.25, 0.77*time, g2); + gl_FragColor = vec4(vec3(0.4, 0.5, 0.6) + vec3(n1+0.75*n2+0.5*n3), 1.0); +} + +-- Noise.spots + +// Cellular noise ("Worley noise") in 3D in GLSL. +// Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. +// This code is released under the conditions of the MIT license. +// See LICENSE file for details. + +// Cellular noise, returning F1 and F2 in a vec2. +// Speeded up by using 2x2x2 search window instead of 3x3x3, +// at the expense of some pattern artifacts. +// F2 is often wrong and has sharp discontinuities. +// If you need a good F2, use the slower 3x3x3 version. +vec2 cellular2x2x2(vec3 P) +{ +#define K 0.142857142857 // 1/7 +#define Ko 0.428571428571 // 1/2-K/2 +#define K2 0.020408163265306 // 1/(7*7) +#define Kz 0.166666666667 // 1/6 +#define Kzo 0.416666666667 // 1/2-1/6*2 +#define jitter 0.8 // smaller jitter gives less errors in F2 + vec3 Pi = mod(floor(P), 289.0); + vec3 Pf = fract(P); + vec4 Pfx = Pf.x + vec4(0.0, -1.0, 0.0, -1.0); + vec4 Pfy = Pf.y + vec4(0.0, 0.0, -1.0, -1.0); + vec4 p = permute(Pi.x + vec4(0.0, 1.0, 0.0, 1.0)); + p = permute(p + Pi.y + vec4(0.0, 0.0, 1.0, 1.0)); + vec4 p1 = permute(p + Pi.z); // z+0 + vec4 p2 = permute(p + Pi.z + vec4(1.0)); // z+1 + vec4 ox1 = fract(p1*K) - Ko; + vec4 oy1 = mod(floor(p1*K), 7.0)*K - Ko; + vec4 oz1 = floor(p1*K2)*Kz - Kzo; // p1 < 289 guaranteed + vec4 ox2 = fract(p2*K) - Ko; + vec4 oy2 = mod(floor(p2*K), 7.0)*K - Ko; + vec4 oz2 = floor(p2*K2)*Kz - Kzo; + vec4 dx1 = Pfx + jitter*ox1; + vec4 dy1 = Pfy + jitter*oy1; + vec4 dz1 = Pf.z + jitter*oz1; + vec4 dx2 = Pfx + jitter*ox2; + vec4 dy2 = Pfy + jitter*oy2; + vec4 dz2 = Pf.z - 1.0 + jitter*oz2; + vec4 d1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1; // z+0 + vec4 d2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2; // z+1 + + // Sort out the two smallest distances (F1, F2) +#if 1 + // Cheat and sort out only F1 + d1 = min(d1, d2); + d1.xy = min(d1.xy, d1.wz); + d1.x = min(d1.x, d1.y); + return sqrt(d1.xx); +#else + // Do it right and sort out both F1 and F2 + vec4 d = min(d1,d2); // F1 is now in d + d2 = max(d1,d2); // Make sure we keep all candidates for F2 + d.xy = (d.x < d.y) ? d.xy : d.yx; // Swap smallest to d.x + d.xz = (d.x < d.z) ? d.xz : d.zx; + d.xw = (d.x < d.w) ? d.xw : d.wx; // F1 is now in d.x + d.yzw = min(d.yzw, d2.yzw); // F2 now not in d2.yzw + d.y = min(d.y, d.z); // nor in d.z + d.y = min(d.y, d.w); // nor in d.w + d.y = min(d.y, d2.x); // F2 is now in d.y + return sqrt(d.xy); // F1 and F2 +#endif +} + +-- Vertex.spots + +uniform float time; + +varying vec3 vTexCoord3D; + +void main(void) { + vTexCoord3D = gl_Vertex.xyz * 4.0 - vec3(0.0, 0.0, time); + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} + +-- Fragment.spots + +varying vec3 vTexCoord3D; + +void main(void) { + vec2 F = cellular2x2x2(vTexCoord3D); + float s = fwidth(F.x); + float n1 = smoothstep(0.4-s, 0.4+s, F.x); + float n2 = smoothstep(0.5-s, 0.5+s, F.x); + gl_FragColor = vec4(n1, n2, n2, 1.0); +} + +-- Noise.tile + +// Cellular noise ("Worley noise") in 3D in GLSL. +// Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. +// This code is released under the conditions of the MIT license. +// See LICENSE file for details. + +// Cellular noise, returning F1 and F2 in a vec2. +// 3x3x3 search region for good F2 everywhere, but a lot +// slower than the 2x2x2 version. +// The code below is a bit scary even to its author, +// but it has at least half decent performance on a +// modern GPU. In any case, it beats any software +// implementation of Worley noise hands down. + +vec2 cellular(vec3 P) +{ +#define K 0.142857142857 // 1/7 +#define Ko 0.428571428571 // 1/2-K/2 +#define K2 0.020408163265306 // 1/(7*7) +#define Kz 0.166666666667 // 1/6 +#define Kzo 0.416666666667 // 1/2-1/6*2 +#define jitter 1.0 // smaller jitter gives more regular pattern + + vec3 Pi = mod(floor(P), 289.0); + vec3 Pf = fract(P) - 0.5; + + vec3 Pfx = Pf.x + vec3(1.0, 0.0, -1.0); + vec3 Pfy = Pf.y + vec3(1.0, 0.0, -1.0); + vec3 Pfz = Pf.z + vec3(1.0, 0.0, -1.0); + + vec3 p = permute(Pi.x + vec3(-1.0, 0.0, 1.0)); + vec3 p1 = permute(p + Pi.y - 1.0); + vec3 p2 = permute(p + Pi.y); + vec3 p3 = permute(p + Pi.y + 1.0); + + vec3 p11 = permute(p1 + Pi.z - 1.0); + vec3 p12 = permute(p1 + Pi.z); + vec3 p13 = permute(p1 + Pi.z + 1.0); + + vec3 p21 = permute(p2 + Pi.z - 1.0); + vec3 p22 = permute(p2 + Pi.z); + vec3 p23 = permute(p2 + Pi.z + 1.0); + + vec3 p31 = permute(p3 + Pi.z - 1.0); + vec3 p32 = permute(p3 + Pi.z); + vec3 p33 = permute(p3 + Pi.z + 1.0); + + vec3 ox11 = fract(p11*K) - Ko; + vec3 oy11 = mod(floor(p11*K), 7.0)*K - Ko; + vec3 oz11 = floor(p11*K2)*Kz - Kzo; // p11 < 289 guaranteed + + vec3 ox12 = fract(p12*K) - Ko; + vec3 oy12 = mod(floor(p12*K), 7.0)*K - Ko; + vec3 oz12 = floor(p12*K2)*Kz - Kzo; + + vec3 ox13 = fract(p13*K) - Ko; + vec3 oy13 = mod(floor(p13*K), 7.0)*K - Ko; + vec3 oz13 = floor(p13*K2)*Kz - Kzo; + + vec3 ox21 = fract(p21*K) - Ko; + vec3 oy21 = mod(floor(p21*K), 7.0)*K - Ko; + vec3 oz21 = floor(p21*K2)*Kz - Kzo; + + vec3 ox22 = fract(p22*K) - Ko; + vec3 oy22 = mod(floor(p22*K), 7.0)*K - Ko; + vec3 oz22 = floor(p22*K2)*Kz - Kzo; + + vec3 ox23 = fract(p23*K) - Ko; + vec3 oy23 = mod(floor(p23*K), 7.0)*K - Ko; + vec3 oz23 = floor(p23*K2)*Kz - Kzo; + + vec3 ox31 = fract(p31*K) - Ko; + vec3 oy31 = mod(floor(p31*K), 7.0)*K - Ko; + vec3 oz31 = floor(p31*K2)*Kz - Kzo; + + vec3 ox32 = fract(p32*K) - Ko; + vec3 oy32 = mod(floor(p32*K), 7.0)*K - Ko; + vec3 oz32 = floor(p32*K2)*Kz - Kzo; + + vec3 ox33 = fract(p33*K) - Ko; + vec3 oy33 = mod(floor(p33*K), 7.0)*K - Ko; + vec3 oz33 = floor(p33*K2)*Kz - Kzo; + + vec3 dx11 = Pfx + jitter*ox11; + vec3 dy11 = Pfy.x + jitter*oy11; + vec3 dz11 = Pfz.x + jitter*oz11; + + vec3 dx12 = Pfx + jitter*ox12; + vec3 dy12 = Pfy.x + jitter*oy12; + vec3 dz12 = Pfz.y + jitter*oz12; + + vec3 dx13 = Pfx + jitter*ox13; + vec3 dy13 = Pfy.x + jitter*oy13; + vec3 dz13 = Pfz.z + jitter*oz13; + + vec3 dx21 = Pfx + jitter*ox21; + vec3 dy21 = Pfy.y + jitter*oy21; + vec3 dz21 = Pfz.x + jitter*oz21; + + vec3 dx22 = Pfx + jitter*ox22; + vec3 dy22 = Pfy.y + jitter*oy22; + vec3 dz22 = Pfz.y + jitter*oz22; + + vec3 dx23 = Pfx + jitter*ox23; + vec3 dy23 = Pfy.y + jitter*oy23; + vec3 dz23 = Pfz.z + jitter*oz23; + + vec3 dx31 = Pfx + jitter*ox31; + vec3 dy31 = Pfy.z + jitter*oy31; + vec3 dz31 = Pfz.x + jitter*oz31; + + vec3 dx32 = Pfx + jitter*ox32; + vec3 dy32 = Pfy.z + jitter*oy32; + vec3 dz32 = Pfz.y + jitter*oz32; + + vec3 dx33 = Pfx + jitter*ox33; + vec3 dy33 = Pfy.z + jitter*oy33; + vec3 dz33 = Pfz.z + jitter*oz33; + + vec3 d11 = dx11 * dx11 + dy11 * dy11 + dz11 * dz11; + vec3 d12 = dx12 * dx12 + dy12 * dy12 + dz12 * dz12; + vec3 d13 = dx13 * dx13 + dy13 * dy13 + dz13 * dz13; + vec3 d21 = dx21 * dx21 + dy21 * dy21 + dz21 * dz21; + vec3 d22 = dx22 * dx22 + dy22 * dy22 + dz22 * dz22; + vec3 d23 = dx23 * dx23 + dy23 * dy23 + dz23 * dz23; + vec3 d31 = dx31 * dx31 + dy31 * dy31 + dz31 * dz31; + vec3 d32 = dx32 * dx32 + dy32 * dy32 + dz32 * dz32; + vec3 d33 = dx33 * dx33 + dy33 * dy33 + dz33 * dz33; + + // Sort out the two smallest distances (F1, F2) +#if 0 + // Cheat and sort out only F1 + vec3 d1 = min(min(d11,d12), d13); + vec3 d2 = min(min(d21,d22), d23); + vec3 d3 = min(min(d31,d32), d33); + vec3 d = min(min(d1,d2), d3); + d.x = min(min(d.x,d.y),d.z); + return sqrt(d.xx); // F1 duplicated, no F2 computed +#else + // Do it right and sort out both F1 and F2 + vec3 d1a = min(d11, d12); + d12 = max(d11, d12); + d11 = min(d1a, d13); // Smallest now not in d12 or d13 + d13 = max(d1a, d13); + d12 = min(d12, d13); // 2nd smallest now not in d13 + vec3 d2a = min(d21, d22); + d22 = max(d21, d22); + d21 = min(d2a, d23); // Smallest now not in d22 or d23 + d23 = max(d2a, d23); + d22 = min(d22, d23); // 2nd smallest now not in d23 + vec3 d3a = min(d31, d32); + d32 = max(d31, d32); + d31 = min(d3a, d33); // Smallest now not in d32 or d33 + d33 = max(d3a, d33); + d32 = min(d32, d33); // 2nd smallest now not in d33 + vec3 da = min(d11, d21); + d21 = max(d11, d21); + d11 = min(da, d31); // Smallest now in d11 + d31 = max(da, d31); // 2nd smallest now not in d31 + d11.xy = (d11.x < d11.y) ? d11.xy : d11.yx; + d11.xz = (d11.x < d11.z) ? d11.xz : d11.zx; // d11.x now smallest + d12 = min(d12, d21); // 2nd smallest now not in d21 + d12 = min(d12, d22); // nor in d22 + d12 = min(d12, d31); // nor in d31 + d12 = min(d12, d32); // nor in d32 + d11.yz = min(d11.yz,d12.xy); // nor in d12.yz + d11.y = min(d11.y,d12.z); // Only two more to go + d11.y = min(d11.y,d11.z); // Done! (Phew!) + return sqrt(d11.xy); // F1, F2 +#endif +} + +-- Vertex.tile + +uniform float time; + +varying vec3 vTexCoord3D; + +void main(void) { + vTexCoord3D = gl_Vertex.xyz * 4.0 + + 0.2 * vec3(snoise(gl_Vertex.xyz + vec3(0.0, 0.0, time)), + snoise(gl_Vertex.xyz + vec3(43.0, 17.0, time)), + snoise(gl_Vertex.xyz + vec3(0.0, -43.0, time-17.0))); + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} + +-- Fragment.tile + +varying vec3 vTexCoord3D; + +void main(void) { + vec2 F = cellular(vTexCoord3D.xyz); + float n = 0.1+F.y-F.x; + gl_FragColor = vec4(n*0.6, n*1.1, n*0.5, 1.0); +} diff --git a/libs/video/renderer/glsl/vid_common_glsl.c b/libs/video/renderer/glsl/vid_common_glsl.c index 2fd29fc86..e03060f68 100644 --- a/libs/video/renderer/glsl/vid_common_glsl.c +++ b/libs/video/renderer/glsl/vid_common_glsl.c @@ -57,6 +57,11 @@ static const char quakeforge_effect[] = #include "libs/video/renderer/glsl/quakeforge.slc" +"--\n" // ensure the last block of the previous file doesn't merge with + // the first block of the next file +// Include Stefan Gustavson's noise functions in the QuakeForge shader +// effect "file". +#include "libs/video/renderer/glsl/sgustavson.slc" ; int glsl_palette; diff --git a/libs/video/renderer/r_cvar.c b/libs/video/renderer/r_cvar.c index df3d44bbc..aa88d6270 100644 --- a/libs/video/renderer/r_cvar.c +++ b/libs/video/renderer/r_cvar.c @@ -614,4 +614,5 @@ R_Init_Cvars (void) r_data->scr_viewsize = &scr_viewsize; R_Particles_Init_Cvars (); + R_Trails_Init_Cvars (); } diff --git a/libs/video/renderer/r_init.c b/libs/video/renderer/r_init.c index d4279fda0..0f0915e4c 100644 --- a/libs/video/renderer/r_init.c +++ b/libs/video/renderer/r_init.c @@ -105,4 +105,5 @@ R_Init (void) r_funcs->R_Init (); R_ClearEfrags (); //FIXME force link of r_efrag.o for qwaq Fog_Init (); + R_Trails_Init (); } diff --git a/libs/video/renderer/r_part.c b/libs/video/renderer/r_part.c index 0a73882e9..b97fff906 100644 --- a/libs/video/renderer/r_part.c +++ b/libs/video/renderer/r_part.c @@ -29,7 +29,6 @@ #endif #include "QF/cvar.h" -#include "QF/qargs.h" #include "QF/render.h" #include "QF/sys.h" @@ -135,6 +134,7 @@ R_RunParticles (float dT) p->pos += dT * p->vel; p->vel += dT * (p->vel * parm->drag + gravity * parm->drag[3]); p->ramp += dT * parm->ramp; + p->scale += dT * parm->scale_rate; p->live -= dT; if (ramp) { p->icolor = ramp[(int)p->ramp]; diff --git a/libs/video/renderer/r_screen.c b/libs/video/renderer/r_screen.c index 4f71d5212..efd51176b 100644 --- a/libs/video/renderer/r_screen.c +++ b/libs/video/renderer/r_screen.c @@ -190,6 +190,9 @@ render_scene (void) EntQueue_Clear (r_ent_queue); r_funcs->render_view (); r_funcs->draw_particles (&r_psystem); + if (r_funcs->draw_trails) { + r_funcs->draw_trails (&r_tsystem); + } r_funcs->draw_transparent (); } diff --git a/libs/video/renderer/r_trails.c b/libs/video/renderer/r_trails.c new file mode 100644 index 000000000..c9b749814 --- /dev/null +++ b/libs/video/renderer/r_trails.c @@ -0,0 +1,236 @@ +/* + r_trails.c + + Interface for trails + + Copyright (C) 2023 Bill Currie + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/cvar.h" +#include "QF/ecs.h" +#include "QF/render.h" +#include "QF/sys.h" + +#include "QF/simd/vec4f.h" + +#include "r_dynamic.h" + +psystem_t r_tsystem; //FIXME singleton +static ecs_system_t trails_system; +static trailpnt_t *trail_point_buffer; +static uint32_t trail_point_count; +static uint32_t trail_point_buffer_size; +static uint32_t allocated_blocks; // bitmask of 64-point blocks in use + +typedef struct { + uint32_t base; + uint32_t count; +} pointset_t; + +enum trails_components { + trails_pointset, + + trails_comp_count +}; + +static void +destroy_pointset (void *comp) +{ + pointset_t *pointset = comp; + int base = pointset->base / 64; + int count = pointset->count / 64; + uint32_t mask = ((1 << count) - 1) << base; + allocated_blocks &= ~mask; + r_tsystem.numparticles = allocated_blocks; +} + +static const component_t trails_components[trails_comp_count] = { + [trails_pointset] = { + .size = sizeof (pointset_t), + .create = 0, + .name = "pointset", + .destroy = destroy_pointset, + }, +}; + +bool +R_Trail_Valid (psystem_t *system, uint32_t trailid) +{ + return ECS_EntValid (trailid, trails_system.reg); +} + +uint32_t +R_Trail_Create (psystem_t *system, int num_points, vec4f_t start) +{ + num_points += 2; // need an extra point at either end + num_points = (num_points + 31) & ~31; // want a multiple of 32 + num_points *= 2; // each point needs two verts + + int blocks = num_points / 64; + uint32_t mask = (1 << blocks) - 1; + int block_ind; + for (block_ind = 0; block_ind <= 32 - blocks; block_ind++) { + if (!(allocated_blocks & mask)) { + break; + } + mask <<= 1; + } + if (allocated_blocks & mask) { + return nullent; + } + allocated_blocks |= mask; + r_tsystem.numparticles = allocated_blocks; + + uint32_t trail = ECS_NewEntity (trails_system.reg); + pointset_t pointset = { + .base = block_ind * 64, + .count = num_points, + }; + Ent_SetComponent (trail, trails_pointset, trails_system.reg, &pointset); + for (uint32_t i = 0; i < pointset.count; i++) { + static float bary[] = {0, 0, 1, 0, 0, 1, 0, 0}; + auto p = trail_point_buffer + pointset.base + i; + *p = (trailpnt_t) { + .pos = start, + .colora = { 0xc0, 0xa0, 0x60, 0x80 }, + .colorb = { 0x30, 0x30, 0x20, 0x00 }, + .trailid = trail, + .live = 10, + }; + p->pos[3] = i & 1 ? 1 : -1; + VectorCopy (bary + i % 3, p->bary); + }; + return trail; +} + +void +R_Trail_Update (psystem_t *system, uint32_t trailid, vec4f_t pos) +{ + pointset_t *p = Ent_GetComponent (trailid, trails_pointset, + trails_system.reg); + + trailpnt_t *points = trail_point_buffer + p->base; + + pos[3] = -1; + vec4f_t dist = pos - points[4].pos; + vec4f_t prev1 = points[2].pos; + vec4f_t prev2 = points[3].pos; + points[2].pos = pos; + pos[3] = 1; + points[3].pos = pos; + + points[0].pos = points[2].pos + dist; + points[1].pos = points[3].pos + dist; + + float len = sqrt (dotf (dist, dist)[0]); + if (len > 16) { + for (uint32_t i = 4; i < p->count; i += 2) { + vec4f_t t1 = points[i + 0].pos; + vec4f_t t2 = points[i + 1].pos; + points[i + 0].pos = prev1; + points[i + 1].pos = prev2; + prev1 = t1; + prev2 = t2; + + points[i + 0].pathoffset = len; + points[i + 1].pathoffset = len; + if (i < p->count - 2) { + dist = points[i + 2].pos - points[i + 0].pos; + len = sqrt (dotf (dist, dist)[0]); + } + } + } else { + for (uint32_t i = 4; i < p->count; i += 2) { + points[i + 0].pathoffset = len; + points[i + 1].pathoffset = len; + if (i < p->count - 2) { + dist = points[i + 2].pos - points[i + 0].pos; + len = sqrt (dotf (dist, dist)[0]); + } + } + } +} + +void +R_Trail_Destroy (psystem_t *system, uint32_t trailid) +{ + ECS_DelEntity (trails_system.reg, trailid); +} + + +void +R_ClearTrails (void) +{ + auto reg = trails_system.reg; + for (uint32_t i = 0; i < reg->num_entities; i++) { + uint32_t ent = reg->entities[i]; + uint32_t ind = Ent_Index (ent); + if (ind == i) { + ECS_DelEntity (reg, ent); + } + } +} + +void +R_RunTrails (float dT) +{ +} + +void +R_Trails_Init_Cvars (void) +{ + Sys_Printf ("R_Trails_Init_Cvars\n"); +} + +static void +trails_shutdown (void *data) +{ + Sys_Printf ("trails_shutdown\n"); + ECS_DelRegistry (trails_system.reg); + Sys_Free (trail_point_buffer, trail_point_buffer_size); +} + +void +R_Trails_Init (void) +{ + Sys_Printf ("R_Trails_Init\n"); + Sys_RegisterShutdown (trails_shutdown, 0); + auto reg = ECS_NewRegistry (); + trails_system.reg = reg; + trails_system.base = ECS_RegisterComponents (reg, trails_components, + trails_comp_count); + ECS_CreateComponentPools (reg); + + trail_point_count = 32*64; + trail_point_buffer_size = trail_point_count * sizeof (trailpnt_t); + trail_point_buffer = Sys_Alloc (trail_point_buffer_size); + + r_tsystem = (psystem_t) { + .maxparticles = trail_point_count, + .particles = (particle_t *) trail_point_buffer, + .partparams = 0,//FIXME + .partramps = 0,//FIXME + }; +} diff --git a/libs/video/renderer/vid_render_glsl.c b/libs/video/renderer/vid_render_glsl.c index 38318ab1d..c164982ec 100644 --- a/libs/video/renderer/vid_render_glsl.c +++ b/libs/video/renderer/vid_render_glsl.c @@ -477,6 +477,7 @@ vid_render_funcs_t glsl_vid_render_funcs = { .Draw_Glyph = glsl_Draw_Glyph, .ParticleSystem = glsl_ParticleSystem, + .TrailSystem = glsl_TrailSystem, .R_Init = glsl_R_Init, .R_ClearState = glsl_R_ClearState, .R_LoadSkys = glsl_R_LoadSkys, @@ -485,6 +486,7 @@ vid_render_funcs_t glsl_vid_render_funcs = { .begin_frame = glsl_begin_frame, .render_view = glsl_render_view, .draw_particles = glsl_R_DrawParticles, + .draw_trails = glsl_R_DrawTrails, .draw_transparent = glsl_draw_transparent, .post_process = glsl_post_process, .set_2d = glsl_set_2d, diff --git a/nq/source/cl_main.c b/nq/source/cl_main.c index 86e654830..65dc052ee 100644 --- a/nq/source/cl_main.c +++ b/nq/source/cl_main.c @@ -59,6 +59,7 @@ #include "compat.h" #include "client/chase.h" +#include "client/effects.h" #include "client/particles.h" #include "client/sbar.h" #include "client/screen.h" @@ -740,6 +741,7 @@ CL_Init (cbuf_t *cbuf) CL_Init_Input (cbuf); CL_Particles_Init (); + CL_Effects_Init (); CL_TEnts_Init (); CL_World_Init (); CL_ClearState (); diff --git a/qw/source/cl_main.c b/qw/source/cl_main.c index 5f6287a82..16b6f45cc 100644 --- a/qw/source/cl_main.c +++ b/qw/source/cl_main.c @@ -98,6 +98,7 @@ #include "buildnum.h" #include "compat.h" +#include "client/effects.h" #include "client/particles.h" #include "client/sbar.h" #include "client/screen.h" @@ -1488,6 +1489,7 @@ CL_Init (void) CL_Init_Input (cl_cbuf); CL_Ents_Init (); CL_Particles_Init (); + CL_Effects_Init (); CL_TEnts_Init (); CL_World_Init (); CL_ClearState ();