From 1c16b1bfbcc0089ab95f53df23a42235cffb2ce7 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 12 Nov 2024 10:48:37 +1000 Subject: [PATCH] Bring back polytrails implementation by TimePath with some fixes --- qcsrc/client/_mod.inc | 1 + qcsrc/client/_mod.qh | 1 + qcsrc/client/polytrails.qc | 91 ++++++++++++++++++++++++++++++ qcsrc/client/polytrails.qh | 42 ++++++++++++++ qcsrc/client/weapons/projectile.qc | 81 ++++++++++++++++++++++++++ xonotic-client.cfg | 2 + 6 files changed, 218 insertions(+) create mode 100644 qcsrc/client/polytrails.qc create mode 100644 qcsrc/client/polytrails.qh diff --git a/qcsrc/client/_mod.inc b/qcsrc/client/_mod.inc index feb5497d1..d29d92b56 100644 --- a/qcsrc/client/_mod.inc +++ b/qcsrc/client/_mod.inc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/client/_mod.qh b/qcsrc/client/_mod.qh index 7ef159d6c..9203312dc 100644 --- a/qcsrc/client/_mod.qh +++ b/qcsrc/client/_mod.qh @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/client/polytrails.qc b/qcsrc/client/polytrails.qc new file mode 100644 index 000000000..6f40faa29 --- /dev/null +++ b/qcsrc/client/polytrails.qc @@ -0,0 +1,91 @@ +#include "polytrails.qh" + +#include + +#define POLYTRAIL_SEEK(_p, _rel) ((POLYTRAIL_BUFSIZE + (_p).polytrail_bufidx + (_rel)) % POLYTRAIL_BUFSIZE) +void Trail_draw(entity this) +{ + if (wasfreed(this.polytrail_follow)) + this.polytrail_follow = NULL; + + // NOTE: need an internal reset distance for warpzones to work properly TODO? + float reset_distance = 200; + + if (!this.polytrail_follow) { + float when = this.polytrail_buftime[this.polytrail_bufidx]; + if (time - when > this.polytrail_lifetime) { + delete(this); + return; + } + } else if(reset_distance > 0 && this.polytrail_cnt > 0 && vdist(this.origin - this.polytrail_follow.origin, >, reset_distance)) { + Trail_recreate(this); + } else { + setorigin(this, this.polytrail_follow.origin); + if (this.polytrail_cnt < 0 || vdist(this.origin - this.polytrail_bufpos[this.polytrail_bufidx], >=, this.polytrail_segmentsize)) { + int i = POLYTRAIL_SEEK(this, 1); + this.polytrail_bufpos[i] = this.origin; + float f = this.polytrail_noise; + // TODO: alternate noise functions (none, chaotic, helix, zigzag, waves, ...) + switch(this.polytrail_noisefunc) + { + default: + case "none": this.polytrail_bufnoise[i] = '0 0 0'; break; + case "chaotic": this.polytrail_bufnoise[i] = randompos(f * '-1 -1 -1', f * '1 1 1'); break; + } + this.polytrail_buftime[i] = time; + this.polytrail_bufidx = i; + this.polytrail_cnt = bound(this.polytrail_cnt, i + 1, POLYTRAIL_BUFSIZE); + } + } + + vector from = this.origin; + int count = this.polytrail_cnt; + for (int i = 0; i < count; ++i) { + int idx = POLYTRAIL_SEEK(this, -i); + float when = this.polytrail_buftime[idx]; + // head: 0, tail: 1 + float rtime = (time - when) / this.polytrail_lifetime; + if (rtime >= 1) { + break; + } + vector to = this.polytrail_bufpos[idx]; + to += lerpvratio('0 0 0', this.polytrail_bufnoise[idx], rtime); + vector rgb = lerpv3ratio(this.polytrail_rgb[0], this.polytrail_rgb[1], this.polytrail_rgb[2], rtime); + float a = lerp3ratio(this.polytrail_alpha[0], this.polytrail_thickness[1], this.polytrail_alpha[2], rtime); + int thickness = lerp3ratio(this.polytrail_thickness[0], this.polytrail_thickness[1], this.polytrail_thickness[2], rtime); + vector thickdir = normalize(cross(normalize(to - from), view_origin - from)) * (thickness / 2); + vector A = from + thickdir; + vector B = from - thickdir; + vector C = to + thickdir; + vector D = to - thickdir; + R_BeginPolygon(this.polytrail_tex, DRAWFLAG_SCREEN, false); + R_PolygonVertex(B, '0 0 0', rgb, a); + R_PolygonVertex(A, '0 1 0', rgb, a); + R_PolygonVertex(C, '1 1 0', rgb, a); + R_PolygonVertex(D, '1 0 0', rgb, a); + R_EndPolygon(); + from = to; + } +} +#undef POLYTRAIL_SEEK + +void Trail_recreate(entity this) +{ + PolyTrail t = NEW(PolyTrail, this.polytrail_follow); + t.polytrail_tex = this.polytrail_tex; + t.polytrail_rgb[0] = this.polytrail_rgb[0]; + t.polytrail_rgb[1] = this.polytrail_rgb[1]; + t.polytrail_rgb[2] = this.polytrail_rgb[2]; + t.polytrail_alpha[0] = this.polytrail_alpha[0]; + t.polytrail_alpha[1] = this.polytrail_alpha[1]; + t.polytrail_alpha[2] = this.polytrail_alpha[2]; + t.polytrail_thickness[0] = this.polytrail_thickness[0]; + t.polytrail_thickness[1] = this.polytrail_thickness[1]; + t.polytrail_thickness[2] = this.polytrail_thickness[2]; + t.polytrail_lifetime = this.polytrail_lifetime; + t.polytrail_segmentsize = this.polytrail_segmentsize; + t.polytrail_noise = this.polytrail_noise; + t.polytrail_noisefunc = this.polytrail_noisefunc; + + this.polytrail_follow = NULL; +} diff --git a/qcsrc/client/polytrails.qh b/qcsrc/client/polytrails.qh new file mode 100644 index 000000000..4f59abfe3 --- /dev/null +++ b/qcsrc/client/polytrails.qh @@ -0,0 +1,42 @@ +#pragma once + +bool autocvar_cl_polytrails = true; + +CLASS(PolyTrail, Object) + ATTRIB(PolyTrail, polytrail_follow, entity, NULL); + ATTRIB(PolyTrail, polytrail_tex, string, string_null); + /** Lifetime per segment */ + ATTRIB(PolyTrail, polytrail_lifetime, float, 0.2); + ATTRIB(PolyTrail, polytrail_noise, float, 0); + ATTRIB(PolyTrail, polytrail_segmentsize, float, 10); + ATTRIB(PolyTrail, polytrail_noisefunc, string, "chaotic"); + ATTRIBARRAY(PolyTrail, polytrail_rgb, vector, 3); + ATTRIBARRAY(PolyTrail, polytrail_alpha, float, 3); + ATTRIBARRAY(PolyTrail, polytrail_thickness, float, 3); + + /** + * Increase as necessary if the buffer is overflowing + * symptom: tail of trail is wrong + * cause: projectiles are too fast for the segment size + */ + const int POLYTRAIL_BUFSIZE = 1 << 7; + /** One or more positional points */ + ATTRIBARRAY(PolyTrail, polytrail_bufpos, vector, POLYTRAIL_BUFSIZE); + /** Noise for ending position */ + ATTRIBARRAY(PolyTrail, polytrail_bufnoise, vector, POLYTRAIL_BUFSIZE); + /** Time of input */ + ATTRIBARRAY(PolyTrail, polytrail_buftime, float, POLYTRAIL_BUFSIZE); + /** Current read index */ + ATTRIB(PolyTrail, polytrail_bufidx, float, -1); + /** Counts positions stored */ + ATTRIB(PolyTrail, polytrail_cnt, float, 0); + + void Trail_draw(entity this); + void Trail_recreate(entity this); + ATTRIB(PolyTrail, draw, void(entity this), Trail_draw); + CONSTRUCTOR(PolyTrail, entity _follow) { + CONSTRUCT(PolyTrail); + this.polytrail_follow = _follow; + IL_PUSH(g_drawables, this); + } +ENDCLASS(PolyTrail) diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc index 893d53ae5..d5f874150 100644 --- a/qcsrc/client/weapons/projectile.qc +++ b/qcsrc/client/weapons/projectile.qc @@ -1,6 +1,7 @@ #include "projectile.qh" #include +#include #include #include #include @@ -360,6 +361,25 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) settouch(this, func_null); this.bouncefactor = WEP_CVAR_SEC(WEP_ELECTRO, bouncefactor); this.bouncestop = WEP_CVAR_SEC(WEP_ELECTRO, bouncestop); + if(autocvar_cl_polytrails) + { + this.traileffect = EFFECT_Null.m_id; + PolyTrail t = NEW(PolyTrail, this); + t.polytrail_tex = "particles/lgbeam.tga"; + t.polytrail_rgb[0] = '0.8 0.8 1'; + t.polytrail_rgb[1] = '0.5 0.5 1'; + t.polytrail_rgb[2] = '0.1 0.1 0.7'; + t.polytrail_alpha[0] = 1; + t.polytrail_alpha[1] = 0.5; + t.polytrail_alpha[2] = 0; + t.polytrail_thickness[0] = 15; + t.polytrail_thickness[1] = 7; + t.polytrail_thickness[2] = 0; + t.polytrail_lifetime = 0.18; + t.polytrail_segmentsize = 10; + t.polytrail_noise = 10; + t.polytrail_noisefunc = "chaotic"; + } break; case PROJECTILE_RPC: case PROJECTILE_ROCKET: @@ -408,6 +428,25 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) case PROJECTILE_CRYLINK_BOUNCING: set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); + if(autocvar_cl_polytrails) + { + this.traileffect = EFFECT_Null.m_id; + PolyTrail t = NEW(PolyTrail, this); + t.polytrail_tex = "gfx/crosshair_ring.tga"; // TODO: dedicated texture + t.polytrail_rgb[0] = '0.7 0 1'; + t.polytrail_rgb[1] = '0.5 0 1'; + t.polytrail_rgb[2] = '0.1 0 0.8'; + t.polytrail_alpha[0] = 0.5; + t.polytrail_alpha[1] = 0.2; + t.polytrail_alpha[2] = 0; + t.polytrail_thickness[0] = 5; + t.polytrail_thickness[1] = 3; + t.polytrail_thickness[2] = 3; + t.polytrail_lifetime = 0.15; + t.polytrail_segmentsize = 80; + t.polytrail_noise = 0; + t.polytrail_noisefunc = "none"; + } break; case PROJECTILE_FIREBALL: loopsound(this, CH_SHOTS_SINGLE, SND_FIREBALL_FLY2, VOL_BASE, ATTEN_NORM); @@ -452,6 +491,48 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) case PROJECTILE_WAKIROCKET: loopsound(this, CH_SHOTS_SINGLE, SND_TAG_ROCKET_FLY, VOL_BASE, ATTEN_NORM); break; + case PROJECTILE_ELECTRO_BEAM: + if(autocvar_cl_polytrails) + { + this.traileffect = EFFECT_Null.m_id; + PolyTrail t = NEW(PolyTrail, this); + t.polytrail_tex = "particles/lgbeam.tga"; + t.polytrail_rgb[0] = '0.75 0.75 1'; + t.polytrail_rgb[1] = '0.45 0.45 1'; + t.polytrail_rgb[2] = '0.1 0.1 0.75'; + t.polytrail_alpha[0] = 1; + t.polytrail_alpha[1] = 0.5; + t.polytrail_alpha[2] = 0; + t.polytrail_thickness[0] = 5; + t.polytrail_thickness[1] = 5; + t.polytrail_thickness[2] = 0; + t.polytrail_lifetime = 0.27; + t.polytrail_segmentsize = 10; + t.polytrail_noise = 5; + t.polytrail_noisefunc = "chaotic"; + } + break; + case PROJECTILE_CRYLINK: + if(autocvar_cl_polytrails) + { + this.traileffect = EFFECT_Null.m_id; + PolyTrail t = NEW(PolyTrail, this); + t.polytrail_tex = "gfx/crosshair_ring.tga"; // TODO: dedicated texture + t.polytrail_rgb[0] = '0.7 0 1'; + t.polytrail_rgb[1] = '0.5 0 1'; + t.polytrail_rgb[2] = '0.1 0 0.8'; + t.polytrail_alpha[0] = 0.75; + t.polytrail_alpha[1] = 0.2; + t.polytrail_alpha[2] = 0; + t.polytrail_thickness[0] = 5; + t.polytrail_thickness[1] = 3; + t.polytrail_thickness[2] = 3; + t.polytrail_lifetime = 0.15; + t.polytrail_segmentsize = 80; + t.polytrail_noise = 0; + t.polytrail_noisefunc = "none"; + } + break; /* case PROJECTILE_WAKICANNON: break; diff --git a/xonotic-client.cfg b/xonotic-client.cfg index 332c4b741..8cd277a9b 100644 --- a/xonotic-client.cfg +++ b/xonotic-client.cfg @@ -951,3 +951,5 @@ set menu_reverted_nonsaved_cvars "" "These cvars are currently marked as saved i // Avoid perf problems near '0 0 0'; `set` without a description is because only current DP has this engine cvar set cl_areagrid_link_SOLID_NOT 0 + +seta cl_polytrails 1 "enable polygon-based trail effect drawing (looks better but may impact performance)" -- 2.39.2