From: Mario Date: Sat, 10 Dec 2016 11:53:24 +0000 (+1000) Subject: Merge branch 'master' into TimePath/polytrails X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=8216df10041fc46e5dceb7a2ca5816f70349e9a5;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into TimePath/polytrails # Conflicts: # qcsrc/client/progs.src # qcsrc/common/physics.qc # qcsrc/lib/Math.qh --- 8216df10041fc46e5dceb7a2ca5816f70349e9a5 diff --cc qcsrc/client/mutators/_mod.inc index 000000000,3dfd4f789..504d8718d mode 000000,100644..100644 --- a/qcsrc/client/mutators/_mod.inc +++ b/qcsrc/client/mutators/_mod.inc @@@ -1,0 -1,2 +1,2 @@@ + // generated file; do not modify -#include ++#include diff --cc qcsrc/client/mutators/mutator/_mod.inc index 000000000,000000000..3dfd4f789 new file mode 100644 --- /dev/null +++ b/qcsrc/client/mutators/mutator/_mod.inc @@@ -1,0 -1,0 +1,2 @@@ ++// generated file; do not modify ++#include diff --cc qcsrc/client/mutators/mutator/_mod.qh index 000000000,000000000..98fb4815c new file mode 100644 --- /dev/null +++ b/qcsrc/client/mutators/mutator/_mod.qh @@@ -1,0 -1,0 +1,1 @@@ ++// generated file; do not modify diff --cc qcsrc/client/mutators/mutator/polytrails.qc index 58092688b,000000000..3915201e1 mode 100644,000000..100644 --- a/qcsrc/client/mutators/mutator/polytrails.qc +++ b/qcsrc/client/mutators/mutator/polytrails.qc @@@ -1,115 -1,0 +1,117 @@@ +bool autocvar_cl_polytrails = false; +float autocvar_cl_polytrail_segmentsize = 10; +float autocvar_cl_polytrail_lifetime = .2; +float autocvar_cl_polytrail_noise = 10; + +CLASS(PolyTrail, Object) - ATTRIB(PolyTrail, polytrail_follow, entity, NULL) - ATTRIB(PolyTrail, polytrail_tex, string, string_null) ++ ATTRIB(PolyTrail, polytrail_follow, entity, NULL); ++ ATTRIB(PolyTrail, polytrail_tex, string, string_null); + /** Lifetime per segment */ - ATTRIB(PolyTrail, polytrail_lifetime, float, autocvar_cl_polytrail_lifetime) - ATTRIBARRAY(PolyTrail, polytrail_rgb, vector, 3) - ATTRIBARRAY(PolyTrail, polytrail_alpha, float, 3) - ATTRIBARRAY(PolyTrail, polytrail_thickness, float, 3) ++ ATTRIB(PolyTrail, polytrail_lifetime, float, autocvar_cl_polytrail_lifetime); ++ 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) ++ ATTRIBARRAY(PolyTrail, polytrail_bufpos, vector, POLYTRAIL_BUFSIZE); + /** Noise for ending position */ - ATTRIBARRAY(PolyTrail, polytrail_bufnoise, vector, POLYTRAIL_BUFSIZE) ++ ATTRIBARRAY(PolyTrail, polytrail_bufnoise, vector, POLYTRAIL_BUFSIZE); + /** Time of input */ - ATTRIBARRAY(PolyTrail, polytrail_buftime, float, POLYTRAIL_BUFSIZE) ++ ATTRIBARRAY(PolyTrail, polytrail_buftime, float, POLYTRAIL_BUFSIZE); + /** Current read index */ - ATTRIB(PolyTrail, polytrail_bufidx, float, -1) ++ ATTRIB(PolyTrail, polytrail_bufidx, float, -1); + /** Counts positions stored */ - ATTRIB(PolyTrail, polytrail_cnt, float, 0) ++ ATTRIB(PolyTrail, polytrail_cnt, float, 0); + #define POLYTRAIL_SEEK(_p, _rel) ((POLYTRAIL_BUFSIZE + (_p).polytrail_bufidx + (_rel)) % POLYTRAIL_BUFSIZE) + - void Trail_draw(); - ATTRIB(PolyTrail, draw, void(), Trail_draw) - void Trail_draw() { - PolyTrail this = self; ++ void Trail_draw(entity this); ++ ATTRIB(PolyTrail, draw, void(entity this), Trail_draw); ++ void Trail_draw(entity this) { + if (wasfreed(this.polytrail_follow)) this.polytrail_follow = NULL; + if (!this.polytrail_follow) { + float when = this.polytrail_buftime[this.polytrail_bufidx]; + if (time - when > this.polytrail_lifetime) { - remove(this); ++ delete(this); + return; + } + } else { + setorigin(this, this.polytrail_follow.origin); + if (this.polytrail_cnt < 0 || vlen(this.origin - this.polytrail_bufpos[this.polytrail_bufidx]) >= autocvar_cl_polytrail_segmentsize) { + int i = POLYTRAIL_SEEK(this, 1); + this.polytrail_bufpos[i] = this.origin; + float f = autocvar_cl_polytrail_noise; + this.polytrail_bufnoise[i] = randompos(f * '-1 -1 -1', f * '1 1 1'); + this.polytrail_buftime[i] = time; + this.polytrail_bufidx = i; + this.polytrail_cnt = bound(this.polytrail_cnt, i + 1, POLYTRAIL_BUFSIZE); + } + } + + int count = this.polytrail_cnt; + for (int i = 0; i < count; ++i) { + int idx = POLYTRAIL_SEEK(this, -i); + float when = this.polytrail_buftime[idx]; + if (time - when >= this.polytrail_lifetime) { + count = i + 1; + break; + } + } + + vector from = this.origin; + for (int i = 0, n = count; i < n; ++i) { + int idx = POLYTRAIL_SEEK(this, -i); + float when = this.polytrail_buftime[idx]; + vector to = this.polytrail_bufpos[idx]; + // head: 0, tail: 1 + float rtime = (time - when) / this.polytrail_lifetime; + noref float rdist = i / n; + 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); + 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; + } + } + CONSTRUCTOR(PolyTrail, entity _follow) { + CONSTRUCT(PolyTrail); + this.polytrail_follow = _follow; + } +ENDCLASS(PolyTrail) + +REGISTER_MUTATOR(polytrails, true); + +MUTATOR_HOOKFUNCTION(polytrails, EditProjectile) { + return = false; + if (!autocvar_cl_polytrails) return; - PolyTrail t = NEW(PolyTrail, self); ++ entity proj = M_ARGV(0, entity); ++ PolyTrail t = NEW(PolyTrail, proj); + t.polytrail_tex = "gfx/trails/plain.tga"; + t.polytrail_rgb[0] = '1 0 0'; + t.polytrail_rgb[1] = '0 1 0'; + t.polytrail_rgb[2] = '0 0 1'; + t.polytrail_alpha[0] = 1; + t.polytrail_alpha[1] = 0.5; + t.polytrail_alpha[2] = 0; + t.polytrail_thickness[0] = 10; + t.polytrail_thickness[1] = 5; + t.polytrail_thickness[2] = 0; ++ ++ IL_PUSH(g_drawables, t); +} diff --cc qcsrc/lib/math.qh index 000000000,92d45bf14..5ed328227 mode 000000,100644..100644 --- a/qcsrc/lib/math.qh +++ b/qcsrc/lib/math.qh @@@ -1,0 -1,300 +1,318 @@@ + #pragma once + + void mean_accumulate(entity e, .float a, .float c, float mean, float value, float weight) + { + if (weight == 0) return; + if (mean == 0) e.(a) *= pow(value, weight); + else e.(a) += pow(value, mean) * weight; + e.(c) += weight; + } + + float mean_evaluate(entity e, .float a, .float c, float mean) + { + if (e.(c) == 0) return 0; + if (mean == 0) return pow(e.(a), 1.0 / e.(c)); + else return pow(e.(a) / e.(c), 1.0 / mean); + } + + #define MEAN_ACCUMULATE(s, prefix, v, w) mean_accumulate(s, prefix##_accumulator, prefix##_count, prefix##_mean, v, w) + #define MEAN_EVALUATE(s, prefix) mean_evaluate(s, prefix##_accumulator, prefix##_count, prefix##_mean) + #define MEAN_DECLARE(prefix, m) float prefix##_mean = m; .float prefix##_count, prefix##_accumulator + + /** Returns a random number between -1.0 and 1.0 */ + #define crandom() (2 * (random() - 0.5)) + + + /* + ================== + Angc used for animations + ================== + */ + + + float angc(float a1, float a2) + { + while (a1 > 180) + a1 -= 360; + while (a1 < -179) + a1 += 360; + while (a2 > 180) + a2 -= 360; + while (a2 < -179) + a2 += 360; + float a = a1 - a2; + while (a > 180) + a -= 360; + while (a < -179) + a += 360; + return a; + } + + float fsnap(float val, float fsize) + { + return rint(val / fsize) * fsize; + } + + vector vsnap(vector point, float fsize) + { + vector vret; + + vret.x = rint(point.x / fsize) * fsize; + vret.y = rint(point.y / fsize) * fsize; + vret.z = ceil(point.z / fsize) * fsize; + + return vret; + } + ++float lerpratio(float f0, float f1, float ratio) { return f0 * (1 - ratio) + f1 * ratio; } ++ ++float lerp(float t0, float f0, float t1, float f1, float t) { return lerpratio(f0, f1, (t - t0) / (t1 - t0)); } ++ ++float lerp3ratio(float f0, float f1, float f2, float ratio) ++{ ++ int mid = 0.5; ++ return ratio < mid ? lerpratio(f0, f1, ratio / mid) : ratio > mid ? lerpratio(f1, f2, (ratio - mid) / mid) : f1; ++} ++ ++vector lerpvratio(vector f0, vector f1, float ratio) { return f0 * (1 - ratio) + f1 * ratio; } ++ ++vector lerpv3ratio(vector f0, vector f1, vector f2, float ratio) ++{ ++ int mid = 0.5; ++ return ratio < mid ? lerpvratio(f0, f1, ratio / mid) : ratio > mid ? lerpvratio(f1, f2, (ratio - mid) / mid) : f1; ++} ++ + vector lerpv(float t0, vector v0, float t1, vector v1, float t) + { + return v0 + (v1 - v0) * ((t - t0) / (t1 - t0)); + } + + vector bezier_quadratic_getpoint(vector a, vector b, vector c, float t) + { + return (c - 2 * b + a) * (t * t) + + (b - a) * (2 * t) + + a; + } + + vector bezier_quadratic_getderivative(vector a, vector b, vector c, float t) + { + return (c - 2 * b + a) * (2 * t) + + (b - a) * 2; + } + + float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float spd) + { + return (((startspeedfactor + endspeedfactor - 2 + ) * spd - 2 * startspeedfactor - endspeedfactor + 3 + ) * spd + startspeedfactor + ) * spd; + } + + bool cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor) + { + if (startspeedfactor < 0 || endspeedfactor < 0) return false; + + /* + // if this is the case, the possible zeros of the first derivative are outside + // 0..1 + We can calculate this condition as condition + if(se <= 3) + return true; + */ + + // better, see below: + if (startspeedfactor <= 3 && endspeedfactor <= 3) return true; + + // if this is the case, the first derivative has no zeros at all + float se = startspeedfactor + endspeedfactor; + float s_e = startspeedfactor - endspeedfactor; + if (3 * (se - 4) * (se - 4) + s_e * s_e <= 12) // an ellipse + return true; + + // Now let s <= 3, s <= 3, s+e >= 3 (triangle) then we get se <= 6 (top right corner). + // we also get s_e <= 6 - se + // 3 * (se - 4)^2 + (6 - se)^2 + // is quadratic, has value 12 at 3 and 6, and value < 12 in between. + // Therefore, above "better" check works! + + return false; + + // known good cases: + // (0, [0..3]) + // (0.5, [0..3.8]) + // (1, [0..4]) + // (1.5, [0..3.9]) + // (2, [0..3.7]) + // (2.5, [0..3.4]) + // (3, [0..3]) + // (3.5, [0.2..2.3]) + // (4, 1) + + /* + On another note: + inflection point is always at (2s + e - 3) / (3s + 3e - 6). + + s + e - 2 == 0: no inflection + + s + e > 2: + 0 < inflection < 1 if: + 0 < 2s + e - 3 < 3s + 3e - 6 + 2s + e > 3 and 2e + s > 3 + + s + e < 2: + 0 < inflection < 1 if: + 0 > 2s + e - 3 > 3s + 3e - 6 + 2s + e < 3 and 2e + s < 3 + + Therefore: there is an inflection point iff: + e outside (3 - s)/2 .. 3 - s*2 + + in other words, if (s,e) in triangle (1,1)(0,3)(0,1.5) or in triangle (1,1)(3,0)(1.5,0) + */ + } + + /** continuous function mapping all reals into -1..1 */ + float float2range11(float f) + { + return f / (fabs(f) + 1); + } + + /** continuous function mapping all reals into 0..1 */ + float float2range01(float f) + { + return 0.5 + 0.5 * float2range11(f); + } + + float median(float a, float b, float c) + { + return (a < c) ? bound(a, b, c) : bound(c, b, a); + } + + float almost_equals(float a, float b) + { + float eps = (max(a, -a) + max(b, -b)) * 0.001; + return a - b < eps && b - a < eps; + } + + float almost_in_bounds(float a, float b, float c) + { + float eps = (max(a, -a) + max(c, -c)) * 0.001; + if (a > c) eps = -eps; + return b == median(a - eps, b, c + eps); + } + + float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d) + { + if (halflifedist > 0) return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist); + else if (halflifedist < 0) return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist); + else return 1; + } + + float power2of(float e) + { + return pow(2, e); + } + + float log2of(float e) + { + // NOTE: generated code + if (e > 2048) + if (e > 131072) + if (e > 1048576) + if (e > 4194304) return 23; + else + if (e > 2097152) return 22; + else return 21; + else + if (e > 524288) return 20; + else + if (e > 262144) return 19; + else return 18; + else + if (e > 16384) + if (e > 65536) return 17; + else + if (e > 32768) return 16; + else return 15; + else + if (e > 8192) return 14; + else + if (e > 4096) return 13; + else return 12; + else + if (e > 32) + if (e > 256) + if (e > 1024) return 11; + else + if (e > 512) return 10; + else return 9; + else + if (e > 128) return 8; + else + if (e > 64) return 7; + else return 6; + else + if (e > 4) + if (e > 16) return 5; + else + if (e > 8) return 4; + else return 3; + else + if (e > 2) return 2; + else + if (e > 1) return 1; + else return 0; + } + + /** ax^2 + bx + c = 0 */ + vector solve_quadratic(float a, float b, float c) + { + vector v; + float D; + v = '0 0 0'; + if (a == 0) + { + if (b != 0) + { + v.x = v.y = -c / b; + v.z = 1; + } + else + { + if (c == 0) + { + // actually, every number solves the equation! + v.z = 1; + } + } + } + else + { + D = b * b - 4 * a * c; + if (D >= 0) + { + D = sqrt(D); + if (a > 0) // put the smaller solution first + { + v.x = ((-b) - D) / (2 * a); + v.y = ((-b) + D) / (2 * a); + } + else + { + v.x = (-b + D) / (2 * a); + v.y = (-b - D) / (2 * a); + } + v.z = 1; + } + else + { + // complex solutions! + D = sqrt(-D); + v.x = -b / (2 * a); + if (a > 0) v.y = D / (2 * a); + else v.y = -D / (2 * a); + v.z = 0; + } + } + return v; + }