]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/mutators/mutator/polytrails.qc
Polytrail noise effect
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / mutators / mutator / polytrails.qc
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;
5
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)
14
15     /**
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
19      */
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)
25     /** Time of input */
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)
32
33     void Trail_draw();
34     ATTRIB(PolyTrail, draw, void(), Trail_draw)
35     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) {
41                 remove(this);
42                 return;
43             }
44         } else {
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);
54             }
55         }
56
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) {
62                 count = i + 1;
63                 break;
64             }
65         }
66
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];
72             // head: 0, tail: 1
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);
89             R_EndPolygon();
90             from = to;
91         }
92     }
93     CONSTRUCTOR(PolyTrail, entity _follow) {
94         CONSTRUCT(PolyTrail);
95         this.polytrail_follow = _follow;
96     }
97 ENDCLASS(PolyTrail)
98
99 REGISTER_MUTATOR(polytrails, true);
100
101 MUTATOR_HOOKFUNCTION(polytrails, EditProjectile) {
102     return = false;
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;
115 }