]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/particles.qc
func_pointparticles: reduce bandwidth
[divverent/nexuiz.git] / data / qcsrc / client / particles.qc
1 vector PointInBrush_vec;
2 entity PointInBrush_brush;
3 .float dphitcontentsmask;
4 float PointInBrush_Recurse()
5 {
6         float s;
7         entity se;
8         float f;
9
10         traceline(PointInBrush_vec, PointInBrush_vec, 0, world);
11         if not(trace_ent)
12                 return 0;
13         if(trace_ent == PointInBrush_brush)
14                 return 1;
15
16         se = trace_ent;
17         s = se.solid;
18         se.solid = SOLID_NOT;
19         f = PointInBrush_Recurse();
20         se.solid = s;
21
22         return f;
23 }
24 float PointInBrush(entity brush, vector point)
25 {
26         float f, s;
27
28         if not(brush.modelindex)
29                 return 1;
30
31         s = brush.solid;
32         brush.solid = SOLID_BSP;
33         PointInBrush_vec = point;
34         PointInBrush_brush = brush;
35         f = PointInBrush_Recurse();
36         brush.solid = s;
37
38         return f;
39 }
40
41 .float cnt; // effect number
42 .vector velocity; // particle velocity
43 .float waterlevel; // direction jitter
44 .float count; // count multiplier
45 .float glow_color; // palette color
46 .float impulse; // density
47 .string noise; // sound
48 .float atten;
49 .float volume;
50 .float absolute;
51 .vector movedir; // trace direction
52 .string bgmscript;
53 .float bgmscriptdecay;
54
55 float pointparticles_scriptbuf;
56 float pointparticles_scriptbufsize;
57 float pointparticles_scriptbufloaded;
58 void PointparticlesScript_Init()
59 {
60         string s;
61         float fh;
62         pointparticles_scriptbuf = pointparticles_scriptbufsize = 0;
63         pointparticles_scriptbufloaded = 1;
64         s = strcat("maps/", mi_shortname, ".bgs");
65         fh = fopen(s, FILE_READ);
66         if(fh < 0)
67                 return;
68         pointparticles_scriptbuf = buf_create();
69         while((s = fgets(fh)))
70         {
71                 bufstr_set(pointparticles_scriptbuf, pointparticles_scriptbufsize, s);
72                 ++pointparticles_scriptbufsize;
73         }
74         fclose(fh);
75 }
76
77 .float scriptline;
78 .float scriptline0;
79 .float scriptstate;
80 .float scriptvelocity;
81 .float scripttime;
82 .float switchedon;
83 void PointparticlesScript_InitEntity(entity e)
84 {
85         if(e.bgmscript != "")
86         {
87                 if(!pointparticles_scriptbufloaded)
88                         PointparticlesScript_Init();
89
90                 string mychar;
91                 float i;
92
93                 e.scriptline0 = -1;
94                 for(i = 0; i < pointparticles_scriptbufsize; ++i)
95                 {
96                         tokenize_sane(bufstr_get(pointparticles_scriptbuf, i));
97                         if(argv(0) == e.bgmscript)
98                                 break;
99                 }
100                 e.scriptline = e.scriptline0 = i;
101                 if(i >= pointparticles_scriptbufsize)
102                 {
103                         print("func_pointparticles: script does not define ", mychar, "\n");
104                         e.bgmscript = "";
105                 }
106         }
107 }
108
109 float PointparticlesScript(entity e)
110 {
111         float t;
112
113         if(e.bgmscript == "")
114                 return 1;
115         
116         if(cvar("bgmvolume") <= 0)
117                 return 0.5;
118
119         e.switchedon = FALSE;
120
121         t = gettime(GETTIME_CDTRACK);
122
123         tokenize_sane(bufstr_get(pointparticles_scriptbuf, e.scriptline));
124         if(t < e.scripttime)
125         {
126                 e.scriptline = e.scriptline0;
127                 e.scriptstate = 0;
128                 tokenize_sane(bufstr_get(pointparticles_scriptbuf, e.scriptline));
129         }
130
131         if(argv(0) != e.bgmscript)
132         {
133                 e.scriptstate = 0; // end of script, will revert to beginning later
134         }
135         else if(t >= stof(argv(1)))
136         {
137                 // time code reached!
138                 e.scriptvelocity = stof(argv(2));
139                 if(e.scriptvelocity > 0)
140                         e.switchedon = TRUE;
141                 e.scripttime = stof(argv(1));
142                 e.scriptline += 1;
143         }
144
145         if(e.scriptstate)
146         {
147                 if(e.bgmscriptdecay >= 1)
148                         return (e.scriptstate == 2);
149                 else
150                         return pow(0.5, (t - e.scripttime) * (e.bgmscriptdecay / (1 - e.bgmscriptdecay))) * e.scriptvelocity;
151         }
152         else
153                 return 0;
154 }
155
156 void Draw_PointParticles()
157 {
158         float n, i, fail;
159         vector p;
160         vector sz;
161         vector o;
162         o = self.origin;
163         sz = self.maxs - self.mins;
164         n = self.impulse * drawframetime;
165         n *= PointparticlesScript(self);
166         if(n == 0)
167                 return;
168         fail = 0;
169         for(i = random(); (self.switchedon || i <= n) && fail <= 64*n; ++i)
170         {
171                 p = o + self.mins;
172                 p_x += random() * sz_x;
173                 p_y += random() * sz_y;
174                 p_z += random() * sz_z;
175                 if(PointInBrush(self, p))
176                 {
177                         if(self.movedir != '0 0 0')
178                         {
179                                 traceline(p, p + normalize(self.movedir) * 4096, 0, world);
180                                 p = trace_endpos;
181                                 pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count, self.glow_color);
182                         }
183                         else
184                                 pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count, self.glow_color);
185                         if(self.noise != "")
186                         {
187                                 self.origin = p;
188                                 sound(self, CHAN_AUTO, self.noise, VOL_BASE * self.volume, self.atten);
189                         }
190                         self.switchedon = 0;
191                 }
192                 else if(self.absolute)
193                 {
194                         ++fail;
195                         --i;
196                 }
197         }
198         self.origin = o;
199 }
200
201 void Ent_PointParticles_Remove()
202 {
203         if(self.noise)
204                 strunzone(self.noise);
205         self.noise = string_null;
206         if(self.bgmscript)
207                 strunzone(self.bgmscript);
208         self.bgmscript = string_null;
209 }
210
211 void Ent_PointParticles()
212 {
213         float f, i;
214         vector v;
215         f = ReadByte();
216         if(f & 2)
217         {
218                 i = ReadCoord(); // density (<0: point, >0: volume)
219                 if(i && !self.impulse)
220                         self.switchedon = 1;
221                 self.impulse = i;
222         }
223         if(f & 4)
224         {
225                 self.origin_x = ReadCoord();
226                 self.origin_y = ReadCoord();
227                 self.origin_z = ReadCoord();
228         }
229         if(f & 1)
230         {
231                 self.modelindex = ReadShort();
232                 if(f & 0x80)
233                 {
234                         if(self.modelindex)
235                         {
236                                 self.mins_x = ReadCoord();
237                                 self.mins_y = ReadCoord();
238                                 self.mins_z = ReadCoord();
239                                 self.maxs_x = ReadCoord();
240                                 self.maxs_y = ReadCoord();
241                                 self.maxs_z = ReadCoord();
242                         }
243                         else
244                         {
245                                 self.mins    = '0 0 0';
246                                 self.maxs_x = ReadCoord();
247                                 self.maxs_y = ReadCoord();
248                                 self.maxs_z = ReadCoord();
249                         }
250                 }
251                 else
252                 {
253                         self.mins = self.maxs = '0 0 0';
254                 }
255
256                 self.cnt = ReadShort(); // effect number
257
258                 if(f & 0x20)
259                 {
260                         self.velocity = decompressShortVector(ReadShort());
261                         self.movedir = decompressShortVector(ReadShort());
262                 }
263                 else
264                 {
265                         self.velocity = self.movedir = '0 0 0';
266                 }
267                 if(f & 0x40)
268                 {
269                         self.waterlevel = ReadShort() / 16.0;
270                         self.count = ReadByte() / 16.0;
271                         self.glow_color = ReadByte();
272                 }
273                 else
274                 {
275                         self.waterlevel = self.count = self.glow_color = 0;
276                 }
277                 if(self.noise)
278                         strunzone(self.noise);
279                 if(self.bgmscript)
280                         strunzone(self.bgmscript);
281                 if(f & 0x10)
282                 {
283                         self.noise = strzone(ReadString());
284                         if(self.noise != "")
285                         {
286                                 self.atten = ReadByte() / 64.0;
287                                 self.volume = ReadByte() / 255.0;
288                         }
289                         self.bgmscript = strzone(ReadString());
290                         if(self.bgmscript != "")
291                                 self.bgmscriptdecay = ReadByte() / 255.0;
292                         PointparticlesScript_InitEntity(self);
293                 }
294                 else
295                 {
296                         self.noise = self.bgmscript = string_null;
297                 }
298         }
299
300         if(f & 2)
301         {
302                 self.absolute = (self.impulse >= 0);
303                 if(!self.absolute)
304                 {
305                         v = self.maxs - self.mins;
306                         self.impulse *= -v_x * v_y * v_z / 262144; // relative: particles per 64^3 cube
307                 }
308         }
309
310         setorigin(self, self.origin);
311         setsize(self, self.mins, self.maxs);
312         self.solid = SOLID_NOT;
313         self.draw = Draw_PointParticles;
314         self.entremove = Ent_PointParticles_Remove;
315 }
316
317 void Draw_Rain()
318 {
319     te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
320 }
321
322 void Draw_Snow()
323 {
324     te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, self.count * drawframetime, self.glow_color);
325 }
326
327 void Ent_RainOrSnow()
328 {
329         self.impulse = ReadByte(); // Rain, Snow, or Whatever
330         self.origin_x = ReadCoord();
331         self.origin_y = ReadCoord();
332         self.origin_z = ReadCoord();
333         self.maxs_x = ReadCoord();
334         self.maxs_y = ReadCoord();
335         self.maxs_z = ReadCoord();
336         self.velocity = decompressShortVector(ReadShort());
337         self.count = ReadShort() * 10;
338         self.glow_color = ReadByte(); // color
339
340         self.mins    = -0.5 * self.maxs;
341         self.maxs    =  0.5 * self.maxs;
342         self.origin  = self.origin - self.mins;
343
344         setorigin(self, self.origin);
345         setsize(self, self.mins, self.maxs);
346         self.solid = SOLID_NOT;
347         if(self.impulse)
348                 self.draw = Draw_Rain;
349         else
350                 self.draw = Draw_Snow;
351 }
352
353 entity zcurve;
354 void zcurveparticles(float effectnum, vector start, vector end, float end_dz, float speed, float depth)
355 {
356         // end_dz:
357         //   IF IT WERE A STRAIGHT LINE, it'd end end_dz above end
358
359         vector mid;
360         mid = (start + end) * 0.5;
361
362         end_dz *= 0.25;
363         mid_z += end_dz;
364
365         --depth;
366         if(depth < 0 || normalize(mid - start) * normalize(end - start) > 0.999999)
367         // TODO make this a variable threshold
368         // currently: 0.081 degrees
369         // 0.99999 would be 0.256 degrees and is visible
370         {
371                 zcurve.velocity = speed * normalize(end - start);
372                 trailparticles(zcurve, effectnum, start, end);
373         }
374         else
375         {
376                 zcurveparticles(effectnum, start, mid, end_dz, speed, depth);
377                 zcurveparticles(effectnum, mid, end, end_dz, speed, depth);
378         }
379 }
380
381 void Net_ReadZCurveParticles()
382 {
383         vector start, end;
384         float end_dz;
385         float effectnum, speed;
386
387         if(!zcurve)
388         {
389                 zcurve = spawn();
390                 zcurve.classname = "zcurve";
391         }
392
393         effectnum = ReadShort();
394
395         start_x = ReadCoord();
396         start_y = ReadCoord();
397         start_z = ReadCoord();
398         end_x = ReadCoord();
399         end_y = ReadCoord();
400         end_z = ReadCoord();
401         end_dz = ReadCoord();
402         speed = ReadShort() * 16;
403
404         zcurveparticles(effectnum, start, end, end_dz, speed, 5); // at most 32 segments
405 }
406
407 void Net_ReadNexgunBeamParticle()
408 {
409         vector shotorg, endpos;
410         shotorg_x = ReadCoord(); shotorg_y = ReadCoord(); shotorg_z = ReadCoord();
411         endpos_x = ReadCoord(); endpos_y = ReadCoord(); endpos_z = ReadCoord();
412         
413         pointparticles(particleeffectnum("nex_muzzleflash"), shotorg, normalize(endpos - shotorg) * 1000, 1);
414         
415         //draw either the old v2.3 beam or the new beam
416         if (cvar("cl_particles_oldnexbeam") && (getstati(STAT_ALLOW_OLDNEXBEAM) || isdemo()))
417                 trailparticles(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos);
418         else
419                 trailparticles(world, particleeffectnum("nex_beam"), shotorg, endpos);
420 }