1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // cg_particles.c
22 //
23 
24 #include "cg_local.h"
25 
26 static cgParticle_t		*cg_freeParticles;
27 static cgParticle_t		cg_particleHeadNode, cg_particleList[MAX_PARTICLES];
28 static int				cg_numParticles;
29 
30 /*
31 =============================================================================
32 
33 	PARTICLE MATERIAL RANDOMIZING
34 
35 =============================================================================
36 */
37 
pRandBloodDrip(void)38 int pRandBloodDrip (void)	{ return PT_BLDDRIP01 + (rand()&1); }
pRandGrnBloodDrip(void)39 int pRandGrnBloodDrip (void){ return PT_BLDDRIP01_GRN + (rand()&1); }
pRandBloodMark(void)40 int pRandBloodMark (void)	{ return PT_BLOOD + (rand()%6); }
pRandGrnBloodMark(void)41 int pRandGrnBloodMark (void){ return PT_GRNBLOOD + (rand()%6); }
pRandSmoke(void)42 int pRandSmoke (void)		{ return PT_SMOKE + (rand()&1); }
pRandGlowSmoke(void)43 int pRandGlowSmoke (void)	{ return PT_SMOKEGLOW + (rand()&1); }
pRandEmbers(void)44 int pRandEmbers (void)		{ return PT_EMBERS1 + (rand()%3); }
pRandFire(void)45 int pRandFire (void)		{ return PT_FIRE1 + (rand()&3); }
46 
47 /*
48 =============================================================================
49 
50 	PARTICLE MANAGEMENT
51 
52 =============================================================================
53 */
54 
55 /*
56 ===============
57 CG_AllocParticle
58 ===============
59 */
CG_AllocParticle(void)60 static cgParticle_t *CG_AllocParticle (void)
61 {
62 	cgParticle_t	*p;
63 
64 	// Take a free particle spot if possible, otherwise steal the oldest one
65 	if (cg_freeParticles && cg_numParticles+1 < cg_particleMax->intVal) {
66 		p = cg_freeParticles;
67 		cg_freeParticles = p->next;
68 	}
69 	else {
70 		p = cg_particleHeadNode.prev;
71 		p->prev->next = p->next;
72 		p->next->prev = p->prev;
73 
74 		cg_numParticles--;
75 	}
76 
77 	// Move to the beginning of the list
78 	p->prev = &cg_particleHeadNode;
79 	p->next = cg_particleHeadNode.next;
80 	p->next->prev = p;
81 	p->prev->next = p;
82 
83 	cg_numParticles++;
84 	return p;
85 }
86 
87 
88 /*
89 ===============
90 CG_FreeParticle
91 ===============
92 */
CG_FreeParticle(cgParticle_t * p)93 static inline void CG_FreeParticle (cgParticle_t *p)
94 {
95 	// Remove from linked active list
96 	p->prev->next = p->next;
97 	p->next->prev = p->prev;
98 
99 	// Insert into linked free list
100 	p->next = cg_freeParticles;
101 	cg_freeParticles = p;
102 
103 	cg_numParticles--;
104 }
105 
106 
107 /*
108 ===============
109 CG_SpawnParticle
110 
111 FIXME: JESUS H FUNCTION PARAMATERS
112 ===============
113 */
CG_SpawnParticle(float org0,float org1,float org2,float angle0,float angle1,float angle2,float vel0,float vel1,float vel2,float accel0,float accel1,float accel2,float red,float green,float blue,float redVel,float greenVel,float blueVel,float alpha,float alphaVel,float size,float sizeVel,uint32 type,uint32 flags,void (* think)(struct cgParticle_s * p,vec3_t org,vec3_t angle,vec4_t color,float * size,float * orient,float * time),qBool thinkNext,byte style,float orient)114 void	CG_SpawnParticle (float org0,					float org1,					float org2,
115 						float angle0,					float angle1,				float angle2,
116 						float vel0,						float vel1,					float vel2,
117 						float accel0,					float accel1,				float accel2,
118 						float red,						float green,				float blue,
119 						float redVel,					float greenVel,				float blueVel,
120 						float alpha,					float alphaVel,
121 						float size,						float sizeVel,
122 						uint32 type,					uint32 flags,
123 						void (*think)(struct cgParticle_s *p, vec3_t org, vec3_t angle, vec4_t color, float *size, float *orient, float *time),
124 						qBool thinkNext,
125 						byte style,
126 						float orient)
127 {
128 	cgParticle_t		*p = NULL;
129 
130 	p = CG_AllocParticle ();
131 	p->time = (float)cg.realTime;
132 
133 	Vec3Set (p->org, org0, org1, org2);
134 	Vec3Copy (p->org, p->oldOrigin);
135 
136 	Vec3Set (p->angle, angle0, angle1, angle2);
137 	Vec3Set (p->vel, vel0, vel1, vel2);
138 	Vec3Set (p->accel, accel0, accel1, accel2);
139 
140 	Vec4Set (p->color, red, green, blue, alpha);
141 	Vec4Set (p->colorVel, redVel, greenVel, blueVel, alphaVel);
142 
143 	p->shader = cgMedia.particleTable[type%PT_PICTOTAL];
144 	p->style = style;
145 	p->flags = flags;
146 
147 	p->size = size;
148 	p->sizeVel = sizeVel;
149 
150 	if (think)
151 		p->think = think;
152 	else
153 		p->think = NULL;
154 
155 	p->thinkNext = thinkNext;
156 
157 	p->orient = orient;
158 }
159 
160 
161 /*
162 ===============
163 CG_ClearParticles
164 ===============
165 */
CG_ClearParticles(void)166 void CG_ClearParticles (void)
167 {
168 	int		i;
169 
170 	// Link particles
171 	cg_freeParticles = &cg_particleList[0];
172 	cg_particleHeadNode.prev = &cg_particleHeadNode;
173 	cg_particleHeadNode.next = &cg_particleHeadNode;
174 	for (i=0 ; i<MAX_PARTICLES ; i++) {
175 		if (i < MAX_PARTICLES-1)
176 			cg_particleList[i].next = &cg_particleList[i+1];
177 
178 		// Store static poly info
179 		cg_particleList[i].outPoly.numVerts = 4;
180 		cg_particleList[i].outPoly.colors = cg_particleList[i].outColor;
181 		cg_particleList[i].outPoly.texCoords = cg_particleList[i].outCoords;
182 		cg_particleList[i].outPoly.vertices = cg_particleList[i].outVertices;
183 		cg_particleList[i].outPoly.shaderTime = 0;
184 	}
185 
186 	cg_particleList[MAX_PARTICLES-1].next = NULL;
187 }
188 
189 
190 /*
191 ===============
192 CG_AddParticles
193 ===============
194 */
CG_AddParticles(void)195 void CG_AddParticles (void)
196 {
197 	cgParticle_t	*p, *next, *hNode;
198 	vec3_t			org, temp;
199 	vec4_t			color;
200 	float			size, orient;
201 	float			time, time2, dist;
202 	int				i, j, pointBits;
203 	float			lightest;
204 	vec3_t			shade;
205 	float			scale;
206 	vec3_t			p_upVec, p_rtVec;
207 	vec3_t			a_upVec, a_rtVec;
208 	vec3_t			point, width, move;
209 	bvec4_t			outColor;
210 	vec3_t			delta, vdelta;
211 	int				num;
212 
213 	CG_AddMapFXToList ();
214 	CG_AddSustains ();
215 	if (!cl_add_particles->intVal)
216 		return;
217 
218 	Vec3Scale (cg.refDef.viewAxis[2], 0.75f, p_upVec);
219 	Vec3Scale (cg.refDef.rightVec, 0.75f, p_rtVec);
220 
221 	num = 0;
222 	hNode = &cg_particleHeadNode;
223 	for (p=hNode->prev ; p!=hNode ; p=next) {
224 		next = p->prev;
225 		num++;
226 
227 		if (p->colorVel[3] > PART_INSTANT) {
228 			time = (cg.realTime - p->time)*0.001f;
229 			color[3] = p->color[3] + time*p->colorVel[3];
230 		}
231 		else {
232 			time = 1;
233 			color[3] = p->color[3];
234 		}
235 
236 		// Faded out
237 		if (color[3] <= 0.0001f || num > cg_particleMax->intVal) {
238 			CG_FreeParticle (p);
239 			continue;
240 		}
241 
242 		if (color[3] > 1.0)
243 			color[3] = 1.0f;
244 
245 		// Origin
246 		time2 = time*time;
247 
248 		org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2;
249 		org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2;
250 		org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2;
251 
252 		if (p->flags & PF_GRAVITY)
253 			org[2] -= (time2 * PART_GRAVITY);
254 
255 		// Culling
256 		switch (p->style) {
257 		case PART_STYLE_ANGLED:
258 		case PART_STYLE_BEAM:
259 		case PART_STYLE_DIRECTION:
260 			break;
261 
262 		default:
263 			if (cg_particleCulling->intVal) {
264 				// Kill particles behind the view
265 				Vec3Subtract (org, cg.refDef.viewOrigin, temp);
266 				VectorNormalizeFastf (temp);
267 				if (DotProduct (temp, cg.refDef.viewAxis[0]) < 0)
268 					goto nextParticle;
269 
270 				// Lessen fillrate consumption
271 				if (!(p->flags & PF_NOCLOSECULL)) {
272 					dist = Vec3DistFast (cg.refDef.viewOrigin, org);
273 					if (dist <= 5)
274 						goto nextParticle;
275 				}
276 			}
277 			break;
278 		}
279 
280 		// sizeVel calcs
281 		if (p->colorVel[3] > PART_INSTANT && p->size != p->sizeVel) {
282 			if (p->size > p->sizeVel) // shrink
283 				size = p->size - ((p->size - p->sizeVel) * (p->color[3] - color[3]));
284 			else // grow
285 				size = p->size + ((p->sizeVel - p->size) * (p->color[3] - color[3]));
286 		}
287 		else {
288 			size = p->size;
289 		}
290 
291 		if (size < 0.0f)
292 			goto nextParticle;
293 
294 		// colorVel calcs
295 		Vec3Copy (p->color, color);
296 		if (p->colorVel[3] > PART_INSTANT) {
297 			for (i=0 ; i<3 ; i++) {
298 				if (p->color[i] != p->colorVel[i]) {
299 					if (p->color[i] > p->colorVel[i])
300 						color[i] = p->color[i] - ((p->color[i] - p->colorVel[i]) * (p->color[3] - color[3]));
301 					else
302 						color[i] = p->color[i] + ((p->colorVel[i] - p->color[i]) * (p->color[3] - color[3]));
303 				}
304 
305 				color[i] = clamp (color[i], 0, 255);
306 			}
307 		}
308 
309 		// Particle shading
310 		if (p->flags & PF_SHADE && cg_particleShading->intVal) {
311 			cgi.R_LightPoint (p->org, shade);
312 
313 			lightest = 0;
314 			for (j=0 ; j<3 ; j++) {
315 				color[j] = ((0.7f * clamp (shade[j], 0.0f, 1.0f)) + 0.3f) * p->color[j];
316 				if (color[j] > lightest)
317 					lightest = color[j];
318 			}
319 
320 			if (lightest > 255.0) {
321 				color[0] *= 255.0f / lightest;
322 				color[1] *= 255.0f / lightest;
323 				color[2] *= 255.0f / lightest;
324 			}
325 		}
326 
327 		// Alpha*color
328 		if (p->flags & PF_ALPHACOLOR)
329 			Vec3Scale (color, color[3], color);
330 
331 		// Think function
332 		orient = p->orient;
333 		if (p->thinkNext && p->think) {
334 			p->thinkNext = qFalse;
335 			p->think (p, org, p->angle, color, &size, &orient, &time);
336 		}
337 
338 		if (color[3] <= 0.0f)
339 			goto nextParticle;
340 
341 		// Contents requirements
342 		pointBits = 0;
343 		if (cg.currGameMod != GAME_MOD_LOX && cg.currGameMod != GAME_MOD_GIEX) { // FIXME: yay hack
344 			if (p->flags & PF_AIRONLY) {
345 				pointBits |= (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER);
346 				if (cgi.CM_PointContents (org, 0) & pointBits) {
347 					p->color[3] = 0;
348 					p->colorVel[3] = 0;
349 					goto nextParticle;
350 				}
351 			}
352 			else {
353 				if (p->flags & PF_LAVAONLY)		pointBits |= CONTENTS_LAVA;
354 				if (p->flags & PF_SLIMEONLY)	pointBits |= CONTENTS_SLIME;
355 				if (p->flags & PF_WATERONLY)	pointBits |= CONTENTS_WATER;
356 
357 				if (pointBits) {
358 					if (!(cgi.CM_PointContents (org, 0) & pointBits)) {
359 						p->color[3] = 0;
360 						p->colorVel[3] = 0;
361 						goto nextParticle;
362 					}
363 				}
364 			}
365 		}
366 
367 		// Add to be rendered
368 		if (p->flags & PF_SCALED) {
369 			scale = (org[0] - cg.refDef.viewOrigin[0]) * cg.refDef.viewAxis[0][0] +
370 					(org[1] - cg.refDef.viewOrigin[1]) * cg.refDef.viewAxis[0][1] +
371 					(org[2] - cg.refDef.viewOrigin[2]) * cg.refDef.viewAxis[0][2];
372 
373 			scale = (scale < 20) ? 1 : 1 + scale * 0.004f;
374 		}
375 		else
376 			scale = 1;
377 
378 		scale = (scale - 1) + size;
379 
380 		// Rendering
381 		outColor[0] = color[0];
382 		outColor[1] = color[1];
383 		outColor[2] = color[2];
384 		outColor[3] = color[3] * 255;
385 
386 		switch (p->style) {
387 		case PART_STYLE_ANGLED:
388 			Angles_Vectors (p->angle, NULL, a_rtVec, a_upVec);
389 
390 			if (orient) {
391 				float c = (float)cos (DEG2RAD (orient)) * scale;
392 				float s = (float)sin (DEG2RAD (orient)) * scale;
393 
394 				// Top left
395 				Vec2Set (p->outCoords[0], 0, 0);
396 				Vec3Set (p->outVertices[0],	org[0] + a_upVec[0]*s - a_rtVec[0]*c,
397 											org[1] + a_upVec[1]*s - a_rtVec[1]*c,
398 											org[2] + a_upVec[2]*s - a_rtVec[2]*c);
399 
400 				// Bottom left
401 				Vec2Set (p->outCoords[1], 0, 1);
402 				Vec3Set (p->outVertices[1],	org[0] - a_upVec[0]*c - a_rtVec[0]*s,
403 											org[1] - a_upVec[1]*c - a_rtVec[1]*s,
404 											org[2] - a_upVec[2]*c - a_rtVec[2]*s);
405 
406 				// Bottom right
407 				Vec2Set (p->outCoords[2], 1, 1);
408 				Vec3Set (p->outVertices[2],	org[0] - a_upVec[0]*s + a_rtVec[0]*c,
409 											org[1] - a_upVec[1]*s + a_rtVec[1]*c,
410 											org[2] - a_upVec[2]*s + a_rtVec[2]*c);
411 
412 				// Top right
413 				Vec2Set (p->outCoords[3], 1, 0);
414 				Vec3Set (p->outVertices[3],	org[0] + a_upVec[0]*c + a_rtVec[0]*s,
415 											org[1] + a_upVec[1]*c + a_rtVec[1]*s,
416 											org[2] + a_upVec[2]*c + a_rtVec[2]*s);
417 			}
418 			else {
419 				// Top left
420 				Vec2Set (p->outCoords[0], 0, 0);
421 				Vec3Set (p->outVertices[0],	org[0] + a_upVec[0]*scale - a_rtVec[0]*scale,
422 											org[1] + a_upVec[1]*scale - a_rtVec[1]*scale,
423 											org[2] + a_upVec[2]*scale - a_rtVec[2]*scale);
424 
425 				// Bottom left
426 				Vec2Set (p->outCoords[1], 0, 1);
427 				Vec3Set (p->outVertices[1],	org[0] - a_upVec[0]*scale - a_rtVec[0]*scale,
428 											org[1] - a_upVec[1]*scale - a_rtVec[1]*scale,
429 											org[2] - a_upVec[2]*scale - a_rtVec[2]*scale);
430 
431 				// Bottom right
432 				Vec2Set (p->outCoords[2], 1, 1);
433 				Vec3Set (p->outVertices[2],	org[0] - a_upVec[0]*scale + a_rtVec[0]*scale,
434 											org[1] - a_upVec[1]*scale + a_rtVec[1]*scale,
435 											org[2] - a_upVec[2]*scale + a_rtVec[2]*scale);
436 
437 				// Top right
438 				Vec2Set (p->outCoords[3], 1, 0);
439 				Vec3Set (p->outVertices[3],	org[0] + a_upVec[0]*scale + a_rtVec[0]*scale,
440 											org[1] + a_upVec[1]*scale + a_rtVec[1]*scale,
441 											org[2] + a_upVec[2]*scale + a_rtVec[2]*scale);
442 			}
443 
444 			// Render it
445 			*(int *)p->outColor[0] = *(int *)outColor;
446 			*(int *)p->outColor[1] = *(int *)outColor;
447 			*(int *)p->outColor[2] = *(int *)outColor;
448 			*(int *)p->outColor[3] = *(int *)outColor;
449 
450 			p->outPoly.shader = p->shader;
451 			Vec3Copy (p->org, p->outPoly.origin);
452 			p->outPoly.radius = scale;
453 
454 			cgi.R_AddPoly (&p->outPoly);
455 			break;
456 
457 		case PART_STYLE_BEAM:
458 			Vec3Subtract (org, cg.refDef.viewOrigin, point);
459 			CrossProduct (point, p->angle, width);
460 			VectorNormalizeFastf (width);
461 			Vec3Scale (width, scale, width);
462 
463 			Vec3Add (org, p->angle, delta);
464 
465 			dist = Vec3DistFast (org, delta);
466 
467 			Vec2Set (p->outCoords[0], 1, dist);
468 			Vec3Set (p->outVertices[0], org[0] + width[0],
469 										org[1] + width[1],
470 										org[2] + width[2]);
471 
472 			Vec2Set (p->outCoords[1], 0, 0);
473 			Vec3Set (p->outVertices[1], org[0] - width[0],
474 										org[1] - width[1],
475 										org[2] - width[2]);
476 
477 			Vec3Add (point, p->angle, point);
478 			CrossProduct (point, p->angle, width);
479 			VectorNormalizeFastf (width);
480 			Vec3Scale (width, scale, width);
481 
482 			Vec2Set (p->outCoords[2], 0, 0);
483 			Vec3Set (p->outVertices[2], org[0] + p->angle[0] - width[0],
484 										org[1] + p->angle[1] - width[1],
485 										org[2] + p->angle[2] - width[2]);
486 
487 			Vec2Set (p->outCoords[3], 1, dist);
488 			Vec3Set (p->outVertices[3], org[0] + p->angle[0] + width[0],
489 										org[1] + p->angle[1] + width[1],
490 										org[2] + p->angle[2] + width[2]);
491 
492 			// Render it
493 			*(int *)p->outColor[0] = *(int *)outColor;
494 			*(int *)p->outColor[1] = *(int *)outColor;
495 			*(int *)p->outColor[2] = *(int *)outColor;
496 			*(int *)p->outColor[3] = *(int *)outColor;
497 
498 			p->outPoly.shader = p->shader;
499 			Vec3Copy (p->org, p->outPoly.origin);
500 			p->outPoly.radius = Vec3DistFast (org, delta);
501 
502 			cgi.R_AddPoly (&p->outPoly);
503 			break;
504 
505 		case PART_STYLE_DIRECTION:
506 			Vec3Add (p->angle, org, vdelta);
507 
508 			Vec3Subtract (org, vdelta, move);
509 			VectorNormalizeFastf (move);
510 
511 			Vec3Copy (move, a_upVec);
512 			Vec3Subtract (cg.refDef.viewOrigin, vdelta, delta);
513 			CrossProduct (a_upVec, delta, a_rtVec);
514 
515 			VectorNormalizeFastf (a_rtVec);
516 
517 			Vec3Scale (a_rtVec, 0.75f, a_rtVec);
518 			Vec3Scale (a_upVec, 0.75f * Vec3LengthFast (p->angle), a_upVec);
519 
520 			// Top left
521 			Vec2Set (p->outCoords[0], 0, 0);
522 			Vec3Set (p->outVertices[0], org[0] + a_upVec[0]*scale - a_rtVec[0]*scale,
523 										org[1] + a_upVec[1]*scale - a_rtVec[1]*scale,
524 										org[2] + a_upVec[2]*scale - a_rtVec[2]*scale);
525 
526 			// Bottom left
527 			Vec2Set (p->outCoords[1], 0, 1);
528 			Vec3Set (p->outVertices[1], org[0] - a_upVec[0]*scale - a_rtVec[0]*scale,
529 										org[1] - a_upVec[1]*scale - a_rtVec[1]*scale,
530 										org[2] - a_upVec[2]*scale - a_rtVec[2]*scale);
531 
532 			// Bottom right
533 			Vec2Set (p->outCoords[2], 1, 1);
534 			Vec3Set (p->outVertices[2], org[0] - a_upVec[0]*scale + a_rtVec[0]*scale,
535 										org[1] - a_upVec[1]*scale + a_rtVec[1]*scale,
536 										org[2] - a_upVec[2]*scale + a_rtVec[2]*scale);
537 
538 			// Top right
539 			Vec2Set (p->outCoords[3], 1, 0);
540 			Vec3Set (p->outVertices[3], org[0] + a_upVec[0]*scale + a_rtVec[0]*scale,
541 										org[1] + a_upVec[1]*scale + a_rtVec[1]*scale,
542 										org[2] + a_upVec[2]*scale + a_rtVec[2]*scale);
543 
544 			// Render it
545 			*(int *)p->outColor[0] = *(int *)outColor;
546 			*(int *)p->outColor[1] = *(int *)outColor;
547 			*(int *)p->outColor[2] = *(int *)outColor;
548 			*(int *)p->outColor[3] = *(int *)outColor;
549 
550 			p->outPoly.shader = p->shader;
551 			Vec3Copy (p->org, p->outPoly.origin);
552 			p->outPoly.radius = scale;
553 
554 			cgi.R_AddPoly (&p->outPoly);
555 			break;
556 
557 		default:
558 		case PART_STYLE_QUAD:
559 			if (orient) {
560 				float c = (float)cos (DEG2RAD (orient)) * scale;
561 				float s = (float)sin (DEG2RAD (orient)) * scale;
562 
563 				// Top left
564 				Vec2Set (p->outCoords[0], 0.0, 0.0);
565 				Vec3Set (p->outVertices[0],	org[0] + cg.refDef.viewAxis[1][0]*c + cg.refDef.viewAxis[2][0]*s,
566 											org[1] + cg.refDef.viewAxis[1][1]*c + cg.refDef.viewAxis[2][1]*s,
567 											org[2] + cg.refDef.viewAxis[1][2]*c + cg.refDef.viewAxis[2][2]*s);
568 
569 				// Bottom left
570 				Vec2Set (p->outCoords[1], 0.0, 1.0);
571 				Vec3Set (p->outVertices[1],	org[0] - cg.refDef.viewAxis[1][0]*s + cg.refDef.viewAxis[2][0]*c,
572 											org[1] - cg.refDef.viewAxis[1][1]*s + cg.refDef.viewAxis[2][1]*c,
573 											org[2] - cg.refDef.viewAxis[1][2]*s + cg.refDef.viewAxis[2][2]*c);
574 
575 				// Bottom right
576 				Vec2Set (p->outCoords[2], 1.0, 1.0);
577 				Vec3Set (p->outVertices[2],	org[0] - cg.refDef.viewAxis[1][0]*c - cg.refDef.viewAxis[2][0]*s,
578 											org[1] - cg.refDef.viewAxis[1][1]*c - cg.refDef.viewAxis[2][1]*s,
579 											org[2] - cg.refDef.viewAxis[1][2]*c - cg.refDef.viewAxis[2][2]*s);
580 
581 				// Top right
582 				Vec2Set (p->outCoords[3], 1.0, 0.0);
583 				Vec3Set (p->outVertices[3],	org[0] + cg.refDef.viewAxis[1][0]*s - cg.refDef.viewAxis[2][0]*c,
584 											org[1] + cg.refDef.viewAxis[1][1]*s - cg.refDef.viewAxis[2][1]*c,
585 											org[2] + cg.refDef.viewAxis[1][2]*s - cg.refDef.viewAxis[2][2]*c);
586 			}
587 			else {
588 				// Top left
589 				Vec2Set (p->outCoords[0], 0, 0);
590 				Vec3Set (p->outVertices[0],	org[0] + cg.refDef.viewAxis[2][0]*scale + cg.refDef.viewAxis[1][0]*scale,
591 											org[1] + cg.refDef.viewAxis[2][1]*scale + cg.refDef.viewAxis[1][1]*scale,
592 											org[2] + cg.refDef.viewAxis[2][2]*scale + cg.refDef.viewAxis[1][2]*scale);
593 
594 				// Bottom left
595 				Vec2Set (p->outCoords[1], 0, 1);
596 				Vec3Set (p->outVertices[1],	org[0] - cg.refDef.viewAxis[2][0]*scale + cg.refDef.viewAxis[1][0]*scale,
597 											org[1] - cg.refDef.viewAxis[2][1]*scale + cg.refDef.viewAxis[1][1]*scale,
598 											org[2] - cg.refDef.viewAxis[2][2]*scale + cg.refDef.viewAxis[1][2]*scale);
599 
600 				// Bottom right
601 				Vec2Set (p->outCoords[2], 1, 1);
602 				Vec3Set (p->outVertices[2],	org[0] - cg.refDef.viewAxis[2][0]*scale - cg.refDef.viewAxis[1][0]*scale,
603 											org[1] - cg.refDef.viewAxis[2][1]*scale - cg.refDef.viewAxis[1][1]*scale,
604 											org[2] - cg.refDef.viewAxis[2][2]*scale - cg.refDef.viewAxis[1][2]*scale);
605 
606 				// Top right
607 				Vec2Set (p->outCoords[3], 1, 0);
608 				Vec3Set (p->outVertices[3],	org[0] + cg.refDef.viewAxis[2][0]*scale - cg.refDef.viewAxis[1][0]*scale,
609 											org[1] + cg.refDef.viewAxis[2][1]*scale - cg.refDef.viewAxis[1][1]*scale,
610 											org[2] + cg.refDef.viewAxis[2][2]*scale - cg.refDef.viewAxis[1][2]*scale);
611 			}
612 
613 			// Render it
614 			*(int *)p->outColor[0] = *(int *)outColor;
615 			*(int *)p->outColor[1] = *(int *)outColor;
616 			*(int *)p->outColor[2] = *(int *)outColor;
617 			*(int *)p->outColor[3] = *(int *)outColor;
618 
619 			p->outPoly.shader = p->shader;
620 			Vec3Copy (p->org, p->outPoly.origin);
621 			p->outPoly.radius = scale;
622 
623 			cgi.R_AddPoly (&p->outPoly);
624 			break;
625 		}
626 
627 nextParticle:
628 		Vec3Copy (org, p->oldOrigin);
629 
630 		// Kill if instant
631 		if (p->colorVel[3] <= PART_INSTANT) {
632 			p->color[3] = 0;
633 			p->colorVel[3] = 0;
634 		}
635 	}
636 }
637