1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 // Ridah, cg_trails.c - draws a trail using multiple junction points
30 
31 #include "cg_local.h"
32 
33 typedef struct trailJunc_s
34 {
35 	struct trailJunc_s *nextGlobal, *prevGlobal;    // next junction in the global list it is in (free or used)
36 	struct trailJunc_s *nextJunc;                   // next junction in the trail
37 	struct trailJunc_s *nextHead, *prevHead;        // next head junc in the world
38 
39 	qboolean inuse, freed;
40 	int ownerIndex;
41 	qhandle_t shader;
42 
43 	int sType;
44 	int flags;
45 	float sTex;
46 	vec3_t pos;
47 	int spawnTime, endTime;
48 	float alphaStart, alphaEnd;
49 	vec3_t colorStart, colorEnd;
50 	float widthStart, widthEnd;
51 
52 	// current settings
53 	float alpha;
54 	float width;
55 	vec3_t color;
56 
57 } trailJunc_t;
58 
59 #define MAX_TRAILJUNCS  4096
60 
61 trailJunc_t trailJuncs[MAX_TRAILJUNCS];
62 trailJunc_t *freeTrails, *activeTrails;
63 trailJunc_t *headTrails;
64 
65 qboolean initTrails = qfalse;
66 
67 int numTrailsInuse;
68 
69 /*
70 ===============
71 CG_ClearTrails
72 ===============
73 */
CG_ClearTrails(void)74 void CG_ClearTrails( void ) {
75 	int i;
76 
77 	memset( trailJuncs, 0, sizeof( trailJunc_t ) * MAX_TRAILJUNCS );
78 
79 	freeTrails = trailJuncs;
80 	activeTrails = NULL;
81 	headTrails = NULL;
82 
83 	for ( i = 0 ; i < MAX_TRAILJUNCS ; i++ )
84 	{
85 		trailJuncs[i].nextGlobal = &trailJuncs[i + 1];
86 
87 		if ( i > 0 ) {
88 			trailJuncs[i].prevGlobal = &trailJuncs[i - 1];
89 		} else {
90 			trailJuncs[i].prevGlobal = NULL;
91 		}
92 
93 		trailJuncs[i].inuse = qfalse;
94 	}
95 	trailJuncs[MAX_TRAILJUNCS - 1].nextGlobal = NULL;
96 
97 	initTrails = qtrue;
98 	numTrailsInuse = 0;
99 }
100 
101 /*
102 ===============
103 CG_SpawnTrailJunc
104 ===============
105 */
CG_SpawnTrailJunc(trailJunc_t * headJunc)106 trailJunc_t *CG_SpawnTrailJunc( trailJunc_t *headJunc ) {
107 	trailJunc_t *j;
108 
109 	if ( !freeTrails ) {
110 		return NULL;
111 	}
112 
113 	if ( cg_paused.integer ) {
114 		return NULL;
115 	}
116 
117 	// select the first free trail, and remove it from the list
118 	j = freeTrails;
119 	freeTrails = j->nextGlobal;
120 	if ( freeTrails ) {
121 		freeTrails->prevGlobal = NULL;
122 	}
123 
124 	j->nextGlobal = activeTrails;
125 	if ( activeTrails ) {
126 		activeTrails->prevGlobal = j;
127 	}
128 	activeTrails = j;
129 	j->prevGlobal = NULL;
130 	j->inuse = qtrue;
131 	j->freed = qfalse;
132 
133 	// if this owner has a headJunc, add us to the start
134 	if ( headJunc ) {
135 		// remove the headJunc from the list of heads
136 		if ( headJunc == headTrails ) {
137 			headTrails = headJunc->nextHead;
138 			if ( headTrails ) {
139 				headTrails->prevHead = NULL;
140 			}
141 		} else {
142 			if ( headJunc->nextHead ) {
143 				headJunc->nextHead->prevHead = headJunc->prevHead;
144 			}
145 			if ( headJunc->prevHead ) {
146 				headJunc->prevHead->nextHead = headJunc->nextHead;
147 			}
148 		}
149 		headJunc->prevHead = NULL;
150 		headJunc->nextHead = NULL;
151 	}
152 	// make us the headTrail
153 	if ( headTrails ) {
154 		headTrails->prevHead = j;
155 	}
156 	j->nextHead = headTrails;
157 	j->prevHead = NULL;
158 	headTrails = j;
159 
160 	j->nextJunc = headJunc; // if headJunc is NULL, then we'll just be the end of the list
161 
162 	numTrailsInuse++;
163 
164 	// debugging
165 //	CG_Printf( "NumTrails: %i\n", numTrailsInuse );
166 
167 	return j;
168 }
169 
170 
171 /*
172 ===============
173 CG_AddTrailJunc
174 
175   returns the index of the trail junction created
176 
177   Used for generic trails
178 ===============
179 */
CG_AddTrailJunc(int headJuncIndex,qhandle_t shader,int spawnTime,int sType,vec3_t pos,int trailLife,float alphaStart,float alphaEnd,float startWidth,float endWidth,int flags,vec3_t colorStart,vec3_t colorEnd,float sRatio,float animSpeed)180 int CG_AddTrailJunc( int headJuncIndex, qhandle_t shader, int spawnTime, int sType, vec3_t pos, int trailLife, float alphaStart, float alphaEnd, float startWidth, float endWidth, int flags, vec3_t colorStart, vec3_t colorEnd, float sRatio, float animSpeed ) {
181 	trailJunc_t *j, *headJunc;
182 
183 	if ( headJuncIndex > 0 ) {
184 		headJunc = &trailJuncs[headJuncIndex - 1];
185 
186 		if ( !headJunc->inuse ) {
187 			headJunc = NULL;
188 		}
189 	} else {
190 		headJunc = NULL;
191 	}
192 
193 	j = CG_SpawnTrailJunc( headJunc );
194 	if ( !j ) {
195 //		CG_Printf("couldnt spawn trail junc\n");
196 		return 0;
197 	}
198 
199 	if ( alphaStart > 1.0 ) {
200 		alphaStart = 1.0;
201 	}
202 	if ( alphaStart < 0.0 ) {
203 		alphaStart = 0.0;
204 	}
205 	if ( alphaEnd > 1.0 ) {
206 		alphaEnd = 1.0;
207 	}
208 	if ( alphaEnd < 0.0 ) {
209 		alphaEnd = 0.0;
210 	}
211 
212 	// setup the trail junction
213 	j->shader = shader;
214 	j->sType = sType;
215 	VectorCopy( pos, j->pos );
216 	j->flags = flags;
217 
218 	j->spawnTime = spawnTime;
219 	j->endTime = spawnTime + trailLife;
220 
221 	VectorCopy( colorStart, j->colorStart );
222 	VectorCopy( colorEnd, j->colorEnd );
223 
224 	j->alphaStart = alphaStart;
225 	j->alphaEnd = alphaEnd;
226 
227 	j->widthStart = startWidth;
228 	j->widthEnd = endWidth;
229 
230 	if ( sType == STYPE_REPEAT ) {
231 		if ( headJunc ) {
232 			j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / sRatio ) / j->widthEnd );
233 		} else {
234 			// FIXME: need a way to specify offset timing
235 			j->sTex = ( animSpeed * ( 1.0 - ( (float)( cg.time % 1000 ) / 1000.0 ) ) ) / ( sRatio );
236 //			j->sTex = 0;
237 		}
238 	}
239 
240 	return ( (int)( j - trailJuncs ) + 1 );
241 }
242 
243 /*
244 ===============
245 CG_AddSparkJunc
246 
247   returns the index of the trail junction created
248 ===============
249 */
CG_AddSparkJunc(int headJuncIndex,qhandle_t shader,vec3_t pos,int trailLife,float alphaStart,float alphaEnd,float startWidth,float endWidth)250 int CG_AddSparkJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, float alphaStart, float alphaEnd, float startWidth, float endWidth ) {
251 	trailJunc_t *j, *headJunc;
252 
253 	if ( headJuncIndex > 0 ) {
254 		headJunc = &trailJuncs[headJuncIndex - 1];
255 
256 		if ( !headJunc->inuse ) {
257 			headJunc = NULL;
258 		}
259 	} else {
260 		headJunc = NULL;
261 	}
262 
263 	j = CG_SpawnTrailJunc( headJunc );
264 	if ( !j ) {
265 		return 0;
266 	}
267 
268 	// setup the trail junction
269 	j->shader = shader;
270 	j->sType = STYPE_STRETCH;
271 	VectorCopy( pos, j->pos );
272 	j->flags = TJFL_NOCULL;     // don't worry about fading up close
273 
274 	j->spawnTime = cg.time;
275 	j->endTime = cg.time + trailLife;
276 
277 	VectorSet( j->colorStart, 1.0, 0.8 + 0.2 * alphaStart, 0.4 + 0.4 * alphaStart );
278 	VectorSet( j->colorEnd, 1.0, 0.8 + 0.2 * alphaEnd, 0.4 + 0.4 * alphaEnd );
279 //	VectorScale( j->colorStart, alphaStart, j->colorStart );
280 //	VectorScale( j->colorEnd, alphaEnd, j->colorEnd );
281 
282 	j->alphaStart = alphaStart * 2;
283 	j->alphaEnd = alphaEnd * 2;
284 //	j->alphaStart = 1.0;
285 //	j->alphaEnd = 1.0;
286 
287 	j->widthStart = startWidth;
288 	j->widthEnd = endWidth;
289 
290 	return ( (int)( j - trailJuncs ) + 1 );
291 }
292 
293 /*
294 ===============
295 CG_AddSmokeJunc
296 
297   returns the index of the trail junction created
298 ===============
299 */
CG_AddSmokeJunc(int headJuncIndex,qhandle_t shader,vec3_t pos,int trailLife,float alpha,float startWidth,float endWidth)300 int CG_AddSmokeJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, float alpha, float startWidth, float endWidth ) {
301 #define ST_RATIO    4.0     // sprite image: width / height
302 	trailJunc_t *j, *headJunc;
303 
304 	if ( headJuncIndex > 0 ) {
305 		headJunc = &trailJuncs[headJuncIndex - 1];
306 
307 		if ( !headJunc->inuse ) {
308 			headJunc = NULL;
309 		}
310 	} else {
311 		headJunc = NULL;
312 	}
313 
314 	j = CG_SpawnTrailJunc( headJunc );
315 	if ( !j ) {
316 		return 0;
317 	}
318 
319 	// setup the trail junction
320 	j->shader = shader;
321 	j->sType = STYPE_REPEAT;
322 	VectorCopy( pos, j->pos );
323 	j->flags = TJFL_FADEIN;
324 
325 	j->spawnTime = cg.time;
326 	j->endTime = cg.time + trailLife;
327 
328 //	VectorSet( j->colorStart, 0.2, 0.2, 0.2 );
329 	VectorSet( j->colorStart, 0.0, 0.0, 0.0 );
330 //	VectorSet( j->colorEnd, 0.1, 0.1, 0.1 );
331 	VectorSet( j->colorEnd, 0.0, 0.0, 0.0 );
332 
333 	j->alphaStart = alpha;
334 	j->alphaEnd = 0.0;
335 
336 	j->widthStart = startWidth;
337 	j->widthEnd = endWidth;
338 
339 	if ( headJunc ) {
340 		j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / ST_RATIO ) / j->widthEnd );
341 	} else {
342 		// first junction, so this will become the "tail" very soon, make it fade out
343 		j->sTex = 0;
344 		j->alphaStart = 0.0;
345 		j->alphaEnd = 0.0;
346 	}
347 
348 	return ( (int)( j - trailJuncs ) + 1 );
349 }
350 
351 void CG_KillTrail( trailJunc_t *t );
352 
353 /*
354 ===========
355 CG_FreeTrailJunc
356 ===========
357 */
CG_FreeTrailJunc(trailJunc_t * junc)358 void CG_FreeTrailJunc( trailJunc_t *junc ) {
359 	// kill any juncs after us, so they aren't left hanging
360 	if ( junc->nextJunc ) {
361 		CG_KillTrail( junc );
362 	}
363 
364 	// make it non-active
365 	junc->inuse = qfalse;
366 	junc->freed = qtrue;
367 	if ( junc->nextGlobal ) {
368 		junc->nextGlobal->prevGlobal = junc->prevGlobal;
369 	}
370 	if ( junc->prevGlobal ) {
371 		junc->prevGlobal->nextGlobal = junc->nextGlobal;
372 	}
373 	if ( junc == activeTrails ) {
374 		activeTrails = junc->nextGlobal;
375 	}
376 
377 	// if it's a head, remove it
378 	if ( junc == headTrails ) {
379 		headTrails = junc->nextHead;
380 	}
381 	if ( junc->nextHead ) {
382 		junc->nextHead->prevHead = junc->prevHead;
383 	}
384 	if ( junc->prevHead ) {
385 		junc->prevHead->nextHead = junc->nextHead;
386 	}
387 	junc->nextHead = NULL;
388 	junc->prevHead = NULL;
389 
390 	// stick it in the free list
391 	junc->prevGlobal = NULL;
392 	junc->nextGlobal = freeTrails;
393 	if ( freeTrails ) {
394 		freeTrails->prevGlobal = junc;
395 	}
396 	freeTrails = junc;
397 
398 	numTrailsInuse--;
399 }
400 
401 /*
402 ===========
403 CG_KillTrail
404 ===========
405 */
CG_KillTrail(trailJunc_t * t)406 void CG_KillTrail( trailJunc_t *t ) {
407 	trailJunc_t *next;
408 
409 	next = t->nextJunc;
410 
411 	// kill the trail here
412 	t->nextJunc = NULL;
413 
414 	if ( next ) {
415 		CG_FreeTrailJunc( next );
416 	}
417 }
418 
419 /*
420 ==============
421 CG_AddTrailToScene
422 
423   TODO: this can do with some major optimization
424 ==============
425 */
426 struct {
427 	vec3_t vforward, vright, vup;
428 } trailOrientation;
429 #define MAX_TRAIL_VERTS     2048
430 static polyVert_t verts[MAX_TRAIL_VERTS];
431 static polyVert_t outVerts[MAX_TRAIL_VERTS * 3];
432 
CG_AddTrailToScene(trailJunc_t * trail,int iteration,int numJuncs)433 void CG_AddTrailToScene( trailJunc_t *trail, int iteration, int numJuncs ) {
434 	int k, i, n, l, numOutVerts;
435 	polyVert_t mid;
436 	float mod[4];
437 	float sInc = 0.0f, s = 0.0f;   // TTimo: init
438 	trailJunc_t *j, *jNext;
439 	vec3_t up, p, v;
440 	// clipping vars
441 	#define TRAIL_FADE_CLOSE_DIST   64.0
442 	#define TRAIL_FADE_FAR_SCALE    4.0
443 	vec3_t viewProj;
444 	float viewDist, fadeAlpha;
445 
446 	// add spark shader at head position
447 	if ( trail->flags & TJFL_SPARKHEADFLARE ) {
448 		j = trail;
449 		VectorCopy( j->pos, p );
450 		VectorMA( p, -j->width * 2, trailOrientation.vup, p );
451 		VectorMA( p, -j->width * 2, trailOrientation.vright, p );
452 		VectorCopy( p, verts[0].xyz );
453 		verts[0].st[0] = 0;
454 		verts[0].st[1] = 0;
455 		verts[0].modulate[0] = 255;
456 		verts[0].modulate[1] = 255;
457 		verts[0].modulate[2] = 255;
458 		verts[0].modulate[3] = ( unsigned char )( j->alpha * 255.0 );
459 
460 		VectorCopy( j->pos, p );
461 		VectorMA( p, -j->width * 2, trailOrientation.vup, p );
462 		VectorMA( p, j->width * 2, trailOrientation.vright, p );
463 		VectorCopy( p, verts[1].xyz );
464 		verts[1].st[0] = 0;
465 		verts[1].st[1] = 1;
466 		verts[1].modulate[0] = 255;
467 		verts[1].modulate[1] = 255;
468 		verts[1].modulate[2] = 255;
469 		verts[1].modulate[3] = ( unsigned char )( j->alpha * 255.0 );
470 
471 		VectorCopy( j->pos, p );
472 		VectorMA( p, j->width * 2, trailOrientation.vup, p );
473 		VectorMA( p, j->width * 2, trailOrientation.vright, p );
474 		VectorCopy( p, verts[2].xyz );
475 		verts[2].st[0] = 1;
476 		verts[2].st[1] = 1;
477 		verts[2].modulate[0] = 255;
478 		verts[2].modulate[1] = 255;
479 		verts[2].modulate[2] = 255;
480 		verts[2].modulate[3] = ( unsigned char )( j->alpha * 255.0 );
481 
482 		VectorCopy( j->pos, p );
483 		VectorMA( p,  j->width * 2, trailOrientation.vup, p );
484 		VectorMA( p, -j->width * 2, trailOrientation.vright, p );
485 		VectorCopy( p, verts[3].xyz );
486 		verts[3].st[0] = 1;
487 		verts[3].st[1] = 0;
488 		verts[3].modulate[0] = 255;
489 		verts[3].modulate[1] = 255;
490 		verts[3].modulate[2] = 255;
491 		verts[3].modulate[3] = ( unsigned char )( j->alpha * 255.0 );
492 
493 		trap_R_AddPolyToScene( cgs.media.sparkFlareShader, 4, verts );
494 	}
495 
496 //	if (trail->flags & TJFL_CROSSOVER && iteration < 1) {
497 //		iteration = 1;
498 //	}
499 
500 	if ( !numJuncs ) {
501 		// first count the number of juncs in the trail
502 		j = trail;
503 		numJuncs = 0;
504 		sInc = 0;
505 		while ( j ) {
506 			numJuncs++;
507 
508 			// check for a dead next junc
509 			if ( !j->inuse && j->nextJunc && !j->nextJunc->inuse ) {
510 				CG_KillTrail( j );
511 			} else if ( j->nextJunc && j->nextJunc->freed ) {
512 				// not sure how this can happen, but it does, and causes infinite loops
513 				j->nextJunc = NULL;
514 			}
515 
516 			if ( j->nextJunc ) {
517 				sInc += VectorDistance( j->nextJunc->pos, j->pos );
518 			}
519 
520 			j = j->nextJunc;
521 		}
522 	}
523 
524 	if ( numJuncs < 2 ) {
525 		return;
526 	}
527 
528 	if ( trail->sType == STYPE_STRETCH ) {
529 		//sInc = ((1.0 - 0.1) / (float)(numJuncs));	// hack, the end of funnel shows a bit of the start (looping)
530 		s = 0.05;
531 		//s = 0.05;
532 	} else if ( trail->sType == STYPE_REPEAT ) {
533 		s = trail->sTex;
534 	}
535 
536 	// now traverse the list
537 	j = trail;
538 	jNext = j->nextJunc;
539 	i = 0;
540 	while ( jNext ) {
541 
542 		// first get the directional vectors to the next junc
543 		GetPerpendicularViewVector( cg.refdef.vieworg, j->pos, jNext->pos, up );
544 
545 		// if it's a crossover, draw it twice
546 		if ( j->flags & TJFL_CROSSOVER ) {
547 			if ( iteration > 0 ) {
548 				ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj );
549 				VectorSubtract( cg.refdef.vieworg, viewProj, v );
550 				VectorNormalize( v );
551 
552 				if ( iteration == 1 ) {
553 					VectorMA( up, 0.3, v, up );
554 				} else {
555 					VectorMA( up, -0.3, v, up );
556 				}
557 				VectorNormalize( up );
558 			}
559 		}
560 		// do fading when moving towards the projection point onto the trail segment vector
561 		else if ( !( j->flags & TJFL_NOCULL ) && ( j->widthEnd > 4 || jNext->widthEnd > 4 ) ) {
562 			ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj );
563 			viewDist = Distance( viewProj, cg.refdef.vieworg );
564 			if ( viewDist < ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE ) ) {
565 				if ( viewDist < TRAIL_FADE_CLOSE_DIST ) {
566 					fadeAlpha = 0.0;
567 				} else {
568 					fadeAlpha = ( viewDist - TRAIL_FADE_CLOSE_DIST ) / ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE );
569 				}
570 				if ( fadeAlpha < j->alpha ) {
571 					j->alpha = fadeAlpha;
572 				}
573 				if ( fadeAlpha < jNext->alpha ) {
574 					jNext->alpha = fadeAlpha;
575 				}
576 			}
577 		}
578 
579 		// now output the QUAD for this segment
580 
581 		// 1 ----
582 		VectorMA( j->pos, 0.5 * j->width, up, p );
583 		VectorCopy( p, verts[i].xyz );
584 		verts[i].st[0] = s;
585 		verts[i].st[1] = 1.0;
586 		for ( k = 0; k < 3; k++ )
587 			verts[i].modulate[k] = ( unsigned char )( j->color[k] * 255.0 );
588 		verts[i].modulate[3] = ( unsigned char )( j->alpha * 255.0 );
589 
590 		// blend this with the previous junc
591 		if ( j != trail ) {
592 			VectorAdd( verts[i].xyz, verts[i - 1].xyz, verts[i].xyz );
593 			VectorScale( verts[i].xyz, 0.5, verts[i].xyz );
594 			VectorCopy( verts[i].xyz, verts[i - 1].xyz );
595 		} else if ( j->flags & TJFL_FADEIN ) {
596 			verts[i].modulate[3] = 0;   // fade in
597 		}
598 
599 		i++;
600 
601 		// 2 ----
602 		VectorMA( p, -1 * j->width, up, p );
603 		VectorCopy( p, verts[i].xyz );
604 		verts[i].st[0] = s;
605 		verts[i].st[1] = 0.0;
606 		for ( k = 0; k < 3; k++ )
607 			verts[i].modulate[k] = ( unsigned char )( j->color[k] * 255.0 );
608 		verts[i].modulate[3] = ( unsigned char )( j->alpha * 255.0 );
609 
610 		// blend this with the previous junc
611 		if ( j != trail ) {
612 			VectorAdd( verts[i].xyz, verts[i - 3].xyz, verts[i].xyz );
613 			VectorScale( verts[i].xyz, 0.5, verts[i].xyz );
614 			VectorCopy( verts[i].xyz, verts[i - 3].xyz );
615 		} else if ( j->flags & TJFL_FADEIN ) {
616 			verts[i].modulate[3] = 0;   // fade in
617 		}
618 
619 		i++;
620 
621 		if ( trail->sType == STYPE_REPEAT ) {
622 			s = jNext->sTex;
623 		} else {
624 			//s += sInc;
625 			s += VectorDistance( j->pos, jNext->pos ) / sInc;
626 			if ( s > 1.0 ) {
627 				s = 1.0;
628 			}
629 		}
630 
631 		// 3 ----
632 		VectorMA( jNext->pos, -0.5 * jNext->width, up, p );
633 		VectorCopy( p, verts[i].xyz );
634 		verts[i].st[0] = s;
635 		verts[i].st[1] = 0.0;
636 		for ( k = 0; k < 3; k++ )
637 			verts[i].modulate[k] = ( unsigned char )( jNext->color[k] * 255.0 );
638 		verts[i].modulate[3] = ( unsigned char )( jNext->alpha * 255.0 );
639 		i++;
640 
641 		// 4 ----
642 		VectorMA( p, jNext->width, up, p );
643 		VectorCopy( p, verts[i].xyz );
644 		verts[i].st[0] = s;
645 		verts[i].st[1] = 1.0;
646 		for ( k = 0; k < 3; k++ )
647 			verts[i].modulate[k] = ( unsigned char )( jNext->color[k] * 255.0 );
648 		verts[i].modulate[3] = ( unsigned char )( jNext->alpha * 255.0 );
649 		i++;
650 
651 		if ( i + 4 > MAX_TRAIL_VERTS ) {
652 			break;
653 		}
654 
655 		j = jNext;
656 		jNext = j->nextJunc;
657 	}
658 
659 	if ( trail->flags & TJFL_FIXDISTORT ) {
660 		// build the list of outVerts, by dividing up the QUAD's into 4 Tri's each, so as to allow
661 		//	any shaped (convex) Quad without bilinear distortion
662 		for ( k = 0, numOutVerts = 0; k < i; k += 4 ) {
663 			VectorCopy( verts[k].xyz, mid.xyz );
664 			mid.st[0] = verts[k].st[0];
665 			mid.st[1] = verts[k].st[1];
666 			for ( l = 0; l < 4; l++ ) {
667 				mod[l] = (float)verts[k].modulate[l];
668 			}
669 			for ( n = 1; n < 4; n++ ) {
670 				VectorAdd( verts[k + n].xyz, mid.xyz, mid.xyz );
671 				mid.st[0] += verts[k + n].st[0];
672 				mid.st[1] += verts[k + n].st[1];
673 				for ( l = 0; l < 4; l++ ) {
674 					mod[l] += (float)verts[k + n].modulate[l];
675 				}
676 			}
677 			VectorScale( mid.xyz, 0.25, mid.xyz );
678 			mid.st[0] *= 0.25;
679 			mid.st[1] *= 0.25;
680 			for ( l = 0; l < 4; l++ ) {
681 				mid.modulate[l] = ( unsigned char )( mod[l] / 4.0 );
682 			}
683 
684 			// now output the tri's
685 			for ( n = 0; n < 4; n++ ) {
686 				outVerts[numOutVerts++] = verts[k + n];
687 				outVerts[numOutVerts++] = mid;
688 				if ( n < 3 ) {
689 					outVerts[numOutVerts++] = verts[k + n + 1];
690 				} else {
691 					outVerts[numOutVerts++] = verts[k];
692 				}
693 			}
694 
695 		}
696 
697 		if ( !( trail->flags & TJFL_NOPOLYMERGE ) ) {
698 			trap_R_AddPolysToScene( trail->shader, 3, &outVerts[0], numOutVerts / 3 );
699 		} else {
700 			int k;
701 			for ( k = 0; k < numOutVerts / 3; k++ ) {
702 				trap_R_AddPolyToScene( trail->shader, 3, &outVerts[k * 3] );
703 			}
704 		}
705 	} else
706 	{
707 		// send the polygons
708 		// FIXME: is it possible to send a GL_STRIP here? We are actually sending 2x the verts we really need to
709 		if ( !( trail->flags & TJFL_NOPOLYMERGE ) ) {
710 			trap_R_AddPolysToScene( trail->shader, 4, &verts[0], i / 4 );
711 		} else {
712 			int k;
713 			for ( k = 0; k < i / 4; k++ ) {
714 				trap_R_AddPolyToScene( trail->shader, 4, &verts[k * 4] );
715 			}
716 		}
717 	}
718 
719 	// do we need to make another pass?
720 	if ( trail->flags & TJFL_CROSSOVER ) {
721 		if ( iteration < 2 ) {
722 			CG_AddTrailToScene( trail, iteration + 1, numJuncs );
723 		}
724 	}
725 
726 }
727 
728 /*
729 ===============
730 CG_AddTrails
731 ===============
732 */
CG_AddTrails(void)733 void CG_AddTrails( void ) {
734 	float lifeFrac;
735 	trailJunc_t *j, *jNext;
736 
737 	if ( !initTrails ) {
738 		CG_ClearTrails();
739 	}
740 
741 	//AngleVectors( cg.snap->ps.viewangles, trailOrientation.vforward, trailOrientation.vright, trailOrientation.vup );
742 	VectorCopy( cg.refdef.viewaxis[0], trailOrientation.vforward );
743 	VectorCopy( cg.refdef.viewaxis[1], trailOrientation.vright );
744 	VectorCopy( cg.refdef.viewaxis[2], trailOrientation.vup );
745 
746 	// update the settings for each junc
747 	j = activeTrails;
748 	while ( j ) {
749 		lifeFrac = (float)( cg.time - j->spawnTime ) / (float)( j->endTime - j->spawnTime );
750 		if ( lifeFrac >= 1.0 ) {
751 			j->inuse = qfalse;          // flag it as dead
752 			j->width = j->widthEnd;
753 			j->alpha = j->alphaEnd;
754 			if ( j->alpha > 1.0 ) {
755 				j->alpha = 1.0;
756 			} else if ( j->alpha < 0.0 ) {
757 				j->alpha = 0.0;
758 			}
759 			VectorCopy( j->colorEnd, j->color );
760 		} else {
761 			j->width = j->widthStart + ( j->widthEnd - j->widthStart ) * lifeFrac;
762 			j->alpha = j->alphaStart + ( j->alphaEnd - j->alphaStart ) * lifeFrac;
763 			if ( j->alpha > 1.0 ) {
764 				j->alpha = 1.0;
765 			} else if ( j->alpha < 0.0 ) {
766 				j->alpha = 0.0;
767 			}
768 			VectorSubtract( j->colorEnd, j->colorStart, j->color );
769 			VectorMA( j->colorStart, lifeFrac, j->color, j->color );
770 		}
771 
772 		j = j->nextGlobal;
773 	}
774 
775 	// draw the trailHeads
776 	j = headTrails;
777 	while ( j ) {
778 		jNext = j->nextHead;        // in case it gets removed
779 		if ( !j->inuse ) {
780 			CG_FreeTrailJunc( j );
781 		} else {
782 			CG_AddTrailToScene( j, 0, 0 );
783 		}
784 		j = jNext;
785 	}
786 }
787