1 bool autocvar_cl_polytrails = false;
2 float autocvar_cl_polytrail_segmentsize = 10;
3 float autocvar_cl_polytrail_lifetime = .2;
4 float autocvar_cl_polytrail_noise = 10;
6 CLASS(PolyTrail, Object)
7 ATTRIB(PolyTrail, polytrail_follow, entity, NULL)
8 ATTRIB(PolyTrail, polytrail_tex, string, string_null)
9 /** Lifetime per segment */
10 ATTRIB(PolyTrail, polytrail_lifetime, float, autocvar_cl_polytrail_lifetime)
11 ATTRIBARRAY(PolyTrail, polytrail_rgb, vector, 3)
12 ATTRIBARRAY(PolyTrail, polytrail_alpha, float, 3)
13 ATTRIBARRAY(PolyTrail, polytrail_thickness, float, 3)
16 * Increase as necessary if the buffer is overflowing
17 * symptom: tail of trail is wrong
18 * cause: projectiles are too fast for the segment size
20 const int POLYTRAIL_BUFSIZE = 1 << 7;
21 /** One or more positional points */
22 ATTRIBARRAY(PolyTrail, polytrail_bufpos, vector, POLYTRAIL_BUFSIZE)
23 /** Noise for ending position */
24 ATTRIBARRAY(PolyTrail, polytrail_bufnoise, vector, POLYTRAIL_BUFSIZE)
26 ATTRIBARRAY(PolyTrail, polytrail_buftime, float, POLYTRAIL_BUFSIZE)
27 /** Current read index */
28 ATTRIB(PolyTrail, polytrail_bufidx, float, -1)
29 /** Counts positions stored */
30 ATTRIB(PolyTrail, polytrail_cnt, float, 0)
31 #define POLYTRAIL_SEEK(_p, _rel) ((POLYTRAIL_BUFSIZE + (_p).polytrail_bufidx + (_rel)) % POLYTRAIL_BUFSIZE)
34 ATTRIB(PolyTrail, draw, void(), Trail_draw)
36 PolyTrail this = self;
37 if (wasfreed(this.polytrail_follow)) this.polytrail_follow = NULL;
38 if (!this.polytrail_follow) {
39 float when = this.polytrail_buftime[this.polytrail_bufidx];
40 if (time - when > this.polytrail_lifetime) {
45 setorigin(this, this.polytrail_follow.origin);
46 if (this.polytrail_cnt < 0 || vlen(this.origin - this.polytrail_bufpos[this.polytrail_bufidx]) >= autocvar_cl_polytrail_segmentsize) {
47 int i = POLYTRAIL_SEEK(this, 1);
48 this.polytrail_bufpos[i] = this.origin;
49 float f = autocvar_cl_polytrail_noise;
50 this.polytrail_bufnoise[i] = randompos(f * '-1 -1 -1', f * '1 1 1');
51 this.polytrail_buftime[i] = time;
52 this.polytrail_bufidx = i;
53 this.polytrail_cnt = bound(this.polytrail_cnt, i + 1, POLYTRAIL_BUFSIZE);
57 int count = this.polytrail_cnt;
58 for (int i = 0; i < count; ++i) {
59 int idx = POLYTRAIL_SEEK(this, -i);
60 float when = this.polytrail_buftime[idx];
61 if (time - when >= this.polytrail_lifetime) {
67 vector from = this.origin;
68 for (int i = 0, n = count; i < n; ++i) {
69 int idx = POLYTRAIL_SEEK(this, -i);
70 float when = this.polytrail_buftime[idx];
71 vector to = this.polytrail_bufpos[idx];
73 float rtime = (time - when) / this.polytrail_lifetime;
74 noref float rdist = i / n;
75 to += lerpvratio('0 0 0', this.polytrail_bufnoise[idx], rtime);
76 vector rgb = lerpv3ratio(this.polytrail_rgb[0], this.polytrail_rgb[1], this.polytrail_rgb[2], rtime);
77 float a = lerp3ratio(this.polytrail_alpha[0], this.polytrail_thickness[1], this.polytrail_alpha[2], rtime);
78 int thickness = lerp3ratio(this.polytrail_thickness[0], this.polytrail_thickness[1], this.polytrail_thickness[2], rtime);
79 vector thickdir = normalize(cross(normalize(to - from), view_origin - from)) * (thickness / 2);
80 vector A = from + thickdir;
81 vector B = from - thickdir;
82 vector C = to + thickdir;
83 vector D = to - thickdir;
84 R_BeginPolygon(this.polytrail_tex, DRAWFLAG_SCREEN);
85 R_PolygonVertex(B, '0 0 0', rgb, a);
86 R_PolygonVertex(A, '0 1 0', rgb, a);
87 R_PolygonVertex(C, '1 1 0', rgb, a);
88 R_PolygonVertex(D, '1 0 0', rgb, a);
93 CONSTRUCTOR(PolyTrail, entity _follow) {
95 this.polytrail_follow = _follow;
99 REGISTER_MUTATOR(polytrails, true);
101 MUTATOR_HOOKFUNCTION(polytrails, EditProjectile) {
103 if (!autocvar_cl_polytrails) return;
104 PolyTrail t = NEW(PolyTrail, self);
105 t.polytrail_tex = "gfx/trails/plain.tga";
106 t.polytrail_rgb[0] = '1 0 0';
107 t.polytrail_rgb[1] = '0 1 0';
108 t.polytrail_rgb[2] = '0 0 1';
109 t.polytrail_alpha[0] = 1;
110 t.polytrail_alpha[1] = 0.5;
111 t.polytrail_alpha[2] = 0;
112 t.polytrail_thickness[0] = 10;
113 t.polytrail_thickness[1] = 5;
114 t.polytrail_thickness[2] = 0;