1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2016 Spike
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 /*
23 The aim of this particle system is to have as much as possible configurable.
24 Some parts still fail here, and are marked FIXME
25 Effects are flushed on new maps.
26 The engine has a few builtins.
27 */
28
29 #include "quakedef.h"
30
31 cvar_t r_fteparticles = {"r_fteparticles","1", CVAR_ARCHIVE};
32
33 #ifdef PSET_SCRIPT
34 #define USE_DECALS
35 #define Con_Printf Con_SafePrintf
36
37 #define frandom() (rand()*(1.0f/(float)RAND_MAX))
38 #define crandom() (rand()*(2.0f/(float)RAND_MAX)-1.0f)
39 #define hrandom() (rand()*(1.0f/(float)RAND_MAX)-0.5f)
40 #define particle_s fparticle_s
41 #define particle_t fparticle_t
42 typedef vec_t vec2_t[2];
43 #define FloatInterpolate(a, bness, b, c) ((c) = (a) + (b - a)*bness)
44 #define Vector2Copy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];}while(0)
45 #define Vector2Set(r,x,y) do{(r)[0] = x; (r)[1] = y;}while(0)
46 #define VectorClear(a) ((a)[0]=(a)[1]=(a)[2]=0)
47 #define VectorInterpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1]),FloatInterpolate((a)[2], bness, (b)[2], (c)[2])
48 #define VectorSet(r,x,y,z) do{(r)[0] = x; (r)[1] = y;(r)[2] = z;}while(0)
49 #define Vector4Clear(a) ((a)[0]=(a)[1]=(a)[2]=(a)[3]=0)
50 #define Vector4Copy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];(b)[3]=(a)[3];}while(0)
51 #define Vector4Scale(in,scale,out) ((out)[0]=(in)[0]*scale,(out)[1]=(in)[1]*scale,(out)[2]=(in)[2]*scale,(out)[3]=(in)[3]*scale)
52 #define FloatToColor(a,b) do { \
53 (b)=(byte)(CLAMP(0.0f, (a), 1.0f) * 255.0f); \
54 } while(0)
55 #define Vector3ToColor(a,b) do { \
56 (b)[0]=(byte)(CLAMP(0.0f, (a)[0], 1.0f) * 255.0f); \
57 (b)[1]=(byte)(CLAMP(0.0f, (a)[1], 1.0f) * 255.0f); \
58 (b)[2]=(byte)(CLAMP(0.0f, (a)[2], 1.0f) * 255.0f); \
59 } while(0)
60 #define Vector4ToColor(a,b) do { \
61 (b)[0]=(byte)(CLAMP(0.0f, (a)[0], 1.0f) * 255.0f); \
62 (b)[1]=(byte)(CLAMP(0.0f, (a)[1], 1.0f) * 255.0f); \
63 (b)[2]=(byte)(CLAMP(0.0f, (a)[2], 1.0f) * 255.0f); \
64 (b)[3]=(byte)(CLAMP(0.0f, (a)[3], 1.0f) * 255.0f); \
65 } while(0)
VectorNormalize2(const vec3_t v,vec3_t out)66 vec_t VectorNormalize2 (const vec3_t v, vec3_t out)
67 {
68 float length, ilength;
69
70 length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
71
72 if (length)
73 {
74 length = sqrt (length); // FIXME
75 ilength = 1/length;
76 out[0] = v[0]*ilength;
77 out[1] = v[1]*ilength;
78 out[2] = v[2]*ilength;
79 }
80 else
81 {
82 VectorClear (out);
83 }
84
85 return length;
86 }
VectorVectors(const vec3_t forward,vec3_t right,vec3_t up)87 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
88 {
89 if (!forward[0] && !forward[1])
90 {
91 if (forward[2])
92 right[1] = -1;
93 else
94 right[1] = 0;
95 right[0] = right[2] = 0;
96 }
97 else
98 {
99 right[0] = forward[1];
100 right[1] = -forward[0];
101 right[2] = 0;
102 VectorNormalize(right);
103 }
104 CrossProduct(right, forward, up);
105 }
106 typedef enum { BM_BLEND/*SRC_ALPHA ONE_MINUS_SRC_ALPHA*/, BM_BLENDCOLOUR/*SRC_COLOR ONE_MINUS_SRC_COLOR*/, BM_ADDA/*SRC_ALPHA ONE*/, BM_ADDC/*GL_SRC_COLOR GL_ONE*/, BM_SUBTRACT/*SRC_ALPHA ONE_MINUS_SRC_COLOR*/, BM_INVMODA/*ZERO ONE_MINUS_SRC_ALPHA*/, BM_INVMODC/*ZERO ONE_MINUS_SRC_COLOR*/, BM_PREMUL/*ONE ONE_MINUS_SRC_ALPHA*/} blendmode_t;
107 typedef struct trailstate_s {
108 struct trailstate_s **key; // key to check if ts has been overwriten
109 struct trailstate_s *assoc; // assoc linked trail
110 struct beamseg_s *lastbeam; // last beam pointer (flagged with BS_LASTSEG)
111 union {
112 float lastdist; // last distance used with particle effect
113 float statetime; // time to emit effect again (used by spawntime field)
114 } state1;
115 union {
116 float laststop; // last stopping point for particle effect
117 float emittime; // used by r_effect emitters
118 } state2;
119 } trailstate_t;
120 #define CON_WARNING "Warning: "
121 entity_t *CL_EntityNum (int num);
122 #define BEF_LINES 1
123
124 #define Z_Malloc malloc
125 #define Z_Free free
126 #define Z_Realloc realloc
127
128 extern int PClassic_PointFile(int c, vec3_t point);
129
130 #define PART_VALID(part) ((part) >= 0 && (part) < numparticletypes)
131
132 static int pe_default = P_INVALID;
133 static int pe_size2 = P_INVALID;
134 static int pe_size3 = P_INVALID;
135 static int pe_defaulttrail = P_INVALID;
136
137 #define SINTABLE_ENTRIES 128
138 static float psintable[SINTABLE_ENTRIES];
139 static float pcostable[SINTABLE_ENTRIES];
140
141 int r_trace_line_cache_counter;
142
143 int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk);
144 int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], trailstate_t **tsk);
145 static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning);
146 static void R_Particles_KillAllEffects(void);
147
buildsintable(void)148 static void buildsintable(void)
149 {
150 int i;
151 for (i = 0; i < SINTABLE_ENTRIES; i++)
152 {
153 psintable[i] = sin((i*M_PI)/(SINTABLE_ENTRIES/2));
154 pcostable[i] = cos((i*M_PI)/(SINTABLE_ENTRIES/2));
155 }
156 }
157 #define sin(x) (psintable[(size_t)(int)((x)*((SINTABLE_ENTRIES/2)/M_PI)) % SINTABLE_ENTRIES])
158 #define cos(x) (pcostable[(size_t)(int)((x)*((SINTABLE_ENTRIES/2)/M_PI)) % SINTABLE_ENTRIES])
159
160 typedef struct particle_s
161 {
162 struct particle_s *next;
163 float die;
164
165 // driver-usable fields
166 vec3_t org;
167 vec4_t rgba;
168 float scale;
169 float s1, t1, s2, t2;
170
171 vec3_t oldorg; //to throttle traces
172 vec3_t vel; //renderer uses for sparks
173 float angle;
174 union {
175 float nextemit;
176 trailstate_t *trailstate;
177 } state;
178 // drivers never touch the following fields
179 float rotationspeed;
180 } particle_t;
181
182 typedef struct clippeddecal_s
183 {
184 struct clippeddecal_s *next;
185 float die;
186
187 int entity; //>0 is a lerpentity, <0 is a csqc ent. 0 is world. woot.
188 qmodel_t *model; //just for paranoia
189
190 vec3_t vertex[3];
191 vec2_t texcoords[3];
192 float valpha[3];
193
194 vec4_t rgba;
195 } clippeddecal_t;
196
197 #define BS_LASTSEG 0x1 // no draw to next, no delete
198 #define BS_DEAD 0x2 // segment is dead
199 #define BS_NODRAW 0x4 // only used for lerp switching
200
201 typedef struct beamseg_s
202 {
203 struct beamseg_s *next; // next in beamseg list
204
205 particle_t *p;
206 int flags; // flags for beamseg
207 vec3_t dir;
208
209 float texture_s;
210 } beamseg_t;
211
212 typedef struct skytris_s {
213 struct skytris_s *next;
214 vec3_t org;
215 vec3_t x;
216 vec3_t y;
217 float area;
218 double nexttime;
219 int ptype;
220 struct msurface_s *face;
221 } skytris_t;
222
223 typedef struct skytriblock_s
224 {
225 struct skytriblock_s *next;
226 unsigned int count;
227 skytris_t tris[1024];
228 } skytriblock_t;
229
230 //this is the required render state for each particle
231 //dynamic per-particle stuff isn't important. only static state.
232 typedef struct {
233 enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_CDECAL, PT_UDECAL, PT_INVISIBLE} type;
234
235 blendmode_t blendmode;
236 gltexture_t *texture;
237 qboolean nearest;
238
239 float scalefactor;
240 float invscalefactor;
241 float stretch;
242 float minstretch; //limits the particle's length to a multiple of its width.
243 int premul; //0: direct rgba. 1: rgb*a,a (blend). 2: rgb*a,0 (add).
244 } plooks_t;
245
246 //these could be deltas or absolutes depending on ramping mode.
247 typedef struct {
248 vec3_t rgb;
249 float alpha;
250 float scale;
251 float rotation;
252 } ramp_t;
253 typedef struct {
254 char name[MAX_QPATH];
255 float vol;
256 float atten;
257 float delay;
258 float pitch;
259 float weight;
260 } partsounds_t;
261 // TODO: merge in alpha with rgb to gain benefit of vector opts
262 typedef struct part_type_s {
263 char name[MAX_QPATH];
264 char config[MAX_QPATH];
265 char texname[MAX_QPATH];
266
267 int numsounds;
268 partsounds_t *sounds;
269
270 vec3_t rgb; //initial colour
271 float alpha;
272 vec3_t rgbchange; //colour delta (per second)
273 float alphachange;
274 vec3_t rgbrand; //random rgb colour to start with
275 float alpharand;
276 int colorindex; //get colour from a palette
277 int colorrand; //and add up to this amount
278 float rgbchangetime;//colour stops changing at this time
279 vec3_t rgbrandsync; //like rgbrand, but a single random value instead of separate (can mix)
280 float scale; //initial scale
281 float scalerand; //with up to this much extra
282 float die, randdie; //how long it lasts (plus some rand)
283 float veladd, randomveladd; //scale the incoming velocity by this much
284 float orgadd, randomorgadd; //spawn the particle this far along its velocity direction
285 float spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards)
286 vec3_t orgbias; //static 3d world-coord bias
287 vec3_t velbias;
288 vec3_t orgwrand; //3d world-coord randomisation without relation to spawn mode
289 vec3_t velwrand; //3d world-coord randomisation without relation to spawn mode
290 float viewspacefrac;
291 float flurry;
292 int surfflagmatch; //this decal only spawns on these surfaces
293 int surfflagmask; //this decal only spawns on these surfaces
294
295 float s1, t1, s2, t2; //texture coords
296 float texsstride; //addition for s for each random slot.
297 int randsmax; //max times the stride can be added
298
299 plooks_t *slooks; //shared looks, so state switches don't apply between particles so much.
300 plooks_t looks; //
301
302 float spawntime; //time limit for trails
303 float spawnchance; //if < 0, particles might not spawn so many
304
305 float rotationstartmin, rotationstartrand;
306 float rotationmin, rotationrand;
307
308 float scaledelta;
309 float countextra;
310 float count;
311 float countrand;
312 float countspacing; //for trails.
313 float countoverflow; //for badly-designed effects, instead of depending on trail state.
314 float rainfrequency; //surface emitter multiplier
315
316 int assoc;
317 int cliptype;
318 int inwater;
319 float clipcount;
320 int emit;
321 float emittime;
322 float emitrand;
323 float emitstart;
324
325 float areaspread;
326 float areaspreadvert;
327
328 float spawnparam1;
329 float spawnparam2;
330 /* float spawnparam3; */
331
332 enum {
333 SM_BOX, //box = even spread within the area
334 SM_CIRCLE, //circle = around edge of a circle
335 SM_BALL, //ball = filled sphere
336 SM_SPIRAL, //spiral = spiral trail
337 SM_TRACER, //tracer = tracer trail
338 SM_TELEBOX, //telebox = q1-style telebox
339 SM_LAVASPLASH, //lavasplash = q1-style lavasplash
340 SM_UNICIRCLE, //unicircle = uniform circle
341 SM_FIELD, //field = synced field (brightfield, etc)
342 SM_DISTBALL, // uneven distributed ball
343 SM_MESHSURFACE //distributed roughly evenly over the surface of the mesh
344 } spawnmode;
345
346 float gravity;
347 vec3_t friction;
348 float clipbounce;
349 float stainonimpact;
350
351 vec3_t dl_rgb;
352 float dl_radius[2];
353 float dl_time;
354 vec4_t dl_decay;
355 float dl_corona_intensity;
356 float dl_corona_scale;
357 vec3_t dl_scales;
358 //PT_NODLSHADOW
359 int dl_cubemapnum;
360
361 enum {RAMP_NONE, RAMP_DELTA, RAMP_NEAREST, RAMP_LERP} rampmode;
362 int rampindexes;
363 ramp_t *ramp;
364
365 int loaded; //0 if not loaded, 1 if automatically loaded, 2 if user loaded
366 particle_t *particles;
367 clippeddecal_t *clippeddecals;
368 beamseg_t *beams;
369 struct part_type_s *nexttorun;
370
371 unsigned int flags;
372 #define PT_VELOCITY 0x0001 // has velocity modifiers
373 #define PT_FRICTION 0x0002 // has friction modifiers
374 #define PT_CHANGESCOLOUR 0x0004
375 #define PT_CITRACER 0x0008 // Q1-style tracer behavior for colorindex
376 #define PT_INVFRAMETIME 0x0010 // apply inverse frametime to count (causes emits to be per frame)
377 #define PT_AVERAGETRAIL 0x0020 // average trail points from start to end, useful with t_lightning, etc
378 #define PT_NOSTATE 0x0040 // don't use trailstate for this emitter (careful with assoc...)
379 #define PT_NOSPREADFIRST 0x0080 // don't randomize org/vel for first generated particle
380 #define PT_NOSPREADLAST 0x0100 // don't randomize org/vel for last generated particle
381 #define PT_TROVERWATER 0x0200 // don't spawn if underwater
382 #define PT_TRUNDERWATER 0x0400 // don't spawn if overwater
383 #define PT_NODLSHADOW 0x0800 // dlights from this effect don't cast shadows.
384 #define PT_WORLDSPACERAND 0x1000 // effect has orgwrand or velwrand properties
385 unsigned int fluidmask;
386
387 unsigned int state;
388 #define PS_INRUNLIST 0x1 // particle type is currently in execution list
389 } part_type_t;
390
391 typedef struct pcfg_s
392 {
393 struct pcfg_s *next;
394 char name[1];
395 } pcfg_t;
396 static pcfg_t *loadedconfigs;
397
398 #ifndef TYPESONLY
399
400 #define crand() (rand()%32767/16383.5f-1)
401
402 #define MAX_BEAMSEGS (1<<11) // default max # of beam segments
403 #define MAX_PARTICLES (1<<18) // max # of particles at one time
404 #define MAX_DECALS (1<<18) // max # of decal fragments at one time
405 #define MAX_TRAILSTATES (1<<10) // default max # of trailstates
406
407 static particle_t *free_particles;
408 static particle_t *particles; //contains the initial list of alloced particles.
409 static int r_numparticles;
410 static int r_particlerecycle;
411
412 static beamseg_t *free_beams;
413 static beamseg_t *beams;
414 static int r_numbeams;
415
416 static clippeddecal_t *free_decals;
417 static clippeddecal_t *decals;
418 static int r_numdecals;
419 static int r_decalrecycle;
420
421 static trailstate_t *trailstates;
422 static int ts_cycle; // current cyclic index of trailstates
423 static int r_numtrailstates;
424
425 static qboolean r_plooksdirty; //a particle effect was changed, reevaluate shared looks.
426
427 static void FinishParticleType(part_type_t *ptype);
428
429 static void R_ParticleDesc_Callback(struct cvar_s *var);
430 static cvar_t r_bouncysparks = {"r_bouncysparks", "1"};
431 static cvar_t r_part_rain = {"r_part_rain", "1"};
432 static cvar_t r_decal_noperpendicular = {"r_decal_noperpendicular", "1"};
433 cvar_t r_particledesc = {"r_particledesc", "classic"};
434 static cvar_t r_part_rain_quantity = {"r_part_rain_quantity", "1"};
435 static cvar_t r_particle_tracelimit = {"r_particle_tracelimit", "16777216"};
436 static cvar_t r_part_sparks = {"r_part_sparks", "1"};
437 static cvar_t r_part_sparks_trifan = {"r_part_sparks_trifan", "1"};
438 static cvar_t r_part_sparks_textured = {"r_part_sparks_textured", "1"};
439 static cvar_t r_part_beams = {"r_part_beams", "1"};
440 static cvar_t r_part_contentswitch = {"r_part_contentswitch", "1"};
441 static cvar_t r_part_density = {"r_part_density", "1"};
442 static cvar_t r_part_maxparticles = {"r_part_maxparticles", "65536"};
443 static cvar_t r_part_maxdecals = {"r_part_maxdecals", "8192"};
444 static cvar_t r_lightflicker = {"r_lightflicker", "1"};
445 extern cvar_t r_showtris;
446 extern cvar_t r_particles;
447
448 static float particletime;
449
450 typedef struct
451 {
452 int firstidx;
453 int firstvert;
454 int numidx;
455 int numvert;
456
457 gltexture_t *texture;
458 blendmode_t blendmode;
459 int beflags;
460 } scenetris_t;
461
462 #define MAX_INDICES 0xffff
463 #define INITIAL_NUM_VERTICES 100000
464 #define INITIAL_NUM_INDICES 150000
465
466 static scenetris_t *cl_stris;
467 static unsigned int cl_numstris;
468 static unsigned int cl_maxstris;
469 static basicvertex_t * cl_strisvert[2];
470 static basicvertex_t * cl_curstrisvert;
471 static unsigned int cl_numstrisvert;
472 static unsigned int cl_maxstrisvert[2];
473 static unsigned short *cl_strisidx[2];
474 static unsigned short *cl_curstrisidx;
475 static unsigned int cl_numstrisidx;
476 static unsigned int cl_maxstrisidx[2];
477
478 /*
479 Q1BSP_RecursiveHullTrace
480 Optimised version of vanilla's SV_RecursiveHullCheck that avoids the excessive pointcontents calls by using the traceline itself to check for contents.
481 call Q1BSP_RecursiveHullCheck for a drop-in replacement of SV_RecursiveHullCheck, if desired.
482 */
483 enum
484 {
485 rht_solid,
486 rht_empty,
487 rht_impact
488 };
489 struct rhtctx_s
490 {
491 vec3_t start, end;
492 mclipnode_t *clipnodes;
493 mplane_t *planes;
494 };
Q1BSP_RecursiveHullTrace(struct rhtctx_s * ctx,int num,float p1f,float p2f,vec3_t p1,vec3_t p2,trace_t * trace)495 static int Q1BSP_RecursiveHullTrace (struct rhtctx_s *ctx, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
496 {
497 mclipnode_t *node;
498 mplane_t *plane;
499 float t1, t2;
500 vec3_t mid;
501 int side;
502 float midf;
503 int rht;
504
505 reenter:
506
507 if (num < 0)
508 {
509 /*hit a leaf*/
510 if (num == CONTENTS_SOLID)
511 {
512 if (trace->allsolid)
513 trace->startsolid = true;
514 return rht_solid;
515 }
516 else
517 {
518 trace->allsolid = false;
519 if (num == CONTENTS_EMPTY)
520 trace->inopen = true;
521 else
522 trace->inwater = true;
523 return rht_empty;
524 }
525 }
526
527 /*its a node*/
528
529 /*get the node info*/
530 node = ctx->clipnodes + num;
531 plane = ctx->planes + node->planenum;
532
533 if (plane->type < 3)
534 {
535 t1 = p1[plane->type] - plane->dist;
536 t2 = p2[plane->type] - plane->dist;
537 }
538 else
539 {
540 t1 = DotProduct (plane->normal, p1) - plane->dist;
541 t2 = DotProduct (plane->normal, p2) - plane->dist;
542 }
543
544 /*if its completely on one side, resume on that side*/
545 if (t1 >= 0 && t2 >= 0)
546 {
547 num = node->children[0];
548 goto reenter;
549 }
550 if (t1 < 0 && t2 < 0)
551 {
552 num = node->children[1];
553 goto reenter;
554 }
555
556 if (plane->type < 3)
557 {
558 t1 = ctx->start[plane->type] - plane->dist;
559 t2 = ctx->end[plane->type] - plane->dist;
560 }
561 else
562 {
563 t1 = DotProduct (plane->normal, ctx->start) - plane->dist;
564 t2 = DotProduct (plane->normal, ctx->end) - plane->dist;
565 }
566
567 side = t1 < 0;
568
569 midf = t1 / (t1 - t2);
570 if (midf < p1f) midf = p1f;
571 if (midf > p2f) midf = p2f;
572 VectorInterpolate(ctx->start, midf, ctx->end, mid);
573
574 rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side], p1f, midf, p1, mid, trace);
575 if (rht != rht_empty && !trace->allsolid)
576 return rht;
577 rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side^1], midf, p2f, mid, p2, trace);
578 if (rht != rht_solid)
579 return rht;
580
581 if (side)
582 {
583 /*we impacted the back of the node, so flip the plane*/
584 trace->plane.dist = -plane->dist;
585 VectorScale(plane->normal, -1, trace->plane.normal);
586 midf = (t1 + DIST_EPSILON) / (t1 - t2);
587 }
588 else
589 {
590 /*we impacted the front of the node*/
591 trace->plane.dist = plane->dist;
592 VectorCopy(plane->normal, trace->plane.normal);
593 midf = (t1 - DIST_EPSILON) / (t1 - t2);
594 }
595
596 t1 = DotProduct (trace->plane.normal, ctx->start) - trace->plane.dist;
597 t2 = DotProduct (trace->plane.normal, ctx->end) - trace->plane.dist;
598 midf = (t1 - DIST_EPSILON) / (t1 - t2);
599 if (midf < 0)
600 midf = 0;
601 if (midf > 1)
602 midf = 1;
603 trace->fraction = midf;
604 VectorCopy (mid, trace->endpos);
605 VectorInterpolate(ctx->start, midf, ctx->end, trace->endpos);
606
607 return rht_impact;
608 }
Q1BSP_RecursiveHullCheck(hull_t * hull,int num,float p1f,float p2f,vec3_t p1,vec3_t p2,trace_t * trace)609 static qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
610 {
611 //this function is basicall meant as a drop-in replacement for fte's SV_RecursiveHullCheck. p1f and p2f must be 0+1 respectively, num must be hull->firstclipnode
612 struct rhtctx_s ctx;
613 VectorCopy(p1, ctx.start);
614 VectorCopy(p2, ctx.end);
615 ctx.clipnodes = hull->clipnodes;
616 ctx.planes = hull->planes;
617 return Q1BSP_RecursiveHullTrace(&ctx, num, p1f, p2f, p1, p2, trace) != rht_impact;
618 }
619
CL_TraceLine(vec3_t start,vec3_t end,vec3_t impact,vec3_t normal,int * entnum)620 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int *entnum)
621 { //FIXME: not sure what to do about startsolid.
622 int i;
623 trace_t trace;
624 float frac = 1;
625 entity_t *ent;
626 vec3_t relstart, relend;
627 VectorCopy (end, impact);
628 VectorSet(normal, 0, 0, 1);
629
630 static int num_trace_line_ents;
631 static int trace_line_ents[MAX_EDICTS];
632 static int cache_valid_count = -1;
633 if (cache_valid_count != r_trace_line_cache_counter)
634 {
635 num_trace_line_ents = 0;
636 for (i = 0; i < cl.num_entities; i++)
637 {
638 ent = &cl.entities[i];
639 if (!ent->model || ent->model->needload || ent->model->type != mod_brush)
640 continue;
641 trace_line_ents[num_trace_line_ents++] = i;
642 }
643 cache_valid_count = r_trace_line_cache_counter;
644 }
645
646 if (entnum)
647 *entnum = 0;
648 for (i = 0; i < num_trace_line_ents; i++)
649 {
650 ent = &cl.entities[trace_line_ents[i]];
651
652 //FIXME: deal with rotations
653 VectorSubtract(start, ent->origin, relstart);
654 VectorSubtract(end, ent->origin, relend);
655
656 memset (&trace, 0, sizeof(trace));
657 trace.fraction = 1;
658 Q1BSP_RecursiveHullCheck(&ent->model->hulls[0], ent->model->hulls[0].firstclipnode, 0, 1, relstart, relend, &trace);
659
660 if (frac > trace.fraction)
661 {
662 frac = trace.fraction;
663
664 //FIXME: deal with rotations.
665 VectorAdd(trace.endpos, ent->origin, impact);
666 VectorCopy (trace.plane.normal, normal);
667
668 if (entnum)
669 *entnum = i;
670 if (frac <= 0)
671 break;
672 }
673 }
674 return frac;
675 }
676
677 //these are not the actual values, but they'll do
678 #define FTECONTENTS_EMPTY 0
679 #define FTECONTENTS_SOLID 1
680 #define FTECONTENTS_WATER 2
681 #define FTECONTENTS_SLIME 4
682 #define FTECONTENTS_LAVA 8
683 #define FTECONTENTS_SKY 16
684 #define FTECONTENTS_FLUID (FTECONTENTS_WATER|FTECONTENTS_SLIME|FTECONTENTS_LAVA|FTECONTENTS_SKY)
685 #define FTECONTENTS_PLAYERCLIP 0
686
687 int SV_HullPointContents (hull_t *hull, int num, vec3_t p);
CL_PointContentsMask(vec3_t p)688 static unsigned int CL_PointContentsMask (vec3_t p)
689 {
690 static const unsigned int cont_qtof[] =
691 {
692 0, //invalid
693 FTECONTENTS_EMPTY,
694 FTECONTENTS_SOLID,
695 FTECONTENTS_WATER,
696 FTECONTENTS_SLIME,
697 FTECONTENTS_LAVA,
698 FTECONTENTS_SKY
699 };
700
701 unsigned int cont;
702
703 cont = -SV_HullPointContents (&cl.worldmodel->hulls[0], 0, p);
704 if (cont < sizeof(cont_qtof)/sizeof(cont_qtof[0]))
705 return cont_qtof[cont];
706 else
707 return cont_qtof[-(CONTENTS_WATER)]; //assume water
708 }
709
710 static int numparticletypes;
711 static part_type_t *part_type;
712 static part_type_t *part_run_list;
713
714 static struct {
715 char *oldn;
716 char *newn;
717 } legacynames[] =
718 {
719 {"t_rocket", "TR_ROCKET"},
720 {"t_grenade", "TR_GRENADE"},
721 {"t_gib", "TR_BLOOD"},
722
723 {"te_plasma", "TE_TEI_PLASMAHIT"},
724 {"te_smoke", "TE_TEI_SMOKE"},
725
726 {NULL}
727 };
728
729 static struct partalias_s
730 {
731 struct partalias_s *next;
732 const char *from;
733 const char *to;
734 } *partaliaslist;
735 typedef struct associatedeffect_s
736 {
737 struct associatedeffect_s *next;
738 char mname[MAX_QPATH];
739 char pname[MAX_QPATH];
740 unsigned int flags;
741 enum
742 {
743 AE_TRAIL,
744 AE_EMIT,
745 } type;
746 } associatedeffect_t;
747 static associatedeffect_t *associatedeffect;
PScript_AssociateEffect_f(void)748 static void PScript_AssociateEffect_f(void)
749 {
750 const char *modelname = Cmd_Argv(1);
751 const char *effectname = Cmd_Argv(2);
752 unsigned int flags = 0;
753 int type;
754 associatedeffect_t *ae;
755 int i;
756
757 if (!strcmp(Cmd_Argv(0), "r_trail"))
758 type = AE_TRAIL;
759 else
760 {
761 type = AE_EMIT;
762 for (i = 3; i < Cmd_Argc(); i++)
763 {
764 const char *fn = Cmd_Argv(i);
765 if (!strcmp(fn, "replace") || !strcmp(fn, "1"))
766 flags |= MOD_EMITREPLACE;
767 else if (!strcmp(fn, "forwards") || !strcmp(fn, "forward"))
768 flags |= MOD_EMITFORWARDS;
769 else if (!strcmp(fn, "0"))
770 ; //1 or 0 are legacy, meaning replace or not
771 else
772 Con_DPrintf("%s %s: unknown flag %s\n", Cmd_Argv(0), modelname, fn);
773 }
774 }
775
776 if (
777 strstr(modelname, "player") ||
778 strstr(modelname, "eyes") ||
779 strstr(modelname, "flag") ||
780 strstr(modelname, "tf_stan") ||
781 strstr(modelname, ".bsp") ||
782 strstr(modelname, "turr"))
783 {
784 //there is a very real possibility of attaching 'large' effects to models so that they become more visible (eg: a stream of particles passing through walls showing you the entity that they're eminating from)
785 Con_Printf("Sorry: Not allowed to attach effects to model \"%s\"\n", modelname);
786 return;
787 }
788
789 if (strlen (modelname) >= MAX_QPATH || strlen(effectname) >= MAX_QPATH)
790 return;
791
792 /*replace the old one if it exists*/
793 for(ae = associatedeffect; ae; ae = ae->next)
794 {
795 if (!strcmp(ae->mname, modelname))
796 if ((ae->type==AE_TRAIL) == (type==AE_TRAIL))
797 break;
798 }
799 if (!ae)
800 {
801 ae = Z_Malloc(sizeof(*ae));
802 strcpy(ae->mname, modelname);
803 ae->next = associatedeffect;
804 associatedeffect = ae;
805 }
806 strcpy(ae->pname, effectname);
807 ae->type = type;
808 ae->flags = flags;
809
810 r_plooksdirty = true;
811 }
P_PartRedirect_f(void)812 static void P_PartRedirect_f(void)
813 {
814 struct partalias_s **link, *l;
815 const char *from = Cmd_Argv(1);
816 const char *to = Cmd_Argv(2);
817
818 //user wants to list all
819 if (!*from)
820 {
821 for (l = partaliaslist; l; l = l->next)
822 {
823 Con_Printf("%s -> %s\n", l->from, l->to);
824 }
825 return;
826 }
827
828 //unlink the current value
829 for (link = &partaliaslist; (l=*link); link = &(*link)->next)
830 {
831 if (!q_strcasecmp(l->from, from))
832 {
833 //they didn't specify a to, so just print out this one effect without removing it.
834 if (Cmd_Argc() == 2)
835 {
836 Con_Printf("particle %s is currently remapped to %s\n", l->from, l->to);
837 return;
838 }
839 *link = l->next;
840 Z_Free(l);
841 break;
842 }
843 }
844
845 //create a new entry.
846 if (*to && q_strcasecmp(from, to))
847 {
848 l = Z_Malloc(sizeof(*l) + strlen(from) + strlen(to) + 2);
849 l->from = (char*)(l + 1);
850 strcpy((char*)l->from, from);
851 l->to = l->from + strlen(l->from)+1;
852 strcpy((char*)l->to, to);
853 l->next = partaliaslist;
854 partaliaslist = l;
855 }
856
857 r_plooksdirty = true;
858 }
PScript_UpdateModelEffects(qmodel_t * mod)859 void PScript_UpdateModelEffects(qmodel_t *mod)
860 {
861 associatedeffect_t *ae;
862 mod->emiteffect = P_INVALID;
863 mod->traileffect = P_INVALID;
864 for(ae = associatedeffect; ae; ae = ae->next)
865 {
866 if (!strcmp(ae->mname, mod->name))
867 {
868 switch(ae->type)
869 {
870 case AE_TRAIL:
871 mod->traileffect = PScript_FindParticleType(ae->pname);
872 break;
873 case AE_EMIT:
874 mod->emiteffect = PScript_FindParticleType(ae->pname);
875 mod->flags &= ~(MOD_EMITREPLACE|MOD_EMITFORWARDS);
876 mod->flags |= ae->flags;
877 break;
878 }
879 }
880 }
881 }
882
P_GetParticleType(const char * config,const char * name)883 static part_type_t *P_GetParticleType(const char *config, const char *name)
884 {
885 int i;
886 part_type_t *ptype;
887 part_type_t *oldlist = part_type;
888 char cfgbuf[MAX_QPATH];
889 char *dot = strchr(name, '.');
890 if (dot && (dot - name) < MAX_QPATH-1)
891 {
892 config = cfgbuf;
893 memcpy(cfgbuf, name, dot - name);
894 cfgbuf[dot - name] = 0;
895 name = dot+1;
896 }
897
898 for (i = 0; legacynames[i].oldn; i++)
899 {
900 if (!strcmp(name, legacynames[i].oldn))
901 {
902 name = legacynames[i].newn;
903 break;
904 }
905 }
906 for (i = 0; i < numparticletypes; i++)
907 {
908 ptype = &part_type[i];
909 if (!q_strcasecmp(ptype->name, name))
910 if (!q_strcasecmp(ptype->config, config)) //must be an exact match.
911 return ptype;
912 }
913 part_type = Z_Realloc(part_type, sizeof(part_type_t)*(numparticletypes+1));
914 ptype = &part_type[numparticletypes++];
915 memset(ptype, 0, sizeof(*ptype));
916 q_strlcpy(ptype->name, name, sizeof(ptype->name));
917 q_strlcpy(ptype->config, config, sizeof(ptype->config));
918 ptype->assoc = P_INVALID;
919 ptype->inwater = P_INVALID;
920 ptype->cliptype = P_INVALID;
921 ptype->emit = P_INVALID;
922
923 if (oldlist)
924 {
925 if (part_run_list)
926 part_run_list = (part_type_t*)((char*)part_run_list - (char*)oldlist + (char*)part_type);
927
928 for (i = 0; i < numparticletypes; i++)
929 if (part_type[i].nexttorun)
930 part_type[i].nexttorun = (part_type_t*)((char*)part_type[i].nexttorun - (char*)oldlist + (char*)part_type);
931 }
932
933 ptype->loaded = 0;
934 ptype->ramp = NULL;
935 ptype->particles = NULL;
936 ptype->beams = NULL;
937
938 r_plooksdirty = true;
939 return ptype;
940 }
941
942 //unconditionally allocates a particle object. this allows out-of-order allocations.
P_AllocateParticleType(const char * config,const char * name)943 static int P_AllocateParticleType(const char *config, const char *name) //guarentees that the particle type exists, returning it's index.
944 {
945 part_type_t *pt = P_GetParticleType(config, name);
946 return pt - part_type;
947 }
948
PScript_RetintEffect(part_type_t * to,part_type_t * from,const char * colourcodes)949 static void PScript_RetintEffect(part_type_t *to, part_type_t *from, const char *colourcodes)
950 {
951 char name[sizeof(to->name)];
952 char config[sizeof(to->config)];
953
954 q_strlcpy(name, to->name, sizeof(to->name));
955 q_strlcpy(config, to->config, sizeof(to->config));
956
957 //'to' was already purged, so we don't need to care about that.
958 memcpy(to, from, sizeof(*to));
959
960 q_strlcpy(to->name, name, sizeof(to->name));
961 q_strlcpy(to->config, config, sizeof(to->config));
962
963 //make sure 'to' has its own copy of any lists, so that we don't have issues when freeing this memory again.
964 if (to->sounds)
965 {
966 to->sounds = Z_Malloc(to->numsounds * sizeof(*to->sounds));
967 memcpy(to->sounds, from->sounds, to->numsounds * sizeof(*to->sounds));
968 }
969 if (to->ramp)
970 {
971 to->ramp = Z_Malloc(to->rampindexes * sizeof(*to->ramp));
972 memcpy(to->ramp, from->ramp, to->rampindexes * sizeof(*to->ramp));
973 }
974
975 //'from' might still have some links so we need to clear those out.
976 to->nexttorun = NULL;
977 to->particles = NULL;
978 to->clippeddecals = NULL;
979 to->beams = NULL;
980 to->slooks = &to->looks;
981 r_plooksdirty = true;
982
983 to->colorindex = strtoul(colourcodes, (char**)&colourcodes, 10);
984 if (*colourcodes == '_')
985 colourcodes++;
986 to->colorrand = strtoul(colourcodes, (char**)&colourcodes, 10);
987 }
988
989 //public interface. get without creating.
PScript_FindParticleType(const char * fullname)990 int PScript_FindParticleType(const char *fullname)
991 {
992 int i;
993 part_type_t *ptype = NULL;
994 char cfg[MAX_QPATH];
995 char *dot;
996 const char *name = fullname;
997
998 //check particle aliases, mostly for tex_sky1 -> weather.te_rain for example, or whatever
999 struct partalias_s *l;
1000 int recurselimit = 5;
1001 for (l = partaliaslist; l; )
1002 {
1003 if (!q_strcasecmp(l->from, name))
1004 {
1005 name = l->to;
1006
1007 if (recurselimit --> 0)
1008 l = partaliaslist;
1009 else
1010 return P_INVALID;
1011 }
1012 else
1013 l = l->next;
1014 }
1015
1016 dot = strchr(name, '.');
1017 if (dot && (dot - name) < MAX_QPATH-1)
1018 {
1019 memcpy(cfg, name, dot - name);
1020 cfg[dot-name] = 0;
1021 name = dot+1;
1022 }
1023 else
1024 *cfg = 0;
1025
1026 for (i = 0; legacynames[i].oldn; i++)
1027 {
1028 if (!strcmp(name, legacynames[i].oldn))
1029 {
1030 name = legacynames[i].newn;
1031 break;
1032 }
1033 }
1034
1035 if (*cfg)
1036 { //favour the namespace if one is specified
1037 for (i = 0; i < numparticletypes; i++)
1038 {
1039 if (!q_strcasecmp(part_type[i].name, name))
1040 {
1041 if (!q_strcasecmp(part_type[i].config, cfg))
1042 {
1043 ptype = &part_type[i];
1044 break;
1045 }
1046 }
1047 }
1048 }
1049 else
1050 {
1051 //but be prepared to load it from any namespace if its not got a namespace specified.
1052 for (i = 0; i < numparticletypes; i++)
1053 {
1054 if (!q_strcasecmp(part_type[i].name, name))
1055 {
1056 ptype = &part_type[i];
1057 if (ptype->loaded) //(mostly) ignore ones that are not currently loaded
1058 break;
1059 }
1060 }
1061 }
1062 if (!ptype || !ptype->loaded)
1063 {
1064 if (!q_strncasecmp(name, "te_explosion2_", 14))
1065 {
1066 int from = PScript_FindParticleType(va("%s.te_explosion2", cfg));
1067 if (from != P_INVALID)
1068 {
1069 int to = P_AllocateParticleType(cfg, name);
1070 PScript_RetintEffect(&part_type[to], &part_type[from], name+14);
1071 return to;
1072 }
1073 }
1074 if (*cfg)
1075 if (P_LoadParticleSet(cfg, true, true))
1076 return PScript_FindParticleType(fullname);
1077
1078 return P_INVALID;
1079 }
1080 return i;
1081 }
1082
CheckAssosiation(const char * config,const char * name,int from)1083 static int CheckAssosiation(const char *config, const char *name, int from)
1084 {
1085 int to, orig;
1086
1087 orig = to = P_AllocateParticleType(config, name);
1088
1089 while(to != P_INVALID)
1090 {
1091 if (to == from)
1092 {
1093 Con_Printf("Assosiation of %s would cause infinate loop\n", name);
1094 return P_INVALID;
1095 }
1096 to = part_type[to].assoc;
1097 }
1098 return orig;
1099 }
1100
P_LoadTexture(part_type_t * ptype,qboolean warn)1101 static void P_LoadTexture(part_type_t *ptype, qboolean warn)
1102 {
1103 if (*ptype->texname)
1104 {
1105 byte *data = NULL;
1106 char filename[MAX_QPATH];
1107 int fwidth=0, fheight=0;
1108 int hunkmark;
1109 char *texname = va("%s%s%s", ptype->texname, ptype->looks.premul?"_premul":"", ptype->looks.nearest?"_nearest":"");
1110
1111 ptype->looks.texture = TexMgr_FindTexture(NULL, texname);
1112 if (!ptype->looks.texture)
1113 {
1114 hunkmark = Hunk_LowMark();
1115 if (!data)
1116 {
1117 q_snprintf (filename, sizeof(filename), "textures/%s", ptype->texname);
1118 data = Image_LoadImage (filename, &fwidth, &fheight);
1119 }
1120 if (!data)
1121 {
1122 q_snprintf (filename, sizeof(filename), "%s", ptype->texname);
1123 data = Image_LoadImage (filename, &fwidth, &fheight);
1124 }
1125
1126 if (data)
1127 {
1128 ptype->looks.texture = TexMgr_LoadImage(NULL, texname, fwidth, fheight, SRC_RGBA, data, filename, 0, (ptype->looks.premul?TEXPREF_PREMULTIPLY:0)|(ptype->looks.nearest?TEXPREF_NEAREST:TEXPREF_LINEAR)|TEXPREF_NOPICMIP|TEXPREF_ALPHA);
1129 }
1130 Hunk_FreeToLowMark(hunkmark);
1131 }
1132 }
1133 else
1134 ptype->looks.texture = 0;
1135
1136 if (!ptype->looks.texture)
1137 {
1138 //the specified texture isn't valid. make something up based upon the particle's type
1139 ptype->s1 = 0;
1140 ptype->t1 = 0;
1141 ptype->s2 = 1;
1142 ptype->t2 = 1;
1143 ptype->randsmax = 1;
1144
1145 #define PARTICLETEXTURESIZE 64
1146 if (ptype->looks.type == PT_SPARK)
1147 {
1148 static gltexture_t *thetex;
1149 if (!thetex)
1150 {
1151 static byte data[4*4*4];
1152 memset(data, 0xff, sizeof(data));
1153 thetex = TexMgr_LoadImage(NULL, "particles/white", 4, 4, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1154 }
1155 ptype->looks.texture = thetex;
1156 }
1157 else if (ptype->looks.type == PT_BEAM) //untextured beams get a single continuous blob
1158 {
1159 static gltexture_t *thetex;
1160 if (!thetex)
1161 {
1162 int y, x;
1163 float dy, d;
1164 static byte data[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4];
1165 memset(data, 0xff, sizeof(data));
1166 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1167 {
1168 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1169 d = 256 * (1 - (dy*dy));
1170 if (d < 0) d = 0;
1171 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1172 {
1173 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (byte) d;
1174 }
1175 }
1176 thetex = TexMgr_LoadImage(NULL, "particles/beamtexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1177 }
1178 ptype->looks.texture = thetex;
1179 }
1180 else if (ptype->looks.type == PT_SPARKFAN) //untextured beams get a single continuous blob
1181 {
1182 static gltexture_t *thetex;
1183 if (!thetex)
1184 {
1185 int y, x;
1186 float dy, dx, d;
1187 static byte data[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4];
1188 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1189 {
1190 dy = y / (PARTICLETEXTURESIZE*0.5f-1);
1191 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1192 {
1193 dx = x / (PARTICLETEXTURESIZE*0.5f-1);
1194 d = 256 * (1 - (dx+dy));
1195 if (d < 0) d = 0;
1196 data[(y*PARTICLETEXTURESIZE+x)*4+0] = (byte) d;
1197 data[(y*PARTICLETEXTURESIZE+x)*4+1] = (byte) d;
1198 data[(y*PARTICLETEXTURESIZE+x)*4+2] = (byte) d;
1199 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (byte) d/2;
1200 }
1201 }
1202 thetex = TexMgr_LoadImage(NULL, "particles/ptritexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1203 }
1204 ptype->looks.texture = thetex;
1205 }
1206 else if (strstr(ptype->texname, "classicparticle"))
1207 {
1208 extern gltexture_t *particletexture1;
1209 ptype->looks.texture = particletexture1;
1210 ptype->s2 = 0.5;
1211 ptype->t2 = 0.5;
1212 }
1213 else if (strstr(ptype->texname, "glow") || strstr(ptype->texname, "ball") || ptype->looks.type == PT_TEXTUREDSPARK) //sparks and special names get a nice circular texture.
1214 {
1215 static gltexture_t *thetex;
1216 if (!thetex)
1217 {
1218 int y, x;
1219 float dy, dx, d;
1220 static byte data[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4];
1221 memset(data, 0xff, sizeof(data));
1222 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1223 {
1224 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1225 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1226 {
1227 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1228 d = 255 * (1 - (dx*dx+dy*dy));
1229 if (d < 0) d = 0;
1230 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (byte) d;
1231 }
1232 }
1233 thetex = TexMgr_LoadImage(NULL, "particles/balltexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1234 }
1235 ptype->looks.texture = thetex;
1236 }
1237 else //anything else gets a fuzzy texture
1238 {
1239 static gltexture_t *thetex;
1240 if (!thetex)
1241 {
1242 int y, x;
1243 static byte exptexture[16][16] =
1244 {
1245 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
1246 {0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0},
1247 {0,0,0,1,1,1,1,1,3,1,1,2,1,0,0,0},
1248 {0,0,0,1,1,1,1,4,4,4,5,4,2,1,1,0},
1249 {0,0,1,1,6,5,5,8,6,8,3,6,3,2,1,0},
1250 {0,0,1,5,6,7,5,6,8,8,8,3,3,1,0,0},
1251 {0,0,0,1,6,8,9,9,9,9,4,6,3,1,0,0},
1252 {0,0,2,1,7,7,9,9,9,9,5,3,1,0,0,0},
1253 {0,0,2,4,6,8,9,9,9,9,8,6,1,0,0,0},
1254 {0,0,2,2,3,5,6,8,9,8,8,4,4,1,0,0},
1255 {0,0,1,2,4,1,8,7,8,8,6,5,4,1,0,0},
1256 {0,1,1,1,7,8,1,6,7,5,4,7,1,0,0,0},
1257 {0,1,2,1,1,5,1,3,4,3,1,1,0,0,0,0},
1258 {0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0},
1259 {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
1260 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
1261 };
1262 static byte data[16*16*4];
1263 for (x=0 ; x<16 ; x++)
1264 {
1265 for (y=0 ; y<16 ; y++)
1266 {
1267 data[(y*16+x)*4+0] = 255;
1268 data[(y*16+x)*4+1] = 255;
1269 data[(y*16+x)*4+2] = 255;
1270 data[(y*16+x)*4+3] = exptexture[x][y]*255/9.0;
1271 }
1272 }
1273 thetex = TexMgr_LoadImage(NULL, "particles/fuzzyparticle", 16, 16, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1274 }
1275 ptype->looks.texture = thetex;
1276 }
1277 }
1278 }
1279
P_ResetToDefaults(part_type_t * ptype)1280 static void P_ResetToDefaults(part_type_t *ptype)
1281 {
1282 particle_t *parts;
1283 part_type_t *torun;
1284 char tnamebuf[sizeof(ptype->name)];
1285 char tconfbuf[sizeof(ptype->config)];
1286
1287 // go with a lazy clear of list.. mark everything as DEAD and let
1288 // the beam rendering handle removing nodes
1289 beamseg_t *beamsegs = ptype->beams;
1290 while (beamsegs)
1291 {
1292 beamsegs->flags |= BS_DEAD;
1293 beamsegs = beamsegs->next;
1294 }
1295
1296 // forget any particles before its wiped
1297 while (ptype->particles)
1298 {
1299 parts = ptype->particles->next;
1300 ptype->particles->next = free_particles;
1301 free_particles = ptype->particles;
1302 ptype->particles = parts;
1303 }
1304
1305 // if we're in the runstate loop through and remove from linked list
1306 if (ptype->state & PS_INRUNLIST)
1307 {
1308 if (part_run_list == ptype)
1309 part_run_list = part_run_list->nexttorun;
1310 else
1311 {
1312 for (torun = part_run_list; torun != NULL; torun = torun->nexttorun)
1313 {
1314 if (torun->nexttorun == ptype)
1315 torun->nexttorun = torun->nexttorun->nexttorun;
1316 }
1317 }
1318 }
1319
1320 //some things need to be preserved before we clear everything.
1321 beamsegs = ptype->beams;
1322 strcpy(tnamebuf, ptype->name);
1323 strcpy(tconfbuf, ptype->config);
1324
1325 //free uneeded info
1326 if (ptype->ramp)
1327 Z_Free(ptype->ramp);
1328 if (ptype->sounds)
1329 Z_Free(ptype->sounds);
1330
1331 //reset everything we're too lazy to specifically set
1332 memset(ptype, 0, sizeof(*ptype));
1333
1334 //now set any non-0 defaults.
1335
1336 ptype->beams = beamsegs;
1337 ptype->rainfrequency = 1;
1338 strcpy(ptype->name, tnamebuf);
1339 strcpy(ptype->config, tconfbuf);
1340 ptype->assoc=P_INVALID;
1341 ptype->inwater = P_INVALID;
1342 ptype->cliptype = P_INVALID;
1343 ptype->emit = P_INVALID;
1344 ptype->fluidmask = FTECONTENTS_FLUID;
1345 ptype->alpha = 1;
1346 ptype->alphachange = 1;
1347 ptype->clipbounce = 0.8;
1348 ptype->clipcount = 1;
1349 ptype->colorindex = -1;
1350 ptype->rotationstartmin = -M_PI; //start with a random angle
1351 ptype->rotationstartrand = M_PI-ptype->rotationstartmin;
1352 ptype->spawnchance = 1;
1353 ptype->dl_time = 0;
1354 VectorSet(ptype->dl_rgb, 1, 1, 1);
1355 ptype->dl_corona_intensity = 0.25;
1356 ptype->dl_corona_scale = 0.5;
1357 VectorSet(ptype->dl_scales, 0, 1, 1);
1358 ptype->looks.stretch = 0.05;
1359
1360 ptype->randsmax = 1;
1361 ptype->s2 = 1;
1362 ptype->t2 = 1;
1363 }
1364
PScript_ReadLine(char * buffer,size_t buffersize,const char * filedata,size_t filesize,size_t * offset)1365 char *PScript_ReadLine(char *buffer, size_t buffersize, const char *filedata, size_t filesize, size_t *offset)
1366 {
1367 const char *start = filedata + *offset;
1368 const char *f = start;
1369 const char *e = filedata+filesize;
1370 if (f >= e)
1371 return NULL; //eof
1372 while (f < e)
1373 {
1374 if (*f++ == '\n')
1375 break;
1376 }
1377
1378 *offset = f-filedata;
1379
1380 buffersize--;
1381 if (buffersize >= (size_t)(f-start))
1382 buffersize = f-start;
1383 memcpy(buffer, start, buffersize);
1384 buffer[buffersize] = 0; //null terminate it
1385
1386 return buffer;
1387 }
1388
1389 //This is the function that loads the effect descriptions.
PScript_ParseParticleEffectFile(const char * config,qboolean part_parseweak,char * context,size_t filesize)1390 void PScript_ParseParticleEffectFile(const char *config, qboolean part_parseweak, char *context, size_t filesize)
1391 {
1392 const char *var, *value;
1393 char *buf;
1394 qboolean settype;
1395 qboolean setalphadelta;
1396 qboolean setbeamlen;
1397
1398 part_type_t *ptype;
1399 int pnum, assoc;
1400 char line[512];
1401 char part_parsenamespace[MAX_QPATH];
1402
1403 byte *palrgba = (byte *)d_8to24table;
1404 size_t offset = 0;
1405
1406 q_strlcpy(part_parsenamespace, config, sizeof(part_parsenamespace));
1407 config = part_parsenamespace;
1408
1409 nexteffect:
1410
1411 if (!PScript_ReadLine(line, sizeof(line), context, filesize, &offset))
1412 return; //eof
1413 reparse:
1414
1415 Cmd_TokenizeString(line);
1416
1417 var = Cmd_Argv(0);
1418
1419
1420 if (!strcmp(var, "r_effect") || !strcmp(var, "r_trail"))
1421 { //add an emit/trail effect to all ents using said model
1422 PScript_AssociateEffect_f();
1423 goto nexteffect;
1424 }
1425 else if (!strcmp(var, "r_partredirect"))
1426 { //add an emit/trail effect to all ents using said model
1427 P_PartRedirect_f();
1428 goto nexteffect;
1429 }
1430 else if (strcmp(var, "r_part"))
1431 {
1432 if (*var)
1433 Con_SafePrintf("Unknown particle command \"%s\"\n", var);
1434 goto nexteffect;
1435 }
1436
1437 settype = false;
1438 setalphadelta = false;
1439 setbeamlen = false;
1440
1441 if (Cmd_Argc()!=2)
1442 {
1443 if (!strcmp(Cmd_Argv(1), "namespace"))
1444 {
1445 q_strlcpy(part_parsenamespace, Cmd_Argv(2), sizeof(part_parsenamespace));
1446 if (Cmd_Argc() >= 4)
1447 part_parseweak = atoi(Cmd_Argv(3));
1448 goto nexteffect;
1449 }
1450 Con_Printf("No name for particle effect\n");
1451 goto nexteffect;
1452 }
1453
1454 buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1455 if (!buf)
1456 return; //eof
1457 while (*buf && *buf <= ' ')
1458 buf++; //no whitespace please.
1459 if (*buf != '{')
1460 {
1461 Con_Printf("This is a multiline command and should be used within config files\n");
1462 goto reparse;
1463 }
1464
1465 var = Cmd_Argv(1);
1466 if (*var == '+')
1467 ptype = P_GetParticleType(config, var+1);
1468 else
1469 ptype = P_GetParticleType(config, var);
1470
1471 //'weak' configs do not replace 'strong' configs
1472 //we allow weak to replace weak as a solution to the +assoc chain thing (to add, we effectively need to 'replace').
1473 if ((part_parseweak && ptype->loaded==2))
1474 {
1475 int depth = 1;
1476 while(1)
1477 {
1478 buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1479 if (!buf)
1480 return;
1481
1482 while (*buf && *buf <= ' ')
1483 buf++; //no whitespace please.
1484 if (*buf == '{')
1485 depth++;
1486 else if (*buf == '}')
1487 {
1488 if (--depth == 0)
1489 break;
1490 }
1491 }
1492 goto nexteffect;
1493 }
1494
1495 if (*var == '+')
1496 {
1497 if (ptype->loaded)
1498 {
1499 int i, parenttype;
1500 char newname[256];
1501 for (i = 0; i < 64; i++)
1502 {
1503 parenttype = ptype - part_type;
1504 q_snprintf(newname, sizeof(newname), "+%i%s", i, ptype->name);
1505 ptype = P_GetParticleType(config, newname);
1506 if (!ptype->loaded)
1507 {
1508 if (part_type[parenttype].assoc != P_INVALID)
1509 Con_Printf("warning: assoc on particle chain %s overridden\n", var+1);
1510 part_type[parenttype].assoc = ptype - part_type;
1511 break;
1512 }
1513 }
1514 if (i == 64)
1515 {
1516 Con_Printf("Too many duplicate names, gave up\n");
1517 return;
1518 }
1519 }
1520 }
1521 else
1522 {
1523 if (ptype->loaded)
1524 {
1525 assoc = ptype->assoc;
1526 while (assoc != P_INVALID && assoc < numparticletypes)
1527 {
1528 if (*part_type[assoc].name == '+')
1529 {
1530 part_type[assoc].loaded = false;
1531 assoc = part_type[assoc].assoc;
1532 }
1533 else
1534 break;
1535 }
1536 }
1537 }
1538 if (!ptype)
1539 {
1540 Con_Printf("Bad name\n");
1541 return;
1542 }
1543
1544 pnum = ptype-part_type;
1545
1546 P_ResetToDefaults(ptype);
1547
1548 while(1)
1549 {
1550 buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1551 if (!buf)
1552 {
1553 Con_Printf("Unexpected end of buffer with effect %s\n", ptype->name);
1554 return;
1555 }
1556 skipread:
1557 while (*buf && *buf <= ' ')
1558 buf++; //no whitespace please.
1559 if (*buf == '}')
1560 break;
1561
1562 Cmd_TokenizeString(buf);
1563 var = Cmd_Argv(0);
1564 value = Cmd_Argv(1);
1565
1566 // TODO: switch this mess to some sort of binary tree to increase parse speed
1567 if (!strcmp(var, "shader"))
1568 {
1569 q_strlcpy(ptype->texname, ptype->name, sizeof(ptype->texname));
1570
1571 buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1572 if (!buf)
1573 continue;
1574 while (*buf && *buf <= ' ')
1575 buf++; //no leading whitespace please.
1576 if (*buf == '{')
1577 {
1578 int nest = 1;
1579 char *str = Z_Malloc(3);
1580 int slen = 2;
1581 str[0] = '{';
1582 str[1] = '\n';
1583 str[2] = 0;
1584 while(nest)
1585 {
1586 buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1587 if (!buf)
1588 {
1589 Con_Printf("Unexpected end of buffer with effect %s\n", ptype->name);
1590 break;
1591 }
1592 while (*buf && *buf <= ' ')
1593 buf++; //no leading whitespace please.
1594 if (*buf == '}')
1595 --nest;
1596 if (*buf == '{')
1597 nest++;
1598 str = Z_Realloc(str, slen + strlen(buf) + 2);
1599 strcpy(str + slen, buf);
1600 slen += strlen(str + slen);
1601 str[slen++] = '\n';
1602 }
1603 str[slen] = 0;
1604 Z_Free(str);
1605 }
1606 else
1607 goto skipread;
1608 }
1609 else if (!strcmp(var, "texture") || !strcmp(var, "linear_texture") || !strcmp(var, "nearest_texture") || !strcmp(var, "nearesttexture"))
1610 {
1611 q_strlcpy(ptype->texname, value, sizeof(ptype->texname));
1612 ptype->looks.nearest = !strncmp(var, "nearest", 7);
1613 }
1614 else if (!strcmp(var, "tcoords"))
1615 {
1616 float tscale;
1617
1618 tscale = atof(Cmd_Argv(5));
1619 if (tscale <= 0)
1620 tscale = 1;
1621
1622 ptype->s1 = atof(value)/tscale;
1623 ptype->t1 = atof(Cmd_Argv(2))/tscale;
1624 ptype->s2 = atof(Cmd_Argv(3))/tscale;
1625 ptype->t2 = atof(Cmd_Argv(4))/tscale;
1626
1627 ptype->randsmax = atoi(Cmd_Argv(6));
1628 if (Cmd_Argc()>7)
1629 ptype->texsstride = atof(Cmd_Argv(7));/*FIXME: divide-by-tscale missing */
1630 else
1631 ptype->texsstride = 1/tscale;
1632
1633 if (ptype->randsmax < 1 || ptype->texsstride == 0)
1634 ptype->randsmax = 1;
1635 }
1636 else if (!strcmp(var, "atlas"))
1637 { //atlas countineachaxis first [last]
1638 int dims;
1639 int i;
1640 int m;
1641
1642 dims = atof(Cmd_Argv(1));
1643 i = atoi(Cmd_Argv(2));
1644 m = atoi(Cmd_Argv(3));
1645 if (dims < 1)
1646 dims = 1;
1647
1648 if (m > (m/dims)*dims+dims-1)
1649 {
1650 m = (m/dims)*dims+dims-1;
1651 Con_Printf("effect %s wraps across an atlased line\n", ptype->name);
1652 }
1653 if (m < i)
1654 m = i;
1655
1656 ptype->s1 = 1.0/dims * (i%dims);
1657 ptype->s2 = 1.0/dims * (1+(i%dims));
1658 ptype->t1 = 1.0/dims * (i/dims);
1659 ptype->t2 = 1.0/dims * (1+(i/dims));
1660
1661 ptype->randsmax = m-i;
1662 ptype->texsstride = ptype->s2-ptype->s1;
1663
1664 //its modulo
1665 ptype->randsmax++;
1666 }
1667 else if (!strcmp(var, "rotation"))
1668 {
1669 ptype->rotationstartmin = atof(value)*M_PI/180;
1670 if (Cmd_Argc()>2)
1671 ptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;
1672 else
1673 ptype->rotationstartrand = 0;
1674
1675 ptype->rotationmin = atof(Cmd_Argv(3))*M_PI/180;
1676 if (Cmd_Argc()>4)
1677 ptype->rotationrand = atof(Cmd_Argv(4))*M_PI/180-ptype->rotationmin;
1678 else
1679 ptype->rotationrand = 0;
1680 }
1681 else if (!strcmp(var, "rotationstart"))
1682 {
1683 ptype->rotationstartmin = atof(value)*M_PI/180;
1684 if (Cmd_Argc()>2)
1685 ptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;
1686 else
1687 ptype->rotationstartrand = 0;
1688 }
1689 else if (!strcmp(var, "rotationspeed"))
1690 {
1691 ptype->rotationmin = atof(value)*M_PI/180;
1692 if (Cmd_Argc()>2)
1693 ptype->rotationrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationmin;
1694 else
1695 ptype->rotationrand = 0;
1696 }
1697 else if (!strcmp(var, "beamtexstep"))
1698 {
1699 ptype->rotationstartmin = 1/atof(value);
1700 ptype->rotationstartrand = 0;
1701 setbeamlen = true;
1702 }
1703 else if (!strcmp(var, "beamtexspeed"))
1704 {
1705 ptype->rotationmin = atof(value);
1706 }
1707 else if (!strcmp(var, "scale"))
1708 {
1709 ptype->scale = atof(value);
1710 if (Cmd_Argc()>2)
1711 ptype->scalerand = atof(Cmd_Argv(2)) - ptype->scale;
1712 }
1713 else if (!strcmp(var, "scalerand"))
1714 ptype->scalerand = atof(value);
1715
1716 else if (!strcmp(var, "scalefactor"))
1717 ptype->looks.scalefactor = atof(value);
1718 else if (!strcmp(var, "scaledelta"))
1719 ptype->scaledelta = atof(value);
1720 else if (!strcmp(var, "stretchfactor")) //affects sparks
1721 {
1722 ptype->looks.stretch = atof(value);
1723 ptype->looks.minstretch = (Cmd_Argc()>2)?atof(Cmd_Argv(2)):0;
1724 }
1725
1726 else if (!strcmp(var, "step"))
1727 {
1728 ptype->countspacing = atof(value);
1729 ptype->count = 1/atof(value);
1730 if (Cmd_Argc()>2)
1731 ptype->countrand = 1/atof(Cmd_Argv(2));
1732 if (Cmd_Argc()>3)
1733 ptype->countextra = atof(Cmd_Argv(3));
1734 }
1735 else if (!strcmp(var, "count"))
1736 {
1737 ptype->countspacing = 0;
1738 ptype->count = atof(value);
1739 if (Cmd_Argc()>2)
1740 ptype->countrand = atof(Cmd_Argv(2));
1741 if (Cmd_Argc()>3)
1742 ptype->countextra = atof(Cmd_Argv(3));
1743 }
1744 else if (!strcmp(var, "rainfrequency"))
1745 { //multiplier to ramp up the effect or whatever (without affecting spawn patterns).
1746 ptype->rainfrequency = atof(value);
1747 }
1748
1749 else if (!strcmp(var, "alpha"))
1750 ptype->alpha = atof(value);
1751 else if (!strcmp(var, "alpharand"))
1752 ptype->alpharand = atof(value);
1753 #ifndef NOLEGACY
1754 else if (!strcmp(var, "alphachange"))
1755 {
1756 Con_DPrintf("%s.%s: alphachange is deprecated, use alphadelta\n", ptype->config, ptype->name);
1757 ptype->alphachange = atof(value);
1758 }
1759 #endif
1760 else if (!strcmp(var, "alphadelta"))
1761 {
1762 ptype->alphachange = atof(value);
1763 setalphadelta = true;
1764 }
1765 else if (!strcmp(var, "die"))
1766 {
1767 ptype->die = atof(value);
1768 if (Cmd_Argc()>2)
1769 {
1770 float mn=ptype->die,mx=atof(Cmd_Argv(2));
1771 if (mn > mx)
1772 {
1773 mn = mx;
1774 mx = ptype->die;
1775 }
1776 ptype->die = mx;
1777 ptype->randdie = mx-mn;
1778 }
1779 }
1780 #ifndef NOLEGACY
1781 else if (!strcmp(var, "diesubrand"))
1782 {
1783 Con_DPrintf("%s.%s: diesubrand is deprecated, use die with two arguments\n", ptype->config, ptype->name);
1784 ptype->randdie = atof(value);
1785 }
1786 #endif
1787
1788 else if (!strcmp(var, "randomvel"))
1789 { //shortcut for velwrand (and velbias for z bias)
1790 ptype->velbias[0] = ptype->velbias[1] = 0;
1791 ptype->velwrand[0] = ptype->velwrand[1] = atof(value);
1792 if (Cmd_Argc()>3)
1793 {
1794 ptype->velbias[2] = atof(Cmd_Argv(2));
1795 ptype->velwrand[2] = atof(Cmd_Argv(3));
1796 ptype->velwrand[2] -= ptype->velbias[2]; /*make vert be the total range*/
1797 ptype->velwrand[2] /= 2; /*vert is actually +/- 1, not 0 to 1, so rescale it*/
1798 ptype->velbias[2] += ptype->velwrand[2]; /*and bias must be centered to the range*/
1799 }
1800 else if (Cmd_Argc()>2)
1801 {
1802 ptype->velwrand[2] = atof(Cmd_Argv(2));
1803 ptype->velbias[2] = 0;
1804 }
1805 else
1806 {
1807 ptype->velwrand[2] = ptype->velwrand[0];
1808 ptype->velbias[2] = 0;
1809 }
1810 }
1811 else if (!strcmp(var, "veladd"))
1812 {
1813 ptype->veladd = atof(value);
1814 ptype->randomveladd = 0;
1815 if (Cmd_Argc()>2)
1816 ptype->randomveladd = atof(Cmd_Argv(2)) - ptype->veladd;
1817 }
1818 else if (!strcmp(var, "orgadd"))
1819 {
1820 ptype->orgadd = atof(value);
1821 ptype->randomorgadd = 0;
1822 if (Cmd_Argc()>2)
1823 ptype->randomorgadd = atof(Cmd_Argv(2)) - ptype->orgadd;
1824 }
1825
1826 else if (!strcmp(var, "orgbias"))
1827 {
1828 ptype->orgbias[0] = atof(value);
1829 ptype->orgbias[1] = atof(Cmd_Argv(2));
1830 ptype->orgbias[2] = atof(Cmd_Argv(3));
1831 }
1832 else if (!strcmp(var, "orgwrand"))
1833 {
1834 ptype->orgwrand[0] = atof(value);
1835 ptype->orgwrand[1] = atof(Cmd_Argv(2));
1836 ptype->orgwrand[2] = atof(Cmd_Argv(3));
1837 }
1838
1839 else if (!strcmp(var, "velbias"))
1840 {
1841 ptype->velbias[0] = atof(value);
1842 ptype->velbias[1] = atof(Cmd_Argv(2));
1843 ptype->velbias[2] = atof(Cmd_Argv(3));
1844 }
1845 else if (!strcmp(var, "velwrand"))
1846 {
1847 ptype->velwrand[0] = atof(value);
1848 ptype->velwrand[1] = atof(Cmd_Argv(2));
1849 ptype->velwrand[2] = atof(Cmd_Argv(3));
1850 }
1851
1852 else if (!strcmp(var, "friction"))
1853 {
1854 ptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(value);
1855
1856 if (Cmd_Argc()>3)
1857 {
1858 ptype->friction[2] = atof(Cmd_Argv(3));
1859 ptype->friction[1] = atof(Cmd_Argv(2));
1860 }
1861 else if (Cmd_Argc()>2)
1862 {
1863 ptype->friction[2] = atof(Cmd_Argv(2));
1864 }
1865 }
1866 else if (!strcmp(var, "gravity"))
1867 ptype->gravity = atof(value);
1868 else if (!strcmp(var, "flurry"))
1869 ptype->flurry = atof(value);
1870
1871 else if (!strcmp(var, "assoc"))
1872 {
1873 assoc = CheckAssosiation(config, value, pnum); //careful - this can realloc all the particle types
1874 ptype = &part_type[pnum];
1875 ptype->assoc = assoc;
1876 }
1877 else if (!strcmp(var, "inwater"))
1878 {
1879 // the underwater effect switch should only occur for
1880 // 1 level so the standard assoc check works
1881 assoc = CheckAssosiation(config, value, pnum);
1882 ptype = &part_type[pnum];
1883 ptype->inwater = assoc;
1884 }
1885 else if (!strcmp(var, "underwater"))
1886 {
1887 ptype->flags |= PT_TRUNDERWATER;
1888
1889 parsefluid:
1890 if ((ptype->flags & (PT_TRUNDERWATER|PT_TROVERWATER)) == (PT_TRUNDERWATER|PT_TROVERWATER))
1891 {
1892 ptype->flags &= ~PT_TRUNDERWATER;
1893 Con_Printf("%s.%s: both over and under water\n", ptype->config, ptype->name);
1894 }
1895 if (Cmd_Argc() == 1)
1896 ptype->fluidmask = FTECONTENTS_FLUID;
1897 else
1898 {
1899 int i = Cmd_Argc();
1900 ptype->fluidmask = 0;
1901 while (i --> 1)
1902 {
1903 const char *value_i = Cmd_Argv(i);
1904 if (!strcmp(value_i, "water"))
1905 ptype->fluidmask |= FTECONTENTS_WATER;
1906 else if (!strcmp(value_i, "slime"))
1907 ptype->fluidmask |= FTECONTENTS_SLIME;
1908 else if (!strcmp(value_i, "lava"))
1909 ptype->fluidmask |= FTECONTENTS_LAVA;
1910 else if (!strcmp(value_i, "sky"))
1911 ptype->fluidmask |= FTECONTENTS_SKY;
1912 else if (!strcmp(value_i, "fluid"))
1913 ptype->fluidmask |= FTECONTENTS_FLUID;
1914 else if (!strcmp(value_i, "solid"))
1915 ptype->fluidmask |= FTECONTENTS_SOLID;
1916 else if (!strcmp(value_i, "playerclip"))
1917 ptype->fluidmask |= FTECONTENTS_PLAYERCLIP;
1918 else if (!strcmp(value_i, "none"))
1919 ptype->fluidmask |= 0;
1920 else
1921 Con_Printf("%s.%s: unknown contents: %s\n", ptype->config, ptype->name, value_i);
1922 }
1923 }
1924 }
1925 else if (!strcmp(var, "notunderwater"))
1926 {
1927 ptype->flags |= PT_TROVERWATER;
1928 goto parsefluid;
1929 }
1930 else if (!strcmp(var, "model"))
1931 {
1932 Con_DPrintf("%s.%s: model particles are not supported in this build\n", ptype->config, ptype->name);
1933 }
1934 else if (!strcmp(var, "sound"))
1935 {
1936 const char *e;
1937 ptype->sounds = Z_Realloc(ptype->sounds, sizeof(partsounds_t)*(ptype->numsounds+1));
1938 q_strlcpy(ptype->sounds[ptype->numsounds].name, Cmd_Argv(1), sizeof(ptype->sounds[ptype->numsounds].name));
1939 if (*ptype->sounds[ptype->numsounds].name)
1940 S_PrecacheSound(ptype->sounds[ptype->numsounds].name);
1941
1942 ptype->sounds[ptype->numsounds].vol = 1;
1943 ptype->sounds[ptype->numsounds].atten = 1;
1944 ptype->sounds[ptype->numsounds].pitch = 100;
1945 ptype->sounds[ptype->numsounds].delay = 0;
1946 ptype->sounds[ptype->numsounds].weight = 0;
1947
1948 strtoul(Cmd_Argv(2), (char**)&e, 0);
1949 while(*e == ' ' || *e == '\t')
1950 e++;
1951 if (*e)
1952 {
1953 int p;
1954 for(p = 2; p < Cmd_Argc(); p++)
1955 {
1956 e = Cmd_Argv(p);
1957
1958 if (!q_strncasecmp(e, "vol=", 4) || !q_strncasecmp(e, "volume=", 7))
1959 ptype->sounds[ptype->numsounds].vol = atof(strchr(e, '=')+1);
1960 else if (!q_strncasecmp(e, "attn=", 5) || !q_strncasecmp(e, "atten=", 6) || !q_strncasecmp(e, "attenuation=", 12))
1961 {
1962 e = strchr(e, '=')+1;
1963 if (!strcmp(e, "none"))
1964 ptype->sounds[ptype->numsounds].atten = 0;
1965 else if (!strcmp(e, "normal"))
1966 ptype->sounds[ptype->numsounds].atten = 1;
1967 else
1968 ptype->sounds[ptype->numsounds].atten = atof(e);
1969 }
1970 else if (!q_strncasecmp(e, "pitch=", 6))
1971 ptype->sounds[ptype->numsounds].pitch = atof(strchr(e, '=')+1);
1972 else if (!q_strncasecmp(e, "delay=", 6))
1973 ptype->sounds[ptype->numsounds].delay = atof(strchr(e, '=')+1);
1974 else if (!q_strncasecmp(e, "weight=", 7))
1975 ptype->sounds[ptype->numsounds].weight = atof(strchr(e, '=')+1);
1976 else
1977 Con_Printf("Bad named argument: %s\n", e);
1978 }
1979 }
1980 else
1981 {
1982 ptype->sounds[ptype->numsounds].vol = atof(Cmd_Argv(2));
1983 if (!ptype->sounds[ptype->numsounds].vol)
1984 ptype->sounds[ptype->numsounds].vol = 1;
1985 ptype->sounds[ptype->numsounds].atten = atof(Cmd_Argv(3));
1986 if (!ptype->sounds[ptype->numsounds].atten)
1987 ptype->sounds[ptype->numsounds].atten = 1;
1988 ptype->sounds[ptype->numsounds].pitch = atof(Cmd_Argv(4));
1989 if (!ptype->sounds[ptype->numsounds].pitch)
1990 ptype->sounds[ptype->numsounds].pitch = 100;
1991 ptype->sounds[ptype->numsounds].delay = atof(Cmd_Argv(5));
1992 if (!ptype->sounds[ptype->numsounds].delay)
1993 ptype->sounds[ptype->numsounds].delay = 0;
1994 ptype->sounds[ptype->numsounds].weight = atof(Cmd_Argv(6));
1995 }
1996 if (!ptype->sounds[ptype->numsounds].weight)
1997 ptype->sounds[ptype->numsounds].weight = 1;
1998 ptype->numsounds++;
1999 }
2000 else if (!strcmp(var, "colorindex"))
2001 {
2002 if (Cmd_Argc()>2)
2003 ptype->colorrand = strtoul(Cmd_Argv(2), NULL, 0);
2004 ptype->colorindex = strtoul(value, NULL, 0);
2005 }
2006 else if (!strcmp(var, "colorrand"))
2007 ptype->colorrand = atoi(value); // now obsolete
2008 else if (!strcmp(var, "citracer"))
2009 ptype->flags |= PT_CITRACER;
2010
2011 else if (!strcmp(var, "red"))
2012 ptype->rgb[0] = atof(value)/255;
2013 else if (!strcmp(var, "green"))
2014 ptype->rgb[1] = atof(value)/255;
2015 else if (!strcmp(var, "blue"))
2016 ptype->rgb[2] = atof(value)/255;
2017 else if (!strcmp(var, "rgb"))
2018 { //byte version
2019 ptype->rgb[0] = ptype->rgb[1] = ptype->rgb[2] = atof(value)/255;
2020 if (Cmd_Argc()>3)
2021 {
2022 ptype->rgb[1] = atof(Cmd_Argv(2))/255;
2023 ptype->rgb[2] = atof(Cmd_Argv(3))/255;
2024 }
2025 }
2026 else if (!strcmp(var, "rgbf"))
2027 { //float version
2028 ptype->rgb[0] = ptype->rgb[1] = ptype->rgb[2] = atof(value);
2029 if (Cmd_Argc()>3)
2030 {
2031 ptype->rgb[1] = atof(Cmd_Argv(2));
2032 ptype->rgb[2] = atof(Cmd_Argv(3));
2033 }
2034 }
2035
2036 else if (!strcmp(var, "reddelta"))
2037 {
2038 ptype->rgbchange[0] = atof(value)/255;
2039 if (!ptype->rgbchangetime)
2040 ptype->rgbchangetime = ptype->die;
2041 }
2042 else if (!strcmp(var, "greendelta"))
2043 {
2044 ptype->rgbchange[1] = atof(value)/255;
2045 if (!ptype->rgbchangetime)
2046 ptype->rgbchangetime = ptype->die;
2047 }
2048 else if (!strcmp(var, "bluedelta"))
2049 {
2050 ptype->rgbchange[2] = atof(value)/255;
2051 if (!ptype->rgbchangetime)
2052 ptype->rgbchangetime = ptype->die;
2053 }
2054 else if (!strcmp(var, "rgbdelta"))
2055 { //byte version
2056 ptype->rgbchange[0] = ptype->rgbchange[1] = ptype->rgbchange[2] = atof(value)/255;
2057 if (Cmd_Argc()>3)
2058 {
2059 ptype->rgbchange[1] = atof(Cmd_Argv(2))/255;
2060 ptype->rgbchange[2] = atof(Cmd_Argv(3))/255;
2061 }
2062 if (!ptype->rgbchangetime)
2063 ptype->rgbchangetime = ptype->die;
2064 }
2065 else if (!strcmp(var, "rgbdeltaf"))
2066 { //float version
2067 ptype->rgbchange[0] = ptype->rgbchange[1] = ptype->rgbchange[2] = atof(value);
2068 if (Cmd_Argc()>3)
2069 {
2070 ptype->rgbchange[1] = atof(Cmd_Argv(2));
2071 ptype->rgbchange[2] = atof(Cmd_Argv(3));
2072 }
2073 if (!ptype->rgbchangetime)
2074 ptype->rgbchangetime = ptype->die;
2075 }
2076 else if (!strcmp(var, "rgbdeltatime"))
2077 ptype->rgbchangetime = atof(value);
2078
2079 else if (!strcmp(var, "redrand"))
2080 ptype->rgbrand[0] = atof(value)/255;
2081 else if (!strcmp(var, "greenrand"))
2082 ptype->rgbrand[1] = atof(value)/255;
2083 else if (!strcmp(var, "bluerand"))
2084 ptype->rgbrand[2] = atof(value)/255;
2085 else if (!strcmp(var, "rgbrand"))
2086 { //byte version
2087 ptype->rgbrand[0] = ptype->rgbrand[1] = ptype->rgbrand[2] = atof(value)/255;
2088 if (Cmd_Argc()>3)
2089 {
2090 ptype->rgbrand[1] = atof(Cmd_Argv(2))/255;
2091 ptype->rgbrand[2] = atof(Cmd_Argv(3))/255;
2092 }
2093 }
2094 else if (!strcmp(var, "rgbrandf"))
2095 { //float version
2096 ptype->rgbrand[0] = ptype->rgbrand[1] = ptype->rgbrand[2] = atof(value);
2097 if (Cmd_Argc()>3)
2098 {
2099 ptype->rgbrand[1] = atof(Cmd_Argv(2));
2100 ptype->rgbrand[2] = atof(Cmd_Argv(3));
2101 }
2102 }
2103
2104 else if (!strcmp(var, "rgbrandsync"))
2105 {
2106 ptype->rgbrandsync[0] = ptype->rgbrandsync[1] = ptype->rgbrandsync[2] = atof(value);
2107 if (Cmd_Argc()>3)
2108 {
2109 ptype->rgbrandsync[1] = atof(Cmd_Argv(2));
2110 ptype->rgbrandsync[2] = atof(Cmd_Argv(3));
2111 }
2112 }
2113 else if (!strcmp(var, "redrandsync"))
2114 ptype->rgbrandsync[0] = atof(value);
2115 else if (!strcmp(var, "greenrandsync"))
2116 ptype->rgbrandsync[1] = atof(value);
2117 else if (!strcmp(var, "bluerandsync"))
2118 ptype->rgbrandsync[2] = atof(value);
2119
2120 else if (!strcmp(var, "stains"))
2121 ptype->stainonimpact = atof(value);
2122 else if (!strcmp(var, "blend"))
2123 {
2124 //small note: use premultiplied alpha where possible. this reduces the required state switches.
2125 ptype->looks.premul = false;
2126 if (!strcmp(value, "adda") || !strcmp(value, "add"))
2127 ptype->looks.blendmode = BM_ADDA;
2128 else if (!strcmp(value, "addc"))
2129 ptype->looks.blendmode = BM_ADDC;
2130 else if (!strcmp(value, "subtract"))
2131 ptype->looks.blendmode = BM_SUBTRACT;
2132 else if (!strcmp(value, "invmoda") || !strcmp(value, "invmod"))
2133 ptype->looks.blendmode = BM_INVMODA;
2134 else if (!strcmp(value, "invmodc"))
2135 ptype->looks.blendmode = BM_INVMODC;
2136 else if (!strcmp(value, "blendcolour") || !strcmp(value, "blendcolor"))
2137 ptype->looks.blendmode = BM_BLENDCOLOUR;
2138 else if (!strcmp(value, "blendalpha") || !strcmp(value, "blend"))
2139 ptype->looks.blendmode = BM_BLEND;
2140 else if (!strcmp(value, "premul_subtract"))
2141 {
2142 ptype->looks.premul = 1;
2143 ptype->looks.blendmode = BM_INVMODC;
2144 }
2145 else if (!strcmp(value, "premul_add"))
2146 {
2147 ptype->looks.premul = 2;
2148 ptype->looks.blendmode = BM_PREMUL;
2149 }
2150 else if (!strcmp(value, "premul_blend"))
2151 {
2152 ptype->looks.premul = 1;
2153 ptype->looks.blendmode = BM_PREMUL;
2154 }
2155 else
2156 {
2157 Con_DPrintf("%s.%s: uses unknown blend type '%s', assuming legacy 'blendalpha'\n", ptype->config, ptype->name, value);
2158 ptype->looks.blendmode = BM_BLEND; //fallback
2159 }
2160 }
2161 else if (!strcmp(var, "spawnmode"))
2162 {
2163 if (!strcmp(value, "circle"))
2164 ptype->spawnmode = SM_CIRCLE;
2165 else if (!strcmp(value, "ball"))
2166 ptype->spawnmode = SM_BALL;
2167 else if (!strcmp(value, "spiral"))
2168 ptype->spawnmode = SM_SPIRAL;
2169 else if (!strcmp(value, "tracer"))
2170 ptype->spawnmode = SM_TRACER;
2171 else if (!strcmp(value, "telebox"))
2172 ptype->spawnmode = SM_TELEBOX;
2173 else if (!strcmp(value, "lavasplash"))
2174 ptype->spawnmode = SM_LAVASPLASH;
2175 else if (!strcmp(value, "uniformcircle"))
2176 ptype->spawnmode = SM_UNICIRCLE;
2177 else if (!strcmp(value, "syncfield"))
2178 {
2179 ptype->spawnmode = SM_FIELD;
2180 #ifndef NOLEGACY
2181 ptype->spawnparam1 = 16;
2182 ptype->spawnparam2 = 0;
2183 #endif
2184 }
2185 else if (!strcmp(value, "distball"))
2186 ptype->spawnmode = SM_DISTBALL;
2187 else if (!strcmp(value, "box"))
2188 ptype->spawnmode = SM_BOX;
2189 else
2190 {
2191 Con_DPrintf("%s.%s: uses unknown spawn type '%s', assuming 'box'\n", ptype->config, ptype->name, value);
2192 ptype->spawnmode = SM_BOX;
2193 }
2194
2195 if (Cmd_Argc()>2)
2196 {
2197 if (Cmd_Argc()>3)
2198 ptype->spawnparam2 = atof(Cmd_Argv(3));
2199 ptype->spawnparam1 = atof(Cmd_Argv(2));
2200 }
2201 }
2202 else if (!strcmp(var, "type"))
2203 {
2204 if (!strcmp(value, "beam"))
2205 ptype->looks.type = PT_BEAM;
2206 else if (!strcmp(value, "spark") || !strcmp(value, "linespark"))
2207 ptype->looks.type = PT_SPARK;
2208 else if (!strcmp(value, "sparkfan") || !strcmp(value, "trianglefan"))
2209 ptype->looks.type = PT_SPARKFAN;
2210 else if (!strcmp(value, "texturedspark"))
2211 ptype->looks.type = PT_TEXTUREDSPARK;
2212 else if (!strcmp(value, "decal") || !strcmp(value, "cdecal"))
2213 ptype->looks.type = PT_CDECAL;
2214 else if (!strcmp(value, "udecal"))
2215 ptype->looks.type = PT_UDECAL;
2216 else if (!strcmp(value, "normal"))
2217 ptype->looks.type = PT_NORMAL;
2218 else
2219 {
2220 Con_DPrintf("%s.%s: uses unknown render type '%s', assuming 'normal'\n", ptype->config, ptype->name, value);
2221 ptype->looks.type = PT_NORMAL; //fallback
2222 }
2223 settype = true;
2224 }
2225 else if (!strcmp(var, "clippeddecal")) //mask, match
2226 {
2227 if (Cmd_Argc()>=2)
2228 {//decal only appears where: (surfflags&mask)==match
2229 ptype->surfflagmatch = ptype->surfflagmask = strtoul(Cmd_Argv(1), NULL, 0);
2230 if (Cmd_Argc()>=3)
2231 ptype->surfflagmatch = strtoul(Cmd_Argv(2), NULL, 0);
2232 }
2233 ptype->looks.type = PT_CDECAL;
2234 settype = true;
2235 }
2236 #ifndef NOLEGACY
2237 else if (!strcmp(var, "isbeam"))
2238 {
2239 Con_DPrintf("%s.%s: isbeam is deprecated, use type beam\n", ptype->config, ptype->name);
2240 ptype->looks.type = PT_BEAM;
2241 settype = true;
2242 }
2243 #endif
2244 else if (!strcmp(var, "spawntime"))
2245 ptype->spawntime = atof(value);
2246 else if (!strcmp(var, "spawnchance"))
2247 ptype->spawnchance = atof(value);
2248 else if (!strcmp(var, "cliptype"))
2249 {
2250 assoc = P_AllocateParticleType(config, value);//careful - this can realloc all the particle types
2251 ptype = &part_type[pnum];
2252 ptype->cliptype = assoc;
2253 }
2254 else if (!strcmp(var, "clipcount"))
2255 ptype->clipcount = atof(value);
2256 else if (!strcmp(var, "clipbounce"))
2257 {
2258 ptype->clipbounce = atof(value);
2259 if (ptype->clipbounce < 0 && ptype->cliptype == P_INVALID)
2260 ptype->cliptype = pnum;
2261 }
2262 else if (!strcmp(var, "bounce"))
2263 {
2264 ptype->cliptype = pnum;
2265 ptype->clipbounce = atof(value);
2266 }
2267
2268 else if (!strcmp(var, "emit"))
2269 {
2270 assoc = P_AllocateParticleType(config, value);//careful - this can realloc all the particle types
2271 ptype = &part_type[pnum];
2272 ptype->emit = assoc;
2273 }
2274 else if (!strcmp(var, "emitinterval"))
2275 ptype->emittime = atof(value);
2276 else if (!strcmp(var, "emitintervalrand"))
2277 ptype->emitrand = atof(value);
2278 else if (!strcmp(var, "emitstart"))
2279 ptype->emitstart = atof(value);
2280
2281 #ifndef NOLEGACY
2282 // old names
2283 else if (!strcmp(var, "areaspread"))
2284 {
2285 Con_DPrintf("%s.%s: areaspread is deprecated, use spawnorg\n", ptype->config, ptype->name);
2286 ptype->areaspread = atof(value);
2287 }
2288 else if (!strcmp(var, "areaspreadvert"))
2289 {
2290 Con_DPrintf("%s.%s: areaspreadvert is deprecated, use spawnorg\n", ptype->config, ptype->name);
2291 ptype->areaspreadvert = atof(value);
2292 }
2293 else if (!strcmp(var, "offsetspread"))
2294 {
2295 Con_DPrintf("%s.%s: offsetspread is deprecated, use spawnvel\n", ptype->config, ptype->name);
2296 ptype->spawnvel = atof(value);
2297 }
2298 else if (!strcmp(var, "offsetspreadvert"))
2299 {
2300 Con_DPrintf("%s.%s: offsetspreadvert is deprecated, use spawnvel\n", ptype->config, ptype->name);
2301 ptype->spawnvelvert = atof(value);
2302 }
2303 #endif
2304
2305 // current names
2306 else if (!strcmp(var, "spawnorg"))
2307 {
2308 ptype->areaspreadvert = ptype->areaspread = atof(value);
2309
2310 if (Cmd_Argc()>2)
2311 ptype->areaspreadvert = atof(Cmd_Argv(2));
2312 }
2313 else if (!strcmp(var, "spawnvel"))
2314 {
2315 ptype->spawnvelvert = ptype->spawnvel = atof(value);
2316
2317 if (Cmd_Argc()>2)
2318 ptype->spawnvelvert = atof(Cmd_Argv(2));
2319 }
2320
2321 #ifndef NOLEGACY
2322 // spawn mode param fields
2323 else if (!strcmp(var, "spawnparam1"))
2324 {
2325 ptype->spawnparam1 = atof(value);
2326 Con_DPrintf("%s.%s: 'spawnparam1' is deprecated, use 'spawnmode foo X'\n", ptype->config, ptype->name);
2327 }
2328 else if (!strcmp(var, "spawnparam2"))
2329 {
2330 ptype->spawnparam2 = atof(value);
2331 Con_DPrintf("%s.%s: 'spawnparam2' is deprecated, use 'spawnmode foo X Y'\n", ptype->config, ptype->name);
2332 }
2333 /* else if (!strcmp(var, "spawnparam3"))
2334 ptype->spawnparam3 = atof(value); */
2335 else if (!strcmp(var, "up"))
2336 {
2337 ptype->orgbias[2] = atof(value);
2338 Con_DPrintf("%s.%s: up is deprecated, use orgbias 0 0 Z\n", ptype->config, ptype->name);
2339 }
2340 #endif
2341
2342 else if (!strcmp(var, "rampmode"))
2343 {
2344 if (!strcmp(value, "none"))
2345 ptype->rampmode = RAMP_NONE;
2346 #ifndef NOLEGACY
2347 else if (!strcmp(value, "absolute"))
2348 {
2349 Con_DPrintf("%s.%s: 'rampmode absolute' is deprecated, use 'rampmode nearest'\n", ptype->config, ptype->name);
2350 ptype->rampmode = RAMP_NEAREST;
2351 }
2352 #endif
2353 else if (!strcmp(value, "nearest"))
2354 ptype->rampmode = RAMP_NEAREST;
2355 else if (!strcmp(value, "lerp")) //don't use the name 'linear'. ramps are there to avoid linear...
2356 ptype->rampmode = RAMP_LERP;
2357 else if (!strcmp(value, "delta"))
2358 ptype->rampmode = RAMP_DELTA;
2359 else
2360 {
2361 Con_DPrintf("%s.%s: uses unknown ramp mode '%s', assuming 'delta'\n", ptype->config, ptype->name, value);
2362 ptype->rampmode = RAMP_DELTA;
2363 }
2364 }
2365 else if (!strcmp(var, "rampindexlist"))
2366 { // better not use this with delta ramps...
2367 int cidx, i;
2368
2369 i = 1;
2370 while (i < Cmd_Argc())
2371 {
2372 ptype->ramp = Z_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));
2373
2374 cidx = atoi(Cmd_Argv(i));
2375 ptype->ramp[ptype->rampindexes].alpha = cidx > 255 ? 0.5 : 1;
2376
2377 cidx = (cidx & 0xff) * 4;
2378 ptype->ramp[ptype->rampindexes].rgb[0] = palrgba[cidx] * (1/255.0);
2379 ptype->ramp[ptype->rampindexes].rgb[1] = palrgba[cidx+1] * (1/255.0);
2380 ptype->ramp[ptype->rampindexes].rgb[2] = palrgba[cidx+2] * (1/255.0);
2381
2382 ptype->ramp[ptype->rampindexes].scale = ptype->scale;
2383
2384 ptype->rampindexes++;
2385 i++;
2386 }
2387 }
2388 else if (!strcmp(var, "rampindex"))
2389 {
2390 int cidx;
2391 ptype->ramp = Z_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));
2392
2393 cidx = atoi(value);
2394 ptype->ramp[ptype->rampindexes].alpha = cidx > 255 ? 0.5 : 1;
2395
2396 if (Cmd_Argc() > 2) // they gave alpha
2397 ptype->ramp[ptype->rampindexes].alpha *= atof(Cmd_Argv(2));
2398
2399 cidx = (cidx & 0xff) * 4;
2400 ptype->ramp[ptype->rampindexes].rgb[0] = palrgba[cidx] * (1/255.0);
2401 ptype->ramp[ptype->rampindexes].rgb[1] = palrgba[cidx+1] * (1/255.0);
2402 ptype->ramp[ptype->rampindexes].rgb[2] = palrgba[cidx+2] * (1/255.0);
2403
2404 if (Cmd_Argc() > 3) // they gave scale
2405 ptype->ramp[ptype->rampindexes].scale = atof(Cmd_Argv(3));
2406 else
2407 ptype->ramp[ptype->rampindexes].scale = ptype->scale;
2408
2409
2410 ptype->rampindexes++;
2411 }
2412 else if (!strcmp(var, "ramp"))
2413 {
2414 ptype->ramp = Z_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));
2415
2416 ptype->ramp[ptype->rampindexes].rgb[0] = atof(value)/255;
2417 if (Cmd_Argc()>3) //seperate rgb
2418 {
2419 ptype->ramp[ptype->rampindexes].rgb[1] = atof(Cmd_Argv(2))/255;
2420 ptype->ramp[ptype->rampindexes].rgb[2] = atof(Cmd_Argv(3))/255;
2421
2422 if (Cmd_Argc()>4) //have we alpha and scale changes?
2423 {
2424 ptype->ramp[ptype->rampindexes].alpha = atof(Cmd_Argv(4));
2425 if (Cmd_Argc()>5) //have we scale changes?
2426 ptype->ramp[ptype->rampindexes].scale = atof(Cmd_Argv(5));
2427 else
2428 ptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;
2429 }
2430 else
2431 {
2432 ptype->ramp[ptype->rampindexes].alpha = ptype->alpha;
2433 ptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;
2434 }
2435 }
2436 else //they only gave one value
2437 {
2438 ptype->ramp[ptype->rampindexes].rgb[1] = ptype->ramp[ptype->rampindexes].rgb[0];
2439 ptype->ramp[ptype->rampindexes].rgb[2] = ptype->ramp[ptype->rampindexes].rgb[0];
2440
2441 ptype->ramp[ptype->rampindexes].alpha = ptype->alpha;
2442 ptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;
2443 }
2444
2445 ptype->rampindexes++;
2446 }
2447 else if (!strcmp(var, "viewspace"))
2448 {
2449 Con_DPrintf("%s.%s: viewspace particles are not supported in this build\n", ptype->config, ptype->name);
2450 }
2451 else if (!strcmp(var, "perframe"))
2452 ptype->flags |= PT_INVFRAMETIME;
2453 else if (!strcmp(var, "averageout"))
2454 ptype->flags |= PT_AVERAGETRAIL;
2455 else if (!strcmp(var, "nostate"))
2456 ptype->flags |= PT_NOSTATE;
2457 else if (!strcmp(var, "nospreadfirst"))
2458 ptype->flags |= PT_NOSPREADFIRST;
2459 else if (!strcmp(var, "nospreadlast"))
2460 ptype->flags |= PT_NOSPREADLAST;
2461
2462 else if (!strcmp(var, "lightradius"))
2463 { //float version
2464 ptype->dl_radius[0] = ptype->dl_radius[1] = atof(value);
2465 if (Cmd_Argc()>2)
2466 ptype->dl_radius[1] = atof(Cmd_Argv(2));
2467 ptype->dl_radius[1] -= ptype->dl_radius[0];
2468 }
2469 else if (!strcmp(var, "lightradiusfade"))
2470 ptype->dl_decay[3] = atof(value);
2471 else if (!strcmp(var, "lightrgb"))
2472 {
2473 ptype->dl_rgb[0] = atof(value);
2474 ptype->dl_rgb[1] = atof(Cmd_Argv(2));
2475 ptype->dl_rgb[2] = atof(Cmd_Argv(3));
2476 }
2477 else if (!strcmp(var, "lightrgbfade"))
2478 {
2479 ptype->dl_decay[0] = atof(value);
2480 ptype->dl_decay[1] = atof(Cmd_Argv(2));
2481 ptype->dl_decay[2] = atof(Cmd_Argv(3));
2482 }
2483 else if (!strcmp(var, "lightcorona"))
2484 {
2485 ptype->dl_corona_intensity = atof(value);
2486 ptype->dl_corona_scale = atof(Cmd_Argv(2));
2487 }
2488 else if (!strcmp(var, "lighttime"))
2489 ptype->dl_time = atof(value);
2490 else if (!strcmp(var, "lightshadows"))
2491 ptype->flags = (ptype->flags & ~PT_NODLSHADOW) | (atof(value)?0:PT_NODLSHADOW);
2492 else if (!strcmp(var, "lightcubemap"))
2493 ptype->dl_cubemapnum = atoi(value);
2494 else if (!strcmp(var, "lightscales"))
2495 { //ambient diffuse specular
2496 ptype->dl_scales[0] = atof(value);
2497 ptype->dl_scales[1] = atof(Cmd_Argv(2));
2498 ptype->dl_scales[2] = atof(Cmd_Argv(3));
2499 }
2500 else if (!strcmp(var, "spawnstain"))
2501 {
2502 Con_DPrintf("%s.%s: spawnstain is not supported in this build\n", ptype->config, ptype->name);
2503 }
2504 else if (Cmd_Argc())
2505 Con_DPrintf("%s.%s: %s is not a recognised particle type field\n", ptype->config, ptype->name, var);
2506 }
2507 ptype->loaded = part_parseweak?1:2;
2508 if (ptype->clipcount < 1)
2509 ptype->clipcount = 1;
2510
2511 if (!settype)
2512 {
2513 if (ptype->looks.type == PT_NORMAL && !*ptype->texname)
2514 {
2515 if (ptype->scale)
2516 {
2517 ptype->looks.type = PT_SPARKFAN;
2518 Con_DPrintf("%s.%s: effect lacks a texture. assuming type sparkfan.\n", ptype->config, ptype->name);
2519 }
2520 else
2521 {
2522 ptype->looks.type = PT_SPARK;
2523 Con_DPrintf("%s.%s: effect lacks a texture. assuming type spark.\n", ptype->config, ptype->name);
2524 }
2525 }
2526 else if (ptype->looks.type == PT_SPARK)
2527 {
2528 if (*ptype->texname)
2529 ptype->looks.type = PT_TEXTUREDSPARK;
2530 else if (ptype->scale)
2531 ptype->looks.type = PT_SPARKFAN;
2532 }
2533 }
2534
2535 // use old behavior if not using alphadelta
2536 if (!setalphadelta)
2537 ptype->alphachange = (-ptype->alphachange / ptype->die) * ptype->alpha;
2538
2539 FinishParticleType(ptype);
2540
2541 if (ptype->looks.type == PT_BEAM && !setbeamlen)
2542 ptype->rotationstartmin = 1/128.0;
2543
2544 goto nexteffect;
2545 }
2546
2547 #if 1//_DEBUG
2548 // R_BeamInfo_f - debug junk
P_BeamInfo_f(void)2549 static void P_BeamInfo_f (void)
2550 {
2551 beamseg_t *bs;
2552 int i, j, k, l, m;
2553
2554 i = 0;
2555
2556 for (bs = free_beams; bs; bs = bs->next)
2557 i++;
2558
2559 Con_Printf("%i free beams\n", i);
2560
2561 for (i = 0; i < numparticletypes; i++)
2562 {
2563 m = l = k = j = 0;
2564 for (bs = part_type[i].beams; bs; bs = bs->next)
2565 {
2566 if (!bs->p)
2567 k++;
2568
2569 if (bs->flags & BS_DEAD)
2570 l++;
2571
2572 if (bs->flags & BS_LASTSEG)
2573 m++;
2574
2575 j++;
2576 }
2577
2578 if (j)
2579 Con_Printf("Type %i = %i NULL p, %i DEAD, %i LASTSEG, %i total\n", i, k, l, m, j);
2580 }
2581 }
2582
P_PartInfo_f(void)2583 static void P_PartInfo_f (void)
2584 {
2585 particle_t *p;
2586 clippeddecal_t *d;
2587 part_type_t *ptype;
2588 int totalp = 0, totald = 0, freep, freed, runningp=0, runningd=0, runninge=0, runningt=0;
2589
2590 int i, j, k;
2591
2592 Con_DPrintf("Full list of effects:\n");
2593 for (i = 0; i < numparticletypes; i++)
2594 {
2595 j = 0;
2596 for (p = part_type[i].particles; p; p = p->next)
2597 j++;
2598 totalp += j;
2599
2600 k = 0;
2601 for (d = part_type[i].clippeddecals; d; d = d->next)
2602 k++;
2603 totald += k;
2604
2605 if (j||k)
2606 {
2607 Con_DPrintf("Type %s.%s = %i+%i total\n", part_type[i].config, part_type[i].name, j,k);
2608 if (!(part_type[i].state & PS_INRUNLIST))
2609 Con_Printf(CON_WARNING "%s.%s NOT RUNNING\n", part_type[i].config, part_type[i].name);
2610 }
2611 }
2612
2613 Con_Printf("Running effects:\n");
2614 // maintain run list
2615 for (ptype = part_run_list; ptype; ptype = ptype->nexttorun)
2616 {
2617 Con_Printf("Type %s.%s", ptype->config, ptype->name);
2618
2619 j = 0;
2620 for (p = ptype->particles; p; p = p->next)
2621 j++;
2622 if (j)
2623 {
2624 Con_Printf("\t%i particles", j);
2625 if (ptype->cliptype >= 0 || ptype->stainonimpact)
2626 {
2627 Con_Printf("(+traceline)");
2628 runningt += j;
2629 }
2630 }
2631 runningp += j;
2632
2633 k = 0;
2634 for (d = ptype->clippeddecals; d; d = d->next)
2635 k++;
2636 if (k)
2637 Con_Printf("%s%i decals", ptype->particles?", ":"\t", k);
2638 runningd += k;
2639
2640 Con_Printf("\n");
2641 runninge++;
2642 }
2643 Con_Printf("End of list\n");
2644
2645 for (p = free_particles, freep = 0; p; p = p->next)
2646 freep++;
2647 for (d = free_decals, freed = 0; d; d = d->next)
2648 freed++;
2649
2650 Con_DPrintf("%i running effects.\n", runninge);
2651 Con_Printf("%i particles, %i free, %i traces.\n", runningp, freep, runningt);
2652 Con_Printf("%i decals, %i free.\n", runningd, freed);
2653
2654 if (totalp != runningp)
2655 Con_Printf("%i particles unaccounted for\n", totalp - runningp);
2656 if (totald != runningd)
2657 Con_Printf("%i decals unaccounted for\n", totald - runningd);
2658 }
2659 #endif
2660
FinishParticleType(part_type_t * ptype)2661 static void FinishParticleType(part_type_t *ptype)
2662 {
2663 //if there is a chance that it moves
2664 if (ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->velbias,ptype->velbias) || ptype->flurry)
2665 ptype->flags |= PT_VELOCITY;
2666 if (DotProduct(ptype->velbias,ptype->velbias) || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->orgwrand,ptype->orgwrand))
2667 ptype->flags |= PT_WORLDSPACERAND;
2668 //if it has friction
2669 if (ptype->friction[0] || ptype->friction[1] || ptype->friction[2])
2670 ptype->flags |= PT_FRICTION;
2671
2672 P_LoadTexture(ptype, true);
2673 if (ptype->dl_decay[3] && !ptype->dl_time)
2674 ptype->dl_time = ptype->dl_radius[0] / ptype->dl_decay[3];
2675 if (ptype->looks.scalefactor > 1 && !ptype->looks.invscalefactor)
2676 {
2677 ptype->scale *= ptype->looks.scalefactor;
2678 ptype->scalerand *= ptype->looks.scalefactor;
2679 /*too lazy to go through ramps*/
2680 ptype->looks.scalefactor = 1;
2681 }
2682 ptype->looks.invscalefactor = 1-ptype->looks.scalefactor;
2683
2684 if (ptype->looks.type == PT_TEXTUREDSPARK && !ptype->looks.stretch)
2685 ptype->looks.stretch = 0.05; //the old default.
2686
2687 if (ptype->looks.type == PT_SPARK && r_part_sparks.value<0)
2688 ptype->looks.type = PT_INVISIBLE;
2689 if (ptype->looks.type == PT_TEXTUREDSPARK && !r_part_sparks_textured.value)
2690 ptype->looks.type = PT_SPARK;
2691 if (ptype->looks.type == PT_SPARKFAN && !r_part_sparks_trifan.value)
2692 ptype->looks.type = PT_SPARK;
2693 if (ptype->looks.type == PT_SPARK && !r_part_sparks.value)
2694 ptype->looks.type = PT_INVISIBLE;
2695 if (ptype->looks.type == PT_BEAM && r_part_beams.value <= 0)
2696 ptype->looks.type = PT_INVISIBLE;
2697
2698 if (ptype->rampmode && !ptype->ramp)
2699 {
2700 ptype->rampmode = RAMP_NONE;
2701 Con_Printf("%s.%s: Particle has a ramp mode but no ramp\n", ptype->config, ptype->name);
2702 }
2703 else if (ptype->ramp && !ptype->rampmode)
2704 {
2705 Con_Printf("%s.%s: Particle has a ramp but no ramp mode\n", ptype->config, ptype->name);
2706 }
2707 r_plooksdirty = true;
2708 }
2709
2710 #ifdef PSET_SCRIPT_EFFECTINFO
FinishEffectinfoParticleType(part_type_t * ptype,qboolean blooddecalonimpact)2711 static void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecalonimpact)
2712 {
2713 if (ptype->looks.type == PT_CDECAL)
2714 {
2715 if (ptype->die == 9999)
2716 ptype->die = 20;
2717 ptype->alphachange = -(ptype->alpha / ptype->die);
2718 }
2719 else if (ptype->looks.type == PT_UDECAL)
2720 {
2721 //dp's decals have a size as a radius. fte's udecals are 'just' quads.
2722 //also, dp uses 'stretch'.
2723 ptype->looks.stretch *= 1/1.414213562373095;
2724 ptype->scale *= ptype->looks.stretch;
2725 ptype->scalerand *= ptype->looks.stretch;
2726 ptype->scaledelta *= ptype->looks.stretch;
2727 ptype->looks.stretch = 1;
2728 }
2729 else if (ptype->looks.type == PT_NORMAL)
2730 {
2731 //fte's textured particles are *0.25 for some reason.
2732 //but fte also uses radiuses, while dp uses total size so we only need to double it here..
2733 ptype->scale *= 2*ptype->looks.stretch;
2734 ptype->scalerand *= 2*ptype->looks.stretch;
2735 ptype->scaledelta *= 2*2*ptype->looks.stretch;
2736 ptype->looks.stretch = 1;
2737 }
2738 if (blooddecalonimpact) //DP blood particles generate decals unconditionally (and prevent blood from bouncing)
2739 ptype->clipbounce = -2;
2740 if (ptype->looks.type == PT_TEXTUREDSPARK)
2741 {
2742 ptype->looks.stretch *= 0.04;
2743 if (ptype->looks.stretch < 0)
2744 ptype->looks.stretch = 0.000001;
2745 }
2746
2747 if (ptype->die == 9999) //internal: means unspecified.
2748 {
2749 if (ptype->alphachange)
2750 ptype->die = (ptype->alpha+ptype->alpharand)/-ptype->alphachange;
2751 else
2752 ptype->die = 15;
2753 }
2754 ptype->looks.minstretch = 0.5;
2755 FinishParticleType(ptype);
2756 }
P_ImportEffectInfo(const char * config,char * line,qboolean part_parseweak)2757 static void P_ImportEffectInfo(const char *config, char *line, qboolean part_parseweak)
2758 {
2759 part_type_t *ptype = NULL;
2760 int parenttype;
2761 char arg[8][1024];
2762 unsigned int args = 0;
2763 qboolean blooddecalonimpact = false; //tracked separately because it needs to override another field
2764
2765 float teximages[256][4];
2766
2767 {
2768 int i;
2769 char *file;
2770 const char *font_line;
2771 char linebuf[1024];
2772 //default assumes 8*8 grid, but we allow more
2773 for (i = 0; i < 256; i++)
2774 {
2775 teximages[i][0] = 1/8.0 * (i & 7);
2776 teximages[i][1] = 1/8.0 * (1+(i & 7));
2777 teximages[i][2] = 1/8.0 * (1+(i>>3));
2778 teximages[i][3] = 1/8.0 * (i>>3);
2779 }
2780
2781 file = (char*)COM_LoadMallocFile("particles/particlefont.txt", NULL);
2782 if (file)
2783 {
2784 size_t offset = 0;
2785 while (PScript_ReadLine(linebuf, sizeof(linebuf), file, com_filesize, &offset))
2786 {
2787 float s1,s2,t1,t2;
2788 font_line = COM_Parse(linebuf);
2789 i = atoi(com_token);
2790 font_line = COM_Parse(font_line);
2791 s1 = atof(com_token);
2792 font_line = COM_Parse(font_line);
2793 t1 = atof(com_token);
2794 font_line = COM_Parse(font_line);
2795 s2 = atof(com_token);
2796 font_line = COM_Parse(font_line);
2797 t2 = atof(com_token);
2798 if (font_line)
2799 {
2800 teximages[i][0] = s1;
2801 teximages[i][1] = s2;
2802 teximages[i][2] = t2;
2803 teximages[i][3] = t1;
2804 }
2805 }
2806 free(file);
2807 }
2808 }
2809
2810 for (;line && *line;)
2811 {
2812 char *eol;
2813
2814 //multi-line comments need special handling.
2815 while (*line == ' ' || *line == '\t')
2816 line++;
2817 if (line[0] == '/' && line[1] == '*')
2818 {
2819 line += 2;
2820 while (*line)
2821 {
2822 if (line[0] == '*' && line[1] == '/')
2823 {
2824 line+=2;
2825 break;
2826 }
2827 line++;
2828 }
2829 continue;
2830 }
2831
2832 eol = strchr(line, '\n');
2833 args = 0;
2834 if (eol)
2835 *eol++=0;
2836 for (args = 0; line; )
2837 {
2838 line = (char*)COM_Parse(line);
2839 if (line && args < sizeof(arg)/sizeof(arg[args]))
2840 {
2841 q_strlcpy(arg[args], com_token, sizeof(arg[args]));
2842 args++;
2843 }
2844 }
2845 line = eol;
2846
2847 if (args <= 0)
2848 continue;
2849
2850 if (!strcmp(arg[0], "effect"))
2851 {
2852 char newname[64];
2853 int i;
2854
2855 if (ptype)
2856 FinishEffectinfoParticleType(ptype, blooddecalonimpact);
2857 blooddecalonimpact = false;
2858
2859 ptype = P_GetParticleType(config, arg[1]);
2860 if (ptype->loaded)
2861 {
2862 for (i = 0; i < 64; i++)
2863 {
2864 parenttype = ptype - part_type;
2865 q_snprintf(newname, sizeof(newname), "%i+%s", i, arg[1]);
2866 ptype = P_GetParticleType(config, newname);
2867 if (!ptype->loaded)
2868 {
2869 part_type[parenttype].assoc = ptype - part_type;
2870 break;
2871 }
2872 }
2873 if (i == 64)
2874 {
2875 Con_Printf("Too many duplicate names, gave up\n");
2876 break;
2877 }
2878 }
2879 P_ResetToDefaults(ptype);
2880 ptype->loaded = part_parseweak?1:2;
2881 ptype->scale = 1;
2882 ptype->alpha = 0;
2883 ptype->alpharand = 1;
2884 ptype->alphachange = -1;
2885 ptype->die = 9999;
2886 strcpy(ptype->texname, "particles/particlefont");
2887 ptype->rgb[0] = 1;
2888 ptype->rgb[1] = 1;
2889 ptype->rgb[2] = 1;
2890
2891 // ptype->spawnmode = SM_BALL;
2892
2893 ptype->colorindex = -1;
2894 ptype->spawnchance = 1;
2895 ptype->looks.scalefactor = 2;
2896 ptype->looks.invscalefactor = 0;
2897 ptype->looks.type = PT_NORMAL;
2898 ptype->looks.blendmode = BM_PREMUL;
2899 ptype->looks.premul = 1;
2900 ptype->looks.stretch = 1;
2901
2902 ptype->dl_time = 0;
2903
2904 i = 63; //default texture is 63.
2905 ptype->s1 = teximages[i][0];
2906 ptype->s2 = teximages[i][1];
2907 ptype->t1 = teximages[i][2];
2908 ptype->t2 = teximages[i][3];
2909 ptype->texsstride = 0;
2910 ptype->randsmax = 1;
2911 }
2912 else if (!ptype)
2913 {
2914 Con_Printf("Bad effectinfo file\n");
2915 break;
2916 }
2917 else if (!strcmp(arg[0], "countabsolute") && args == 2)
2918 ptype->countextra = atof(arg[1]);
2919 else if (!strcmp(arg[0], "count") && args == 2)
2920 ptype->count = atof(arg[1]);
2921 else if (!strcmp(arg[0], "type") && args == 2)
2922 {
2923 if (!strcmp(arg[1], "decal") || !strcmp(arg[1], "cdecal"))
2924 {
2925 ptype->looks.type = PT_CDECAL;
2926 ptype->looks.blendmode = BM_INVMODC;
2927 ptype->looks.premul = 2;
2928 }
2929 else if (!strcmp(arg[1], "udecal"))
2930 {
2931 ptype->looks.type = PT_UDECAL;
2932 ptype->looks.blendmode = BM_INVMODC;
2933 ptype->looks.premul = 2;
2934 }
2935 else if (!strcmp(arg[1], "alphastatic"))
2936 {
2937 ptype->looks.type = PT_NORMAL;
2938 ptype->looks.blendmode = BM_PREMUL;//BM_BLEND;
2939 ptype->looks.premul = 1;
2940 }
2941 else if (!strcmp(arg[1], "static"))
2942 {
2943 ptype->looks.type = PT_NORMAL;
2944 ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2945 ptype->looks.premul = 2;
2946 }
2947 else if (!strcmp(arg[1], "smoke"))
2948 {
2949 ptype->looks.type = PT_NORMAL;
2950 ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2951 ptype->looks.premul = 2;
2952 }
2953 else if (!strcmp(arg[1], "spark"))
2954 {
2955 ptype->looks.type = PT_TEXTUREDSPARK;
2956 ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2957 ptype->looks.premul = 2;
2958 }
2959 else if (!strcmp(arg[1], "bubble"))
2960 {
2961 ptype->looks.type = PT_NORMAL;
2962 ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2963 ptype->looks.premul = 2;
2964 }
2965 else if (!strcmp(arg[1], "blood"))
2966 {
2967 ptype->looks.type = PT_NORMAL;
2968 ptype->looks.blendmode = BM_INVMODC;
2969 ptype->looks.premul = 2;
2970 ptype->gravity = 800*1;
2971 blooddecalonimpact = true;
2972 }
2973 else if (!strcmp(arg[1], "beam"))
2974 {
2975 ptype->looks.type = PT_BEAM;
2976 ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2977 ptype->looks.premul = 2;
2978 }
2979 else if (!strcmp(arg[1], "snow"))
2980 {
2981 ptype->looks.type = PT_NORMAL;
2982 ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2983 ptype->looks.premul = 2;
2984 ptype->flurry = 32; //may not still be valid later, but at least it would be an obvious issue with the original.
2985 }
2986 else
2987 {
2988 Con_Printf("effectinfo type %s not supported\n", arg[1]);
2989 }
2990 }
2991 else if (!strcmp(arg[0], "tex") && args == 3)
2992 {
2993 int mini = atoi(arg[1]);
2994 int maxi = atoi(arg[2]);
2995 ptype->s1 = teximages[mini][0];
2996 ptype->s2 = teximages[mini][1];
2997 ptype->t1 = teximages[mini][2];
2998 ptype->t2 = teximages[mini][3];
2999 ptype->texsstride = teximages[(mini+1)&(sizeof(teximages)/sizeof(teximages[0])-1)][0] - teximages[mini][0];
3000 ptype->randsmax = (maxi - mini);
3001 if (ptype->randsmax < 1)
3002 ptype->randsmax = 1;
3003 }
3004 else if (!strcmp(arg[0], "size") && args == 3)
3005 {
3006 float s1 = atof(arg[1]), s2 = atof(arg[2]);
3007 ptype->scale = s1;
3008 ptype->scalerand = (s2-s1);
3009 }
3010 else if (!strcmp(arg[0], "sizeincrease") && args == 2)
3011 ptype->scaledelta = atof(arg[1]);
3012 else if (!strcmp(arg[0], "color") && args == 3)
3013 {
3014 unsigned int rgb1 = strtoul(arg[1], NULL, 0), rgb2 = strtoul(arg[2], NULL, 0);
3015 int i;
3016 for (i = 0; i < 3; i++)
3017 {
3018 ptype->rgb[i] = ((rgb1>>(16-i*8)) & 0xff)/255.0;
3019 ptype->rgbrand[i] = (int)(((rgb2>>(16-i*8)) & 0xff) - ((rgb1>>(16-i*8)) & 0xff))/255.0;
3020 ptype->rgbrandsync[i] = 1;
3021 }
3022 }
3023 else if (!strcmp(arg[0], "alpha") && args == 4)
3024 {
3025 float a1 = atof(arg[1]), a2 = atof(arg[2]), f = atof(arg[3]);
3026 if (a1 > a2)
3027 { //backwards
3028 ptype->alpha = a2/256;
3029 ptype->alpharand = (a1-a2)/256;
3030 }
3031 else
3032 {
3033 ptype->alpha = a1/256;
3034 ptype->alpharand = (a2-a1)/256;
3035 }
3036 ptype->alphachange = -f/256;
3037 }
3038 else if (!strcmp(arg[0], "velocityoffset") && args == 4)
3039 { /*a 3d world-coord addition*/
3040 ptype->velbias[0] = atof(arg[1]);
3041 ptype->velbias[1] = atof(arg[2]);
3042 ptype->velbias[2] = atof(arg[3]);
3043 }
3044 else if (!strcmp(arg[0], "velocityjitter") && args == 4)
3045 {
3046 ptype->velwrand[0] = atof(arg[1]);
3047 ptype->velwrand[1] = atof(arg[2]);
3048 ptype->velwrand[2] = atof(arg[3]);
3049 }
3050 else if (!strcmp(arg[0], "originoffset") && args == 4)
3051 { /*a 3d world-coord addition*/
3052 ptype->orgbias[0] = atof(arg[1]);
3053 ptype->orgbias[1] = atof(arg[2]);
3054 ptype->orgbias[2] = atof(arg[3]);
3055 }
3056 else if (!strcmp(arg[0], "originjitter") && args == 4)
3057 {
3058 ptype->orgwrand[0] = atof(arg[1]);
3059 ptype->orgwrand[1] = atof(arg[2]);
3060 ptype->orgwrand[2] = atof(arg[3]);
3061 }
3062 else if (!strcmp(arg[0], "gravity") && args == 2)
3063 {
3064 ptype->gravity = 800*atof(arg[1]);
3065 }
3066 else if (!strcmp(arg[0], "bounce") && args == 2)
3067 {
3068 ptype->clipbounce = atof(arg[1]);
3069 if (ptype->clipbounce < 0)
3070 ptype->cliptype = ptype - part_type;
3071 }
3072 else if (!strcmp(arg[0], "airfriction") && args == 2)
3073 ptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(arg[1]);
3074 else if (!strcmp(arg[0], "liquidfriction") && args == 2)
3075 ;
3076 else if (!strcmp(arg[0], "underwater") && args == 1)
3077 ptype->flags |= PT_TRUNDERWATER;
3078 else if (!strcmp(arg[0], "notunderwater") && args == 1)
3079 ptype->flags |= PT_TROVERWATER;
3080 else if (!strcmp(arg[0], "velocitymultiplier") && args == 2)
3081 ptype->veladd = atof(arg[1]);
3082 else if (!strcmp(arg[0], "trailspacing") && args == 2)
3083 {
3084 ptype->countspacing = atof(arg[1]);
3085 ptype->count = 1 / ptype->countspacing;
3086 }
3087 else if (!strcmp(arg[0], "time") && args == 3)
3088 {
3089 ptype->die = atof(arg[1]);
3090 ptype->randdie = atof(arg[2]) - ptype->die;
3091 if (ptype->randdie < 0)
3092 {
3093 ptype->die = atof(arg[2]);
3094 ptype->randdie = atof(arg[1]) - ptype->die;
3095 }
3096 }
3097 else if (!strcmp(arg[0], "stretchfactor") && args == 2)
3098 ptype->looks.stretch = atof(arg[1]);
3099 else if (!strcmp(arg[0], "blend") && args == 2)
3100 {
3101 if (!strcmp(arg[1], "invmod"))
3102 {
3103 ptype->looks.blendmode = BM_INVMODC;
3104 ptype->looks.premul = 2;
3105 }
3106 else if (!strcmp(arg[1], "alpha"))
3107 {
3108 ptype->looks.blendmode = BM_PREMUL;
3109 ptype->looks.premul = 1;
3110 }
3111 else if (!strcmp(arg[1], "add"))
3112 {
3113 ptype->looks.blendmode = BM_PREMUL;
3114 ptype->looks.premul = 2;
3115 }
3116 else
3117 Con_Printf("effectinfo 'blend %s' not supported\n", arg[1]);
3118 }
3119 else if (!strcmp(arg[0], "orientation") && args == 2)
3120 {
3121 if (!strcmp(arg[1], "billboard"))
3122 ptype->looks.type = PT_NORMAL;
3123 else if (!strcmp(arg[1], "spark"))
3124 ptype->looks.type = PT_TEXTUREDSPARK;
3125 else if (!strcmp(arg[1], "oriented")) //FIXME: not sure this points the right way. also, its double-sided in dp.
3126 {
3127 if (ptype->looks.type != PT_CDECAL)
3128 ptype->looks.type = PT_UDECAL;
3129 }
3130 else if (!strcmp(arg[1], "beam"))
3131 ptype->looks.type = PT_BEAM;
3132 else
3133 Con_Printf("effectinfo 'orientation %s' not supported\n", arg[1]);
3134 }
3135 else if (!strcmp(arg[0], "lightradius") && args == 2)
3136 {
3137 ptype->dl_radius[0] = atof(arg[1]);
3138 ptype->dl_radius[1] = 0;
3139 }
3140 else if (!strcmp(arg[0], "lightradiusfade") && args == 2)
3141 ptype->dl_decay[3] = atof(arg[1]);
3142 else if (!strcmp(arg[0], "lightcolor") && args == 4)
3143 {
3144 ptype->dl_rgb[0] = atof(arg[1]);
3145 ptype->dl_rgb[1] = atof(arg[2]);
3146 ptype->dl_rgb[2] = atof(arg[3]);
3147 }
3148 else if (!strcmp(arg[0], "lighttime") && args == 2)
3149 ptype->dl_time = atof(arg[1]);
3150 else if (!strcmp(arg[0], "lightshadow") && args == 2)
3151 ptype->flags = (ptype->flags & ~PT_NODLSHADOW) | (!atoi(arg[1])?PT_NODLSHADOW:0);
3152 else if (!strcmp(arg[0], "lightcubemapnum") && args == 2)
3153 ptype->dl_cubemapnum = atoi(arg[1]);
3154 else if (!strcmp(arg[0], "lightcorona") && args == 3)
3155 {
3156 ptype->dl_corona_intensity = atof(arg[1])*0.25; //dp scales them by 0.25
3157 ptype->dl_corona_scale = atof(arg[2]);
3158 }
3159 #if 1
3160 else if (!strcmp(arg[0], "staincolor") && args == 3) //stainmaps multiplier
3161 Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3162 else if (!strcmp(arg[0], "stainalpha") && args == 3) //affects stainmaps AND stain-decals.
3163 Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3164 else if (!strcmp(arg[0], "stainsize") && args == 3) //affects stainmaps AND stain-decals.
3165 Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3166 else if (!strcmp(arg[0], "staintex") && args == 3) //actually spawns a decal
3167 Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3168 else if (!strcmp(arg[0], "stainless") && args == 2)
3169 Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3170 #endif
3171 else if (!strcmp(arg[0], "rotate") && args == 5)
3172 {
3173 ptype->rotationstartmin = atof(arg[1]);
3174 ptype->rotationstartrand = atof(arg[2]) - ptype->rotationstartmin;
3175 ptype->rotationmin = atof(arg[3]);
3176 ptype->rotationrand = atof(arg[4]) - ptype->rotationmin;
3177 ptype->rotationstartmin *= M_PI/180;
3178 ptype->rotationstartrand *= M_PI/180;
3179 ptype->rotationmin *= M_PI/180;
3180 ptype->rotationrand *= M_PI/180;
3181 ptype->rotationstartmin += M_PI/4;
3182 }
3183 else
3184 Con_Printf("Particle effect token not recognised, or invalid args: %s %s %s %s %s %s\n", arg[0], args<2?"":arg[1], args<3?"":arg[2], args<4?"":arg[3], args<5?"":arg[4], args<6?"":arg[5]);
3185 args = 0;
3186 }
3187
3188 if (ptype)
3189 FinishEffectinfoParticleType(ptype, blooddecalonimpact);
3190
3191 r_plooksdirty = true;
3192 }
3193
P_ImportEffectInfo_Name(char * config)3194 static qboolean P_ImportEffectInfo_Name(char *config)
3195 {
3196 char *file;
3197
3198 file = (char*)COM_LoadMallocFile(va("%s.txt", config), NULL);
3199 if (!file)
3200 {
3201 Con_Printf("%s.txt not found\n", config);
3202 return false;
3203 }
3204 P_ImportEffectInfo(config, file, false);
3205 free(file);
3206 return true;
3207 }
3208 #endif
3209
3210 /*
3211 ===============
3212 R_InitParticles
3213 ===============
3214 */
PScript_InitParticles(void)3215 void PScript_InitParticles (void)
3216 {
3217 Cvar_RegisterVariable(&r_fteparticles); //johnfitz
3218 Cvar_RegisterVariable(&r_bouncysparks);
3219 Cvar_RegisterVariable(&r_part_rain);
3220 Cvar_RegisterVariable(&r_decal_noperpendicular);
3221 Cvar_RegisterVariable(&r_particledesc);
3222 Cvar_RegisterVariable(&r_part_rain_quantity);
3223 Cvar_RegisterVariable(&r_particle_tracelimit);
3224 Cvar_RegisterVariable(&r_part_sparks);
3225 Cvar_RegisterVariable(&r_part_sparks_trifan);
3226 Cvar_RegisterVariable(&r_part_sparks_textured);
3227 Cvar_RegisterVariable(&r_part_beams);
3228 Cvar_RegisterVariable(&r_part_contentswitch);
3229 Cvar_RegisterVariable(&r_part_density);
3230 Cvar_RegisterVariable(&r_part_maxparticles);
3231 Cvar_RegisterVariable(&r_part_maxdecals);
3232 Cvar_RegisterVariable(&r_lightflicker);
3233
3234 Cmd_AddCommand("r_partredirect", P_PartRedirect_f);
3235
3236 //#if _DEBUG
3237 Cmd_AddCommand("r_partinfo", P_PartInfo_f);
3238 Cmd_AddCommand("r_beaminfo", P_BeamInfo_f);
3239 //#endif
3240 }
3241
PScript_ClearSurfaceParticles(qmodel_t * mod)3242 void PScript_ClearSurfaceParticles(qmodel_t *mod)
3243 {
3244 mod->skytime = 0;
3245 mod->skytris = NULL;
3246 while(mod->skytrimem)
3247 {
3248 void *f = mod->skytrimem;
3249 mod->skytrimem = mod->skytrimem->next;
3250 Z_Free(f);
3251 }
3252 }
PScript_ClearAllSurfaceParticles(void)3253 static void PScript_ClearAllSurfaceParticles(void)
3254 { //make sure we hit all models, even ones from the previous map. maybe this is overkill
3255 extern qmodel_t mod_known[];
3256 extern int mod_numknown;
3257 int i;
3258 for (i = 0; i < mod_numknown; i++)
3259 PScript_ClearSurfaceParticles(&mod_known[i]);
3260 }
3261
PScript_Shutdown(void)3262 void PScript_Shutdown (void)
3263 {
3264 Cvar_SetCallback(&r_particledesc, NULL);
3265
3266 CL_ClearTrailStates();
3267
3268 pe_default = P_INVALID;
3269 pe_size2 = P_INVALID;
3270 pe_size3 = P_INVALID;
3271 pe_defaulttrail = P_INVALID;
3272
3273 while(loadedconfigs)
3274 {
3275 pcfg_t *cfg;
3276 cfg = loadedconfigs;
3277 loadedconfigs = cfg->next;
3278 Z_Free(cfg);
3279 }
3280
3281 while (numparticletypes > 0)
3282 {
3283 numparticletypes--;
3284 if (part_type[numparticletypes].sounds)
3285 Z_Free(part_type[numparticletypes].sounds);
3286 if (part_type[numparticletypes].ramp)
3287 Z_Free(part_type[numparticletypes].ramp);
3288 }
3289 Z_Free (part_type);
3290 part_type = NULL;
3291 part_run_list = NULL;
3292
3293 Z_Free (particles);
3294 particles = NULL;
3295 Z_Free (beams);
3296 beams = NULL;
3297 Z_Free (decals);
3298 decals = NULL;
3299 Z_Free (trailstates);
3300 trailstates = NULL;
3301
3302 free_particles = NULL;
3303 free_decals = NULL;
3304 free_beams = NULL;
3305
3306 PScript_ClearAllSurfaceParticles();
3307
3308 r_numparticles = 0;
3309 r_numdecals = 0;
3310 }
3311
PScript_Startup(void)3312 qboolean PScript_Startup (void)
3313 {
3314 int newmaxp, newmaxd;
3315
3316 newmaxp = r_part_maxparticles.value;
3317 if (newmaxp < 1)
3318 newmaxp = 1;
3319 if (newmaxp > MAX_PARTICLES)
3320 newmaxp = MAX_PARTICLES;
3321 newmaxd = r_part_maxdecals.value;
3322 if (newmaxd < 1)
3323 newmaxd = 1;
3324 if (newmaxd > MAX_DECALS)
3325 newmaxd = MAX_DECALS;
3326
3327 if (!r_numparticles) //already inited
3328 {
3329 r_numparticles = newmaxp;
3330 r_numdecals = newmaxd;
3331
3332 buildsintable();
3333
3334 r_numbeams = MAX_BEAMSEGS;
3335 r_numtrailstates = MAX_TRAILSTATES;
3336
3337 particles = (particle_t *)Z_Malloc (r_numparticles * sizeof(particle_t));
3338
3339 beams = (beamseg_t *)Z_Malloc (r_numbeams * sizeof(beamseg_t));
3340
3341 decals = (clippeddecal_t *)Z_Malloc (r_numdecals * sizeof(clippeddecal_t));
3342
3343 trailstates = (trailstate_t *)Z_Malloc (r_numtrailstates * sizeof(trailstate_t));
3344 memset(trailstates, 0, r_numtrailstates * sizeof(trailstate_t));
3345 ts_cycle = 0;
3346
3347 Cvar_SetCallback(&r_particledesc, R_ParticleDesc_Callback);
3348 }
3349 r_particledesc.callback(&r_particledesc);
3350
3351 return true;
3352 }
3353
PScript_RecalculateSkyTris(void)3354 void PScript_RecalculateSkyTris (void)
3355 {
3356 qmodel_t *m = cl.worldmodel;
3357 size_t modidx;
3358
3359 PScript_ClearAllSurfaceParticles();
3360
3361 for (modidx = 0; modidx < MAX_MODELS; modidx++)
3362 {
3363 m = cl.model_precache[modidx];
3364
3365 if (m && !m->needload && m->type == mod_brush)
3366 {
3367 int t;
3368 int i;
3369 int ptype;
3370 msurface_t *surf;
3371 char key[128];
3372 const char *data = COM_Parse(m->entities);
3373 int *remaps;
3374 remaps = malloc(sizeof(*remaps)*m->numtextures);
3375 if (!remaps)
3376 break;
3377 for (t = 0; t < m->numtextures; t++)
3378 remaps[t] = P_INVALID;
3379
3380 //parse the worldspawn entity fields for "_texpart_FOO" keys to give texture "FOO" particles from the effect specified by the value
3381 if (data && com_token[0] == '{')
3382 {
3383 while (1)
3384 {
3385 data = COM_Parse(data);
3386 if (!data)
3387 break; // error
3388 if (com_token[0] == '}')
3389 break; // end of worldspawn
3390 if (com_token[0] == '_')
3391 strcpy(key, com_token + 1);
3392 else
3393 strcpy(key, com_token);
3394 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3395 key[strlen(key)-1] = 0;
3396 data = COM_Parse(data);
3397 if (!data)
3398 break; // error
3399 if (!q_strncasecmp("texpart_", key, 8))
3400 {
3401 /*in quakespasm there are always two textures added on the end (rather than pointing to textures outside the model)*/
3402 for (t = 0; t < m->numtextures-2; t++)
3403 {
3404 if (!m->textures[t])
3405 continue;
3406 if (!q_strcasecmp(key+8, m->textures[t]->name))
3407 remaps[t] = PScript_FindParticleType(com_token);
3408 }
3409 }
3410 }
3411 }
3412
3413 for (t = 0; t < m->numtextures; t++)
3414 {
3415 ptype = remaps[t];
3416 if (ptype == P_INVALID && m->textures[t])
3417 ptype = PScript_FindParticleType(va("tex_%s", m->textures[t]->name));
3418
3419 if (ptype >= 0)
3420 {
3421 for (i=0; i<m->nummodelsurfaces; i++)
3422 {
3423 surf = m->surfaces + i + m->firstmodelsurface;
3424 if (surf->texinfo->texture == m->textures[t])
3425 {
3426 /*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/
3427 PScript_EmitSkyEffectTris(m, surf, ptype);
3428 }
3429 }
3430 }
3431 }
3432 free(remaps);
3433 }
3434 }
3435 }
3436 /*
3437 ===============
3438 P_ClearParticles
3439 ===============
3440 */
PScript_ClearParticles(void)3441 void PScript_ClearParticles (void)
3442 {
3443 int i;
3444
3445 PScript_Startup();
3446
3447 free_particles = &particles[0];
3448 for (i=0 ;i<r_numparticles ; i++)
3449 particles[i].next = &particles[i+1];
3450 particles[r_numparticles-1].next = NULL;
3451
3452 free_decals = &decals[0];
3453 for (i=0 ;i<r_numdecals ; i++)
3454 decals[i].next = &decals[i+1];
3455 decals[r_numdecals-1].next = NULL;
3456
3457 free_beams = &beams[0];
3458 for (i=0 ;i<r_numbeams ; i++)
3459 {
3460 beams[i].p = NULL;
3461 beams[i].flags = BS_DEAD;
3462 beams[i].next = &beams[i+1];
3463 }
3464 beams[r_numbeams-1].next = NULL;
3465
3466 particletime = cl.time;
3467
3468 for (i = 0; i < numparticletypes; i++)
3469 {
3470 P_LoadTexture(&part_type[i], false);
3471 }
3472
3473 for (i = 0; i < numparticletypes; i++)
3474 {
3475 part_type[i].clippeddecals = NULL;
3476 part_type[i].particles = NULL;
3477 part_type[i].beams = NULL;
3478 }
3479
3480 PScript_ClearAllSurfaceParticles();
3481 r_plooksdirty = true;
3482
3483 CL_ClearTrailStates();
3484 }
3485
P_LoadParticleSet(char * name,qboolean implicit,qboolean showwarning)3486 static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning)
3487 {
3488 char *file;
3489 pcfg_t *cfg;
3490
3491 if (!*name)
3492 return false;
3493
3494 //protect against configs being loaded multiple times. this can easily happen with namespaces (especially if an effect is missing).
3495 for (cfg = loadedconfigs; cfg; cfg = cfg->next)
3496 {
3497 //already loaded?
3498 if (!strcmp(cfg->name, name))
3499 return false;
3500 }
3501 cfg = Z_Malloc(sizeof(*cfg) + strlen(name));
3502 if (!cfg)
3503 return false;
3504 strcpy(cfg->name, name);
3505 cfg->next = loadedconfigs;
3506 loadedconfigs = cfg;
3507
3508 if (!strcmp(name, "classic"))
3509 {
3510 #ifdef PSET_CLASSIC
3511 if (fallback)
3512 fallback->ShutdownParticles();
3513 fallback = &pe_classic;
3514 if (fallback)
3515 {
3516 fallback->InitParticles();
3517 fallback->ClearParticles();
3518 }
3519 #endif
3520 return true;
3521 }
3522
3523 file = (char*)COM_LoadMallocFile(va("particles/%s.cfg", name), NULL);
3524 if (!file)
3525 file = (char*)COM_LoadMallocFile(va("%s.cfg", name), NULL);
3526 if (file)
3527 {
3528 PScript_ParseParticleEffectFile(name, implicit, file, com_filesize);
3529 free(file);
3530 }
3531 else
3532 {
3533 #ifdef PSET_SCRIPT_EFFECTINFO
3534 if (!strcmp(name, "effectinfo") || !strncmp(name, "effectinfo_", 11))
3535 {
3536 //FIXME: we're loading this too early to deal with per-map stuff.
3537 //FIXME: wait until after particle precache info has been received, and only reload if the loaded configs actually changed.
3538 P_ImportEffectInfo_Name(name);
3539 return true;
3540 }
3541 #endif
3542 if (showwarning)
3543 Con_Printf(CON_WARNING "Couldn't find particle description %s\n", name);
3544 return false;
3545 }
3546 return true;
3547 }
3548
R_Particles_KillAllEffects(void)3549 static void R_Particles_KillAllEffects(void)
3550 {
3551 int i;
3552 pcfg_t *cfg;
3553
3554 for (i = 0; i < numparticletypes; i++)
3555 {
3556 *part_type[i].texname = '\0';
3557 part_type[i].scale = 0;
3558 part_type[i].loaded = 0;
3559 if (part_type->ramp)
3560 Z_Free(part_type->ramp);
3561 part_type->ramp = NULL;
3562 part_type->rampmode = RAMP_NONE;
3563 }
3564
3565 while(loadedconfigs)
3566 {
3567 cfg = loadedconfigs;
3568 loadedconfigs = cfg->next;
3569 Z_Free(cfg);
3570 }
3571 }
3572
R_ParticleDesc_Callback(struct cvar_s * var)3573 static void R_ParticleDesc_Callback(struct cvar_s *var)
3574 {
3575 const char *c;
3576
3577 R_Particles_KillAllEffects();
3578 r_plooksdirty = true;
3579
3580 for (c = var->string; (c=COM_Parse(c)); )
3581 {
3582 if (*com_token)
3583 P_LoadParticleSet(com_token, false, true);
3584 }
3585
3586 if (cls.state == ca_connected && cl.model_precache[1])
3587 {
3588 //per-map configs. because we can.
3589 memcpy(com_token, "map_", 4);
3590 COM_FileBase(cl.model_precache[1]->name, com_token+4, sizeof(com_token)-4);
3591 P_LoadParticleSet(com_token, false, false);
3592 }
3593
3594 //make sure nothing is stale.
3595 CL_RegisterParticles();
3596 }
3597
P_AddRainParticles(qmodel_t * mod,vec3_t axis[3],vec3_t eorg,float contribution)3598 static void P_AddRainParticles(qmodel_t *mod, vec3_t axis[3], vec3_t eorg, float contribution)
3599 {
3600 float x;
3601 float y;
3602 part_type_t *type;
3603
3604 vec3_t org, vdist, worg, wnorm;
3605
3606 skytris_t *st;
3607 if (!r_part_rain_quantity.value)
3608 return;
3609
3610 mod->skytime += contribution;
3611
3612 for (st = mod->skytris; st; st = st->next)
3613 {
3614 if ((unsigned int)st->ptype >= (unsigned int)numparticletypes)
3615 continue;
3616 type = &part_type[st->ptype];
3617 if (!type->loaded) //woo, batch skipping.
3618 continue;
3619
3620 while (st->nexttime < mod->skytime)
3621 {
3622 if (!free_particles)
3623 return;
3624
3625 st->nexttime += 10000.0/(st->area*r_part_rain_quantity.value*type->rainfrequency);
3626
3627 x = frandom()*frandom();
3628 y = frandom() * (1-x);
3629 VectorMA(st->org, x, st->x, org);
3630 VectorMA(org, y, st->y, org);
3631
3632 worg[0] = DotProduct(org, axis[0]) + eorg[0];
3633 worg[1] = -DotProduct(org, axis[1]) + eorg[1];
3634 worg[2] = DotProduct(org, axis[2]) + eorg[2];
3635
3636 //ignore it if its too far away
3637 VectorSubtract(worg, r_refdef.vieworg, vdist);
3638 if (VectorLength(vdist) > (1024+512)*frandom())
3639 continue;
3640
3641 if (st->face->flags & SURF_PLANEBACK)
3642 VectorScale(st->face->plane->normal, -1, vdist);
3643 else
3644 VectorCopy(st->face->plane->normal, vdist);
3645
3646 wnorm[0] = DotProduct(vdist, axis[0]);
3647 wnorm[1] = -DotProduct(vdist, axis[1]);
3648 wnorm[2] = DotProduct(vdist, axis[2]);
3649
3650 VectorMA(worg, 0.5, wnorm, worg);
3651 if (!(CL_PointContentsMask(worg) & FTECONTENTS_SOLID)) //should be paranoia, at least for the world.
3652 {
3653 PScript_RunParticleEffectState(worg, wnorm, 1, st->ptype, NULL);
3654 }
3655 }
3656 }
3657 }
3658
R_Part_SkyTri(qmodel_t * mod,float * v1,float * v2,float * v3,msurface_t * surf,int ptype)3659 static void R_Part_SkyTri(qmodel_t *mod, float *v1, float *v2, float *v3, msurface_t *surf, int ptype)
3660 {
3661 float dot;
3662 float xm;
3663 float ym;
3664 float theta;
3665 vec3_t xd;
3666 vec3_t yd;
3667
3668 skytris_t *st;
3669
3670 skytriblock_t *mem = mod->skytrimem;
3671 if (!mem || mem->count == sizeof(mem->tris)/sizeof(mem->tris[0]))
3672 {
3673 mod->skytrimem = Z_Malloc(sizeof(*mod->skytrimem));
3674 mod->skytrimem->next = mem;
3675 mod->skytrimem->count = 0;
3676 mem = mod->skytrimem;
3677 }
3678
3679 st = &mem->tris[mem->count];
3680 VectorCopy(v1, st->org);
3681 VectorSubtract(v2, st->org, st->x);
3682 VectorSubtract(v3, st->org, st->y);
3683
3684 VectorCopy(st->x, xd);
3685 VectorCopy(st->y, yd);
3686
3687 xm = VectorLength(xd);
3688 ym = VectorLength(yd);
3689
3690 dot = DotProduct(xd, yd);
3691 theta = acos(dot/(xm*ym));
3692 st->area = sin(theta)*xm*ym;
3693 st->nexttime = mod->skytime;
3694 st->face = surf;
3695 st->ptype = ptype;
3696
3697 if (st->area<=0)
3698 return;//bummer.
3699 mem->count++;
3700
3701 st->next = mod->skytris;
3702 mod->skytris = st;
3703 }
3704
PScript_EmitSkyEffectTris(qmodel_t * mod,msurface_t * fa,int ptype)3705 void PScript_EmitSkyEffectTris(qmodel_t *mod, msurface_t *fa, int ptype)
3706 {
3707 vec3_t verts[64];
3708 int v1;
3709 int v2;
3710 int v3;
3711 int numverts;
3712 int i, lindex;
3713 float *vec;
3714
3715 if (ptype < 0 || ptype >= numparticletypes)
3716 return;
3717
3718 //
3719 // convert edges back to a normal polygon
3720 //
3721 numverts = 0;
3722 for (i=0 ; i<fa->numedges ; i++)
3723 {
3724 lindex = mod->surfedges[fa->firstedge + i];
3725
3726 if (lindex > 0)
3727 vec = mod->vertexes[mod->edges[lindex].v[0]].position;
3728 else
3729 vec = mod->vertexes[mod->edges[-lindex].v[1]].position;
3730 VectorCopy (vec, verts[numverts]);
3731 numverts++;
3732
3733 if (numverts>=64)
3734 {
3735 Con_Printf("Too many verts on sky surface\n");
3736 return;
3737 }
3738 }
3739
3740 v1 = 0;
3741 v2 = 1;
3742 for (v3 = 2; v3 < numverts; v3++)
3743 {
3744 R_Part_SkyTri(mod, verts[v1], verts[v2], verts[v3], fa, ptype);
3745
3746 v2 = v3;
3747 }
3748 }
3749
3750 // Trailstate functions
P_CleanTrailstate(trailstate_t * ts)3751 static void P_CleanTrailstate(trailstate_t *ts)
3752 {
3753 // clear LASTSEG flag from lastbeam so it can be reused
3754 if (ts->lastbeam)
3755 {
3756 ts->lastbeam->flags &= ~BS_LASTSEG;
3757 ts->lastbeam->flags |= BS_NODRAW;
3758 }
3759
3760 // clean structure
3761 memset(ts, 0, sizeof(trailstate_t));
3762 }
3763
PScript_DelinkTrailstate(trailstate_t ** tsk)3764 void PScript_DelinkTrailstate(trailstate_t **tsk)
3765 {
3766 trailstate_t *ts;
3767 trailstate_t *assoc;
3768
3769 if (*tsk == NULL)
3770 return; // not linked to a trailstate
3771
3772 ts = *tsk; // store old pointer
3773 *tsk = NULL; // clear pointer
3774
3775 if (ts->key != tsk)
3776 return; // prevent overwrite
3777
3778 assoc = ts->assoc; // store assoc
3779 P_CleanTrailstate(ts); // clean directly linked trailstate
3780
3781 // clean trailstates assoc linked
3782 while (assoc)
3783 {
3784 ts = assoc->assoc;
3785 P_CleanTrailstate(assoc);
3786 assoc = ts;
3787 }
3788 }
3789
P_NewTrailstate(trailstate_t ** key)3790 static trailstate_t *P_NewTrailstate(trailstate_t **key)
3791 {
3792 trailstate_t *ts;
3793
3794 // bounds check here in case r_numtrailstates changed
3795 if (ts_cycle >= r_numtrailstates)
3796 ts_cycle = 0;
3797
3798 // get trailstate
3799 ts = trailstates + ts_cycle;
3800
3801 // clear trailstate
3802 P_CleanTrailstate(ts);
3803
3804 // set key
3805 ts->key = key;
3806
3807 // advance index cycle
3808 ts_cycle++;
3809
3810 // return clean trailstate
3811 return ts;
3812 }
3813
3814 #define NUMVERTEXNORMALS 162
3815 static float r_avertexnormals[NUMVERTEXNORMALS][3] = {
3816 #include "anorms.h"
3817 };
3818 static vec2_t avelocities[NUMVERTEXNORMALS];
3819 #define BEAMLENGTH 16
3820
PScript_EffectSpawned(part_type_t * ptype,vec3_t org,vec3_t axis[3],int dlkey,float countscale)3821 static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t axis[3], int dlkey, float countscale)
3822 {
3823 if (ptype->dl_radius[0] || ptype->dl_radius[1])// && r_rocketlight.value)
3824 {
3825 float radius;
3826 dlight_t *dl;
3827
3828 static int flickertime;
3829 static int flicker;
3830 int i = realtime*20;
3831 if (flickertime != i)
3832 {
3833 flickertime = i;
3834 flicker = rand();
3835 }
3836 radius = ptype->dl_radius[0] + (r_lightflicker.value?((flicker + dlkey*2000)&0xffff)*(1.0f/0xffff):0.5)*ptype->dl_radius[1];
3837
3838 dl = CL_AllocDlight (dlkey);
3839 VectorCopy (org, dl->origin);
3840 dl->radius = radius;
3841 dl->minlight = 0;
3842 dl->die = cl.time + ptype->dl_time;
3843 dl->decay = ptype->dl_decay[3];
3844 VectorCopy(ptype->dl_rgb, dl->color);
3845 }
3846 if (ptype->numsounds)
3847 {
3848 int i;
3849 float w,tw;
3850 for (i = 0, tw = 0; i < ptype->numsounds; i++)
3851 tw += ptype->sounds[i].weight;
3852 w = frandom() * tw; //select the sound by weight
3853 //and figure out which one that weight corresponds to
3854 for (i = 0, tw = 0; i < ptype->numsounds; i++)
3855 {
3856 tw += ptype->sounds[i].weight;
3857 if (w <= tw)
3858 {
3859 if (*ptype->sounds[i].name && ptype->sounds[i].vol > 0)
3860 { //FIXME: no delay, no pitch
3861 S_StartSound(0, 0, S_PrecacheSound(ptype->sounds[i].name), org, ptype->sounds[i].vol, ptype->sounds[i].atten);
3862 }
3863 break;
3864 }
3865 }
3866 }
3867 }
3868
3869 #ifdef USE_DECALS
3870 typedef struct
3871 {
3872 part_type_t *ptype;
3873 int entity;
3874 qmodel_t *model;
3875 vec3_t center;
3876 vec3_t normal;
3877 vec3_t tangent1;
3878 vec3_t tangent2;
3879
3880 float scale0;
3881 float scale1;
3882 float scale2;
3883
3884 float bias1;
3885 float bias2;
3886 } decalctx_t;
PScript_AddDecals(void * vctx,vec3_t * points,size_t numtris)3887 static void PScript_AddDecals(void *vctx, vec3_t *points, size_t numtris)
3888 {
3889 decalctx_t *ctx = vctx;
3890 part_type_t *ptype = ctx->ptype;
3891 clippeddecal_t *d;
3892 unsigned int i;
3893 vec3_t vec;
3894 byte *palrgba = (byte *)d_8to24table;
3895 while(numtris-->0)
3896 {
3897 if (!free_decals)
3898 break;
3899
3900 d = free_decals;
3901 free_decals = d->next;
3902 d->next = ptype->clippeddecals;
3903 ptype->clippeddecals = d;
3904
3905 for (i = 0; i < 3; i++)
3906 {
3907 VectorCopy(points[i], d->vertex[i]);
3908 VectorSubtract(d->vertex[i], ctx->center, vec);
3909 d->texcoords[i][0] = (DotProduct(vec, ctx->tangent1)*ctx->scale1)+ctx->bias1;
3910 d->texcoords[i][1] = (DotProduct(vec, ctx->tangent2)*ctx->scale2)+ctx->bias2;
3911 if (r_decal_noperpendicular.value)
3912 {
3913 //the decal code is already making sure the surfaces are mostly aligned, which should solve some issues.
3914 //this means we can make sure that there's NO fading at all, so no issues if the center of the effect is not actually aligned with any surface (yay inprecision).
3915 d->valpha[i] = 1;
3916 }
3917 else
3918 {
3919 //fade the alpha depending on the distance from the center)
3920 //FIXME: should be fabsed by glsl so that linear interpolation works correctly
3921 d->valpha[i] = 1 - fabs((DotProduct(vec, ctx->normal)*ctx->scale0));
3922 }
3923 }
3924 points += 3;
3925
3926 d->entity = ctx->entity;
3927 d->model = ctx->model;
3928 d->die = ptype->randdie*frandom();
3929
3930 if (ptype->die)
3931 d->rgba[3] = ptype->alpha + d->die*ptype->alphachange;
3932 else
3933 d->rgba[3] = ptype->alpha;
3934 d->rgba[3] += ptype->alpharand*frandom();
3935
3936 if (ptype->colorindex >= 0)
3937 {
3938 int cidx;
3939 cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
3940 cidx = ptype->colorindex + cidx;
3941 if (cidx > 255)
3942 d->rgba[3] = d->rgba[3] / 2; // Hexen 2 style transparency
3943 cidx = (cidx & 0xff) * 4;
3944 d->rgba[0] = palrgba[cidx] * (1/255.0);
3945 d->rgba[1] = palrgba[cidx+1] * (1/255.0);
3946 d->rgba[2] = palrgba[cidx+2] * (1/255.0);
3947 }
3948 else
3949 VectorCopy(ptype->rgb, d->rgba);
3950
3951 vec[2] = frandom();
3952 vec[0] = vec[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
3953 vec[1] = vec[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
3954 vec[2] = vec[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
3955 d->rgba[0] += vec[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*d->die;
3956 d->rgba[1] += vec[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*d->die;
3957 d->rgba[2] += vec[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*d->die;
3958
3959 d->die = particletime + ptype->die - d->die;
3960
3961 if (ptype->looks.type != PT_CDECAL)
3962 d->die += 20;
3963
3964 // maintain run list
3965 if (!(ptype->state & PS_INRUNLIST))
3966 {
3967 ptype->nexttorun = part_run_list;
3968 part_run_list = ptype;
3969 ptype->state |= PS_INRUNLIST;
3970 }
3971 }
3972 }
3973
3974
3975 typedef struct fragmentdecal_s fragmentdecal_t;
3976 static void Mod_ClipDecal(qmodel_t *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *points, size_t numpoints), void *ctx);
3977
3978 //clipped decals actually work by defining the area of the decal with some planes, and then chopping away the entirety of the world based upon those planes (hurrah for bsp to trivially reject most of it)
3979 //the decal is then textured according to some texture projection.
3980 #define MAXFRAGMENTVERTS (128*3)
3981 struct fragmentdecal_s
3982 {
3983 vec3_t center;
3984
3985 vec3_t normal;
3986 vec3_t planenorm[6];
3987 float planedist[6];
3988 int numplanes;
3989
3990 vec_t radius;
3991
3992 //will only appear on surfaces with the matching surfaceflag
3993 unsigned int surfflagmask;
3994 unsigned int surfflagmatch;
3995
3996 void (*callback)(void *ctx, vec3_t *points, size_t numpoints);
3997 void *ctx;
3998 };
Fragment_ClipPolyToPlane(vec3_t * inverts,vec3_t * outverts,int incount,float * plane,float planedist)3999 static int Fragment_ClipPolyToPlane(vec3_t *inverts, vec3_t *outverts, int incount, float *plane, float planedist)
4000 {
4001 float dotv[MAXFRAGMENTVERTS+1];
4002 char keep[MAXFRAGMENTVERTS+1];
4003 #define KEEP_KILL 0
4004 #define KEEP_KEEP 1
4005 #define KEEP_BORDER 2
4006 int i;
4007 int outcount = 0;
4008 int clippedcount = 0;
4009 float d;
4010 float *p1, *p2;
4011 float *out;
4012 #define FRAG_EPSILON (1.0/32) //0.5
4013
4014 for (i = 0; i < incount; i++)
4015 {
4016 dotv[i] = DotProduct(inverts[i], plane) - planedist;
4017 if (dotv[i]<-FRAG_EPSILON)
4018 {
4019 keep[i] = KEEP_KILL;
4020 clippedcount++;
4021 }
4022 else if (dotv[i] > FRAG_EPSILON)
4023 keep[i] = KEEP_KEEP;
4024 else
4025 keep[i] = KEEP_BORDER;
4026 }
4027 dotv[i] = dotv[0];
4028 keep[i] = keep[0];
4029
4030 if (clippedcount == incount)
4031 return 0; //all were clipped
4032 if (clippedcount == 0)
4033 { //none were clipped
4034 for (i = 0; i < incount; i++)
4035 VectorCopy(inverts[i], outverts[i]);
4036 return incount;
4037 }
4038
4039 for (i = 0; i < incount; i++)
4040 {
4041 p1 = inverts[i];
4042 if (keep[i] == KEEP_BORDER)
4043 {
4044 out = outverts[outcount++];
4045 VectorCopy(p1, out);
4046 continue;
4047 }
4048 if (keep[i] == KEEP_KEEP)
4049 {
4050 out = outverts[outcount++];
4051 VectorCopy(p1, out);
4052 }
4053 if (keep[i+1] == KEEP_BORDER || keep[i] == keep[i+1])
4054 continue;
4055 p2 = inverts[(i+1)%incount];
4056 d = dotv[i] - dotv[i+1];
4057 if (d)
4058 d = dotv[i] / d;
4059
4060 out = outverts[outcount++];
4061 VectorInterpolate(p1, d, p2, out);
4062 }
4063 return outcount;
4064 }
Fragment_ClipPoly(fragmentdecal_t * dec,int numverts,vec3_t * inverts)4065 static void Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, vec3_t *inverts)
4066 {
4067 //emit the triangle, and clip it's fragments.
4068 int p;
4069 vec3_t verts[2][MAXFRAGMENTVERTS];
4070 vec3_t decalfragmentverts[MAXFRAGMENTVERTS];
4071 int flip;
4072 vec3_t d1, d2, n;
4073 size_t numtris;
4074
4075 if (numverts > MAXFRAGMENTVERTS)
4076 return;
4077
4078 if (r_decal_noperpendicular.value)
4079 {
4080 VectorSubtract(inverts[1], inverts[0], d1);
4081 for (p = 2; ; p++)
4082 {
4083 if (p >= numverts)
4084 return;
4085 VectorSubtract(inverts[p], inverts[0], d2);
4086 CrossProduct(d1, d2, n);
4087 if (DotProduct(n,n)>.1)
4088 break;
4089 }
4090 VectorNormalizeFast(n);
4091 if (DotProduct(n, dec->normal) < 0.1)
4092 return; //faces too far way from the normal
4093 }
4094
4095 flip = 0;
4096 //clip to the first plane specially, so we don't have extra copys
4097 numverts = Fragment_ClipPolyToPlane(inverts, verts[flip], numverts, dec->planenorm[0], dec->planedist[0]);
4098
4099 if (numverts < 3) //totally clipped.
4100 return;
4101
4102 //clip the polygon to the 6 planes.
4103 for (p = 1; p < dec->numplanes; p++)
4104 {
4105 numverts = Fragment_ClipPolyToPlane(verts[flip], verts[flip^1], numverts, dec->planenorm[p], dec->planedist[p]);
4106 flip^=1;
4107
4108 if (numverts < 3) //totally clipped.
4109 return;
4110 }
4111
4112 //decompose the resulting polygon into triangles.
4113
4114 numtris = 0;
4115 while(numverts-->2)
4116 {
4117 if (numtris+3 > MAXFRAGMENTVERTS)
4118 {
4119 dec->callback(dec->ctx, decalfragmentverts, numtris);
4120 numtris = 0;
4121 break;
4122 }
4123
4124 VectorCopy(verts[flip][0], decalfragmentverts[numtris*3+0]);
4125 VectorCopy(verts[flip][numverts-1], decalfragmentverts[numtris*3+1]);
4126 VectorCopy(verts[flip][numverts], decalfragmentverts[numtris*3+2]);
4127 numtris++;
4128 }
4129 if (numtris)
4130 dec->callback(dec->ctx, decalfragmentverts, numtris);
4131 }
4132 //this could be inlined, but I'm lazy.
Q1BSP_Fragment_Surface(fragmentdecal_t * dec,msurface_t * surf)4133 static void Q1BSP_Fragment_Surface (fragmentdecal_t *dec, msurface_t *surf)
4134 {
4135 int i;
4136 vec3_t verts[MAXFRAGMENTVERTS];
4137 glpoly_t *poly;
4138 float *poly_vert;
4139
4140 //water and sky should not get decals.
4141 if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))
4142 return;
4143
4144 for (poly = surf->polys; poly; poly = poly->next)
4145 {
4146 if (poly->numverts > MAXFRAGMENTVERTS)
4147 continue;
4148
4149 for (i = 0; i < poly->numverts; i++)
4150 {
4151 poly_vert = &poly->verts[0][0] + (i * VERTEXSIZE);
4152 VectorCopy(poly_vert, verts[i]);
4153 }
4154 Fragment_ClipPoly(dec, i, verts);
4155 }
4156 }
Q1BSP_ClipDecalToNodes(qmodel_t * mod,fragmentdecal_t * dec,mnode_t * node)4157 static void Q1BSP_ClipDecalToNodes (qmodel_t *mod, fragmentdecal_t *dec, mnode_t *node)
4158 {
4159 mplane_t *splitplane;
4160 float dist;
4161 msurface_t *surf;
4162 unsigned int i;
4163
4164 if (node->contents < 0)
4165 return;
4166
4167 splitplane = node->plane;
4168 dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist;
4169
4170 if (dist > dec->radius)
4171 {
4172 Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]);
4173 return;
4174 }
4175 if (dist < -dec->radius)
4176 {
4177 Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]);
4178 return;
4179 }
4180
4181 // mark the polygons
4182 surf = mod->surfaces + node->firstsurface;
4183 if (r_decal_noperpendicular.value)
4184 {
4185 for (i=0 ; i<node->numsurfaces ; i++, surf++)
4186 {
4187 if (surf->flags & SURF_PLANEBACK)
4188 {
4189 if (-DotProduct(surf->plane->normal, dec->normal) > -0.5)
4190 continue;
4191 }
4192 else
4193 {
4194 if (DotProduct(surf->plane->normal, dec->normal) > -0.5)
4195 continue;
4196 }
4197 Q1BSP_Fragment_Surface(dec, surf);
4198 }
4199 }
4200 else
4201 {
4202 for (i=0 ; i<node->numsurfaces ; i++, surf++)
4203 Q1BSP_Fragment_Surface(dec, surf);
4204 }
4205
4206 Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]);
4207 Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]);
4208 }
4209
4210
Mod_ClipDecal(qmodel_t * mod,vec3_t center,vec3_t normal,vec3_t tangent1,vec3_t tangent2,float size,unsigned int surfflagmask,unsigned int surfflagmatch,void (* callback)(void * ctx,vec3_t * points,size_t numpoints),void * ctx)4211 static void Mod_ClipDecal(qmodel_t *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *points, size_t numpoints), void *ctx)
4212 { //quad marks a full, independant quad
4213 int p;
4214 float r;
4215 fragmentdecal_t dec;
4216
4217 VectorCopy(center, dec.center);
4218 VectorCopy(normal, dec.normal);
4219 dec.radius = 0;
4220 dec.callback = callback;
4221 dec.ctx = ctx;
4222 dec.surfflagmask = surfflagmask;
4223 dec.surfflagmatch = surfflagmatch;
4224
4225 VectorCopy(tangent1, dec.planenorm[0]);
4226 VectorScale(tangent1, -1, dec.planenorm[1]);
4227 VectorCopy(tangent2, dec.planenorm[2]);
4228 VectorScale(tangent2, -1, dec.planenorm[3]);
4229 VectorCopy(dec.normal, dec.planenorm[4]);
4230 VectorScale(dec.normal, -1, dec.planenorm[5]);
4231 for (p = 0; p < 6; p++)
4232 {
4233 r = sqrt(DotProduct(dec.planenorm[p], dec.planenorm[p]));
4234 VectorScale(dec.planenorm[p], 1/r, dec.planenorm[p]);
4235 r*= size/2;
4236 if (r > dec.radius)
4237 dec.radius = r;
4238 dec.planedist[p] = -(r - DotProduct(dec.center, dec.planenorm[p]));
4239 }
4240 dec.numplanes = 6;
4241
4242 if (mod && !mod->needload && mod->type == mod_brush)
4243 Q1BSP_ClipDecalToNodes(mod, &dec, mod->nodes + mod->hulls[0].firstclipnode);
4244 }
4245 #endif
4246
4247 void PerpendicularVector( vec3_t dst, const vec3_t src );
4248
PScript_RunParticleEffectState(vec3_t org,vec3_t dir,float count,int typenum,trailstate_t ** tsk)4249 int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk)
4250 {
4251 part_type_t *ptype = &part_type[typenum];
4252 int i, j, k, l, spawnspc;
4253 float m, pcount;//, orgadd, veladd;
4254 vec3_t axis[3]={{1,0,0},{0,1,0},{0,0,-1}};
4255 particle_t *p;
4256 beamseg_t *b, *bfirst;
4257 vec3_t ofsvec, arsvec; // offsetspread vec, areaspread vec
4258 vec3_t bestdir;
4259
4260 float orgadd, veladd;
4261 trailstate_t *ts;
4262 byte *palrgba = (byte *)d_8to24table;
4263
4264 if (typenum < 0 || typenum >= numparticletypes)
4265 return 1;
4266
4267 if (!ptype->loaded)
4268 return 1;
4269
4270 // inwater check, switch only once
4271 if (r_part_contentswitch.value && ptype->inwater >= 0 && cl.worldmodel)
4272 {
4273 unsigned int cont;
4274 cont = CL_PointContentsMask(org);
4275
4276 if (cont & FTECONTENTS_FLUID)
4277 ptype = &part_type[ptype->inwater];
4278 }
4279
4280 // eliminate trailstate if flag set
4281 if (ptype->flags & PT_NOSTATE)
4282 tsk = NULL;
4283
4284 // trailstate allocation/deallocation
4285 if (tsk)
4286 {
4287 // if *tsk = NULL get a new one
4288 if (*tsk == NULL)
4289 {
4290 ts = P_NewTrailstate(tsk);
4291 *tsk = ts;
4292 }
4293 else
4294 {
4295 ts = *tsk;
4296
4297 if (ts->key != tsk) // trailstate was overwritten
4298 {
4299 ts = P_NewTrailstate(tsk); // so get a new one
4300 *tsk = ts;
4301 }
4302 }
4303 }
4304 else
4305 ts = NULL;
4306
4307 // get msvc to shut up
4308 j = k = l = 0;
4309 m = 0;
4310
4311 while(ptype)
4312 {
4313 if (r_part_contentswitch.value && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel)
4314 {
4315 unsigned int cont;
4316 cont = CL_PointContentsMask(org);
4317
4318 if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))
4319 goto skip;
4320 if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
4321 goto skip;
4322 }
4323
4324 if (dir && (dir[0] || dir[1] || dir[2]))
4325 {
4326 VectorCopy(dir, axis[2]);
4327 VectorNormalize(axis[2]);
4328 PerpendicularVector(axis[0], axis[2]);
4329 VectorNormalize(axis[0]);
4330 CrossProduct(axis[2], axis[0], axis[1]);
4331 VectorNormalize(axis[1]);
4332 }
4333 PScript_EffectSpawned(ptype, org, axis, 0, count);
4334
4335 if (ptype->looks.type == PT_CDECAL)
4336 {
4337 #ifdef USE_DECALS
4338 vec3_t vec={0.5, 0.5, 0.5};
4339 int n;
4340 decalctx_t ctx;
4341 vec3_t start, end;
4342
4343 if (!free_decals)
4344 return 0;
4345
4346 ctx.entity = 0;
4347
4348 VectorCopy(org, ctx.center);
4349 if (!dir || (dir[0] == 0 && dir[1] == 0 && dir[2] == 0))
4350 {
4351 float bestfrac = 1;
4352 float frac;
4353 vec3_t impact, normal;
4354 int what;
4355 bestdir[0] = 0;
4356 bestdir[1] = 0.73;
4357 bestdir[2] = 0.73;
4358 VectorNormalize(bestdir);
4359 for (n = 0; n < 6; n++)
4360 {
4361 if (n >= 3)
4362 {
4363 end[0] = (n==3)*16;
4364 end[1] = (n==4)*16;
4365 end[2] = (n==5)*16;
4366 }
4367 else
4368 {
4369 end[0] = -(n==0)*16;
4370 end[1] = -(n==1)*16;
4371 end[2] = -(n==2)*16;
4372 }
4373 VectorSubtract(org, end, start);
4374 VectorAdd(org, end, end);
4375
4376
4377 frac = CL_TraceLine(start, end, impact, normal, &what);
4378 if (bestfrac > frac)
4379 {
4380 bestfrac = frac;
4381 VectorCopy(normal, bestdir);
4382 VectorCopy(impact, ctx.center);
4383 ctx.entity = what;
4384 }
4385 }
4386 dir = bestdir;
4387 }
4388 else
4389 { //try to get it exactly on the plane, otherwise network or collision inprecisions can leave us further away from the surface than the radius of the decal
4390 VectorSubtract(org, dir, start);
4391 VectorAdd(org, dir, end);
4392 CL_TraceLine(start, end, ctx.center, bestdir, &ctx.entity);
4393 }
4394 if (ctx.entity)
4395 {
4396 entity_t *ent = CL_EntityNum(ctx.entity);
4397 if (ent->model) //looks like its active.
4398 {
4399 ctx.model = ent->model;
4400 //FIXME: rotate normal
4401 VectorSubtract(ctx.center, ent->origin, ctx.center);
4402 }
4403 else
4404 {
4405 ctx.entity = 0;
4406 ctx.model = cl.worldmodel;
4407 }
4408 }
4409 else
4410 {
4411 ctx.entity = 0;
4412 ctx.model = cl.worldmodel;
4413 }
4414 if (!ctx.model)
4415 return 0;
4416
4417 VectorScale(dir, -1, ctx.normal);
4418 VectorNormalize(ctx.normal);
4419
4420 //we know the normal now. pick two random tangents.
4421 VectorNormalize(vec);
4422 CrossProduct(ctx.normal, vec, ctx.tangent1);
4423 RotatePointAroundVector(ctx.tangent2, ctx.normal, ctx.tangent1, frandom()*360);
4424 CrossProduct(ctx.normal, ctx.tangent2, ctx.tangent1);
4425
4426 VectorNormalize(ctx.tangent1);
4427 VectorNormalize(ctx.tangent2);
4428
4429 ctx.ptype = ptype;
4430 ctx.scale1 = ptype->s2 - ptype->s1;
4431 ctx.bias1 = ptype->s1 + ctx.scale1/2;
4432 ctx.scale2 = ptype->t2 - ptype->t1;
4433 ctx.bias2 = ptype->t1 + ctx.scale2/2;
4434 m = ptype->scale + frandom() * ptype->scalerand;
4435 ctx.scale0 = 2.0 / m;
4436 ctx.scale1 /= m;
4437 ctx.scale2 /= m;
4438
4439 if (ptype->randsmax!=1)
4440 ctx.bias1 += ptype->texsstride * (rand()%ptype->randsmax);
4441
4442 //inserts decals through a callback.
4443 Mod_ClipDecal(ctx.model, ctx.center, ctx.normal, ctx.tangent2, ctx.tangent1, m, ptype->surfflagmask, ptype->surfflagmatch, PScript_AddDecals, &ctx);
4444 #endif
4445 if (ptype->assoc < 0)
4446 break;
4447 ptype = &part_type[ptype->assoc];
4448 continue;
4449 }
4450 // init spawn specific variables
4451 b = bfirst = NULL;
4452 spawnspc = 8;
4453 pcount = ptype->countextra + r_part_density.value*count*(ptype->count+ptype->countrand*frandom());
4454 if (ptype->flags & PT_INVFRAMETIME)
4455 pcount /= host_frametime;
4456 if (ts)
4457 pcount += ts->state2.emittime;
4458
4459 switch (ptype->spawnmode)
4460 {
4461 case SM_UNICIRCLE:
4462 m = pcount;
4463 if (ptype->looks.type == PT_BEAM)
4464 m--;
4465
4466 if (m < 1)
4467 m = 0;
4468 else
4469 m = (M_PI*2)/m;
4470
4471 if (ptype->spawnparam1) /* use for weird shape hacks */
4472 m *= ptype->spawnparam1;
4473 break;
4474 case SM_TELEBOX:
4475 spawnspc = 4;
4476 l = -ptype->areaspreadvert;
4477 case SM_LAVASPLASH:
4478 j = k = -ptype->areaspread;
4479 if (ptype->spawnparam1)
4480 m = ptype->spawnparam1;
4481 else
4482 m = 0.55752; /* default weird number for tele/lavasplash used in vanilla Q1 */
4483
4484 if (ptype->spawnparam2)
4485 spawnspc = (int)ptype->spawnparam2;
4486 break;
4487 case SM_FIELD:
4488 if (!avelocities[0][0])
4489 {
4490 for (j=0 ; j<NUMVERTEXNORMALS ; j++)
4491 {
4492 avelocities[j][0] = (rand()&255) * 0.01;
4493 avelocities[j][1] = (rand()&255) * 0.01;
4494 }
4495 }
4496
4497 j = 0;
4498 m = 0;
4499 break;
4500 default: //others don't need intitialisation
4501 break;
4502 }
4503
4504 // time limit (for completeness)
4505 if (ptype->spawntime && ts)
4506 {
4507 if (ts->state1.statetime > particletime)
4508 return 0; // timelimit still in effect
4509
4510 ts->state1.statetime = particletime + ptype->spawntime; // record old time
4511 }
4512
4513 // random chance for point effects
4514 if (ptype->spawnchance < frandom())
4515 {
4516 i = ceil(pcount);
4517 break;
4518 }
4519
4520 /*this is a hack, use countextra=1, count=0*/
4521 if (!ptype->die && ptype->count == 1 && ptype->countrand == 0 && pcount < 1)
4522 pcount = 1;
4523
4524 // particle spawning loop
4525 for (i = 0; i < pcount; i++)
4526 {
4527 if (!free_particles)
4528 break;
4529 p = free_particles;
4530 if (ptype->looks.type == PT_BEAM)
4531 {
4532 if (!free_beams)
4533 break;
4534 if (b)
4535 {
4536 b = b->next = free_beams;
4537 free_beams = free_beams->next;
4538 }
4539 else
4540 {
4541 b = bfirst = free_beams;
4542 free_beams = free_beams->next;
4543 }
4544 b->texture_s = i; // TODO: FIX THIS NUMBER
4545 b->flags = 0;
4546 b->p = p;
4547 VectorClear(b->dir);
4548 }
4549 free_particles = p->next;
4550 p->next = ptype->particles;
4551 ptype->particles = p;
4552
4553 p->die = ptype->randdie*frandom();
4554 p->scale = ptype->scale+ptype->scalerand*frandom();
4555 if (ptype->die)
4556 p->rgba[3] = ptype->alpha+p->die*ptype->alphachange;
4557 else
4558 p->rgba[3] = ptype->alpha;
4559 p->rgba[3] += ptype->alpharand*frandom();
4560 // p->color = 0;
4561 if (ptype->emittime < 0)
4562 p->state.trailstate = NULL;
4563 else
4564 p->state.nextemit = particletime + ptype->emitstart - p->die;
4565
4566 p->rotationspeed = ptype->rotationmin + frandom()*ptype->rotationrand;
4567 p->angle = ptype->rotationstartmin + frandom()*ptype->rotationstartrand;
4568 p->s1 = ptype->s1;
4569 p->t1 = ptype->t1;
4570 p->s2 = ptype->s2;
4571 p->t2 = ptype->t2;
4572 if (ptype->randsmax!=1)
4573 {
4574 m = ptype->texsstride * (rand()%ptype->randsmax);
4575 p->s1 += m;
4576 p->s2 += m;
4577 }
4578
4579 if (ptype->colorindex >= 0)
4580 {
4581 int cidx;
4582 cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
4583 cidx = ptype->colorindex + cidx;
4584 if (cidx > 255)
4585 p->rgba[3] = p->rgba[3] / 2; // Hexen 2 style transparency
4586 cidx = (cidx & 0xff) * 4;
4587 p->rgba[0] = palrgba[cidx] * (1/255.0);
4588 p->rgba[1] = palrgba[cidx+1] * (1/255.0);
4589 p->rgba[2] = palrgba[cidx+2] * (1/255.0);
4590 }
4591 else
4592 VectorCopy(ptype->rgb, p->rgba);
4593
4594 // use org temporarily for rgbsync
4595 p->org[2] = frandom();
4596 p->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
4597 p->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
4598 p->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
4599
4600 p->rgba[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;
4601 p->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
4602 p->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
4603
4604 p->vel[0] = 0;
4605 p->vel[1] = 0;
4606 p->vel[2] = 0;
4607
4608 // handle spawn modes (org/vel)
4609 switch (ptype->spawnmode)
4610 {
4611 case SM_BOX:
4612 ofsvec[0] = crandom();
4613 ofsvec[1] = crandom();
4614 ofsvec[2] = crandom();
4615
4616 arsvec[0] = ofsvec[0]*ptype->areaspread;
4617 arsvec[1] = ofsvec[1]*ptype->areaspread;
4618 arsvec[2] = ofsvec[2]*ptype->areaspreadvert;
4619 break;
4620 case SM_TELEBOX:
4621 ofsvec[0] = k;
4622 ofsvec[1] = j;
4623 ofsvec[2] = l+4;
4624 VectorNormalize(ofsvec);
4625 VectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);
4626
4627 // org is just like the original
4628 arsvec[0] = j + (rand()%spawnspc);
4629 arsvec[1] = k + (rand()%spawnspc);
4630 arsvec[2] = l + (rand()%spawnspc);
4631
4632 // advance telebox loop
4633 j += spawnspc;
4634 if (j >= ptype->areaspread)
4635 {
4636 j = -ptype->areaspread;
4637 k += spawnspc;
4638 if (k >= ptype->areaspread)
4639 {
4640 k = -ptype->areaspread;
4641 l += spawnspc;
4642 if (l >= ptype->areaspreadvert)
4643 l = -ptype->areaspreadvert;
4644 }
4645 }
4646 break;
4647 case SM_LAVASPLASH:
4648 // calc directions, org with temp vector
4649 ofsvec[0] = k + (rand()%spawnspc);
4650 ofsvec[1] = j + (rand()%spawnspc);
4651 ofsvec[2] = 256;
4652
4653 arsvec[0] = ofsvec[0];
4654 arsvec[1] = ofsvec[1];
4655 arsvec[2] = frandom()*ptype->areaspreadvert;
4656
4657 VectorNormalize(ofsvec);
4658 VectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);
4659
4660 // advance splash loop
4661 j += spawnspc;
4662 if (j >= ptype->areaspread)
4663 {
4664 j = -ptype->areaspread;
4665 k += spawnspc;
4666 if (k >= ptype->areaspread)
4667 k = -ptype->areaspread;
4668 }
4669 break;
4670 case SM_UNICIRCLE:
4671 ofsvec[0] = cos(m*i);
4672 ofsvec[1] = sin(m*i);
4673 ofsvec[2] = 0;
4674 VectorScale(ofsvec, ptype->areaspread, arsvec);
4675 break;
4676 case SM_FIELD:
4677 arsvec[0] = (cl.time * avelocities[i][0]) + m;
4678 arsvec[1] = (cl.time * avelocities[i][1]) + m;
4679 arsvec[2] = cos(arsvec[1]);
4680
4681 ofsvec[0] = arsvec[2]*cos(arsvec[0]);
4682 ofsvec[1] = arsvec[2]*sin(arsvec[0]);
4683 ofsvec[2] = -sin(arsvec[1]);
4684
4685 orgadd = ptype->spawnparam2 * sin(cl.time+j+m);
4686 arsvec[0] = r_avertexnormals[j][0]*(ptype->areaspread+orgadd) + ofsvec[0]*ptype->spawnparam1;
4687 arsvec[1] = r_avertexnormals[j][1]*(ptype->areaspread+orgadd) + ofsvec[1]*ptype->spawnparam1;
4688 arsvec[2] = r_avertexnormals[j][2]*(ptype->areaspreadvert+orgadd) + ofsvec[2]*ptype->spawnparam1;
4689
4690 VectorNormalize(ofsvec);
4691
4692 j++;
4693 if (j >= NUMVERTEXNORMALS)
4694 {
4695 j = 0;
4696 m += 0.1762891; // some BS number to try to "randomize" things
4697 }
4698 break;
4699 case SM_DISTBALL:
4700 {
4701 float rdist;
4702
4703 rdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));
4704
4705 // this is a strange spawntype, which is based on the fact that
4706 // crandom()*crandom() provides something similar to an exponential
4707 // probability curve
4708 ofsvec[0] = hrandom();
4709 ofsvec[1] = hrandom();
4710 if (ptype->areaspreadvert)
4711 ofsvec[2] = hrandom();
4712 else
4713 ofsvec[2] = 0;
4714
4715 VectorNormalize(ofsvec);
4716 VectorScale(ofsvec, rdist, ofsvec);
4717
4718 arsvec[0] = ofsvec[0]*ptype->areaspread;
4719 arsvec[1] = ofsvec[1]*ptype->areaspread;
4720 arsvec[2] = ofsvec[2]*ptype->areaspreadvert;
4721 }
4722 break;
4723 default: // SM_BALL, SM_CIRCLE
4724 {
4725 ofsvec[0] = hrandom();
4726 ofsvec[1] = hrandom();
4727 if (ptype->areaspreadvert)
4728 ofsvec[2] = hrandom();
4729 else
4730 ofsvec[2] = 0;
4731
4732 VectorNormalize(ofsvec);
4733 if (ptype->spawnmode != SM_CIRCLE)
4734 VectorScale(ofsvec, frandom(), ofsvec);
4735
4736 arsvec[0] = ofsvec[0]*ptype->areaspread;
4737 arsvec[1] = ofsvec[1]*ptype->areaspread;
4738 arsvec[2] = ofsvec[2]*ptype->areaspreadvert;
4739 }
4740 break;
4741 }
4742
4743 // apply arsvec+ofsvec
4744 orgadd = ptype->orgadd + frandom()*ptype->randomorgadd;
4745 veladd = ptype->veladd + frandom()*ptype->randomveladd;
4746
4747 if (dir)
4748 veladd *= VectorLength(dir);
4749 VectorMA(p->vel, ofsvec[0]*ptype->spawnvel, axis[0], p->vel);
4750 VectorMA(p->vel, ofsvec[1]*ptype->spawnvel, axis[1], p->vel);
4751 VectorMA(p->vel, veladd+ofsvec[2]*ptype->spawnvelvert, axis[2], p->vel);
4752
4753 VectorMA(org, arsvec[0], axis[0], p->org);
4754 VectorMA(p->org, arsvec[1], axis[1], p->org);
4755 VectorMA(p->org, orgadd+arsvec[2], axis[2], p->org);
4756
4757 if (ptype->flags & PT_WORLDSPACERAND)
4758 {
4759 do
4760 {
4761 ofsvec[0] = crand();
4762 ofsvec[1] = crand();
4763 ofsvec[2] = crand();
4764 } while(DotProduct(ofsvec,ofsvec)>1); //crap, but I'm trying to mimic dp
4765 p->org[0] += ofsvec[0] * ptype->orgwrand[0];
4766 p->org[1] += ofsvec[1] * ptype->orgwrand[1];
4767 p->org[2] += ofsvec[2] * ptype->orgwrand[2];
4768 p->vel[0] += ofsvec[0] * ptype->velwrand[0];
4769 p->vel[1] += ofsvec[1] * ptype->velwrand[1];
4770 p->vel[2] += ofsvec[2] * ptype->velwrand[2];
4771 VectorAdd(p->vel, ptype->velbias, p->vel);
4772 }
4773 VectorAdd(p->org, ptype->orgbias, p->org);
4774
4775 p->die = particletime + ptype->die - p->die;
4776
4777 VectorCopy(p->org, p->oldorg);
4778 }
4779
4780 // update beam list
4781 if (ptype->looks.type == PT_BEAM)
4782 {
4783 if (b)
4784 {
4785 // update dir for bfirst for certain modes since it will never get updated
4786 switch (ptype->spawnmode)
4787 {
4788 case SM_UNICIRCLE:
4789 // kinda hackish here, assuming ofsvec contains the point at i-1
4790 arsvec[0] = cos(m*(i-2));
4791 arsvec[1] = sin(m*(i-2));
4792 arsvec[2] = 0;
4793 VectorSubtract(b->p->org, arsvec, bfirst->dir);
4794 VectorNormalize(bfirst->dir);
4795 break;
4796 default:
4797 break;
4798 }
4799
4800 b->flags |= BS_NODRAW;
4801 b->next = ptype->beams;
4802 ptype->beams = bfirst;
4803 }
4804 }
4805
4806 // save off emit times in trailstate
4807 if (ts)
4808 ts->state2.emittime = pcount - i;
4809
4810 // maintain run list
4811 if (!(ptype->state & PS_INRUNLIST) && (ptype->particles || ptype->clippeddecals))
4812 {
4813 if (part_run_list)
4814 {
4815 //insert after, to try to avoid edge-case weirdness
4816 ptype->nexttorun = part_run_list->nexttorun;
4817 part_run_list->nexttorun = ptype;
4818 }
4819 else
4820 {
4821 ptype->nexttorun = part_run_list;
4822 part_run_list = ptype;
4823 }
4824 ptype->state |= PS_INRUNLIST;
4825 }
4826
4827 skip:
4828
4829 // go to next associated effect
4830 if (ptype->assoc < 0)
4831 break;
4832
4833 // new trailstate
4834 if (ts)
4835 {
4836 tsk = &(ts->assoc);
4837 // if *tsk = NULL get a new one
4838 if (*tsk == NULL)
4839 {
4840 ts = P_NewTrailstate(tsk);
4841 *tsk = ts;
4842 }
4843 else
4844 {
4845 ts = *tsk;
4846
4847 if (ts->key != tsk) // trailstate was overwritten
4848 {
4849 ts = P_NewTrailstate(tsk); // so get a new one
4850 *tsk = ts;
4851 }
4852 }
4853 }
4854
4855 ptype = &part_type[ptype->assoc];
4856 }
4857
4858 return 0;
4859 }
4860
PScript_RunParticleEffectTypeString(vec3_t org,vec3_t dir,float count,const char * name)4861 int PScript_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, const char *name)
4862 {
4863 if (r_fteparticles.value == 0)
4864 return 1;
4865 int type = PScript_FindParticleType(name);
4866 if (type < 0)
4867 return 1;
4868
4869 return PScript_RunParticleEffectState(org, dir, count, type, NULL);
4870 }
4871
PScript_EntParticleTrail(vec3_t oldorg,entity_t * ent,const char * name)4872 int PScript_EntParticleTrail(vec3_t oldorg, entity_t *ent, const char *name)
4873 {
4874 if (r_fteparticles.value == 0)
4875 return 1;
4876 float timeinterval = cl.time - cl.oldtime;
4877 vec3_t axis[3];
4878 int type = PScript_FindParticleType(name);
4879 if (type < 0)
4880 return 1;
4881
4882 AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
4883 return PScript_ParticleTrail(oldorg, ent->origin, type, timeinterval, ent-cl.entities, axis, &ent->trailstate);
4884 }
4885
4886 /*
4887 ===============
4888 P_RunParticleEffect
4889
4890 ===============
4891 */
PScript_RunParticleEffect(vec3_t org,vec3_t dir,int color,int count)4892 int PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
4893 {
4894 if (r_fteparticles.value == 0)
4895 return false;
4896 int ptype;
4897
4898 ptype = PScript_FindParticleType(va("pe_%i", color));
4899 if (PScript_RunParticleEffectState(org, dir, count, ptype, NULL))
4900 {
4901 if (count > 130 && PART_VALID(pe_size3))
4902 {
4903 part_type[pe_size3].colorindex = color & ~0x7;
4904 part_type[pe_size3].colorrand = 8;
4905 return PScript_RunParticleEffectState(org, dir, count, pe_size3, NULL);
4906 }
4907 else if (count > 20 && PART_VALID(pe_size2))
4908 {
4909 part_type[pe_size2].colorindex = color & ~0x7;
4910 part_type[pe_size2].colorrand = 8;
4911 return PScript_RunParticleEffectState(org, dir, count, pe_size2, NULL);
4912 }
4913 else if (PART_VALID(pe_default))
4914 {
4915 part_type[pe_default].colorindex = color & ~0x7;
4916 part_type[pe_default].colorrand = 8;
4917 return PScript_RunParticleEffectState(org, dir, count, pe_default, NULL);
4918 }
4919 return true;
4920 }
4921 return false;
4922 }
4923
PScript_RunParticleWeather(vec3_t minb,vec3_t maxb,vec3_t dir,float count,int colour,const char * efname)4924 void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, const char *efname)
4925 {
4926 vec3_t org;
4927 int i, j;
4928 float num;
4929 float invcount;
4930
4931 int ptype = PScript_FindParticleType(va("te_%s_%i", efname, colour));
4932 if (!PART_VALID(ptype))
4933 {
4934 ptype = PScript_FindParticleType(va("te_%s", efname));
4935 if (!PART_VALID(ptype))
4936 ptype = pe_default;
4937 if (!PART_VALID(ptype))
4938 return;
4939 part_type[ptype].colorindex = colour;
4940 }
4941
4942 invcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1
4943 count = count * part_type[ptype].count;
4944
4945 for (i=0 ; i<count ; i++)
4946 {
4947 if (!free_particles)
4948 return;
4949
4950 for (j=0 ; j<3 ; j++)
4951 {
4952 num = rand() / (float)RAND_MAX;
4953 org[j] = minb[j] + num*(maxb[j]-minb[j]);
4954 }
4955 PScript_RunParticleEffectState(org, dir, invcount, ptype, NULL);
4956 }
4957 }
4958
PScript_ParticleTrailSpawn(vec3_t startpos,vec3_t end,part_type_t * ptype,float timeinterval,trailstate_t ** tsk,int dlkey,vec3_t dlaxis[3])4959 static void PScript_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptype, float timeinterval, trailstate_t **tsk, int dlkey, vec3_t dlaxis[3])
4960 {
4961 vec3_t vec, vstep, right, up, start;
4962 float len;
4963 int tcount;
4964 particle_t *p;
4965 beamseg_t *b;
4966 beamseg_t *bfirst;
4967 trailstate_t *ts;
4968 float count;
4969
4970 float veladd = -ptype->veladd;
4971 float step;
4972 float stop;
4973 float tdegree = 2.0*M_PI/256; /* MSVC whine */
4974 float sdegree = 0;
4975 float nrfirst, nrlast;
4976 byte *palrgba = (byte *)d_8to24table;
4977
4978 VectorCopy(startpos, start);
4979
4980 // eliminate trailstate if flag set
4981 if (ptype->flags & PT_NOSTATE)
4982 tsk = NULL;
4983
4984 // trailstate allocation/deallocation
4985 if (tsk)
4986 {
4987 // if *tsk = NULL get a new one
4988 if (*tsk == NULL)
4989 {
4990 ts = P_NewTrailstate(tsk);
4991 *tsk = ts;
4992 }
4993 else
4994 {
4995 ts = *tsk;
4996
4997 if (ts->key != tsk) // trailstate was overwritten
4998 {
4999 ts = P_NewTrailstate(tsk); // so get a new one
5000 *tsk = ts;
5001 }
5002 }
5003 }
5004 else
5005 ts = NULL;
5006
5007 PScript_EffectSpawned(ptype, start, dlaxis, dlkey, 1);
5008
5009 if (ptype->assoc>=0)
5010 {
5011 if (ts)
5012 PScript_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, &(ts->assoc));
5013 else
5014 PScript_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, NULL);
5015 }
5016
5017 if (r_part_contentswitch.value && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel)
5018 {
5019 unsigned int cont;
5020 cont = CL_PointContentsMask(startpos);
5021
5022 if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))
5023 return;
5024 if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
5025 return;
5026 }
5027
5028 // time limit for trails
5029 if (ptype->spawntime && ts)
5030 {
5031 if (ts->state1.statetime > particletime)
5032 return; // timelimit still in effect
5033
5034 ts->state1.statetime = particletime + ptype->spawntime; // record old time
5035 ts = NULL; // clear trailstate so we don't save length/lastseg
5036 }
5037
5038 // random chance for trails
5039 if (ptype->spawnchance < frandom())
5040 return; // don't spawn but return success
5041
5042 if (!ptype->die)
5043 ts = NULL;
5044
5045 VectorSubtract (end, start, vec);
5046 len = VectorNormalize (vec);
5047
5048 // use ptype step to calc step vector and step size
5049 if (ptype->countspacing)
5050 {
5051 step = ptype->countspacing; //particles per qu
5052 step /= r_part_density.value; //scaled...
5053
5054 if (ptype->countextra)
5055 {
5056 count = ptype->countextra;
5057 if (step>0)
5058 count += len/step;
5059 step = len/count;
5060 }
5061 }
5062 else
5063 {
5064 step = ptype->count * r_part_density.value * timeinterval;
5065 step += ptype->countextra; //particles per frame
5066 step += ptype->countoverflow;
5067 count = (int)step;
5068 ptype->countoverflow = step-count; //the part that we're forgetting, to add to the next frame...
5069 if (count<=0)
5070 return;
5071 else
5072 step = len/count; //particles per second
5073 }
5074
5075 if (ptype->flags & PT_AVERAGETRAIL)
5076 {
5077 float tavg;
5078 // mangle len/step to get last point to be at end
5079 tavg = len / step;
5080 tavg = tavg / ceil(tavg);
5081 step *= tavg;
5082 len += step;
5083 }
5084
5085 VectorScale(vec, step, vstep);
5086
5087 // add offset
5088 // VectorAdd(start, ptype->orgbias, start);
5089
5090 // spawn mode precalculations
5091 if (ptype->spawnmode == SM_SPIRAL)
5092 {
5093 VectorVectors(vec, right, up);
5094
5095 // precalculate degree of rotation
5096 if (ptype->spawnparam1)
5097 tdegree = 2.0*M_PI/ptype->spawnparam1; /* distance per rotation inversed */
5098 sdegree = ptype->spawnparam2*(M_PI/180);
5099 }
5100 else if (ptype->spawnmode == SM_CIRCLE)
5101 {
5102 VectorVectors(vec, right, up);
5103 }
5104
5105 // store last stop here for lack of a better solution besides vectors
5106 if (ts)
5107 {
5108 ts->state2.laststop = stop = ts->state2.laststop + len; //when to stop
5109 len = ts->state1.lastdist;
5110 }
5111 else
5112 {
5113 stop = len;
5114 len = 0;
5115 }
5116
5117 // len = ts->lastdist/step;
5118 // len = (len - (int)len)*step;
5119 // VectorMA (start, -len, vec, start);
5120
5121 if (ptype->flags & PT_NOSPREADFIRST)
5122 nrfirst = len + step*1.5;
5123 else
5124 nrfirst = len;
5125
5126 if (ptype->flags & PT_NOSPREADLAST)
5127 nrlast = stop;
5128 else
5129 nrlast = stop + step;
5130
5131 b = bfirst = NULL;
5132
5133 if (len < stop)
5134 count = (stop-len) / step;
5135 else
5136 {
5137 count = 0;
5138 step = 0;
5139 VectorClear(vstep);
5140 }
5141 // count += ptype->countextra;
5142
5143 while (count-->0)//len < stop)
5144 {
5145 len += step;
5146
5147 if (!free_particles)
5148 {
5149 len = stop;
5150 break;
5151 }
5152
5153 p = free_particles;
5154 if (ptype->looks.type == PT_BEAM)
5155 {
5156 if (!free_beams)
5157 {
5158 len = stop;
5159 break;
5160 }
5161 if (b)
5162 {
5163 b = b->next = free_beams;
5164 free_beams = free_beams->next;
5165 }
5166 else
5167 {
5168 b = bfirst = free_beams;
5169 free_beams = free_beams->next;
5170 }
5171 b->texture_s = len; // not sure how to calc this
5172 b->flags = 0;
5173 b->p = p;
5174 VectorCopy(vec, b->dir);
5175 }
5176
5177 free_particles = p->next;
5178 p->next = ptype->particles;
5179 ptype->particles = p;
5180
5181 p->die = ptype->randdie*frandom();
5182 p->scale = ptype->scale+ptype->scalerand*frandom();
5183 if (ptype->die)
5184 p->rgba[3] = ptype->alpha+p->die*ptype->alphachange;
5185 else
5186 p->rgba[3] = ptype->alpha;
5187 p->rgba[3] += ptype->alpharand*frandom();
5188 // p->color = 0;
5189
5190 // if (ptype->spawnmode == SM_TRACER)
5191 if (ptype->spawnparam1)
5192 tcount = (int)(len * ptype->count / ptype->spawnparam1);
5193 else
5194 tcount = (int)(len * ptype->count);
5195
5196 if (ptype->colorindex >= 0)
5197 {
5198 int cidx;
5199 cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
5200 if (ptype->flags & PT_CITRACER) // colorindex behavior as per tracers in std Q1
5201 cidx += ((tcount & 4) << 1);
5202
5203 cidx = ptype->colorindex + cidx;
5204 if (cidx > 255)
5205 p->rgba[3] = p->rgba[3] / 2;
5206 cidx = (cidx & 0xff) * 4;
5207 p->rgba[0] = palrgba[cidx] * (1/255.0);
5208 p->rgba[1] = palrgba[cidx+1] * (1/255.0);
5209 p->rgba[2] = palrgba[cidx+2] * (1/255.0);
5210 }
5211 else
5212 VectorCopy(ptype->rgb, p->rgba);
5213
5214 // use org temporarily for rgbsync
5215 p->org[2] = frandom();
5216 p->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
5217 p->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
5218 p->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
5219
5220 p->rgba[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;
5221 p->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
5222 p->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
5223
5224 VectorClear (p->vel);
5225 if (ptype->emittime < 0)
5226 p->state.trailstate = NULL; // init trailstate
5227 else
5228 p->state.nextemit = particletime + ptype->emitstart - p->die;
5229
5230 p->rotationspeed = ptype->rotationmin + frandom()*ptype->rotationrand;
5231 p->angle = ptype->rotationstartmin + frandom()*ptype->rotationstartrand;
5232 p->s1 = ptype->s1;
5233 p->t1 = ptype->t1;
5234 p->s2 = ptype->s2;
5235 p->t2 = ptype->t2;
5236 if (ptype->randsmax!=1)
5237 {
5238 float offs;
5239 offs = ptype->texsstride * (rand()%ptype->randsmax);
5240 p->s1 += offs;
5241 p->s2 += offs;
5242 while (p->s1 >= 1)
5243 {
5244 p->s1 -= 1;
5245 p->s2 -= 1;
5246 p->t1 += ptype->texsstride;
5247 p->t2 += ptype->texsstride;
5248 }
5249 }
5250
5251 if (len < nrfirst || len >= nrlast)
5252 {
5253 // no offset or areaspread for these particles...
5254 p->vel[0] = vec[0]*veladd;
5255 p->vel[1] = vec[1]*veladd;
5256 p->vel[2] = vec[2]*veladd;
5257
5258 VectorCopy(start, p->org);
5259 }
5260 else
5261 {
5262 switch(ptype->spawnmode)
5263 {
5264 case SM_TRACER:
5265 if (tcount & 1)
5266 {
5267 p->vel[0] = vec[1]*ptype->spawnvel;
5268 p->vel[1] = -vec[0]*ptype->spawnvel;
5269 p->org[0] = vec[1]*ptype->areaspread;
5270 p->org[1] = -vec[0]*ptype->areaspread;
5271 }
5272 else
5273 {
5274 p->vel[0] = -vec[1]*ptype->spawnvel;
5275 p->vel[1] = vec[0]*ptype->spawnvel;
5276 p->org[0] = -vec[1]*ptype->areaspread;
5277 p->org[1] = vec[0]*ptype->areaspread;
5278 }
5279
5280 p->vel[0] += vec[0]*veladd;
5281 p->vel[1] += vec[1]*veladd;
5282 p->vel[2] = vec[2]*veladd;
5283
5284 p->org[0] += start[0];
5285 p->org[1] += start[1];
5286 p->org[2] = start[2];
5287 break;
5288 case SM_SPIRAL:
5289 {
5290 float tsin, tcos;
5291 float tright, tup;
5292
5293 tcos = cos(len*tdegree+sdegree);
5294 tsin = sin(len*tdegree+sdegree);
5295
5296 tright = tcos*ptype->areaspread;
5297 tup = tsin*ptype->areaspread;
5298
5299 p->org[0] = start[0] + right[0]*tright + up[0]*tup;
5300 p->org[1] = start[1] + right[1]*tright + up[1]*tup;
5301 p->org[2] = start[2] + right[2]*tright + up[2]*tup;
5302
5303 tright = tcos*ptype->spawnvel;
5304 tup = tsin*ptype->spawnvel;
5305
5306 p->vel[0] = vec[0]*veladd + right[0]*tright + up[0]*tup;
5307 p->vel[1] = vec[1]*veladd + right[1]*tright + up[1]*tup;
5308 p->vel[2] = vec[2]*veladd + right[2]*tright + up[2]*tup;
5309 }
5310 break;
5311 // TODO: directionalize SM_BALL/SM_CIRCLE/SM_DISTBALL
5312 case SM_BALL:
5313 p->org[0] = crandom();
5314 p->org[1] = crandom();
5315 p->org[2] = crandom();
5316 VectorNormalize(p->org);
5317 VectorScale(p->org, frandom(), p->org);
5318
5319 p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;
5320 p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;
5321 p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;
5322
5323 p->org[0] = p->org[0]*ptype->areaspread + start[0];
5324 p->org[1] = p->org[1]*ptype->areaspread + start[1];
5325 p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
5326 break;
5327
5328 case SM_CIRCLE:
5329 {
5330 float tsin, tcos;
5331
5332 tcos = cos(len*tdegree)*ptype->areaspread;
5333 tsin = sin(len*tdegree)*ptype->areaspread;
5334
5335 p->org[0] = start[0] + right[0]*tcos + up[0]*tsin + vstep[0] * (len*tdegree);
5336 p->org[1] = start[1] + right[1]*tcos + up[1]*tsin + vstep[1] * (len*tdegree);
5337 p->org[2] = start[2] + right[2]*tcos + up[2]*tsin + vstep[2] * (len*tdegree)*50;
5338
5339 tcos = cos(len*tdegree)*ptype->spawnvel;
5340 tsin = sin(len*tdegree)*ptype->spawnvel;
5341
5342 p->vel[0] = vec[0]*veladd + right[0]*tcos + up[0]*tsin;
5343 p->vel[1] = vec[1]*veladd + right[1]*tcos + up[1]*tsin;
5344 p->vel[2] = vec[2]*veladd + right[2]*tcos + up[2]*tsin;
5345 }
5346 break;
5347
5348 case SM_DISTBALL:
5349 {
5350 float rdist;
5351
5352 rdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));
5353
5354 // this is a strange spawntype, which is based on the fact that
5355 // crandom()*crandom() provides something similar to an exponential
5356 // probability curve
5357 p->org[0] = crandom();
5358 p->org[1] = crandom();
5359 p->org[2] = crandom();
5360
5361 VectorNormalize(p->org);
5362 VectorScale(p->org, rdist, p->org);
5363
5364 p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;
5365 p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;
5366 p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;
5367
5368 p->org[0] = p->org[0]*ptype->areaspread + start[0];
5369 p->org[1] = p->org[1]*ptype->areaspread + start[1];
5370 p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
5371 }
5372 break;
5373 default:
5374 p->org[0] = crandom();
5375 p->org[1] = crandom();
5376 p->org[2] = crandom();
5377
5378 p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;
5379 p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;
5380 p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;
5381
5382 p->org[0] = p->org[0]*ptype->areaspread + start[0];
5383 p->org[1] = p->org[1]*ptype->areaspread + start[1];
5384 p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
5385 break;
5386 }
5387
5388 if (ptype->orgadd)
5389 {
5390 p->org[0] += vec[0]*ptype->orgadd;
5391 p->org[1] += vec[1]*ptype->orgadd;
5392 p->org[2] += vec[2]*ptype->orgadd;
5393 }
5394 }
5395 if (ptype->flags & PT_WORLDSPACERAND)
5396 {
5397 vec3_t vtmp;
5398 do
5399 {
5400 vtmp[0] = crand();
5401 vtmp[1] = crand();
5402 vtmp[2] = crand();
5403 } while(DotProduct(vtmp,vtmp)>1); //crap, but I'm trying to mimic dp
5404 p->org[0] += vtmp[0] * ptype->orgwrand[0];
5405 p->org[1] += vtmp[1] * ptype->orgwrand[1];
5406 p->org[2] += vtmp[2] * ptype->orgwrand[2];
5407 p->vel[0] += vtmp[0] * ptype->velwrand[0];
5408 p->vel[1] += vtmp[1] * ptype->velwrand[1];
5409 p->vel[2] += vtmp[2] * ptype->velwrand[2];
5410 VectorAdd(p->vel, ptype->velbias, p->vel);
5411 }
5412 VectorAdd(p->org, ptype->orgbias, p->org);
5413
5414 VectorAdd (start, vstep, start);
5415
5416 if (ptype->countrand)
5417 {
5418 float rstep = frandom() / ptype->countrand;
5419 VectorMA(start, rstep, vec, start);
5420 step += rstep;
5421 }
5422
5423 p->die = particletime + ptype->die - p->die;
5424 VectorCopy(p->org, p->oldorg);
5425 }
5426
5427 if (ts)
5428 {
5429 ts->state1.lastdist = len;
5430
5431 // update beamseg list
5432 if (ptype->looks.type == PT_BEAM)
5433 {
5434 if (b)
5435 {
5436 if (ptype->beams)
5437 {
5438 if (ts->lastbeam)
5439 {
5440 b->next = ts->lastbeam->next;
5441 ts->lastbeam->next = bfirst;
5442 ts->lastbeam->flags &= ~BS_LASTSEG;
5443 }
5444 else
5445 {
5446 b->next = ptype->beams;
5447 ptype->beams = bfirst;
5448 }
5449 }
5450 else
5451 {
5452 ptype->beams = bfirst;
5453 b->next = NULL;
5454 }
5455
5456 b->flags |= BS_LASTSEG;
5457 ts->lastbeam = b;
5458 }
5459
5460 if ((!free_particles || !free_beams) && ts->lastbeam)
5461 {
5462 ts->lastbeam->flags &= ~BS_LASTSEG;
5463 ts->lastbeam->flags |= BS_NODRAW;
5464 ts->lastbeam = NULL;
5465 }
5466 }
5467 }
5468 else if (ptype->looks.type == PT_BEAM)
5469 {
5470 if (b)
5471 {
5472 b->flags |= BS_NODRAW;
5473 b->next = ptype->beams;
5474 ptype->beams = bfirst;
5475 }
5476 }
5477
5478 // maintain run list
5479 if (!(ptype->state & PS_INRUNLIST))
5480 {
5481 ptype->nexttorun = part_run_list;
5482 part_run_list = ptype;
5483 ptype->state |= PS_INRUNLIST;
5484 }
5485
5486 return;
5487 }
5488
PScript_ParticleTrail(vec3_t startpos,vec3_t end,int type,float timeinterval,int dlkey,vec3_t axis[3],trailstate_t ** tsk)5489 int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], trailstate_t **tsk)
5490 {
5491 part_type_t *ptype = &part_type[type];
5492
5493 if (r_fteparticles.value == 0)
5494 return 1;
5495
5496 if (type < 0 || type >= numparticletypes)
5497 return 1; //bad value
5498
5499 if (!ptype->loaded)
5500 return 1;
5501
5502 // inwater check, switch only once
5503 if (r_part_contentswitch.value && ptype->inwater >= 0 && cl.worldmodel)
5504 {
5505 unsigned int cont;
5506 cont = CL_PointContentsMask(startpos);
5507
5508 if (cont & FTECONTENTS_FLUID)
5509 ptype = &part_type[ptype->inwater];
5510 }
5511
5512 PScript_ParticleTrailSpawn (startpos, end, ptype, timeinterval, tsk, dlkey, axis);
5513 return 0;
5514 }
5515
5516 static int current_buffer_index = 0;
5517 static VkBuffer vertex_buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
5518 static vulkan_memory_t vertex_buffers_memory[2] = {{VK_NULL_HANDLE, 0, 0}, {VK_NULL_HANDLE, 0, 0}};
5519 static VkBuffer index_buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
5520 static vulkan_memory_t index_buffers_memory[2] = {{VK_NULL_HANDLE, 0, 0}, {VK_NULL_HANDLE, 0, 0}};
5521
ReallocateVertexBuffer()5522 static void ReallocateVertexBuffer()
5523 {
5524 VkResult err;
5525
5526 vulkan_memory_t old_memory = vertex_buffers_memory[current_buffer_index];
5527 const basicvertex_t* old_cl_curstrisvert = cl_curstrisvert;
5528 const int old_maxstrisvert = cl_maxstrisvert[current_buffer_index];
5529
5530 cl_maxstrisvert[current_buffer_index] = q_max(cl_maxstrisvert[current_buffer_index] * 2, INITIAL_NUM_VERTICES);
5531 const VkDeviceSize new_size = cl_maxstrisvert[current_buffer_index] * sizeof(basicvertex_t);
5532 Sys_Printf("Reallocating FTE particle vertex buffer (%u KB)\n", (int)(new_size / 1024));
5533
5534 VkBufferCreateInfo buffer_create_info;
5535 memset(&buffer_create_info, 0, sizeof(buffer_create_info));
5536 buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5537 buffer_create_info.size = new_size;
5538 buffer_create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
5539
5540 err = vkCreateBuffer(vulkan_globals.device, &buffer_create_info, NULL, &vertex_buffers[current_buffer_index]);
5541 if (err != VK_SUCCESS)
5542 Sys_Error("vkCreateBuffer failed");
5543 GL_SetObjectName((uint64_t)vertex_buffers[current_buffer_index], VK_OBJECT_TYPE_BUFFER, "FTE Particle Vertex Buffer");
5544
5545 VkMemoryRequirements memory_requirements;
5546 vkGetBufferMemoryRequirements(vulkan_globals.device, vertex_buffers[current_buffer_index], &memory_requirements);
5547
5548 const int align_mod = memory_requirements.size % memory_requirements.alignment;
5549 const int aligned_size = ((memory_requirements.size % memory_requirements.alignment) == 0)
5550 ? memory_requirements.size
5551 : (memory_requirements.size + memory_requirements.alignment - align_mod);
5552
5553 VkMemoryAllocateInfo memory_allocate_info;
5554 memset(&memory_allocate_info, 0, sizeof(memory_allocate_info));
5555 memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
5556 memory_allocate_info.allocationSize = aligned_size;
5557 memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
5558
5559 R_AllocateVulkanMemory(&vertex_buffers_memory[current_buffer_index], &memory_allocate_info, VULKAN_MEMORY_TYPE_HOST);
5560 GL_SetObjectName((uint64_t)vertex_buffers_memory[current_buffer_index].handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "FTE Particle Vertex Buffer");
5561
5562 err = vkBindBufferMemory(vulkan_globals.device, vertex_buffers[current_buffer_index], vertex_buffers_memory[current_buffer_index].handle, 0);
5563 if (err != VK_SUCCESS)
5564 Sys_Error("vkBindBufferMemory failed");
5565
5566 err = vkMapMemory(vulkan_globals.device, vertex_buffers_memory[current_buffer_index].handle, 0, new_size, 0, (void**)&cl_curstrisvert);
5567 if (err != VK_SUCCESS)
5568 Sys_Error("vkMapMemory failed");
5569 cl_strisvert[current_buffer_index] = cl_curstrisvert;
5570
5571 if (old_memory.handle != VK_NULL_HANDLE)
5572 {
5573 // Copy over data from old buffer
5574 memcpy(cl_curstrisvert, old_cl_curstrisvert, old_maxstrisvert * sizeof(basicvertex_t));
5575
5576 vkUnmapMemory(vulkan_globals.device, old_memory.handle);
5577 R_FreeVulkanMemory(&old_memory);
5578 }
5579 }
5580
ReallocateIndexBuffer()5581 static void ReallocateIndexBuffer()
5582 {
5583 VkResult err;
5584
5585 vulkan_memory_t old_memory = index_buffers_memory[current_buffer_index];
5586 const unsigned short * old_cl_curstrisidx = cl_curstrisidx;
5587 const int old_maxstrisidx = cl_maxstrisidx[current_buffer_index];
5588
5589 cl_maxstrisidx[current_buffer_index] = q_max(cl_maxstrisidx[current_buffer_index] * 2, INITIAL_NUM_INDICES);
5590 const VkDeviceSize new_size = cl_maxstrisidx[current_buffer_index] * sizeof(unsigned short);
5591 Sys_Printf("Reallocating FTE particle index buffer (%u KB)\n", (int)(new_size / 1024));
5592
5593 VkBufferCreateInfo buffer_create_info;
5594 memset(&buffer_create_info, 0, sizeof(buffer_create_info));
5595 buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5596 buffer_create_info.size = new_size;
5597 buffer_create_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
5598
5599 err = vkCreateBuffer(vulkan_globals.device, &buffer_create_info, NULL, &index_buffers[current_buffer_index]);
5600 if (err != VK_SUCCESS)
5601 Sys_Error("vkCreateBuffer failed");
5602 GL_SetObjectName((uint64_t)index_buffers[current_buffer_index], VK_OBJECT_TYPE_BUFFER, "FTE Particle Index Buffer");
5603
5604 VkMemoryRequirements memory_requirements;
5605 vkGetBufferMemoryRequirements(vulkan_globals.device, index_buffers[current_buffer_index], &memory_requirements);
5606
5607 const int align_mod = memory_requirements.size % memory_requirements.alignment;
5608 const int aligned_size = ((memory_requirements.size % memory_requirements.alignment) == 0)
5609 ? memory_requirements.size
5610 : (memory_requirements.size + memory_requirements.alignment - align_mod);
5611
5612 VkMemoryAllocateInfo memory_allocate_info;
5613 memset(&memory_allocate_info, 0, sizeof(memory_allocate_info));
5614 memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
5615 memory_allocate_info.allocationSize = aligned_size;
5616 memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
5617
5618 R_AllocateVulkanMemory(&index_buffers_memory[current_buffer_index], &memory_allocate_info, VULKAN_MEMORY_TYPE_HOST);
5619 GL_SetObjectName((uint64_t)index_buffers_memory[current_buffer_index].handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "FTE Particle index Buffer");
5620
5621 err = vkBindBufferMemory(vulkan_globals.device, index_buffers[current_buffer_index], index_buffers_memory[current_buffer_index].handle, 0);
5622 if (err != VK_SUCCESS)
5623 Sys_Error("vkBindBufferMemory failed");
5624
5625 err = vkMapMemory(vulkan_globals.device, index_buffers_memory[current_buffer_index].handle, 0, new_size, 0, (void**)&cl_curstrisidx);
5626 if (err != VK_SUCCESS)
5627 Sys_Error("vkMapMemory failed");
5628 cl_strisidx[current_buffer_index] = cl_curstrisidx;
5629
5630 if (old_memory.handle != VK_NULL_HANDLE)
5631 {
5632 // Copy over data from old buffer
5633 memcpy(cl_curstrisidx, old_cl_curstrisidx, old_maxstrisidx * sizeof(unsigned short));
5634
5635 vkUnmapMemory(vulkan_globals.device, old_memory.handle);
5636 R_FreeVulkanMemory(&old_memory);
5637 }
5638 }
5639
5640 static vec3_t pright, pup;
5641
R_AddFanSparkParticle(scenetris_t * t,particle_t * p,plooks_t * type)5642 static void R_AddFanSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
5643 {
5644 vec3_t v, cr, o2;
5645 float scale;
5646
5647 if (cl_numstrisvert+3 > cl_maxstrisvert[current_buffer_index])
5648 ReallocateVertexBuffer();
5649
5650 scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]
5651 + (p->org[2] - r_origin[2])*vpn[2];
5652 scale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);
5653 if (scale < 20)
5654 scale = 0.05;
5655 else
5656 scale = 0.05 + scale * 0.0001;
5657
5658 if (type->premul)
5659 {
5660 vec4_t rgba;
5661 float a = p->rgba[3];
5662 if (a > 1)
5663 a = 1;
5664 a *= 255.0f;
5665 rgba[0] = p->rgba[0] * a;
5666 rgba[1] = p->rgba[1] * a;
5667 rgba[2] = p->rgba[2] * a;
5668 rgba[3] = (type->premul==2)?0:a;
5669 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5670 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5671 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5672 }
5673 else
5674 {
5675 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5676 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5677 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5678 }
5679
5680 Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5681 Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
5682 Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t1);
5683
5684
5685 VectorMA(p->org, -scale, p->vel, o2);
5686 VectorSubtract(r_refdef.vieworg, o2, v);
5687 CrossProduct(v, p->vel, cr);
5688 VectorNormalize(cr);
5689
5690 VectorCopy(p->org, cl_curstrisvert[cl_numstrisvert+0].position);
5691 VectorMA(o2, -p->scale, cr, cl_curstrisvert[cl_numstrisvert+1].position);
5692 VectorMA(o2, p->scale, cr, cl_curstrisvert[cl_numstrisvert+2].position);
5693
5694 if (cl_numstrisidx+3 > cl_maxstrisidx[current_buffer_index])
5695 ReallocateIndexBuffer();
5696
5697 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5698 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5699 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5700
5701 cl_numstrisvert += 3;
5702
5703 t->numvert += 3;
5704 t->numidx += 3;
5705
5706 }
5707
R_AddLineSparkParticle(scenetris_t * t,particle_t * p,plooks_t * type)5708 static void R_AddLineSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
5709 {
5710 if (cl_numstrisvert+2 > cl_maxstrisvert[current_buffer_index])
5711 ReallocateVertexBuffer();
5712
5713 if (type->premul)
5714 {
5715 vec4_t scaled_color;
5716 float a = p->rgba[3];
5717 if (a > 1)
5718 a = 1;
5719 VectorScale(p->rgba, a, scaled_color);
5720 Vector3ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+0].color);
5721 FloatToColor((type->premul==2)?0:a, cl_curstrisvert[cl_numstrisvert+0].color[3]);
5722 Vector4Clear(cl_curstrisvert[cl_numstrisvert+1].color);
5723 }
5724 else
5725 {
5726 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5727 Vector3ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5728 cl_curstrisvert[cl_numstrisvert+1].color[3] = 0;
5729 }
5730 Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5731 Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s2, p->t2);
5732
5733 VectorCopy(p->org, cl_curstrisvert[cl_numstrisvert+0].position);
5734 VectorMA(p->org, -1.0/10, p->vel, cl_curstrisvert[cl_numstrisvert+1].position);
5735
5736 if (cl_numstrisidx+2 > cl_maxstrisidx[current_buffer_index])
5737 ReallocateIndexBuffer();
5738
5739 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5740 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5741
5742 cl_numstrisvert += 2;
5743
5744 t->numvert += 2;
5745 t->numidx += 2;
5746 }
5747
R_AddTSparkParticle(scenetris_t * t,particle_t * p,plooks_t * type)5748 static void R_AddTSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
5749 {
5750 vec3_t v, cr, o2;
5751
5752 if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5753 ReallocateVertexBuffer();
5754
5755 if (type->premul)
5756 {
5757 vec4_t rgba;
5758 float a = p->rgba[3];
5759 if (a > 1)
5760 a = 1;
5761 rgba[0] = p->rgba[0] * a;
5762 rgba[1] = p->rgba[1] * a;
5763 rgba[2] = p->rgba[2] * a;
5764 rgba[3] = (type->premul==2)?0:a;
5765 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5766 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5767 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5768 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5769 }
5770 else
5771 {
5772 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5773 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5774 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5775 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5776 }
5777
5778 Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5779 Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
5780 Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t2);
5781 Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, p->s2, p->t1);
5782
5783 {
5784 vec3_t movedir;
5785 float halfscale = p->scale*0.5;
5786 float length = VectorNormalize2(p->vel, movedir);
5787 if (type->stretch < 0)
5788 length = -type->stretch; //fixed lengths
5789 else if (type->stretch)
5790 length *= type->stretch; //velocity multiplier
5791 else
5792 Sys_Error("type->stretch should be 0.05\n");
5793 // length *= 0.05; //fallback
5794
5795 if (length < halfscale * type->minstretch)
5796 length = halfscale * type->minstretch;
5797
5798 VectorMA(p->org, -length, movedir, o2);
5799 VectorSubtract(r_refdef.vieworg, o2, v);
5800 CrossProduct(v, p->vel, cr);
5801 VectorNormalize(cr);
5802 VectorMA(o2, -p->scale/2, cr, cl_curstrisvert[cl_numstrisvert+0].position);
5803 VectorMA(o2, p->scale/2, cr, cl_curstrisvert[cl_numstrisvert+1].position);
5804
5805 VectorMA(p->org, length, movedir, o2);
5806 }
5807
5808 VectorSubtract(r_refdef.vieworg, o2, v);
5809 CrossProduct(v, p->vel, cr);
5810 VectorNormalize(cr);
5811
5812 VectorMA(o2, p->scale*0.5, cr, cl_curstrisvert[cl_numstrisvert+2].position);
5813 VectorMA(o2, -p->scale*0.5, cr, cl_curstrisvert[cl_numstrisvert+3].position);
5814
5815
5816
5817 if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
5818 ReallocateIndexBuffer();
5819
5820 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5821 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5822 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5823 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5824 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5825 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
5826
5827 cl_numstrisvert += 4;
5828
5829 t->numvert += 4;
5830 t->numidx += 6;
5831 }
5832
R_DrawParticleBeam(scenetris_t * t,beamseg_t * b,plooks_t * type)5833 static void R_DrawParticleBeam(scenetris_t *t, beamseg_t *b, plooks_t *type)
5834 {
5835 vec3_t v;
5836 vec3_t cr;
5837 beamseg_t *c;
5838 particle_t *p;
5839 particle_t *q;
5840 float ts;
5841
5842 c = b->next;
5843
5844 q = c->p;
5845 if (!q)
5846 return;
5847 p = b->p;
5848
5849 if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5850 ReallocateVertexBuffer();
5851
5852 VectorSubtract(r_refdef.vieworg, q->org, v);
5853 VectorNormalize(v);
5854 CrossProduct(c->dir, v, cr);
5855 VectorNormalize(cr);
5856 ts = c->texture_s*q->angle + particletime*q->rotationspeed;
5857 Vector4ToColor(q->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5858 Vector4ToColor(q->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5859 Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, ts, p->t1);
5860 Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, ts, p->t2);
5861 VectorMA(q->org, -q->scale, cr, cl_curstrisvert[cl_numstrisvert+0].position);
5862 VectorMA(q->org, q->scale, cr, cl_curstrisvert[cl_numstrisvert+1].position);
5863
5864 VectorSubtract(r_refdef.vieworg, p->org, v);
5865 VectorNormalize(v);
5866 CrossProduct(b->dir, v, cr); // replace with old p->dir?
5867 VectorNormalize(cr);
5868 ts = b->texture_s*p->angle + particletime*p->rotationspeed;
5869 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5870 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5871 Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, ts, p->t2);
5872 Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, ts, p->t1);
5873 VectorMA(p->org, p->scale, cr, cl_curstrisvert[cl_numstrisvert+2].position);
5874 VectorMA(p->org, -p->scale, cr, cl_curstrisvert[cl_numstrisvert+3].position);
5875
5876 t->numvert += 4;
5877
5878 if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
5879 ReallocateIndexBuffer();
5880
5881 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5882 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5883 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5884 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5885 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5886 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
5887 cl_numstrisvert += 4;
5888 t->numidx += 4;
5889 }
5890
R_AddClippedDecal(scenetris_t * t,clippeddecal_t * d,plooks_t * type)5891 static void R_AddClippedDecal(scenetris_t *t, clippeddecal_t *d, plooks_t *type)
5892 {
5893 if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5894 ReallocateVertexBuffer();
5895
5896 if (d->entity > 0)
5897 {
5898 entity_t *le = CL_EntityNum(d->entity);
5899 if (le->angles[0] || le->angles[1] || le->angles[2])
5900 { //FIXME: deal with rotated entities.
5901 d->die = -1;
5902 return;
5903 }
5904 VectorAdd(d->vertex[0], le->origin, cl_curstrisvert[cl_numstrisvert+0].position);
5905 VectorAdd(d->vertex[1], le->origin, cl_curstrisvert[cl_numstrisvert+1].position);
5906 VectorAdd(d->vertex[2], le->origin, cl_curstrisvert[cl_numstrisvert+2].position);
5907 }
5908 else
5909 {
5910 VectorCopy(d->vertex[0], cl_curstrisvert[cl_numstrisvert+0].position);
5911 VectorCopy(d->vertex[1], cl_curstrisvert[cl_numstrisvert+1].position);
5912 VectorCopy(d->vertex[2], cl_curstrisvert[cl_numstrisvert+2].position);
5913 }
5914
5915 if (type->premul)
5916 {
5917 vec4_t rgba;
5918 vec4_t scaled_color;
5919 float a = d->rgba[3];
5920 if (a > 1)
5921 a = 1;
5922 rgba[0] = d->rgba[0] * a;
5923 rgba[1] = d->rgba[1] * a;
5924 rgba[2] = d->rgba[2] * a;
5925 rgba[3] = (type->premul==2)?0:a;
5926 Vector4Scale(rgba, d->valpha[0], scaled_color);
5927 Vector4ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+0].color);
5928 Vector4Scale(rgba, d->valpha[1], scaled_color);
5929 Vector4ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+1].color);
5930 Vector4Scale(rgba, d->valpha[2], scaled_color);
5931 Vector4ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+2].color);
5932 }
5933 else
5934 {
5935 vec4_t rgba;
5936 rgba[0] = d->rgba[0];
5937 rgba[1] = d->rgba[1];
5938 rgba[2] = d->rgba[2];
5939 rgba[3] = d->rgba[3] * d->valpha[0];
5940 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5941 rgba[3] = d->rgba[3] * d->valpha[1];
5942 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5943 rgba[3] = d->rgba[3] * d->valpha[2];
5944 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5945 }
5946
5947 Vector2Copy(d->texcoords[0], cl_curstrisvert[cl_numstrisvert+0].texcoord);
5948 Vector2Copy(d->texcoords[1], cl_curstrisvert[cl_numstrisvert+1].texcoord);
5949 Vector2Copy(d->texcoords[2], cl_curstrisvert[cl_numstrisvert+2].texcoord);
5950
5951 if (cl_numstrisidx+3 > cl_maxstrisidx[current_buffer_index])
5952 ReallocateIndexBuffer();
5953
5954 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5955 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5956 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5957
5958 cl_numstrisvert += 3;
5959
5960 t->numvert += 3;
5961 t->numidx += 3;
5962 }
5963
R_AddUnclippedDecal(scenetris_t * t,particle_t * p,plooks_t * type)5964 static void R_AddUnclippedDecal(scenetris_t *t, particle_t *p, plooks_t *type)
5965 {
5966 float x, y;
5967 vec3_t sdir, tdir;
5968
5969 if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5970 ReallocateVertexBuffer();
5971
5972 if (type->premul)
5973 {
5974 vec4_t rgba;
5975 float a = p->rgba[3];
5976 if (a > 1)
5977 a = 1;
5978 rgba[0] = p->rgba[0] * a;
5979 rgba[1] = p->rgba[1] * a;
5980 rgba[2] = p->rgba[2] * a;
5981 rgba[3] = (type->premul==2)?0:a;
5982 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5983 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5984 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5985 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5986 }
5987 else
5988 {
5989 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5990 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5991 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5992 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5993 }
5994
5995 Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5996 Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
5997 Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t2);
5998 Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, p->s2, p->t1);
5999
6000 // if (p->vel[1] == 1)
6001 {
6002 VectorSet(sdir, 1, 0, 0);
6003 VectorSet(tdir, 0, 1, 0);
6004 }
6005
6006 if (p->angle)
6007 {
6008 x = sin(p->angle)*p->scale;
6009 y = cos(p->angle)*p->scale;
6010
6011 cl_curstrisvert[cl_numstrisvert+0].position[0] = p->org[0] - x*sdir[0] - y*tdir[0];
6012 cl_curstrisvert[cl_numstrisvert+0].position[1] = p->org[1] - x*sdir[1] - y*tdir[1];
6013 cl_curstrisvert[cl_numstrisvert+0].position[2] = p->org[2] - x*sdir[2] - y*tdir[2];
6014 cl_curstrisvert[cl_numstrisvert+1].position[0] = p->org[0] - y*sdir[0] + x*tdir[0];
6015 cl_curstrisvert[cl_numstrisvert+1].position[1] = p->org[1] - y*sdir[1] + x*tdir[1];
6016 cl_curstrisvert[cl_numstrisvert+1].position[2] = p->org[2] - y*sdir[2] + x*tdir[2];
6017 cl_curstrisvert[cl_numstrisvert+2].position[0] = p->org[0] + x*sdir[0] + y*tdir[0];
6018 cl_curstrisvert[cl_numstrisvert+2].position[1] = p->org[1] + x*sdir[1] + y*tdir[1];
6019 cl_curstrisvert[cl_numstrisvert+2].position[2] = p->org[2] + x*sdir[2] + y*tdir[2];
6020 cl_curstrisvert[cl_numstrisvert+3].position[0] = p->org[0] + y*sdir[0] - x*tdir[0];
6021 cl_curstrisvert[cl_numstrisvert+3].position[1] = p->org[1] + y*sdir[1] - x*tdir[1];
6022 cl_curstrisvert[cl_numstrisvert+3].position[2] = p->org[2] + y*sdir[2] - x*tdir[2];
6023 }
6024 else
6025 {
6026 VectorMA(p->org, -p->scale, tdir, cl_curstrisvert[cl_numstrisvert+0].position);
6027 VectorMA(p->org, -p->scale, sdir, cl_curstrisvert[cl_numstrisvert+1].position);
6028 VectorMA(p->org, p->scale, tdir, cl_curstrisvert[cl_numstrisvert+2].position);
6029 VectorMA(p->org, p->scale, sdir, cl_curstrisvert[cl_numstrisvert+3].position);
6030 }
6031
6032 if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
6033 ReallocateIndexBuffer();
6034
6035 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6036 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
6037 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6038 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6039 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6040 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
6041
6042 cl_numstrisvert += 4;
6043
6044 t->numvert += 4;
6045 t->numidx += 6;
6046 }
6047
R_AddTexturedParticle(scenetris_t * t,particle_t * p,plooks_t * type)6048 static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type)
6049 {
6050 float scale, x, y;
6051
6052 if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
6053 ReallocateVertexBuffer();
6054
6055 if (type->scalefactor == 1)
6056 scale = p->scale*0.25;
6057 else
6058 {
6059 scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]
6060 + (p->org[2] - r_origin[2])*vpn[2];
6061 scale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);
6062 if (scale < 20)
6063 scale = 0.25;
6064 else
6065 scale = 0.25 + scale * 0.001;
6066 }
6067
6068 if (type->premul)
6069 {
6070 vec4_t rgba;
6071 float a = p->rgba[3];
6072 if (a > 1)
6073 a = 1;
6074 rgba[0] = p->rgba[0] * a;
6075 rgba[1] = p->rgba[1] * a;
6076 rgba[2] = p->rgba[2] * a;
6077 rgba[3] = (type->premul==2)?0:a;
6078 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
6079 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
6080 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
6081 Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+3].color);
6082 }
6083 else
6084 {
6085 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
6086 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
6087 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
6088 Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
6089 }
6090
6091 Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
6092 Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
6093 Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t2);
6094 Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, p->s2, p->t1);
6095
6096 if (p->angle)
6097 {
6098 x = sin(p->angle)*scale;
6099 y = cos(p->angle)*scale;
6100
6101 cl_curstrisvert[cl_numstrisvert+0].position[0] = p->org[0] - x*pright[0] - y*pup[0];
6102 cl_curstrisvert[cl_numstrisvert+0].position[1] = p->org[1] - x*pright[1] - y*pup[1];
6103 cl_curstrisvert[cl_numstrisvert+0].position[2] = p->org[2] - x*pright[2] - y*pup[2];
6104 cl_curstrisvert[cl_numstrisvert+1].position[0] = p->org[0] - y*pright[0] + x*pup[0];
6105 cl_curstrisvert[cl_numstrisvert+1].position[1] = p->org[1] - y*pright[1] + x*pup[1];
6106 cl_curstrisvert[cl_numstrisvert+1].position[2] = p->org[2] - y*pright[2] + x*pup[2];
6107 cl_curstrisvert[cl_numstrisvert+2].position[0] = p->org[0] + x*pright[0] + y*pup[0];
6108 cl_curstrisvert[cl_numstrisvert+2].position[1] = p->org[1] + x*pright[1] + y*pup[1];
6109 cl_curstrisvert[cl_numstrisvert+2].position[2] = p->org[2] + x*pright[2] + y*pup[2];
6110 cl_curstrisvert[cl_numstrisvert+3].position[0] = p->org[0] + y*pright[0] - x*pup[0];
6111 cl_curstrisvert[cl_numstrisvert+3].position[1] = p->org[1] + y*pright[1] - x*pup[1];
6112 cl_curstrisvert[cl_numstrisvert+3].position[2] = p->org[2] + y*pright[2] - x*pup[2];
6113 }
6114 else
6115 {
6116 VectorMA(p->org, -scale, pup, cl_curstrisvert[cl_numstrisvert+0].position);
6117 VectorMA(p->org, -scale, pright, cl_curstrisvert[cl_numstrisvert+1].position);
6118 VectorMA(p->org, scale, pup, cl_curstrisvert[cl_numstrisvert+2].position);
6119 VectorMA(p->org, scale, pright, cl_curstrisvert[cl_numstrisvert+3].position);
6120 }
6121
6122 if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
6123 ReallocateIndexBuffer();
6124
6125 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6126 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
6127 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6128 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6129 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6130 cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
6131
6132 cl_numstrisvert += 4;
6133
6134 t->numvert += 4;
6135 t->numidx += 6;
6136 }
6137
PScript_DrawParticleTypes(float pframetime)6138 static void PScript_DrawParticleTypes (float pframetime)
6139 {
6140 void (*bdraw)(scenetris_t *t, beamseg_t *p, plooks_t *type);
6141 void (*tdraw)(scenetris_t *t, particle_t *p, plooks_t *type);
6142
6143 vec3_t oldorg;
6144 vec3_t stop, normal;
6145 part_type_t *type, *lastvalidtype;
6146 particle_t *p, *kill;
6147 clippeddecal_t *d, *dkill;
6148 ramp_t *ramp;
6149 float grav;
6150 vec3_t friction;
6151 scenetris_t *scenetri;
6152 float dist;
6153 particle_t *kill_list, *kill_first; //the kill list is to stop particles from being freed and reused whilst still in this loop
6154 //which is bad because beams need to find out when particles died. Reuse can do wierd things.
6155 //remember that they're not drawn instantly either.
6156 beamseg_t *b, *bkill;
6157
6158 int traces=r_particle_tracelimit.value;
6159 int rampind;
6160 static float flurrytime;
6161 qboolean doflurry;
6162 int batchflags;
6163 unsigned int i, o;
6164
6165 if (r_plooksdirty)
6166 {
6167 int j, k;
6168
6169 pe_default = PScript_FindParticleType("PE_DEFAULT");
6170 pe_size2 = PScript_FindParticleType("PE_SIZE2");
6171 pe_size3 = PScript_FindParticleType("PE_SIZE3");
6172 pe_defaulttrail = PScript_FindParticleType("PE_DEFAULTTRAIL");
6173
6174 for (j = 0; j < numparticletypes; j++)
6175 {
6176 //set the fallback
6177 part_type[j].slooks = &part_type[j].looks;
6178 for (k = j-1; k-- > 0;)
6179 {
6180 if (!memcmp(&part_type[j].looks, &part_type[k].looks, sizeof(plooks_t)))
6181 {
6182 part_type[j].slooks = part_type[k].slooks;
6183 break;
6184 }
6185 }
6186 }
6187 r_plooksdirty = false;
6188 CL_RegisterParticles();
6189 PScript_RecalculateSkyTris();
6190 }
6191
6192 VectorScale (vup, 1.5, pup);
6193 VectorScale (vright, 1.5, pright);
6194
6195 kill_list = kill_first = NULL;
6196
6197 flurrytime -= pframetime;
6198 if (flurrytime < 0)
6199 {
6200 doflurry = true;
6201 flurrytime = 0.1+frandom()*0.3;
6202 }
6203 else
6204 doflurry = false;
6205
6206
6207 if (!free_decals)
6208 {
6209 //mark some as dead, so we can keep spawning new ones next frame.
6210 for (i = 0; i < 256; i++)
6211 {
6212 decals[r_decalrecycle].die = -1;
6213 if (++r_decalrecycle >= r_numdecals)
6214 r_decalrecycle = 0;
6215 }
6216 }
6217 if (!free_particles)
6218 {
6219 //mark some as dead.
6220 for (i = 0; i < 256; i++)
6221 {
6222 particles[r_particlerecycle].die = -1;
6223 if (++r_particlerecycle >= r_numparticles)
6224 r_particlerecycle = 0;
6225 }
6226 }
6227
6228 for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun)
6229 {
6230 if (type->clippeddecals)
6231 {
6232 if (cl_numstris && cl_stris[cl_numstris-1].texture == type->looks.texture && cl_stris[cl_numstris-1].blendmode == type->looks.blendmode && cl_stris[cl_numstris-1].beflags == 0)
6233 scenetri = &cl_stris[cl_numstris-1];
6234 else
6235 {
6236 if (cl_numstris == cl_maxstris)
6237 {
6238 cl_maxstris+=8;
6239 cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6240 }
6241 scenetri = &cl_stris[cl_numstris++];
6242 scenetri->texture = type->looks.texture;
6243 scenetri->blendmode = type->looks.blendmode;
6244 scenetri->beflags = 0;
6245 scenetri->firstidx = cl_numstrisidx;
6246 scenetri->firstvert = cl_numstrisvert;
6247 scenetri->numvert = 0;
6248 scenetri->numidx = 0;
6249 }
6250
6251 for ( ;; )
6252 {
6253 dkill = type->clippeddecals;
6254 if (dkill && dkill->die < particletime)
6255 {
6256 type->clippeddecals = dkill->next;
6257 dkill->next = free_decals;
6258 free_decals = dkill;
6259 continue;
6260 }
6261 break;
6262 }
6263 for (d=type->clippeddecals ; d ; d=d->next)
6264 {
6265 for ( ;; )
6266 {
6267 dkill = d->next;
6268 if (dkill && dkill->die < particletime)
6269 {
6270 d->next = dkill->next;
6271 dkill->next = free_decals;
6272 free_decals = dkill;
6273 continue;
6274 }
6275 break;
6276 }
6277
6278
6279 if (d->die - particletime <= type->die)
6280 {
6281 switch (type->rampmode)
6282 {
6283 case RAMP_NEAREST:
6284 rampind = (int)(type->rampindexes * (type->die - (d->die - particletime)) / type->die);
6285 if (rampind >= type->rampindexes)
6286 rampind = type->rampindexes - 1;
6287 ramp = type->ramp + rampind;
6288 VectorCopy(ramp->rgb, d->rgba);
6289 d->rgba[3] = ramp->alpha;
6290 break;
6291 case RAMP_LERP:
6292 {
6293 float frac = (type->rampindexes * (type->die - (d->die - particletime)) / type->die);
6294 int s1, s2;
6295 s1 = frac;
6296 s2 = s1+1;
6297 if (s1 > type->rampindexes-1)
6298 s1 = type->rampindexes-1;
6299 if (s2 > type->rampindexes-1)
6300 s2 = type->rampindexes-1;
6301 frac -= s1;
6302 VectorInterpolate(type->ramp[s1].rgb, frac, type->ramp[s2].rgb, d->rgba);
6303 FloatInterpolate(type->ramp[s1].alpha, frac, type->ramp[s2].alpha, d->rgba[3]);
6304 }
6305 break;
6306 case RAMP_DELTA: //particle ramps
6307 ramp = type->ramp + (int)(type->rampindexes * (type->die - (d->die - particletime)) / type->die);
6308 VectorMA(d->rgba, pframetime, ramp->rgb, d->rgba);
6309 d->rgba[3] -= pframetime*ramp->alpha;
6310 break;
6311 case RAMP_NONE: //particle changes acording to it's preset properties.
6312 if (particletime < (d->die-type->die+type->rgbchangetime))
6313 {
6314 d->rgba[0] += pframetime*type->rgbchange[0];
6315 d->rgba[1] += pframetime*type->rgbchange[1];
6316 d->rgba[2] += pframetime*type->rgbchange[2];
6317 }
6318 d->rgba[3] += pframetime*type->alphachange;
6319 }
6320 }
6321
6322 if (cl_numstrisvert - scenetri->firstvert >= MAX_INDICES-6)
6323 {
6324 //generate a new mesh if the old one overflowed. yay smc...
6325 if (cl_numstris == cl_maxstris)
6326 {
6327 cl_maxstris+=8;
6328 cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6329 }
6330 scenetri = &cl_stris[cl_numstris++];
6331 scenetri->texture = scenetri[-1].texture;
6332 scenetri->blendmode = scenetri[-1].blendmode;
6333 scenetri->beflags = scenetri[-1].beflags;
6334 scenetri->firstidx = cl_numstrisidx;
6335 scenetri->firstvert = cl_numstrisvert;
6336 scenetri->numvert = 0;
6337 scenetri->numidx = 0;
6338 }
6339 R_AddClippedDecal(scenetri, d, type->slooks);
6340 }
6341 }
6342
6343 bdraw = NULL;
6344 tdraw = NULL;
6345 batchflags = 0;
6346
6347 // set drawing methods by type and cvars and hope branch
6348 // prediction takes care of the rest
6349 switch(type->looks.type)
6350 {
6351 default:
6352 case PT_INVISIBLE:
6353 break;
6354 case PT_BEAM:
6355 bdraw = R_DrawParticleBeam;
6356 break;
6357 case PT_CDECAL:
6358 break;
6359 case PT_UDECAL:
6360 tdraw = R_AddUnclippedDecal;
6361 break;
6362 case PT_NORMAL:
6363 tdraw = R_AddTexturedParticle;
6364 break;
6365 case PT_SPARK:
6366 tdraw = R_AddLineSparkParticle;
6367 batchflags = BEF_LINES;
6368 break;
6369 case PT_SPARKFAN:
6370 tdraw = R_AddFanSparkParticle;
6371 break;
6372 case PT_TEXTUREDSPARK:
6373 tdraw = R_AddTSparkParticle;
6374 break;
6375 }
6376
6377 if (cl_numstris && cl_stris[cl_numstris-1].texture == type->looks.texture && cl_stris[cl_numstris-1].blendmode == type->looks.blendmode && cl_stris[cl_numstris-1].beflags == batchflags)
6378 scenetri = &cl_stris[cl_numstris-1];
6379 else
6380 {
6381 if (cl_numstris == cl_maxstris)
6382 {
6383 cl_maxstris+=8;
6384 cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6385 }
6386 scenetri = &cl_stris[cl_numstris++];
6387 scenetri->texture = type->looks.texture;
6388 scenetri->blendmode = type->looks.blendmode;
6389 scenetri->beflags = batchflags;
6390 scenetri->firstidx = cl_numstrisidx;
6391 scenetri->firstvert = cl_numstrisvert;
6392 scenetri->numvert = 0;
6393 scenetri->numidx = 0;
6394 }
6395
6396 if (!type->die)
6397 {
6398 while ((p=type->particles))
6399 {
6400 if (scenetri && tdraw)
6401 {
6402 if (cl_numstrisvert - scenetri->firstvert >= MAX_INDICES-6)
6403 {
6404 //generate a new mesh if the old one overflowed. yay smc...
6405 if (cl_numstris == cl_maxstris)
6406 {
6407 cl_maxstris+=8;
6408 cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6409 }
6410 scenetri = &cl_stris[cl_numstris++];
6411 scenetri->texture = scenetri[-1].texture;
6412 scenetri->blendmode = scenetri[-1].blendmode;
6413 scenetri->beflags = scenetri[-1].beflags;
6414 scenetri->firstidx = cl_numstrisidx;
6415 scenetri->firstvert = cl_numstrisvert;
6416 scenetri->numvert = 0;
6417 scenetri->numidx = 0;
6418 }
6419 tdraw(scenetri, p, type->slooks);
6420 }
6421
6422 // make sure emitter runs at least once
6423 if (type->emit >= 0 && type->emitstart <= 0)
6424 PScript_RunParticleEffectState(p->org, p->vel, 1, type->emit, NULL);
6425
6426 type->particles = p->next;
6427 p->next = kill_list;
6428 kill_list = p;
6429 if (!kill_first) // branch here is probably faster than list traversal later
6430 kill_first = p;
6431 }
6432
6433 if (type->beams)
6434 {
6435 b = type->beams;
6436 }
6437
6438 while ((b=type->beams) && (b->flags & BS_DEAD))
6439 {
6440 type->beams = b->next;
6441 b->next = free_beams;
6442 free_beams = b;
6443 }
6444
6445 while (b)
6446 {
6447 if (!(b->flags & BS_NODRAW))
6448 {
6449 // no BS_NODRAW implies b->next != NULL
6450 // BS_NODRAW should imply b->next == NULL or b->next->flags & BS_DEAD
6451 VectorCopy(b->next->p->org, stop);
6452 VectorCopy(b->p->org, oldorg);
6453 VectorSubtract(stop, oldorg, b->next->dir);
6454 VectorNormalize(b->next->dir);
6455 if (bdraw)
6456 bdraw(scenetri, b, type->slooks);
6457 }
6458
6459 // clean up dead entries ahead of current
6460 for ( ;; )
6461 {
6462 bkill = b->next;
6463 if (bkill && (bkill->flags & BS_DEAD))
6464 {
6465 b->next = bkill->next;
6466 bkill->next = free_beams;
6467 free_beams = bkill;
6468 continue;
6469 }
6470 break;
6471 }
6472
6473 b->flags |= BS_DEAD;
6474 b = b->next;
6475 }
6476
6477 goto endtype;
6478 }
6479
6480 //kill off early ones.
6481 if (type->emittime < 0)
6482 {
6483 for ( ;; )
6484 {
6485 kill = type->particles;
6486 if (kill && kill->die < particletime)
6487 {
6488 PScript_DelinkTrailstate(&kill->state.trailstate);
6489 type->particles = kill->next;
6490 kill->next = kill_list;
6491 kill_list = kill;
6492 if (!kill_first)
6493 kill_first = kill;
6494 continue;
6495 }
6496 break;
6497 }
6498 }
6499 else
6500 {
6501 for ( ;; )
6502 {
6503 kill = type->particles;
6504 if (kill && kill->die < particletime)
6505 {
6506 type->particles = kill->next;
6507 kill->next = kill_list;
6508 kill_list = kill;
6509 if (!kill_first)
6510 kill_first = kill;
6511 continue;
6512 }
6513 break;
6514 }
6515 }
6516
6517 grav = type->gravity*pframetime;
6518 friction[0] = 1 - type->friction[0]*pframetime;
6519 friction[1] = 1 - type->friction[1]*pframetime;
6520 friction[2] = 1 - type->friction[2]*pframetime;
6521
6522 for (p=type->particles ; p ; p=p->next)
6523 {
6524 if (type->emittime < 0)
6525 {
6526 for ( ;; )
6527 {
6528 kill = p->next;
6529 if (kill && kill->die < particletime)
6530 {
6531 PScript_DelinkTrailstate(&kill->state.trailstate);
6532 p->next = kill->next;
6533 kill->next = kill_list;
6534 kill_list = kill;
6535 if (!kill_first)
6536 kill_first = kill;
6537 continue;
6538 }
6539 break;
6540 }
6541 }
6542 else
6543 {
6544 for ( ;; )
6545 {
6546 kill = p->next;
6547 if (kill && kill->die < particletime)
6548 {
6549 p->next = kill->next;
6550 kill->next = kill_list;
6551 kill_list = kill;
6552 if (!kill_first)
6553 kill_first = kill;
6554 continue;
6555 }
6556 break;
6557 }
6558 }
6559
6560 VectorCopy(p->org, oldorg);
6561 if (type->flags & PT_VELOCITY)
6562 {
6563 p->org[0] += p->vel[0]*pframetime;
6564 p->org[1] += p->vel[1]*pframetime;
6565 p->org[2] += p->vel[2]*pframetime;
6566 p->vel[2] -= grav;
6567 if (type->flags & PT_FRICTION)
6568 {
6569 p->vel[0] *= friction[0];
6570 p->vel[1] *= friction[1];
6571 p->vel[2] *= friction[2];
6572 }
6573 if (type->flurry && doflurry)
6574 { //these should probably be partially synced,
6575 p->vel[0] += crandom() * type->flurry;
6576 p->vel[1] += crandom() * type->flurry;
6577 }
6578 }
6579
6580 p->angle += p->rotationspeed*pframetime;
6581
6582 switch (type->rampmode)
6583 {
6584 case RAMP_NEAREST:
6585 rampind = (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);
6586 if (rampind >= type->rampindexes)
6587 rampind = type->rampindexes - 1;
6588 ramp = type->ramp + rampind;
6589 VectorCopy(ramp->rgb, p->rgba);
6590 p->rgba[3] = ramp->alpha;
6591 p->scale = ramp->scale;
6592 break;
6593 case RAMP_LERP:
6594 {
6595 float frac = (type->rampindexes * (type->die - (p->die - particletime)) / type->die);
6596 int s1, s2;
6597 s1 = frac;
6598 s2 = s1+1;
6599 if (s1 > type->rampindexes-1)
6600 s1 = type->rampindexes-1;
6601 if (s2 > type->rampindexes-1)
6602 s2 = type->rampindexes-1;
6603 frac -= s1;
6604 VectorInterpolate(type->ramp[s1].rgb, frac, type->ramp[s2].rgb, p->rgba);
6605 FloatInterpolate(type->ramp[s1].alpha, frac, type->ramp[s2].alpha, p->rgba[3]);
6606 FloatInterpolate(type->ramp[s1].scale, frac, type->ramp[s2].scale, p->scale);
6607 }
6608 break;
6609 case RAMP_DELTA: //particle ramps
6610 rampind = (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);
6611 if (rampind >= type->rampindexes)
6612 rampind = type->rampindexes - 1;
6613 ramp = type->ramp + rampind;
6614 VectorMA(p->rgba, pframetime, ramp->rgb, p->rgba);
6615 p->rgba[3] -= pframetime*ramp->alpha;
6616 p->scale += pframetime*ramp->scale;
6617 break;
6618 case RAMP_NONE: //particle changes acording to it's preset properties.
6619 if (particletime < (p->die-type->die+type->rgbchangetime))
6620 {
6621 p->rgba[0] += pframetime*type->rgbchange[0];
6622 p->rgba[1] += pframetime*type->rgbchange[1];
6623 p->rgba[2] += pframetime*type->rgbchange[2];
6624 }
6625 p->rgba[3] += pframetime*type->alphachange;
6626 p->scale += pframetime*type->scaledelta;
6627 }
6628
6629 if (type->emit >= 0)
6630 {
6631 if (type->emittime < 0)
6632 PScript_ParticleTrail(oldorg, p->org, type->emit, pframetime, 0, NULL, &p->state.trailstate);
6633 else if (p->state.nextemit < particletime)
6634 {
6635 p->state.nextemit = particletime + type->emittime + frandom()*type->emitrand;
6636 PScript_RunParticleEffectState(p->org, p->vel, 1, type->emit, NULL);
6637 }
6638 }
6639
6640 if (type->cliptype>=0 && r_bouncysparks.value)
6641 {
6642 VectorSubtract(p->org, p->oldorg, stop);
6643 if (!type->clipbounce || DotProduct(stop,stop) > 10*10)
6644 {
6645 int e;
6646 if (traces-->0&&CL_TraceLine(p->oldorg, p->org, stop, normal, &e)<1)
6647 {
6648 if (type->clipbounce < 0)
6649 {
6650 p->die = -1;
6651 #ifdef USE_DECALS
6652 if (type->clipbounce == -2)
6653 { //this type of particle splatters itself as a decal when it hits a wall.
6654 decalctx_t ctx;
6655 float m;
6656 vec3_t vec={0.5, 0.5, 0.431};
6657 qmodel_t *model;
6658
6659 ctx.entity = e;
6660 if (!ctx.entity)
6661 {
6662 model = cl.worldmodel;
6663 VectorCopy(p->org, ctx.center);
6664 }
6665 else if (e)
6666 { //this trace hit a door or something.
6667 entity_t *ent = CL_EntityNum(e);
6668 model = ent->model;
6669 VectorSubtract(p->org, ent->origin, ctx.center);
6670 //FIXME: rotate center+normal around entity.
6671 }
6672 else
6673 continue; //err, no idea.
6674
6675 VectorScale(normal, -1, ctx.normal);
6676 VectorNormalize(ctx.normal);
6677
6678 VectorNormalize(vec);
6679 CrossProduct(ctx.normal, vec, ctx.tangent1);
6680 RotatePointAroundVector(ctx.tangent2, ctx.normal, ctx.tangent1, frandom()*360);
6681 CrossProduct(ctx.normal, ctx.tangent2, ctx.tangent1);
6682
6683 VectorNormalize(ctx.tangent1);
6684 VectorNormalize(ctx.tangent2);
6685
6686 ctx.ptype = type;
6687 ctx.scale1 = type->s2 - type->s1;
6688 ctx.bias1 = type->s1 + (ctx.scale1*0.5);
6689 ctx.scale2 = type->t2 - type->t1;
6690 ctx.bias2 = type->t1 + (ctx.scale2*0.5);
6691 m = p->scale*(1.5+frandom()*0.5)*0.5; //decals should be a little bigger, for some reason.
6692 ctx.scale0 = 2.0 / m;
6693 ctx.scale1 /= m;
6694 ctx.scale2 /= m;
6695
6696 //inserts decals through a callback.
6697 Mod_ClipDecal(model, ctx.center, ctx.normal, ctx.tangent2, ctx.tangent1, m, type->surfflagmask, type->surfflagmatch, PScript_AddDecals, &ctx);
6698 }
6699 #endif
6700 continue;
6701 }
6702 else if (part_type + type->cliptype == type)
6703 { //bounce
6704 dist = DotProduct(p->vel, normal);// * (-1-(rand()/(float)0x7fff)/2);
6705 dist *= -type->clipbounce;
6706 VectorMA(p->vel, dist, normal, p->vel);
6707 VectorCopy(stop, p->org);
6708
6709 if (!*type->texname && VectorLength(p->vel)<1000*pframetime && type->looks.type == PT_NORMAL)
6710 {
6711 p->die = -1;
6712 continue;
6713 }
6714 }
6715 else
6716 {
6717 p->die = -1;
6718 VectorNormalize(p->vel);
6719
6720 if (type->clipbounce)
6721 {
6722 VectorScale(normal, type->clipbounce, normal);
6723 PScript_RunParticleEffectState(stop, normal, type->clipcount/part_type[type->cliptype].count, type->cliptype, NULL);
6724 }
6725 else
6726 PScript_RunParticleEffectState(stop, p->vel, type->clipcount/part_type[type->cliptype].count, type->cliptype, NULL);
6727 continue;
6728 }
6729 }
6730 VectorCopy(p->org, p->oldorg);
6731 }
6732 }
6733 if (scenetri && tdraw)
6734 {
6735 if (cl_numstrisvert - scenetri->firstvert >= MAX_INDICES-6)
6736 {
6737 //generate a new mesh if the old one overflowed. yay smc...
6738 if (cl_numstris == cl_maxstris)
6739 {
6740 cl_maxstris+=8;
6741 cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6742 }
6743 scenetri = &cl_stris[cl_numstris++];
6744 scenetri->texture = scenetri[-1].texture;
6745 scenetri->blendmode = scenetri[-1].blendmode;
6746 scenetri->beflags = scenetri[-1].beflags;
6747 scenetri->firstidx = cl_numstrisidx;
6748 scenetri->firstvert = cl_numstrisvert;
6749 scenetri->numvert = 0;
6750 scenetri->numidx = 0;
6751 }
6752 tdraw(scenetri, p, type->slooks);
6753 }
6754 }
6755
6756 // beams are dealt with here
6757
6758 // kill early entries
6759 for ( ;; )
6760 {
6761 bkill = type->beams;
6762 if (bkill && (bkill->flags & BS_DEAD || bkill->p->die < particletime) && !(bkill->flags & BS_LASTSEG))
6763 {
6764 type->beams = bkill->next;
6765 bkill->next = free_beams;
6766 free_beams = bkill;
6767 continue;
6768 }
6769 break;
6770 }
6771
6772
6773 b = type->beams;
6774 if (b)
6775 {
6776 for ( ;; )
6777 {
6778 if (b->next)
6779 {
6780 // mark dead entries
6781 if (b->flags & (BS_LASTSEG|BS_DEAD|BS_NODRAW))
6782 {
6783 // kill some more dead entries
6784 for ( ;; )
6785 {
6786 bkill = b->next;
6787 if (bkill && (bkill->flags & BS_DEAD) && !(bkill->flags & BS_LASTSEG))
6788 {
6789 b->next = bkill->next;
6790 bkill->next = free_beams;
6791 free_beams = bkill;
6792 continue;
6793 }
6794 break;
6795 }
6796
6797 if (!bkill) // have to check so we don't hit NULL->next
6798 continue;
6799 }
6800 else
6801 {
6802 if (!(b->next->flags & BS_DEAD))
6803 {
6804 VectorCopy(b->next->p->org, stop);
6805 VectorCopy(b->p->org, oldorg);
6806 VectorSubtract(stop, oldorg, b->next->dir);
6807 VectorNormalize(b->next->dir);
6808 if (bdraw)
6809 {
6810 VectorAdd(stop, oldorg, stop);
6811 VectorScale(stop, 0.5, stop);
6812 }
6813 }
6814
6815 if (b->p->die < particletime)
6816 b->flags |= BS_DEAD;
6817 }
6818 }
6819 else
6820 {
6821 if (b->p->die < particletime) // end of the list check
6822 b->flags |= BS_DEAD;
6823
6824 break;
6825 }
6826
6827 if (b->p->die < particletime)
6828 b->flags |= BS_DEAD;
6829
6830 b = b->next;
6831 }
6832 }
6833
6834 endtype:
6835
6836 // delete from run list if necessary
6837 if (!type->particles && !type->beams && !type->clippeddecals)
6838 {
6839 if (!lastvalidtype)
6840 part_run_list = type->nexttorun;
6841 else if (lastvalidtype->nexttorun == type)
6842 lastvalidtype->nexttorun = type->nexttorun;
6843 else
6844 lastvalidtype->nexttorun->nexttorun = type->nexttorun;
6845 type->state &= ~PS_INRUNLIST;
6846 }
6847 else
6848 lastvalidtype = type;
6849 }
6850
6851 // lazy delete for particles is done here
6852 if (kill_list)
6853 {
6854 kill_first->next = free_particles;
6855 free_particles = kill_list;
6856 }
6857
6858 particletime += pframetime;
6859
6860 if (!cl_numstris)
6861 return;
6862
6863 R_BeginDebugUtilsLabel ("FTE Particles");
6864 Fog_DisableGFog ();
6865
6866 for (o = 0; o < 3; o++)
6867 {
6868 static int blend_modes_order[] = {1, 1, 2, 2, 0, 0, 0, 2};
6869 for (i = 0; i < cl_numstris; i++)
6870 {
6871 scenetris_t* tris = &cl_stris[i];
6872 const int blend_mode = tris->blendmode;
6873 if (blend_modes_order[blend_mode] != o)
6874 continue;
6875 const qboolean draw_lines = ((tris->beflags & BEF_LINES) != 0);
6876 if (!vulkan_globals.non_solid_fill && draw_lines)
6877 continue; // Can't draw lines
6878
6879 const vulkan_pipeline_t pipeline = vulkan_globals.fte_particle_pipelines[blend_mode + (draw_lines ? 8 : 0)];
6880 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
6881 gltexture_t* tex = (tris->beflags & BEF_LINES) ? whitetexture : tris->texture;
6882
6883 const int num_indices = tris->numidx;
6884 const VkDeviceSize vertex_buffer_offset = 0;
6885 vulkan_globals.vk_cmd_bind_index_buffer(vulkan_globals.command_buffer, index_buffers[current_buffer_index], 0, VK_INDEX_TYPE_UINT16);
6886 vulkan_globals.vk_cmd_bind_vertex_buffers(vulkan_globals.command_buffer, 0, 1, &vertex_buffers[current_buffer_index], &vertex_buffer_offset);
6887 vulkan_globals.vk_cmd_bind_descriptor_sets(vulkan_globals.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout.handle, 0, 1, &tex->descriptor_set, 0, NULL);
6888 vulkan_globals.vk_cmd_draw_indexed(vulkan_globals.command_buffer, num_indices, 1, tris->firstidx, tris->firstvert, 0);
6889 }
6890 }
6891 R_EndDebugUtilsLabel ();
6892 }
6893
6894 /*
6895 ===============
6896 PScript_DrawParticles
6897 ===============
6898 */
PScript_DrawParticles(void)6899 void PScript_DrawParticles (void)
6900 {
6901 int i;
6902 entity_t *ent;
6903 vec3_t axis[3];
6904 float pframetime;
6905 static float oldtime;
6906
6907 pframetime = cl.time - oldtime;
6908 if (pframetime < 0)
6909 pframetime = 0;
6910 if (pframetime > 1)
6911 pframetime = 1;
6912 oldtime = cl.time;
6913
6914 current_buffer_index = (current_buffer_index + 1) % 2;
6915 cl_numstris = 0;
6916 cl_numstrisvert = 0;
6917 cl_numstrisidx = 0;
6918 cl_curstrisvert = cl_strisvert[current_buffer_index];
6919 cl_curstrisidx = cl_strisidx[current_buffer_index];
6920
6921 if (!r_particles.value)
6922 return;
6923
6924 if (r_part_rain.value && r_fteparticles.value)
6925 {
6926 for (i = 0; i < cl.num_entities; i++)
6927 {
6928 ent = &cl.entities[i];
6929 if (!ent->model || ent->model->needload)
6930 continue;
6931 if (!ent->model->skytris)
6932 continue;
6933 AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
6934 //this timer, as well as the per-tri timer, are unable to deal with certain rates+sizes. it would be good to fix that...
6935 //it would also be nice to do mdls too...
6936 P_AddRainParticles(ent->model, axis, ent->origin, pframetime);
6937 }
6938 }
6939
6940 PScript_DrawParticleTypes(pframetime);
6941 }
6942
6943 /*
6944 ===============
6945 R_DrawParticles_ShowTris
6946 ===============
6947 */
PScript_DrawParticles_ShowTris(void)6948 void PScript_DrawParticles_ShowTris (void)
6949 {
6950 if (r_showtris.value == 1)
6951 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.showtris_pipeline);
6952 else
6953 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.showtris_depth_test_pipeline);
6954
6955 for (unsigned int i = 0; i < cl_numstris; i++)
6956 {
6957 scenetris_t* tris = &cl_stris[i];
6958 const int num_indices = tris->numidx;
6959 const VkDeviceSize vertex_buffer_offset = 0;
6960 vulkan_globals.vk_cmd_bind_index_buffer(vulkan_globals.command_buffer, index_buffers[current_buffer_index], 0, VK_INDEX_TYPE_UINT16);
6961 vulkan_globals.vk_cmd_bind_vertex_buffers(vulkan_globals.command_buffer, 0, 1, &vertex_buffers[current_buffer_index], &vertex_buffer_offset);
6962 vulkan_globals.vk_cmd_draw_indexed(vulkan_globals.command_buffer, num_indices, 1, tris->firstidx, tris->firstvert, 0);
6963 }
6964 }
6965
6966 #endif
6967 #endif
6968
6969
6970