]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/radarmap.qc
Properly support team field on trigger_multiple
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / command / radarmap.qc
1 #include "radarmap.qh"
2 #ifdef RADARMAP
3
4 #include <common/command/_mod.qh>
5
6 #include "../g_world.qh"
7 #include "../g_subs.qh"
8
9 #include <common/util.qh>
10
11 #include <lib/csqcmodel/sv_model.qh>
12
13 // ===============================================
14 //      Generates radar map images for use in the HUD
15 // ===============================================
16
17 float FullTraceFraction(vector a, vector mi, vector ma, vector b)
18 {
19         vector c;
20         float white, black;
21
22         white = 0.001;
23         black = 0.001;
24
25         c = a;
26
27         float n, m;
28         n = m = 0;
29
30         while (vdist(c - b, >, 1))
31         {
32                 ++m;
33
34                 tracebox(c, mi, ma, b, MOVE_WORLDONLY, NULL);
35                 ++n;
36
37                 if (!trace_startsolid)
38                 {
39                         black += vlen(trace_endpos - c);
40                         c = trace_endpos;
41                 }
42
43                 n += tracebox_inverted(c, mi, ma, b, MOVE_WORLDONLY, NULL, false, NULL);
44
45                 white += vlen(trace_endpos - c);
46                 c = trace_endpos;
47         }
48
49         if (n > 200) LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations");
50
51         return white / (black + white);
52 }
53 float RadarMapAtPoint_Trace(float e, float f, float w, float h, float zmin, float zsize, float q)
54 {
55         vector a, b, mi, ma;
56
57         mi = '0 0 0';
58         ma = '1 0 0' * w + '0 1 0' * h;
59         a = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
60         b = '1 0 0' * e + '0 1 0' * f + '0 0 1' * (zsize + zmin);
61
62         return FullTraceFraction(a, mi, ma, b);
63 }
64 float RadarMapAtPoint_LineBlock(float e, float f, float w, float h, float zmin, float zsize, float q)
65 {
66         vector o, mi, ma;
67         float i, r;
68         vector dz;
69
70         q = 256 * q - 1;
71         // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value
72
73         mi = '0 0 0';
74         dz = (zsize / q) * '0 0 1';
75         ma = '1 0 0' * w + '0 1 0' * h + dz;
76         o = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
77
78         if (e < world.absmin.x - w) return 0;
79         if (f < world.absmin.y - h) return 0;
80         if (e > world.absmax.x) return 0;
81         if (f > world.absmax.y) return 0;
82
83         r = 0;
84         for (i = 0; i < q; ++i)
85         {
86                 vector v1, v2;
87                 v1 = v2 = o + dz * i + mi;
88                 v1_x += random() * (ma.x - mi.x);
89                 v1_y += random() * (ma.y - mi.y);
90                 v1_z += random() * (ma.z - mi.z);
91                 v2_x += random() * (ma.x - mi.x);
92                 v2_y += random() * (ma.y - mi.y);
93                 v2_z += random() * (ma.z - mi.z);
94                 traceline(v1, v2, MOVE_WORLDONLY, NULL);
95                 if (trace_startsolid || trace_fraction < 1) ++r;
96         }
97         return r / q;
98 }
99 float RadarMapAtPoint_Block(float e, float f, float w, float h, float zmin, float zsize, float q)
100 {
101         vector o, mi, ma;
102         float i, r;
103         vector dz;
104
105         q = 256 * q - 1;
106         // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value
107
108         mi = '0 0 0';
109         dz = (zsize / q) * '0 0 1';
110         ma = '1 0 0' * w + '0 1 0' * h + dz;
111         o = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
112
113         if (e < world.absmin.x - w) return 0;
114         if (f < world.absmin.y - h) return 0;
115         if (e > world.absmax.x) return 0;
116         if (f > world.absmax.y) return 0;
117
118         r = 0;
119         for (i = 0; i < q; ++i)
120         {
121                 tracebox(o + dz * i, mi, ma, o + dz * i, MOVE_WORLDONLY, NULL);
122                 if (trace_startsolid) ++r;
123         }
124         return r / q;
125 }
126 float RadarMapAtPoint_Sample(float e, float f, float w, float h, float zmin, float zsize, float q)
127 {
128         vector a, b, mi, ma;
129
130         q *= 4;  // choose q so it matches the regular algorithm in speed
131
132         q = 256 * q - 1;
133         // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value
134
135         mi = '0 0 0';
136         ma = '1 0 0' * w + '0 1 0' * h;
137         a = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin;
138         b = '1 0 0' * w + '0 1 0' * h + '0 0 1' * zsize;
139
140         float c, i;
141         c = 0;
142
143         for (i = 0; i < q; ++i)
144         {
145                 vector v;
146                 v.x = a.x + random() * b.x;
147                 v.y = a.y + random() * b.y;
148                 v.z = a.z + random() * b.z;
149                 traceline(v, v, MOVE_WORLDONLY, NULL);
150                 if (trace_startsolid) ++c;
151         }
152
153         return c / q;
154 }
155 void sharpen_set(int b, float v)
156 {
157         sharpen_buffer[b + 2 * RADAR_WIDTH_MAX] = v;
158 }
159 float sharpen_getpixel(int b, int c)
160 {
161         if (b < 0) return 0;
162         if (b >= RADAR_WIDTH_MAX) return 0;
163         if (c < 0) return 0;
164         if (c > 2) return 0;
165         return sharpen_buffer[b + c * RADAR_WIDTH_MAX];
166 }
167 float sharpen_get(float b, float a)
168 {
169         float sum = sharpen_getpixel(b, 1);
170         if (a == 0) return sum;
171         sum *= (8 + 1 / a);
172         sum -= sharpen_getpixel(b - 1, 0);
173         sum -= sharpen_getpixel(b - 1, 1);
174         sum -= sharpen_getpixel(b - 1, 2);
175         sum -= sharpen_getpixel(b + 1, 0);
176         sum -= sharpen_getpixel(b + 1, 1);
177         sum -= sharpen_getpixel(b + 1, 2);
178         sum -= sharpen_getpixel(b, 0);
179         sum -= sharpen_getpixel(b, 2);
180         return bound(0, sum * a, 1);
181 }
182 void sharpen_shift(int w)
183 {
184         for (int i = 0; i < w; ++i)
185         {
186                 sharpen_buffer[i] = sharpen_buffer[i + RADAR_WIDTH_MAX];
187                 sharpen_buffer[i + RADAR_WIDTH_MAX] = sharpen_buffer[i + 2 * RADAR_WIDTH_MAX];
188                 sharpen_buffer[i + 2 * RADAR_WIDTH_MAX] = 0;
189         }
190 }
191 void sharpen_init(int w)
192 {
193         for (int i = 0; i < w; ++i)
194         {
195                 sharpen_buffer[i] = 0;
196                 sharpen_buffer[i + RADAR_WIDTH_MAX] = 0;
197                 sharpen_buffer[i + 2 * RADAR_WIDTH_MAX] = 0;
198         }
199 }
200 void RadarMap_Next()
201 {
202         if (radarmapper.count & 4)
203         {
204                 localcmd("quit\n");
205         }
206         else if (radarmapper.count & 2)
207         {
208                 localcmd(strcat("defer 1 \"sv_cmd radarmap --flags ", ftos(radarmapper.count), strcat(" --res ", ftos(radarmapper.size.x), " ", ftos(radarmapper.size.y), " --sharpen ", ftos(radarmapper.ltime), " --qual ", ftos(radarmapper.size.z)), "\"\n"));
209                 GotoNextMap(0);
210         }
211         delete(radarmapper);
212         radarmapper = NULL;
213 }
214 void RadarMap_Think(entity this)
215 {
216         // rough map entity
217         //   cnt: current line
218         //   size: pixel width/height
219         //   maxs: cell width/height
220         //   frame: counter
221
222         float i, x, l;
223         string si;
224
225         if (this.frame == 0)
226         {
227                 // initialize
228                 get_mi_min_max_texcoords(1);
229                 this.mins = mi_picmin;
230                 this.maxs_x = (mi_picmax.x - mi_picmin.x) / this.size.x;
231                 this.maxs_y = (mi_picmax.y - mi_picmin.y) / this.size.y;
232                 this.maxs_z = mi_max.z - mi_min.z;
233                 LOG_INFO("Picture mins/maxs: ", ftos(this.maxs.x), " and ", ftos(this.maxs.y), " should match");
234                 this.netname = strzone(strcat("gfx/", mi_shortname, "_radar.xpm"));
235                 if (!(this.count & 1))
236                 {
237                         this.cnt = fopen(this.netname, FILE_READ);
238                         if (this.cnt < 0) this.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.tga"), FILE_READ);
239                         if (this.cnt < 0) this.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.png"), FILE_READ);
240                         if (this.cnt < 0) this.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.jpg"), FILE_READ);
241                         if (this.cnt < 0) this.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.tga"), FILE_READ);
242                         if (this.cnt < 0) this.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.png"), FILE_READ);
243                         if (this.cnt < 0) this.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.jpg"), FILE_READ);
244                         if (this.cnt >= 0)
245                         {
246                                 fclose(this.cnt);
247
248                                 LOG_INFO(this.netname, " already exists, aborting (you may want to specify --force)");
249                                 RadarMap_Next();
250                                 return;
251                         }
252                 }
253                 this.cnt = fopen(this.netname, FILE_WRITE);
254                 if (this.cnt < 0)
255                 {
256                         LOG_INFO("Error writing ", this.netname);
257                         delete(this);
258                         radarmapper = NULL;
259                         return;
260                 }
261                 LOG_INFO("Writing to ", this.netname, "...");
262                 fputs(this.cnt, "/* XPM */\n");
263                 fputs(this.cnt, "static char *RadarMap[] = {\n");
264                 fputs(this.cnt, "/* columns rows colors chars-per-pixel */\n");
265                 fputs(this.cnt, strcat("\"", ftos(this.size.x), " ", ftos(this.size.y), " 256 2\",\n"));
266                 for (i = 0; i < 256; ++i)
267                 {
268                         si = substring(doublehex, i * 2, 2);
269                         fputs(this.cnt, strcat("\"", si, " c #", si, si, si, "\",\n"));
270                 }
271                 this.frame += 1;
272                 this.nextthink = time;
273                 sharpen_init(this.size.x);
274         }
275         else if (this.frame <= this.size.y)
276         {
277                 // fill the sharpen buffer with this line
278                 sharpen_shift(this.size.x);
279                 i = this.count & 24;
280
281                 switch (i)
282                 {
283                         case 0:
284                         default:
285                                 for (x = 0; x < this.size.x; ++x)
286                                 {
287                                         l = RadarMapAtPoint_Block(this.mins.x + x * this.maxs.x, this.mins.y + (this.size.y - this.frame) * this.maxs.y, this.maxs.x, this.maxs.y, this.mins.z, this.maxs.z, this.size.z);
288                                         sharpen_set(x, l);
289                                 }
290                                 break;
291                         case 8:
292                                 for (x = 0; x < this.size.x; ++x)
293                                 {
294                                         l = RadarMapAtPoint_Trace(this.mins.x + x * this.maxs.x, this.mins.y + (this.size.y - this.frame) * this.maxs.y, this.maxs.x, this.maxs.y, this.mins.z, this.maxs.z, this.size.z);
295                                         sharpen_set(x, l);
296                                 }
297                                 break;
298                         case 16:
299                                 for (x = 0; x < this.size.x; ++x)
300                                 {
301                                         l = RadarMapAtPoint_Sample(this.mins.x + x * this.maxs.x, this.mins.y + (this.size.y - this.frame) * this.maxs.y, this.maxs.x, this.maxs.y, this.mins.z, this.maxs.z, this.size.z);
302                                         sharpen_set(x, l);
303                                 }
304                                 break;
305                         case 24:
306                                 for (x = 0; x < this.size.x; ++x)
307                                 {
308                                         l = RadarMapAtPoint_LineBlock(this.mins.x + x * this.maxs.x, this.mins.y + (this.size.y - this.frame) * this.maxs.y, this.maxs.x, this.maxs.y, this.mins.z, this.maxs.z, this.size.z);
309                                         sharpen_set(x, l);
310                                 }
311                                 break;
312                 }
313
314                 // do we have enough lines?
315                 if (this.frame >= 2)
316                 {
317                         // write a pixel line
318                         fputs(this.cnt, "\"");
319                         for (x = 0; x < this.size.x; ++x)
320                         {
321                                 l = sharpen_get(x, this.ltime);
322                                 fputs(this.cnt, substring(doublehex, 2 * floor(l * 256.0), 2));
323                         }
324                         if (this.frame == this.size.y)
325                         {
326                                 fputs(this.cnt, "\"\n");
327                         }
328                         else
329                         {
330                                 fputs(this.cnt, "\",\n");
331                                 LOG_INFO(ftos(this.size.y - this.frame), " lines left");
332                         }
333                 }
334
335                 // is this the last line? then write back the missing line
336                 if (this.frame == this.size.y)
337                 {
338                         sharpen_shift(this.size.x);
339                         // write a pixel line
340                         fputs(this.cnt, "\"");
341                         for (x = 0; x < this.size.x; ++x)
342                         {
343                                 l = sharpen_get(x, this.ltime);
344                                 fputs(this.cnt, substring(doublehex, 2 * floor(l * 256.0), 2));
345                         }
346                         if (this.frame == this.size.y)
347                         {
348                                 fputs(this.cnt, "\"\n");
349                         }
350                         else
351                         {
352                                 fputs(this.cnt, "\",\n");
353                                 LOG_INFO(ftos(this.size.y - this.frame), " lines left");
354                         }
355                 }
356
357                 this.frame += 1;
358                 this.nextthink = time;
359         }
360         else
361         {
362                 // close the file
363                 fputs(this.cnt, "};\n");
364                 fclose(this.cnt);
365                 LOG_INFO("Finished. Please edit data/", this.netname, " with an image editing application and place it in the TGA format in the gfx folder.");
366                 RadarMap_Next();
367         }
368 }
369
370 bool RadarMap_Make(float argc)
371 {
372         float i;
373
374         if (!radarmapper)
375         {
376                 radarmapper = new(radarmapper);
377                 setthink(radarmapper, RadarMap_Think);
378                 radarmapper.nextthink = time;
379                 radarmapper.count = 8;  // default to the --trace method, as it is faster now
380                 radarmapper.ltime = 1;
381                 radarmapper.size = '512 512 1';
382                 for (i = 1; i < argc; ++i)
383                 {
384                         switch (argv(i))
385                         {
386                                 case "--force":
387                                 { radarmapper.count |= 1;
388                                   break;
389                                 }
390                                 case "--loop":
391                                 { radarmapper.count |= 2;
392                                   break;
393                                 }
394                                 case "--quit":
395                                 { radarmapper.count |= 4;
396                                   break;
397                                 }
398                                 case "--block":
399                                 { radarmapper.count &= ~24;
400                                   break;
401                                 }
402                                 case "--trace":
403                                 { radarmapper.count &= ~24;
404                                   radarmapper.count |= 8;
405                                   break;
406                                 }
407                                 case "--sample":
408                                 { radarmapper.count &= ~24;
409                                   radarmapper.count |= 16;
410                                   break;
411                                 }
412                                 case "--lineblock":
413                                 { radarmapper.count |= 24;
414                                   break;
415                                 }
416                                 case "--flags":
417                                 { ++i;
418                                   radarmapper.count = stof(argv(i));
419                                   break;
420                                 }  // for the recursive call
421                                 case "--sharpen":
422                                 { ++i;
423                                   radarmapper.ltime = stof(argv(i));
424                                   break;
425                                 }  // for the recursive call
426                                 case "--res":  // minor alias
427                                 case "--resolution":
428                                 { ++i;
429                                   radarmapper.size_x = stof(argv(i));
430                                   ++i;
431                                   radarmapper.size_y = stof(argv(i));
432                                   break;
433                                 }
434                                 case "--qual":  // minor alias
435                                 case "--quality":
436                                 { ++i;
437                                   radarmapper.size_z = stof(argv(i));
438                                   break;
439                                 }
440
441                                 default:
442                                         i = argc;
443                                         delete(radarmapper);
444                                         radarmapper = NULL;
445                                         break;
446                         }
447                 }
448
449                 if (radarmapper)  // after doing the arguments, see if we successfully went forward.
450                 {
451                         LOG_INFO("Radarmap entity spawned.");
452                         return true;  // if so, don't print usage.
453                 }
454         }
455
456         return false;
457 }
458 #endif