1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 #include "client.h"
24 #include "cl_cgameapi.h"
25 #include "FxScheduler.h"
26 
27 extern int		drawnFx;
28 
29 //--------------------------
30 //
31 // Base Effect Class
32 //
33 //--------------------------
CEffect(void)34 CEffect::CEffect(void) :
35 	mFlags(0),
36 	mMatImpactFX(MATIMPACTFX_NONE),
37 	mMatImpactParm(-1),
38 	mSoundRadius(-1),
39 	mSoundVolume(-1)
40 {
41 	memset( &mRefEnt, 0, sizeof( mRefEnt ));
42 }
43 
44 //--------------------------
45 //
46 // Derived Particle Class
47 //
48 //--------------------------
49 
50 //----------------------------
Init(void)51 void CParticle::Init(void)
52 {
53 	mRefEnt.radius = 0.0f;
54 	if (mFlags & FX_PLAYER_VIEW)
55 	{
56 		mOrigin1[0] = irand(0, 639);
57 		mOrigin1[1] = irand(0, 479);
58 	}
59 }
60 
61 //----------------------------
Die(void)62 void CParticle::Die(void)
63 {
64 	if ( mFlags & FX_DEATH_RUNS_FX && !(mFlags & FX_KILL_ON_IMPACT) )
65 	{
66 		vec3_t	norm;
67 
68 		// Man, this just seems so, like, uncool and stuff...
69 		VectorSet( norm, flrand(-1.0f, 1.0f), flrand(-1.0f, 1.0f), flrand(-1.0f, 1.0f));
70 		VectorNormalize( norm );
71 
72 		theFxScheduler.PlayEffect( mDeathFxID, mOrigin1, norm );
73 	}
74 }
75 
76 //----------------------------
Cull(void)77 bool CParticle::Cull(void)
78 {
79 	vec3_t	dir;
80 
81 	if (mFlags & FX_PLAYER_VIEW)
82 	{
83 		// this will be drawn as a 2D effect so don't cull it
84 		return false;
85 	}
86 
87 	// Get the direction to the view
88 	VectorSubtract( mOrigin1, theFxHelper.refdef->vieworg, dir );
89 
90 	// Check if it's behind the viewer
91 	if ( (DotProduct( theFxHelper.refdef->viewaxis[0], dir )) < 0) // cg.cosHalfFOV * (len - mRadius) )
92 	{
93 		return true;
94 	}
95 
96 	// don't cull if this is hacked to show up close to the inview wpn
97 	if (mFlags & FX_DEPTH_HACK)
98 	{
99 		return false;
100 	}
101 	// Can't be too close
102 	float len = VectorLengthSquared( dir );
103 	if ( len < fx_nearCull->value )
104 	{
105 		return true;
106 	}
107 
108 	return false;
109 }
110 
111 //----------------------------
Draw(void)112 void CParticle::Draw(void)
113 {
114 	if ( mFlags & FX_DEPTH_HACK )
115 	{
116 		// Not sure if first person needs to be set, but it can't hurt?
117 		mRefEnt.renderfx |= RF_DEPTHHACK;
118 	}
119 
120 	if (mFlags & FX_PLAYER_VIEW)
121 	{
122 		vec4_t	color;
123 
124 		color[0] = mRefEnt.shaderRGBA[0] / 255.0;
125 		color[1] = mRefEnt.shaderRGBA[1] / 255.0;
126 		color[2] = mRefEnt.shaderRGBA[2] / 255.0;
127 		color[3] = mRefEnt.shaderRGBA[3] / 255.0;
128 
129 		// add this 2D effect to the proper list. it will get drawn after the trap->RenderScene call
130 		theFxScheduler.Add2DEffect(mOrigin1[0], mOrigin1[1], mRefEnt.radius, mRefEnt.radius, color, mRefEnt.customShader);
131 	}
132 	else
133 	{
134 		// Add our refEntity to the scene
135 		VectorCopy( mOrigin1, mRefEnt.origin );
136 
137 		theFxHelper.AddFxToScene(&mRefEnt);
138 	}
139 	drawnFx++;
140 }
141 
142 //----------------------------
143 // Update
144 //----------------------------
Update(void)145 bool CParticle::Update(void)
146 {
147 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
148 	if ( mTimeStart > theFxHelper.mTime )
149 	{
150 		return false;
151 	}
152 
153 	if ( mFlags & FX_RELATIVE )
154 	{
155 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
156 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
157 			return false;
158 		}
159 
160 		vec3_t	org;
161 		matrix3_t	ax;
162 
163 		// Get our current position and direction
164 		if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, org, ax))
165 		{	//could not get bolt
166 			return false;
167 		}
168 		vec3_t 	realVel, realAccel;
169 
170 		VectorMA( org, mOrgOffset[0], ax[0], org );
171 		VectorMA( org, mOrgOffset[1], ax[1], org );
172 		VectorMA( org, mOrgOffset[2], ax[2], org );
173 
174 		const float	time = (theFxHelper.mTime - mTimeStart) * 0.001f;
175 		// calc the real velocity and accel vectors
176 		VectorScale( ax[0], mVel[0], realVel );
177 		VectorMA( realVel, mVel[1], ax[1], realVel );
178 		VectorMA( realVel, mVel[2], ax[2], realVel );
179 		//realVel[2] += 0.5f * mGravity * time;
180 
181 		VectorScale( ax[0], mAccel[0], realAccel );
182 		VectorMA( realAccel, mAccel[1], ax[1], realAccel );
183 		VectorMA( realAccel, mAccel[2], ax[2], realAccel );
184 
185 		// Get our real velocity at the current time, taking into account the effects of acceleartion.  NOTE: not sure if this is even 100% correct math-wise
186 		VectorMA( realVel, time, realAccel, realVel );
187 
188 		// Now move us to where we should be at the given time
189 		VectorMA( org, time, realVel, mOrigin1 );
190 
191 	}
192 	else if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
193 	{
194 		// we are marked for death
195 		return false;
196 	}
197 
198 
199 	if ( !Cull() )
200 	{
201 		// Only update these if the thing is visible.
202 		UpdateSize();
203 		UpdateRGB();
204 		UpdateAlpha();
205 		UpdateRotation();
206 
207 		Draw();
208 	}
209 
210 	return true;
211 }
212 
213 //----------------------------
214 // Update Origin
215 //----------------------------
UpdateOrigin(void)216 bool CParticle::UpdateOrigin(void)
217 {
218 	vec3_t	new_origin;
219 
220 	VectorMA( mVel, theFxHelper.mRealTime, mAccel, mVel );
221 
222 	// Predict the new position
223 	new_origin[0] = mOrigin1[0] + (theFxHelper.mRealTime * mVel[0]);// + (theFxHelper.mHalfRealTimeSq * mVel[0]);
224 	new_origin[1] = mOrigin1[1] + (theFxHelper.mRealTime * mVel[1]);// + (theFxHelper.mHalfRealTimeSq * mVel[1]);
225 	new_origin[2] = mOrigin1[2] + (theFxHelper.mRealTime * mVel[2]);// + (theFxHelper.mHalfRealTimeSq * mVel[2]);
226 
227 	// Only perform physics if this object is tagged to do so
228 	if ( (mFlags & FX_APPLY_PHYSICS) && !(mFlags & FX_PLAYER_VIEW) )
229 	{
230 		if ( mFlags & FX_EXPENSIVE_PHYSICS )
231 		{
232 			trace_t	trace;
233 			float	dot;
234 
235 			if ( mFlags & FX_USE_BBOX )
236 			{
237 				if (mFlags & FX_GHOUL2_TRACE)
238 				{
239 					theFxHelper.G2Trace( trace, mOrigin1, mMin, mMax, new_origin, -1, MASK_SOLID );
240 				}
241 				else
242 				{
243 					theFxHelper.Trace( trace, mOrigin1, mMin, mMax, new_origin, -1, MASK_SOLID );
244 				}
245 			}
246 			else
247 			{
248 				if (mFlags & FX_GHOUL2_TRACE)
249 				{
250 					theFxHelper.G2Trace( trace, mOrigin1, NULL, NULL, new_origin, -1, MASK_PLAYERSOLID );
251 				}
252 				else
253 				{
254 					theFxHelper.Trace( trace, mOrigin1, NULL, NULL, new_origin, -1, MASK_SOLID );
255 				}
256 			}
257 
258 			// Hit something
259 			if (trace.startsolid || trace.allsolid)
260 			{
261 				VectorClear( mVel );
262 				VectorClear( mAccel );
263 
264 				if ((mFlags & FX_GHOUL2_TRACE) && (mFlags & FX_IMPACT_RUNS_FX))
265 				{
266 					static vec3_t bsNormal = {0, 1, 0};
267 
268 					theFxScheduler.PlayEffect( mImpactFxID, trace.endpos, bsNormal );
269 				}
270 
271 				mFlags &= ~(FX_APPLY_PHYSICS | FX_IMPACT_RUNS_FX);
272 
273 				return true;
274 			}
275 			else if ( trace.fraction < 1.0f )//&& !trace.startsolid && !trace.allsolid )
276 			{
277 				if ( mFlags & FX_IMPACT_RUNS_FX && !(trace.surfaceFlags & SURF_NOIMPACT ))
278 				{
279 					theFxScheduler.PlayEffect( mImpactFxID, trace.endpos, trace.plane.normal );
280 				}
281 
282 				// may need to interact with the material type we hit
283 				theFxScheduler.MaterialImpact(&trace, (CEffect*)this);
284 
285 				if ( mFlags & FX_KILL_ON_IMPACT	)
286 				{
287 					// time to die
288 					return false;
289 				}
290 
291 				VectorMA( mVel, theFxHelper.mRealTime * trace.fraction, mAccel, mVel );
292 
293 				dot = DotProduct( mVel, trace.plane.normal );
294 
295 				VectorMA( mVel, -2.0f * dot, trace.plane.normal, mVel );
296 
297 				VectorScale( mVel, mElasticity, mVel );
298 				mElasticity *= 0.5f;
299 
300 				// If the velocity is too low, make it stop moving, rotating, and turn off physics to avoid
301 				//	doing expensive operations when they aren't needed
302 				//if ( trace.plane.normal[2] > 0.33f && mVel[2] < 10.0f )
303 				if (VectorLengthSquared(mVel) < 100.0f)
304 				{
305 					VectorClear( mVel );
306 					VectorClear( mAccel );
307 
308 					mFlags &= ~(FX_APPLY_PHYSICS | FX_IMPACT_RUNS_FX);
309 				}
310 
311 				// Set the origin to the exact impact point
312 				VectorMA( trace.endpos, 1.0f, trace.plane.normal, mOrigin1 );
313 				return true;
314 			}
315 		}
316 	}
317 
318 	// No physics were done to this object, move it
319 	VectorCopy( new_origin, mOrigin1 );
320 
321 	if (mFlags & FX_PLAYER_VIEW)
322 	{
323 		if (mOrigin1[0] < 0 || mOrigin1[0] > 639 || mOrigin1[1] < 0 || mOrigin1[1] > 479)
324 		{
325 			return false;
326 		}
327 	}
328 
329 	return true;
330 }
331 
332 //----------------------------
333 // Update Size
334 //----------------------------
UpdateSize(void)335 void CParticle::UpdateSize(void)
336 {
337 	// completely biased towards start if it doesn't get overridden
338 	float	perc1 = 1.0f, perc2 = 1.0f;
339 
340 	if ( (mFlags & FX_SIZE_LINEAR) )
341 	{
342 		// calculate element biasing
343 		perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart) / (float)(mTimeEnd - mTimeStart);
344 	}
345 
346 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR, FX_WAVE, or FX_CLAMP
347 	if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_NONLINEAR )
348 	{
349 		if ( theFxHelper.mTime > mSizeParm )
350 		{
351 			// get percent done, using parm as the start of the non-linear fade
352 			perc2 = 1.0f - (float)(theFxHelper.mTime - mSizeParm) / (float)(mTimeEnd - mSizeParm);
353 		}
354 
355 		if ( mFlags & FX_SIZE_LINEAR )
356 		{
357 			// do an even blend
358 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
359 		}
360 		else
361 		{
362 			// just copy it over...sigh
363 			perc1 = perc2;
364 		}
365 	}
366 	else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_WAVE )
367 	{
368 		// wave gen, with parm being the frequency multiplier
369 		perc1 = perc1 * cosf( (theFxHelper.mTime - mTimeStart) * mSizeParm );
370 	}
371 	else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_CLAMP )
372 	{
373 		if ( theFxHelper.mTime < mSizeParm )
374 		{
375 			// get percent done, using parm as the start of the non-linear fade
376 			perc2 = (float)(mSizeParm - theFxHelper.mTime) / (float)(mSizeParm - mTimeStart);
377 		}
378 		else
379 		{
380 			perc2 = 0.0f; // make it full size??
381 		}
382 
383 		if ( (mFlags & FX_SIZE_LINEAR) )
384 		{
385 			// do an even blend
386 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
387 		}
388 		else
389 		{
390 			// just copy it over...sigh
391 			perc1 = perc2;
392 		}
393 	}
394 
395 	// If needed, RAND can coexist with linear and either non-linear or wave.
396 	if ( mFlags & FX_SIZE_RAND )
397 	{
398 		// Random simply modulates the existing value
399 		perc1 = flrand(0.0f, perc1);
400 	}
401 
402 	mRefEnt.radius = (mSizeStart * perc1) + (mSizeEnd * (1.0f - perc1));
403 }
404 
ClampRGB(const vec3_t in,byte * out)405 void ClampRGB( const vec3_t in, byte *out )
406 {
407 	int r;
408 
409 	for ( int i=0; i<3; i++ ) {
410 		r = Q_ftol(in[i] * 255.0f);
411 
412 		if ( r < 0 )
413 			r = 0;
414 		else if ( r > 255 )
415 			r = 255;
416 
417 		out[i] = (byte)r;
418 	}
419 }
420 
421 //----------------------------
422 // Update RGB
423 //----------------------------
UpdateRGB(void)424 void CParticle::UpdateRGB(void)
425 {
426 	// completely biased towards start if it doesn't get overridden
427 	float	perc1 = 1.0f, perc2 = 1.0f;
428 	vec3_t	res;
429 
430 	if ( (mFlags & FX_RGB_LINEAR) )
431 	{
432 		// calculate element biasing
433 		perc1 = 1.0f - (float)( theFxHelper.mTime - mTimeStart ) / (float)( mTimeEnd - mTimeStart );
434 	}
435 
436 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR, FX_WAVE, or FX_CLAMP
437 	if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_NONLINEAR )
438 	{
439 		if ( theFxHelper.mTime > mRGBParm )
440 		{
441 			// get percent done, using parm as the start of the non-linear fade
442 			perc2 = 1.0f - (float)( theFxHelper.mTime - mRGBParm ) / (float)( mTimeEnd - mRGBParm );
443 		}
444 
445 		if ( (mFlags & FX_RGB_LINEAR) )
446 		{
447 			// do an even blend
448 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
449 		}
450 		else
451 		{
452 			// just copy it over...sigh
453 			perc1 = perc2;
454 		}
455 	}
456 	else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_WAVE )
457 	{
458 		// wave gen, with parm being the frequency multiplier
459 		perc1 = perc1 * cosf(( theFxHelper.mTime - mTimeStart ) * mRGBParm );
460 	}
461 	else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_CLAMP )
462 	{
463 		if ( theFxHelper.mTime < mRGBParm )
464 		{
465 			// get percent done, using parm as the start of the non-linear fade
466 			perc2 = (float)(mRGBParm - theFxHelper.mTime) / (float)(mRGBParm - mTimeStart);
467 		}
468 		else
469 		{
470 			perc2 = 0.0f; // make it full size??
471 		}
472 
473 		if (( mFlags & FX_RGB_LINEAR ))
474 		{
475 			// do an even blend
476 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
477 		}
478 		else
479 		{
480 			// just copy it over...sigh
481 			perc1 = perc2;
482 		}
483 	}
484 
485 	// If needed, RAND can coexist with linear and either non-linear or wave.
486 	if ( mFlags & FX_RGB_RAND )
487 	{
488 		// Random simply modulates the existing value
489 		perc1 = flrand(0.0f, perc1);
490 	}
491 
492 	// Now get the correct color
493 	VectorScale( mRGBStart, perc1, res );
494 	VectorMA( res, 1.0f - perc1, mRGBEnd, res );
495 
496 	ClampRGB( res, (byte*)(&mRefEnt.shaderRGBA) );
497 }
498 
499 //----------------------------
500 // Update Alpha
501 //----------------------------
UpdateAlpha(void)502 void CParticle::UpdateAlpha(void)
503 {
504 	int		alpha;
505 
506 	// completely biased towards start if it doesn't get overridden
507 	float	perc1 = 1.0f, perc2 = 1.0f;
508 
509 	if ( (mFlags & FX_ALPHA_LINEAR) )
510 	{
511 		// calculate element biasing
512 		perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart) / (float)(mTimeEnd - mTimeStart);
513 	}
514 
515 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR, FX_WAVE, or FX_CLAMP
516 	if (( mFlags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_NONLINEAR )
517 	{
518 		if ( theFxHelper.mTime > mAlphaParm )
519 		{
520 			// get percent done, using parm as the start of the non-linear fade
521 			perc2 = 1.0f - (float)(theFxHelper.mTime - mAlphaParm) / (float)(mTimeEnd - mAlphaParm);
522 		}
523 
524 		if (( mFlags & FX_ALPHA_LINEAR ))
525 		{
526 			// do an even blend
527 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
528 		}
529 		else
530 		{
531 			// just copy it over...sigh
532 			perc1 = perc2;
533 		}
534 	}
535 	else if (( mFlags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_WAVE )
536 	{
537 		// wave gen, with parm being the frequency multiplier
538 		perc1 = perc1 * cosf( (theFxHelper.mTime - mTimeStart) * mAlphaParm );
539 	}
540 	else if (( mFlags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_CLAMP )
541 	{
542 		if ( theFxHelper.mTime < mAlphaParm )
543 		{
544 			// get percent done, using parm as the start of the non-linear fade
545 			perc2 = (float)(mAlphaParm - theFxHelper.mTime) / (float)(mAlphaParm - mTimeStart);
546 		}
547 		else
548 		{
549 			perc2 = 0.0f; // make it full size??
550 		}
551 
552 		if (( mFlags & FX_ALPHA_LINEAR ))
553 		{
554 			// do an even blend
555 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
556 		}
557 		else
558 		{
559 			// just copy it over...sigh
560 			perc1 = perc2;
561 		}
562 	}
563 
564 	perc1 = (mAlphaStart * perc1) + (mAlphaEnd * (1.0f - perc1));
565 
566 	// We should be in the right range, but clamp to ensure
567 	perc1 = Com_Clamp(0.0f, 1.0f, perc1);
568 
569 	// If needed, RAND can coexist with linear and either non-linear or wave.
570 	if ( mFlags & FX_ALPHA_RAND )
571 	{
572 		// Random simply modulates the existing value
573 		perc1 = flrand(0.0f, perc1);
574 	}
575 
576 	alpha = Com_Clamp(0, 255, perc1 * 255.0f);
577 	if ( mFlags & FX_USE_ALPHA )
578 	{
579 		// should use this when using art that has an alpha channel
580 		 mRefEnt.shaderRGBA[3] = (byte)alpha;
581 	}
582 	else
583 	{
584 		// Modulate the rgb fields by the alpha value to do the fade, works fine for additive blending
585 		mRefEnt.shaderRGBA[0] = ((int)mRefEnt.shaderRGBA[0] * alpha) >> 8;
586 		mRefEnt.shaderRGBA[1] = ((int)mRefEnt.shaderRGBA[1] * alpha) >> 8;
587 		mRefEnt.shaderRGBA[2] = ((int)mRefEnt.shaderRGBA[2] * alpha) >> 8;
588 	}
589 }
590 
591 //--------------------------
UpdateRotation(void)592 void CParticle::UpdateRotation(void)
593 {
594 	mRefEnt.rotation += theFxHelper.mFrameTime * 0.01f * mRotationDelta;
595 	mRotationDelta *= ( 1.0f - ( theFxHelper.mFrameTime * 0.0007f )); // decay rotationDelta
596 }
597 
598 
599 //--------------------------------
600 //
601 // Derived Oriented Particle Class
602 //
603 //--------------------------------
COrientedParticle(void)604 COrientedParticle::COrientedParticle(void)
605 {
606 	mRefEnt.reType = RT_ORIENTED_QUAD;
607 }
608 
609 //----------------------------
Cull(void)610 bool COrientedParticle::Cull(void)
611 {
612 	vec3_t	dir;
613 //	float	len;
614 
615 	// Get the direction to the view
616 	VectorSubtract( mOrigin1, theFxHelper.refdef->vieworg, dir );
617 
618 	// Check if it's behind the viewer
619 	if ( (DotProduct( theFxHelper.refdef->viewaxis[0], dir )) < 0 )
620 	{
621 		return true;
622 	}
623 
624 //	len = VectorLengthSquared( dir );
625 
626 	// don't cull stuff that's associated with inview wpns
627 	if ( mFlags & FX_DEPTH_HACK )
628 	{
629 		return false;
630 	}
631 	// Can't be too close
632 //	if ( len < fx_nearCull->value * fx_nearCull->value)
633 //	{
634 //		return true;
635 //	}
636 
637 	return false;
638 }
639 
640 //----------------------------
Draw(void)641 void COrientedParticle::Draw(void)
642 {
643 	if ( mFlags & FX_DEPTH_HACK )
644 	{
645 		// Not sure if first person needs to be set
646 		mRefEnt.renderfx |= RF_DEPTHHACK;
647 	}
648 
649 	// Add our refEntity to the scene
650 	VectorCopy( mOrigin1, mRefEnt.origin );
651 	if ( !(mFlags&FX_RELATIVE) )
652 	{
653 		VectorCopy( mNormal, mRefEnt.axis[0] );
654 		MakeNormalVectors( mRefEnt.axis[0], mRefEnt.axis[1], mRefEnt.axis[2] );
655 	}
656 
657 	theFxHelper.AddFxToScene( &mRefEnt );
658 	drawnFx++;
659 }
660 
661 //----------------------------
662 // Update
663 //----------------------------
Update(void)664 bool COrientedParticle::Update(void)
665 {
666 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
667 	if ( mTimeStart > theFxHelper.mTime )
668 	{
669 		return false;
670 	}
671 
672 	if ( mFlags & FX_RELATIVE )
673 	{
674 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
675 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
676 			return false;
677 		}
678 		vec3_t	org;
679 		matrix3_t	ax;
680 
681 		// Get our current position and direction
682 		if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, org, ax))
683 		{	//could not get bolt
684 			return false;
685 		}
686 		vec3_t 	realVel, realAccel;
687 
688 		VectorMA( org, mOrgOffset[0], ax[0], org );
689 		VectorMA( org, mOrgOffset[1], ax[1], org );
690 		VectorMA( org, mOrgOffset[2], ax[2], org );
691 
692 		const float	time = (theFxHelper.mTime - mTimeStart) * 0.001f;
693 		// calc the real velocity and accel vectors
694 		VectorScale( ax[0], mVel[0], realVel );
695 		VectorMA( realVel, mVel[1], ax[1], realVel );
696 		VectorMA( realVel, mVel[2], ax[2], realVel );
697 //		realVel[2] += 0.5f * mGravity * time;
698 
699 		VectorScale( ax[0], mAccel[0], realAccel );
700 		VectorMA( realAccel, mAccel[1], ax[1], realAccel );
701 		VectorMA( realAccel, mAccel[2], ax[2], realAccel );
702 
703 		// Get our real velocity at the current time, taking into account the effects of acceleartion.  NOTE: not sure if this is even 100% correct math-wise
704 		VectorMA( realVel, time, realAccel, realVel );
705 
706 		// Now move us to where we should be at the given time
707 		VectorMA( org, time, realVel, mOrigin1 );
708 
709 		//use the normalOffset and add that to the actual normal of the bolt
710 		//NOTE: not tested!!!
711 		VectorCopy( ax[0], mRefEnt.axis[0] );
712 		VectorCopy( ax[1], mRefEnt.axis[1] );
713 		VectorCopy( ax[2], mRefEnt.axis[2] );
714 
715 		//vec3_t	offsetAngles;
716 		//VectorSet( offsetAngles, 0, 90, 90 );
717 
718 		matrix3_t	offsetAxis;
719 		//NOTE: mNormal is actually PITCH YAW and ROLL offsets
720 		AnglesToAxis( mNormal, offsetAxis );
721 		MatrixMultiply( offsetAxis, ax, mRefEnt.axis );
722 	}
723 	else if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
724 	{
725 		// we are marked for death
726 		return false;
727 	}
728 
729 	if ( !Cull() )
730 	{	// Only update these if the thing is visible.
731 		UpdateSize();
732 		UpdateRGB();
733 		UpdateAlpha();
734 		UpdateRotation();
735 
736 		Draw();
737 	}
738 
739 	return true;
740 }
741 
742 
743 //----------------------------
744 //
745 // Derived Line Class
746 //
747 //----------------------------
CLine(void)748 CLine::CLine(void)
749 {
750 	mRefEnt.reType = RT_LINE;
751 }
752 
753 //----------------------------
Draw(void)754 void CLine::Draw(void)
755 {
756 	if ( mFlags & FX_DEPTH_HACK )
757 	{
758 		// Not sure if first person needs to be set, but it can't hurt?
759 		mRefEnt.renderfx |= RF_DEPTHHACK;
760 	}
761 
762 	VectorCopy( mOrigin1, mRefEnt.origin );
763 	VectorCopy( mOrigin2, mRefEnt.oldorigin );
764 
765 	theFxHelper.AddFxToScene(&mRefEnt);
766 	drawnFx++;
767 }
768 
769 //----------------------------
Update(void)770 bool CLine::Update(void)
771 {
772 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
773 	if ( mTimeStart > theFxHelper.mTime )
774 	{
775 		return false;
776 	}
777 
778 	if ( mFlags & FX_RELATIVE )
779 	{
780 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
781 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
782 			return false;
783 		}
784 
785 		matrix3_t	ax;
786 		// Get our current position and direction
787 		if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, mOrigin1, ax))
788 		{	//could not get bolt
789 			return false;
790 		}
791 
792 		VectorAdd(mOrigin1, mOrgOffset, mOrigin1);	//add the offset to the bolt point
793 
794 		VectorMA( mOrigin1, mVel[0], ax[0], mOrigin2 );
795 		VectorMA( mOrigin2, mVel[1], ax[1], mOrigin2 );
796 		VectorMA( mOrigin2, mVel[2], ax[2], mOrigin2 );
797 	}
798 
799 	if ( !Cull())
800 	{
801 		// Only update these if the thing is visible.
802 		UpdateSize();
803 		UpdateRGB();
804 		UpdateAlpha();
805 
806 		Draw();
807 	}
808 
809 	return true;
810 }
811 
812 //----------------------------
813 //
814 // Derived Electricity Class
815 //
816 //----------------------------
CElectricity(void)817 CElectricity::CElectricity(void)
818 {
819 	mRefEnt.reType = RT_ELECTRICITY;
820 }
821 
822 //----------------------------
Initialize(void)823 void CElectricity::Initialize(void)
824 {
825 	mRefEnt.frame = flrand(0.0f, 1.0f) * 1265536.0f;
826 	mRefEnt.axis[0][2] = theFxHelper.mTime + (mTimeEnd - mTimeStart); // endtime
827 
828 	if ( mFlags & FX_DEPTH_HACK )
829 	{
830 		mRefEnt.renderfx |= RF_DEPTHHACK;
831 	}
832 
833 	if ( mFlags & FX_BRANCH )
834 	{
835 		mRefEnt.renderfx |= RF_FORKED;
836 	}
837 
838 	if ( mFlags & FX_TAPER )
839 	{
840 		mRefEnt.renderfx |= RF_TAPERED;
841 	}
842 
843 	if ( mFlags & FX_GROW )
844 	{
845 		mRefEnt.renderfx |= RF_GROW;
846 	}
847 }
848 
849 //----------------------------
Draw(void)850 void CElectricity::Draw(void)
851 {
852 	VectorCopy( mOrigin1, mRefEnt.origin );
853 	VectorCopy( mOrigin2, mRefEnt.oldorigin );
854 	mRefEnt.axis[0][0] = mChaos;
855 	mRefEnt.axis[0][1] = mTimeEnd - mTimeStart;
856 
857 	theFxHelper.AddFxToScene( &mRefEnt );
858 	drawnFx++;
859 }
860 
861 //----------------------------
Update(void)862 bool CElectricity::Update(void)
863 {
864 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
865 	if ( mTimeStart > theFxHelper.mTime )
866 	{
867 		return false;
868 	}
869 
870 	if ( mFlags & FX_RELATIVE )
871 	{
872 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
873 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
874 			return false;
875 		}
876 
877 		matrix3_t	ax;
878 		// Get our current position and direction
879 		if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, mOrigin1, ax))
880 		{	//could not get bolt
881 			return false;
882 		}
883 
884 		VectorAdd(mOrigin1, mOrgOffset, mOrigin1);	//add the offset to the bolt point
885 
886 		VectorMA( mOrigin1, mVel[0], ax[0], mOrigin2 );
887 		VectorMA( mOrigin2, mVel[1], ax[1], mOrigin2 );
888 		VectorMA( mOrigin2, mVel[2], ax[2], mOrigin2 );
889 	}
890 
891 	if ( !Cull())
892 	{
893 		// Only update these if the thing is visible.
894 		UpdateSize();
895 		UpdateRGB();
896 		UpdateAlpha();
897 
898 		Draw();
899 	}
900 
901 	return true;
902 }
903 
904 //----------------------------
905 //
906 // Derived Tail Class
907 //
908 //----------------------------
CTail(void)909 CTail::CTail(void)
910 {
911 	mRefEnt.reType = RT_LINE;
912 }
913 
914 //----------------------------
Draw(void)915 void CTail::Draw(void)
916 {
917 	if ( mFlags & FX_DEPTH_HACK )
918 	{
919 		// Not sure if first person needs to be set
920 		mRefEnt.renderfx |= RF_DEPTHHACK;
921 	}
922 
923 	VectorCopy( mOrigin1, mRefEnt.origin );
924 
925 	theFxHelper.AddFxToScene(&mRefEnt);
926 	drawnFx++;
927 }
928 
929 //----------------------------
Update(void)930 bool CTail::Update(void)
931 {
932 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
933 	if ( mTimeStart > theFxHelper.mTime )
934 	{
935 		return false;
936 	}
937 
938 	if ( mFlags & FX_RELATIVE )
939 	{
940 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
941 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
942 			return false;
943 		}
944 		vec3_t	org;
945 		matrix3_t	ax;
946 		if (mModelNum>=0 && mBoltNum>=0)	//bolt style
947 		{
948 			if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, org, ax))
949 			{	//could not get bolt
950 				return false;
951 			}
952 		}
953 
954 		vec3_t 	realVel, realAccel;
955 
956 		VectorMA( org, mOrgOffset[0], ax[0], org );
957 		VectorMA( org, mOrgOffset[1], ax[1], org );
958 		VectorMA( org, mOrgOffset[2], ax[2], org );
959 
960 		// calc the real velocity and accel vectors
961 		// FIXME: if you want right and up movement in addition to the forward movement, you'll have to convert dir into a set of perp. axes and do some extra work
962 		VectorScale( ax[0], mVel[0], realVel );
963 		VectorMA( realVel, mVel[1], ax[1], realVel );
964 		VectorMA( realVel, mVel[2], ax[2], realVel );
965 
966 		VectorScale( ax[0], mAccel[0], realAccel );
967 		VectorMA( realAccel, mAccel[1], ax[1], realAccel );
968 		VectorMA( realAccel, mAccel[2], ax[2], realAccel );
969 
970 		const float	time = (theFxHelper.mTime - mTimeStart) * 0.001f;
971 
972 		// Get our real velocity at the current time, taking into account the effects of acceleration.  NOTE: not sure if this is even 100% correct math-wise
973 		VectorMA( realVel, time, realAccel, realVel );
974 
975 		// Now move us to where we should be at the given time
976 		VectorMA( org, time, realVel, mOrigin1 );
977 
978 		// Just calc an old point some time in the past, doesn't really matter when
979 		VectorMA( org, (time - 0.003f), realVel, mOldOrigin );
980 	}
981 #ifdef _DEBUG
982 	else if ( !fx_freeze->integer )
983 #else
984 	else
985 #endif
986 	{
987 		VectorCopy( mOrigin1, mOldOrigin );
988 	}
989 
990 	if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
991 	{
992 		// we are marked for death
993 		return false;
994 	}
995 
996 	if ( !Cull() )
997 	{
998 		// Only update these if the thing is visible.
999 		UpdateSize();
1000 		UpdateLength();
1001 		UpdateRGB();
1002 		UpdateAlpha();
1003 
1004 		CalcNewEndpoint();
1005 		Draw();
1006 	}
1007 
1008 	return true;
1009 }
1010 
1011 //----------------------------
UpdateLength(void)1012 void CTail::UpdateLength(void)
1013 {
1014 	// completely biased towards start if it doesn't get overridden
1015 	float	perc1 = 1.0f, perc2 = 1.0f;
1016 
1017 	if ( mFlags & FX_LENGTH_LINEAR )
1018 	{
1019 		// calculate element biasing
1020 		perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart) / (float)(mTimeEnd - mTimeStart);
1021 	}
1022 
1023 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
1024 	if (( mFlags & FX_LENGTH_PARM_MASK ) == FX_LENGTH_NONLINEAR )
1025 	{
1026 		if ( theFxHelper.mTime > mLengthParm )
1027 		{
1028 			// get percent done, using parm as the start of the non-linear fade
1029 			perc2 = 1.0f - (float)(theFxHelper.mTime - mLengthParm) / (float)(mTimeEnd - mLengthParm);
1030 		}
1031 
1032 		if ( mFlags & FX_LENGTH_LINEAR )
1033 		{
1034 			// do an even blend
1035 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1036 		}
1037 		else
1038 		{
1039 			// just copy it over...sigh
1040 			perc1 = perc2;
1041 		}
1042 	}
1043 	else if (( mFlags & FX_LENGTH_PARM_MASK ) == FX_LENGTH_WAVE )
1044 	{
1045 		// wave gen, with parm being the frequency multiplier
1046 		perc1 = perc1 * cosf( (theFxHelper.mTime - mTimeStart) * mLengthParm );
1047 	}
1048 	else if (( mFlags & FX_LENGTH_PARM_MASK ) == FX_LENGTH_CLAMP )
1049 	{
1050 		if ( theFxHelper.mTime < mLengthParm )
1051 		{
1052 			// get percent done, using parm as the start of the non-linear fade
1053 			perc2 = (float)(mLengthParm - theFxHelper.mTime) / (float)(mLengthParm - mTimeStart);
1054 		}
1055 		else
1056 		{
1057 			perc2 = 0.0f; // make it full size??
1058 		}
1059 
1060 		if ( mFlags & FX_LENGTH_LINEAR )
1061 		{
1062 			// do an even blend
1063 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1064 		}
1065 		else
1066 		{
1067 			// just copy it over...sigh
1068 			perc1 = perc2;
1069 		}
1070 	}
1071 
1072 	// If needed, RAND can coexist with linear and either non-linear or wave.
1073 	if ( mFlags & FX_LENGTH_RAND )
1074 	{
1075 		// Random simply modulates the existing value
1076 		perc1 = flrand(0.0f, perc1);
1077 	}
1078 
1079 	mLength = (mLengthStart * perc1) + (mLengthEnd * (1.0f - perc1));
1080 }
1081 
1082 
1083 //----------------------------
CalcNewEndpoint(void)1084 void CTail::CalcNewEndpoint(void)
1085 {
1086 	vec3_t temp;
1087 
1088 	// FIXME:  Hmmm, this looks dumb when physics are on and a bounce happens
1089 	VectorSubtract( mOldOrigin, mOrigin1, temp );
1090 
1091 	// I wish we didn't have to do a VectorNormalize every frame.....
1092 	VectorNormalize( temp );
1093 
1094 	VectorMA( mOrigin1, mLength, temp, mRefEnt.oldorigin );
1095 }
1096 
1097 
1098 //----------------------------
1099 //
1100 // Derived Cylinder Class
1101 //
1102 //----------------------------
CCylinder(void)1103 CCylinder::CCylinder(void)
1104 {
1105 	mRefEnt.reType = RT_CYLINDER;
1106 	mTraceEnd = qfalse;
1107 }
1108 
Cull(void)1109 bool CCylinder::Cull(void)
1110 {
1111 	if (mTraceEnd)
1112 	{ //eh, don't cull variable-length cylinders
1113 		return false;
1114 	}
1115 
1116 	return CTail::Cull();
1117 }
1118 
UpdateLength(void)1119 void CCylinder::UpdateLength(void)
1120 {
1121 	if (mTraceEnd)
1122 	{
1123 		vec3_t temp;
1124 		trace_t tr;
1125 
1126 		VectorMA( mOrigin1, FX_MAX_TRACE_DIST, mRefEnt.axis[0], temp );
1127 		theFxHelper.Trace( tr, mOrigin1, NULL, NULL, temp, -1, MASK_SOLID );
1128 		VectorSubtract( tr.endpos, mOrigin1, temp );
1129 		mLength = VectorLength(temp);
1130 	}
1131 	else
1132 	{
1133 		CTail::UpdateLength();
1134 	}
1135 }
1136 
1137 //----------------------------
Draw(void)1138 void CCylinder::Draw(void)
1139 {
1140 	if ( mFlags & FX_DEPTH_HACK )
1141 	{
1142 		// Not sure if first person needs to be set, but it can't hurt?
1143 		mRefEnt.renderfx |= RF_DEPTHHACK;
1144 	}
1145 
1146 	VectorCopy( mOrigin1, mRefEnt.origin );
1147 	VectorMA( mOrigin1, mLength, mRefEnt.axis[0], mRefEnt.oldorigin );
1148 
1149 	theFxHelper.AddFxToScene(&mRefEnt);
1150 	drawnFx++;
1151 }
1152 
1153 //----------------------------
1154 // Update Size2
1155 //----------------------------
UpdateSize2(void)1156 void CCylinder::UpdateSize2(void)
1157 {
1158 	// completely biased towards start if it doesn't get overridden
1159 	float	perc1 = 1.0f, perc2 = 1.0f;
1160 
1161 	if ( mFlags & FX_SIZE2_LINEAR )
1162 	{
1163 		// calculate element biasing
1164 		perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart) / (float)(mTimeEnd - mTimeStart);
1165 	}
1166 
1167 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
1168 	if (( mFlags & FX_SIZE2_PARM_MASK ) == FX_SIZE2_NONLINEAR )
1169 	{
1170 		if ( theFxHelper.mTime > mSize2Parm )
1171 		{
1172 			// get percent done, using parm as the start of the non-linear fade
1173 			perc2 = 1.0f - (float)(theFxHelper.mTime - mSize2Parm) / (float)(mTimeEnd - mSize2Parm);
1174 		}
1175 
1176 		if ( (mFlags & FX_SIZE2_LINEAR) )
1177 		{
1178 			// do an even blend
1179 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1180 		}
1181 		else
1182 		{
1183 			// just copy it over...sigh
1184 			perc1 = perc2;
1185 		}
1186 	}
1187 	else if (( mFlags & FX_SIZE2_PARM_MASK ) == FX_SIZE2_WAVE )
1188 	{
1189 		// wave gen, with parm being the frequency multiplier
1190 		perc1 = perc1 * cosf( (theFxHelper.mTime - mTimeStart) * mSize2Parm );
1191 	}
1192 	else if (( mFlags & FX_SIZE2_PARM_MASK ) == FX_SIZE2_CLAMP )
1193 	{
1194 		if ( theFxHelper.mTime < mSize2Parm )
1195 		{
1196 			// get percent done, using parm as the start of the non-linear fade
1197 			perc2 = (float)(mSize2Parm - theFxHelper.mTime) / (float)(mSize2Parm - mTimeStart);
1198 		}
1199 		else
1200 		{
1201 			perc2 = 0.0f; // make it full size??
1202 		}
1203 
1204 		if ( mFlags & FX_SIZE2_LINEAR )
1205 		{
1206 			// do an even blend
1207 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1208 		}
1209 		else
1210 		{
1211 			// just copy it over...sigh
1212 			perc1 = perc2;
1213 		}
1214 	}
1215 
1216 	// If needed, RAND can coexist with linear and either non-linear or wave.
1217 	if ( mFlags & FX_SIZE2_RAND )
1218 	{
1219 		// Random simply modulates the existing value
1220 		perc1 = flrand(0.0f, perc1);
1221 	}
1222 
1223 	mRefEnt.rotation = (mSize2Start * perc1) + (mSize2End * (1.0f - perc1));
1224 }
1225 
1226 //----------------------------
Update(void)1227 bool CCylinder::Update(void)
1228 {
1229 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
1230 	if ( mTimeStart > theFxHelper.mTime )
1231 	{
1232 		return false;
1233 	}
1234 
1235 	if ( mFlags & FX_RELATIVE )
1236 	{
1237 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
1238 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
1239 			return false;
1240 		}
1241 
1242 		matrix3_t	ax;
1243 		// Get our current position and direction
1244 		if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, mOrigin1, ax))
1245 		{	//could not get bolt
1246 			return false;
1247 		}
1248 
1249 		VectorAdd(mOrigin1, mOrgOffset, mOrigin1);	//add the offset to the bolt point
1250 
1251 		VectorCopy( ax[0], mRefEnt.axis[0] );
1252 		//FIXME: should mNormal be a modifier on the forward axis?
1253 		/*
1254 		VectorMA( mOrigin1, mNormal[0], ax[0], mOrigin2 );
1255 		VectorMA( mOrigin2, mNormal[1], ax[1], mOrigin2 );
1256 		VectorMA( mOrigin2, mNormal[2], ax[2], mOrigin2 );
1257 		*/
1258 	}
1259 
1260 	if ( !Cull() )
1261 	{
1262 		// Only update these if the thing is visible.
1263 		UpdateSize();
1264 		UpdateSize2();
1265 		UpdateLength();
1266 		UpdateRGB();
1267 		UpdateAlpha();
1268 
1269 		Draw();
1270 	}
1271 
1272 	return true;
1273 }
1274 
1275 
1276 //----------------------------
1277 //
1278 // Derived Emitter Class
1279 //
1280 //----------------------------
CEmitter(void)1281 CEmitter::CEmitter(void)
1282 {
1283 	// There may or may not be a model, but if there isn't one,
1284 	//	we just won't bother adding the refEnt in our Draw func
1285 	mRefEnt.reType = RT_MODEL;
1286 }
1287 
1288 //----------------------------
~CEmitter(void)1289 CEmitter::~CEmitter(void)
1290 {
1291 }
1292 
1293 //----------------------------
1294 // Draw
1295 //----------------------------
Draw(void)1296 void CEmitter::Draw(void)
1297 {
1298 	// Emitters don't draw themselves, but they may need to add an attached model
1299 	if ( mFlags & FX_ATTACHED_MODEL )
1300 	{
1301 		mRefEnt.nonNormalizedAxes = qtrue;
1302 
1303 		VectorCopy( mOrigin1, mRefEnt.origin );
1304 
1305 	 	VectorScale( mRefEnt.axis[0], mRefEnt.radius, mRefEnt.axis[0] );
1306 	 	VectorScale( mRefEnt.axis[1], mRefEnt.radius, mRefEnt.axis[1] );
1307 	 	VectorScale( mRefEnt.axis[2], mRefEnt.radius, mRefEnt.axis[2] );
1308 
1309 		theFxHelper.AddFxToScene((miniRefEntity_t*)0);// I hate having to do this, but this needs to get added as a regular refEntity
1310 		theFxHelper.AddFxToScene(&mRefEnt);
1311 	}
1312 
1313 	// If we are emitting effects, we had better be careful because just calling it every cgame frame could
1314 	//	either choke up the effects system on a fast machine, or look really nasty on a low end one.
1315 	if ( mFlags & FX_EMIT_FX )
1316 	{
1317 		vec3_t	org, v;
1318 		float	ftime, time2, step;
1319 		int		t, dif;
1320 
1321 #define TRAIL_RATE		12 // we "think" at about a 60hz rate
1322 
1323 		// Pick a target step distance and square it
1324 		step = mDensity + flrand(-mVariance, mVariance);
1325 		step *= step;
1326 
1327 		dif = 0;
1328 
1329 		for ( t = mOldTime; t <= theFxHelper.mTime; t += TRAIL_RATE )
1330 		{
1331 			dif += TRAIL_RATE;
1332 
1333 			// ?Not sure if it's better to update this before or after updating the origin
1334 			VectorMA( mOldVelocity, dif * 0.001f, mAccel, v );
1335 
1336 			// Calc the time differences
1337 			ftime = dif * 0.001f;
1338 			time2 = ftime * ftime * 0.5f;
1339 
1340 			// Predict the new position
1341 			org[0] = mOldOrigin[0] + (ftime * v[0]) + (time2 * v[0]);
1342 			org[1] = mOldOrigin[1] + (ftime * v[1]) + (time2 * v[1]);
1343 			org[2] = mOldOrigin[2] + (ftime * v[2]) + (time2 * v[2]);
1344 
1345 			// Is it time to draw an effect?
1346 			if ( DistanceSquared( org, mOldOrigin ) >= step )
1347 			{
1348 				// Pick a new target step distance and square it
1349 				step = mDensity + flrand(-mVariance, mVariance);
1350 				step *= step;
1351 
1352 				// We met the step criteria so, we should add in the effect
1353 				theFxScheduler.PlayEffect( mEmitterFxID, org, mRefEnt.axis );
1354 
1355 				VectorCopy( org, mOldOrigin );
1356 				VectorCopy( v, mOldVelocity );
1357 				dif = 0;
1358 				mOldTime = t;
1359 			}
1360 		}
1361 	}
1362 	drawnFx++;
1363 }
1364 
1365 //----------------------------
Update(void)1366 bool CEmitter::Update(void)
1367 {
1368 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
1369 	if ( mTimeStart > theFxHelper.mTime )
1370 	{
1371 		return false;
1372 	}
1373 
1374 	// Use this to track if we've stopped moving
1375 	VectorCopy( mOrigin1, mOldOrigin );
1376 	VectorCopy( mVel, mOldVelocity );
1377 
1378 	if ( mFlags & FX_RELATIVE )
1379 	{
1380 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
1381 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
1382 			return false;
1383 		}
1384 		assert(0);//need this?
1385 
1386 	}
1387 	if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
1388 	{
1389 		// we are marked for death
1390 		return false;
1391 	}
1392 
1393 	bool moving = false;
1394 
1395 	// If the thing is no longer moving, kill the angle delta, but don't do it too quickly or it will
1396 	//	look very artificial.  Don't do it too slowly or it will look like there is no friction.
1397 	if ( VectorCompare( mOldOrigin, mOrigin1 ))
1398 	{
1399 		VectorScale( mAngleDelta, 0.7f, mAngleDelta );
1400 	}
1401 	else
1402 	{
1403 		moving = true;
1404 	}
1405 
1406 	if ( mFlags & FX_PAPER_PHYSICS )
1407 	{
1408 		// do this in a more framerate independent manner
1409 		float sc = ( 20.0f / theFxHelper.mFrameTime);
1410 
1411 		// bah, evil clamping
1412 		if ( sc >= 1.0f )
1413 		{
1414 			sc = 1.0f;
1415 		}
1416 
1417 		if ( moving )
1418 		{
1419 			// scale the velocity down, basically inducing drag.  Acceleration ( gravity ) should keep it pulling down, which is what we want.
1420 			VectorScale( mVel, (sc * 0.8f + 0.2f ) * 0.92f, mVel );
1421 
1422 			// add some chaotic motion based on the way we are oriented
1423 			VectorMA( mVel, (1.5f - sc) * 4.0f, mRefEnt.axis[0], mVel );
1424 			VectorMA( mVel, (1.5f - sc) * 4.0f, mRefEnt.axis[1], mVel );
1425 		}
1426 
1427 		// make us settle so we can lay flat
1428 		mAngles[0] *= (0.97f * (sc * 0.4f + 0.6f ));
1429 		mAngles[2] *= (0.97f * (sc * 0.4f + 0.6f ));
1430 
1431 		// decay our angle delta so we don't rotate as quickly
1432 		VectorScale( mAngleDelta, (0.96f * (sc * 0.1f + 0.9f )), mAngleDelta );
1433 	}
1434 
1435 	UpdateAngles();
1436 	UpdateSize();
1437 
1438 	Draw();
1439 
1440 	return true;
1441 }
1442 
1443 //----------------------------
UpdateAngles(void)1444 void CEmitter::UpdateAngles(void)
1445 {
1446 	VectorMA( mAngles, theFxHelper.mFrameTime * 0.01f, mAngleDelta, mAngles ); // was 0.001f, but then you really have to jack up the delta to even notice anything
1447 	AnglesToAxis( mAngles, mRefEnt.axis );
1448 }
1449 
1450 
1451 //--------------------------
1452 //
1453 // Derived Light Class
1454 //
1455 //--------------------------
1456 
1457 //----------------------------
Draw(void)1458 void CLight::Draw(void)
1459 {
1460 	theFxHelper.AddLightToScene( mOrigin1, mRefEnt.radius, mRefEnt.origin[0], mRefEnt.origin[1], mRefEnt.origin[2] );
1461 	drawnFx++;
1462 }
1463 
1464 //----------------------------
1465 // Update
1466 //----------------------------
Update(void)1467 bool CLight::Update(void)
1468 {
1469 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
1470 	if ( mTimeStart > theFxHelper.mTime )
1471 	{
1472 		return false;
1473 	}
1474 
1475 	if ( mFlags & FX_RELATIVE )
1476 	{
1477 		if ( !re->G2API_IsGhoul2InfovValid (*mGhoul2))
1478 		{	// the thing we are bolted to is no longer valid, so we may as well just die.
1479 			return false;
1480 		}
1481 
1482 		matrix3_t	ax;
1483 		// Get our current position and direction
1484 		if (!theFxHelper.GetOriginAxisFromBolt(mGhoul2, mEntNum, mModelNum, mBoltNum, mOrigin1, ax))
1485 		{	//could not get bolt
1486 			return false;
1487 		}
1488 
1489 		VectorMA( mOrigin1, mOrgOffset[0], ax[0], mOrigin1 );
1490 		VectorMA( mOrigin1, mOrgOffset[1], ax[1], mOrigin1 );
1491 		VectorMA( mOrigin1, mOrgOffset[2], ax[2], mOrigin1 );
1492 	}
1493 
1494 	UpdateSize();
1495 	UpdateRGB();
1496 
1497 	Draw();
1498 
1499 	return true;
1500 }
1501 
1502 //----------------------------
1503 // Update Size
1504 //----------------------------
UpdateSize(void)1505 void CLight::UpdateSize(void)
1506 {
1507 	// completely biased towards start if it doesn't get overridden
1508 	float	perc1 = 1.0f, perc2 = 1.0f;
1509 
1510 	if ( mFlags & FX_SIZE_LINEAR )
1511 	{
1512 		// calculate element biasing
1513 		perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart) / (float)(mTimeEnd - mTimeStart);
1514 	}
1515 
1516 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
1517 	if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_NONLINEAR )
1518 	{
1519 		if ( theFxHelper.mTime > mSizeParm )
1520 		{
1521 			// get percent done, using parm as the start of the non-linear fade
1522 			perc2 = 1.0f - (float)(theFxHelper.mTime - mSizeParm) / (float)(mTimeEnd - mSizeParm);
1523 		}
1524 
1525 		if ( (mFlags & FX_SIZE_LINEAR) )
1526 		{
1527 			// do an even blend
1528 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1529 		}
1530 		else
1531 		{
1532 			// just copy it over...sigh
1533 			perc1 = perc2;
1534 		}
1535 	}
1536 	else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_WAVE )
1537 	{
1538 		// wave gen, with parm being the frequency multiplier
1539 		perc1 = perc1 * cosf( (theFxHelper.mTime - mTimeStart) * mSizeParm );
1540 	}
1541 	else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_CLAMP )
1542 	{
1543 		if ( theFxHelper.mTime < mSizeParm )
1544 		{
1545 			// get percent done, using parm as the start of the non-linear fade
1546 			perc2 = (float)(mSizeParm - theFxHelper.mTime) / (float)(mSizeParm - mTimeStart);
1547 		}
1548 		else
1549 		{
1550 			perc2 = 0.0f; // make it full size??
1551 		}
1552 
1553 		if ( mFlags & FX_SIZE_LINEAR )
1554 		{
1555 			// do an even blend
1556 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1557 		}
1558 		else
1559 		{
1560 			// just copy it over...sigh
1561 			perc1 = perc2;
1562 		}
1563 	}
1564 
1565 	// If needed, RAND can coexist with linear and either non-linear or wave.
1566 	if ( mFlags & FX_SIZE_RAND )
1567 	{
1568 		// Random simply modulates the existing value
1569 		perc1 = flrand(0.0f, perc1);
1570 	}
1571 
1572 	mRefEnt.radius = (mSizeStart * perc1) + (mSizeEnd * (1.0f - perc1));
1573 }
1574 
1575 //----------------------------
1576 // Update RGB
1577 //----------------------------
UpdateRGB(void)1578 void CLight::UpdateRGB(void)
1579 {
1580 	// completely biased towards start if it doesn't get overridden
1581 	float	perc1 = 1.0f, perc2 = 1.0f;
1582 	vec3_t	res;
1583 
1584 	if ( mFlags & FX_RGB_LINEAR )
1585 	{
1586 		// calculate element biasing
1587 		perc1 = 1.0f - (float)( theFxHelper.mTime - mTimeStart ) / (float)( mTimeEnd - mTimeStart );
1588 	}
1589 
1590 	// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
1591 	if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_NONLINEAR )
1592 	{
1593 		if ( theFxHelper.mTime > mRGBParm )
1594 		{
1595 			// get percent done, using parm as the start of the non-linear fade
1596 			perc2 = 1.0f - (float)( theFxHelper.mTime - mRGBParm ) / (float)( mTimeEnd - mRGBParm );
1597 		}
1598 
1599 		if ( mFlags & FX_RGB_LINEAR )
1600 		{
1601 			// do an even blend
1602 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1603 		}
1604 		else
1605 		{
1606 			// just copy it over...sigh
1607 			perc1 = perc2;
1608 		}
1609 	}
1610 	else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_WAVE )
1611 	{
1612 		// wave gen, with parm being the frequency multiplier
1613 		perc1 = perc1 * cosf(( theFxHelper.mTime - mTimeStart ) * mRGBParm );
1614 	}
1615 	else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_CLAMP )
1616 	{
1617 		if ( theFxHelper.mTime < mRGBParm )
1618 		{
1619 			// get percent done, using parm as the start of the non-linear fade
1620 			perc2 = (float)(mRGBParm - theFxHelper.mTime) / (float)(mRGBParm - mTimeStart);
1621 		}
1622 		else
1623 		{
1624 			perc2 = 0.0f; // make it full size??
1625 		}
1626 
1627 		if ( mFlags & FX_RGB_LINEAR )
1628 		{
1629 			// do an even blend
1630 			perc1 = perc1 * 0.5f + perc2 * 0.5f;
1631 		}
1632 		else
1633 		{
1634 			// just copy it over...sigh
1635 			perc1 = perc2;
1636 		}
1637 	}
1638 
1639 	// If needed, RAND can coexist with linear and either non-linear or wave.
1640 	if ( mFlags & FX_RGB_RAND )
1641 	{
1642 		// Random simply modulates the existing value
1643 		perc1 = flrand(0.0f, perc1);
1644 	}
1645 
1646 	// Now get the correct color
1647 	VectorScale( mRGBStart, perc1, res );
1648 	VectorMA(res, ( 1.0f - perc1 ), mRGBEnd, mRefEnt.origin);
1649 }
1650 
1651 //--------------------------
1652 //
1653 // Derived Trail Class
1654 //
1655 //--------------------------
1656 #define NEW_MUZZLE	0
1657 #define NEW_TIP		1
1658 #define OLD_TIP		2
1659 #define OLD_MUZZLE	3
1660 
1661 //----------------------------
Draw()1662 void CTrail::Draw()
1663 {
1664 	polyVert_t	verts[3];
1665 //	vec3_t		color;
1666 
1667 	// build the first tri out of the new muzzle...new tip...old muzzle
1668 	VectorCopy( mVerts[NEW_MUZZLE].origin, verts[0].xyz );
1669 	VectorCopy( mVerts[NEW_TIP].origin, verts[1].xyz );
1670 	VectorCopy( mVerts[OLD_MUZZLE].origin, verts[2].xyz );
1671 
1672 //	VectorScale( mVerts[NEW_MUZZLE].curRGB, mVerts[NEW_MUZZLE].curAlpha, color );
1673 	verts[0].modulate[0] = mVerts[NEW_MUZZLE].rgb[0];
1674 	verts[0].modulate[1] = mVerts[NEW_MUZZLE].rgb[1];
1675 	verts[0].modulate[2] = mVerts[NEW_MUZZLE].rgb[2];
1676 	verts[0].modulate[3] = mVerts[NEW_MUZZLE].alpha;
1677 
1678 //	VectorScale( mVerts[NEW_TIP].curRGB, mVerts[NEW_TIP].curAlpha, color );
1679 	verts[1].modulate[0] = mVerts[NEW_TIP].rgb[0];
1680 	verts[1].modulate[1] = mVerts[NEW_TIP].rgb[1];
1681 	verts[1].modulate[2] = mVerts[NEW_TIP].rgb[2];
1682 	verts[1].modulate[3] = mVerts[NEW_TIP].alpha;
1683 
1684 //	VectorScale( mVerts[OLD_MUZZLE].curRGB, mVerts[OLD_MUZZLE].curAlpha, color );
1685 	verts[2].modulate[0] = mVerts[OLD_MUZZLE].rgb[0];
1686 	verts[2].modulate[1] = mVerts[OLD_MUZZLE].rgb[1];
1687 	verts[2].modulate[2] = mVerts[OLD_MUZZLE].rgb[2];
1688 	verts[2].modulate[3] = mVerts[OLD_MUZZLE].alpha;
1689 
1690 	verts[0].st[0] = mVerts[NEW_MUZZLE].curST[0];
1691 	verts[0].st[1] = mVerts[NEW_MUZZLE].curST[1];
1692 	verts[1].st[0] = mVerts[NEW_TIP].curST[0];
1693 	verts[1].st[1] = mVerts[NEW_TIP].curST[1];
1694 	verts[2].st[0] = mVerts[OLD_MUZZLE].curST[0];
1695 	verts[2].st[1] = mVerts[OLD_MUZZLE].curST[1];
1696 
1697 	// Add this tri
1698 	theFxHelper.AddPolyToScene( mShader, 3, verts );
1699 
1700 	// build the second tri out of the old muzzle...old tip...new tip
1701 	VectorCopy( mVerts[OLD_MUZZLE].origin, verts[0].xyz );
1702 	VectorCopy( mVerts[OLD_TIP].origin, verts[1].xyz );
1703 	VectorCopy( mVerts[NEW_TIP].origin, verts[2].xyz );
1704 
1705 //	VectorScale( mVerts[OLD_MUZZLE].curRGB, mVerts[OLD_MUZZLE].curAlpha, color );
1706 	verts[0].modulate[0] = mVerts[OLD_MUZZLE].rgb[0];
1707 	verts[0].modulate[1] = mVerts[OLD_MUZZLE].rgb[1];
1708 	verts[0].modulate[2] = mVerts[OLD_MUZZLE].rgb[2];
1709 	verts[0].modulate[3] = mVerts[OLD_MUZZLE].alpha;
1710 
1711 //	VectorScale( mVerts[OLD_TIP].curRGB, mVerts[OLD_TIP].curAlpha, color );
1712 	verts[1].modulate[0] = mVerts[OLD_TIP].rgb[0];
1713 	verts[1].modulate[1] = mVerts[OLD_TIP].rgb[1];
1714 	verts[1].modulate[2] = mVerts[OLD_TIP].rgb[2];
1715 	verts[0].modulate[3] = mVerts[OLD_TIP].alpha;
1716 
1717 //	VectorScale( mVerts[NEW_TIP].curRGB, mVerts[NEW_TIP].curAlpha, color );
1718 	verts[2].modulate[0] = mVerts[NEW_TIP].rgb[0];
1719 	verts[2].modulate[1] = mVerts[NEW_TIP].rgb[1];
1720 	verts[2].modulate[2] = mVerts[NEW_TIP].rgb[2];
1721 	verts[0].modulate[3] = mVerts[NEW_TIP].alpha;
1722 
1723 	verts[0].st[0] = mVerts[OLD_MUZZLE].curST[0];
1724 	verts[0].st[1] = mVerts[OLD_MUZZLE].curST[1];
1725 	verts[1].st[0] = mVerts[OLD_TIP].curST[0];
1726 	verts[1].st[1] = mVerts[OLD_TIP].curST[1];
1727 	verts[2].st[0] = mVerts[NEW_TIP].curST[0];
1728 	verts[2].st[1] = mVerts[NEW_TIP].curST[1];
1729 
1730 	// Add this tri
1731 	theFxHelper.AddPolyToScene( mShader, 3, verts );
1732 
1733 	drawnFx++;
1734 }
1735 
1736 //----------------------------
1737 // Update
1738 //----------------------------
Update()1739 bool CTrail::Update()
1740 {
1741 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
1742 	if ( mTimeStart > theFxHelper.mTime )
1743 	{
1744 		return false;
1745 	}
1746 
1747 	float perc = (float)(mTimeEnd - theFxHelper.mTime) / (float)(mTimeEnd - mTimeStart);
1748 
1749 	for ( int t = 0; t < 4; t++ )
1750 	{
1751 //		mVerts[t].curAlpha = mVerts[t].alpha * perc + mVerts[t].destAlpha * ( 1.0f - perc );
1752 //		if ( mVerts[t].curAlpha < 0.0f )
1753 //		{
1754 //			mVerts[t].curAlpha = 0.0f;
1755 //		}
1756 
1757 //		VectorScale( mVerts[t].rgb, perc, mVerts[t].curRGB );
1758 //		VectorMA( mVerts[t].curRGB, ( 1.0f - perc ), mVerts[t].destrgb, mVerts[t].curRGB );
1759 		mVerts[t].curST[0] = mVerts[t].ST[0] * perc + mVerts[t].destST[0] * ( 1.0f - perc );
1760 		if ( mVerts[t].curST[0] > 1.0f )
1761 		{
1762 			mVerts[t].curST[0] = 1.0f;
1763 		}
1764 		mVerts[t].curST[1] = mVerts[t].ST[1] * perc + mVerts[t].destST[1] * ( 1.0f - perc );
1765 	}
1766 
1767 	Draw();
1768 
1769 	return true;
1770 }
1771 
1772 //--------------------------
1773 //
1774 // Derived Poly Class
1775 //
1776 //--------------------------
1777 
1778 //----------------------------
Cull(void)1779 bool CPoly::Cull(void)
1780 {
1781 	vec3_t	dir;
1782 
1783 	// Get the direction to the view
1784 	VectorSubtract( mOrigin1, theFxHelper.refdef->vieworg, dir );
1785 
1786 	// Check if it's behind the viewer
1787 	if ( (DotProduct( theFxHelper.refdef->viewaxis[0], dir )) < 0 )
1788 	{
1789 		return true;
1790 	}
1791 
1792 	float len = VectorLengthSquared( dir );
1793 
1794 	// Can't be too close
1795 	if ( len < fx_nearCull->value * fx_nearCull->value)
1796 	{
1797 		return true;
1798 	}
1799 
1800 	return false;
1801 }
1802 
1803 //----------------------------
Draw(void)1804 void CPoly::Draw(void)
1805 {
1806 	polyVert_t	verts[MAX_CPOLY_VERTS];
1807 
1808 	for ( int i = 0; i < mCount; i++ )
1809 	{
1810 		// Add our midpoint and vert offset to get the actual vertex
1811 		VectorAdd( mOrigin1, mOrg[i], verts[i].xyz );
1812 
1813 		// Assign the same color to each vert
1814 		for ( int k=0; k<4; k++ )
1815 			verts[i].modulate[k] = mRefEnt.shaderRGBA[k];
1816 
1817 		// Copy the ST coords
1818 		VectorCopy2( mST[i], verts[i].st );
1819 	}
1820 
1821 	// Add this poly
1822 	theFxHelper.AddPolyToScene( mRefEnt.customShader, mCount, verts );
1823 	drawnFx++;
1824 }
1825 
1826 //----------------------------
CalcRotateMatrix(void)1827 void CPoly::CalcRotateMatrix(void)
1828 {
1829 	float	cosX, cosZ;
1830 	float	sinX, sinZ;
1831 	float	rad;
1832 
1833 	// rotate around Z
1834 	rad = DEG2RAD( mRotDelta[YAW] * theFxHelper.mFrameTime * 0.01f );
1835 	cosZ = cosf( rad );
1836 	sinZ = sinf( rad );
1837 	// rotate around X
1838 	rad = DEG2RAD( mRotDelta[PITCH] * theFxHelper.mFrameTime * 0.01f );
1839 	cosX = cosf( rad );
1840 	sinX = sinf( rad );
1841 
1842 /*Pitch - aroundx  Yaw - around z
1843 1 0  0			 c -s 0
1844 0 c -s			 s  c 0
1845 0 s  c			 0  0 1
1846 */
1847 	mRot[0][0] = cosZ;
1848 	mRot[1][0] = -sinZ;
1849 	mRot[2][0] = 0;
1850 	mRot[0][1] = cosX * sinZ;
1851 	mRot[1][1] = cosX * cosZ;
1852 	mRot[2][1] = -sinX;
1853 	mRot[0][2] = sinX * sinZ;
1854 	mRot[1][2] = sinX * cosZ;
1855 	mRot[2][2] = cosX;
1856 /*
1857 // ROLL is not supported unless anyone complains, if it needs to be added, use this format
1858 Roll
1859 
1860  c 0 s
1861  0 1 0
1862 -s 0 c
1863 */
1864 	mLastFrameTime = theFxHelper.mFrameTime;
1865 }
1866 
1867 //--------------------------------
Rotate(void)1868 void CPoly::Rotate(void)
1869 {
1870 	vec3_t	temp[MAX_CPOLY_VERTS];
1871 	float	dif = fabs( (float)(mLastFrameTime - theFxHelper.mFrameTime) );
1872 
1873 	if ( dif > 0.1f * mLastFrameTime )
1874 	{
1875 		CalcRotateMatrix();
1876 	}
1877 
1878 	// Multiply our rotation matrix by each of the offset verts to get their new position
1879 	for ( int i = 0; i < mCount; i++ )
1880 	{
1881 		VectorRotate( mOrg[i], mRot, temp[i] );
1882 		VectorCopy( temp[i], mOrg[i] );
1883 	}
1884 }
1885 
1886 //----------------------------
1887 // Update
1888 //----------------------------
Update(void)1889 bool CPoly::Update(void)
1890 {
1891 	// Game pausing can cause dumb time things to happen, so kill the effect in this instance
1892 	if ( mTimeStart > theFxHelper.mTime )
1893 	{
1894 		return false;
1895 	}
1896 
1897 	// If our timestamp hasn't exired yet, we won't even consider doing any kind of motion
1898 	if ( theFxHelper.mTime > mTimeStamp )
1899 	{
1900 		vec3_t mOldOrigin;
1901 
1902 		VectorCopy( mOrigin1, mOldOrigin );
1903 
1904 		if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
1905 		{
1906 			// we are marked for death
1907 			return false;
1908 		}
1909 
1910 		// Only rotate whilst moving
1911 		if ( !VectorCompare( mOldOrigin, mOrigin1 ))
1912 		{
1913 			Rotate();
1914 		}
1915 	}
1916 
1917 	if ( !Cull())
1918 	{
1919 		// Only update these if the thing is visible.
1920 		UpdateRGB();
1921 		UpdateAlpha();
1922 
1923 		Draw();
1924 	}
1925 
1926 	return true;
1927 }
1928 
1929 //----------------------------
PolyInit(void)1930 void CPoly::PolyInit(void)
1931 {
1932 	if ( mCount < 3 )
1933 	{
1934 		return;
1935 	}
1936 
1937 	int		i;
1938 	vec3_t	org = {0.0f, 0.0f ,0.0f};
1939 
1940 	// Find our midpoint
1941 	for ( i = 0; i < mCount; i++ )
1942 	{
1943 		VectorAdd( org, mOrg[i], org );
1944 	}
1945 
1946 	VectorScale( org, (float)(1.0f / mCount), org );
1947 
1948 	// now store our midpoint for physics purposes
1949 	VectorCopy( org, mOrigin1 );
1950 
1951 	// Now we process the passed in points and make it so that they aren't actually the point...
1952 	//	rather, they are the offset from mOrigin1.
1953 	for ( i = 0; i < mCount; i++ )
1954 	{
1955 		VectorSubtract( mOrg[i], mOrigin1, mOrg[i] );
1956 	}
1957 
1958 	CalcRotateMatrix();
1959 }
1960 
1961 /*
1962 -------------------------
1963 CBezier
1964 
1965 Bezier curve line
1966 -------------------------
1967 */
Cull(void)1968 bool CBezier::Cull( void )
1969 {
1970 	vec3_t	dir;
1971 
1972 	VectorSubtract( mOrigin1, theFxHelper.refdef->vieworg, dir );
1973 
1974 	//Check if it's in front of the viewer
1975 	if ( (DotProduct( theFxHelper.refdef->viewaxis[0], dir )) >= 0 )
1976 	{
1977 		return false;	//don't cull
1978 	}
1979 
1980 	VectorSubtract( mOrigin2, theFxHelper.refdef->vieworg, dir );
1981 
1982 	//Check if it's in front of the viewer
1983 	if ( (DotProduct( theFxHelper.refdef->viewaxis[0], dir )) >= 0 )
1984 	{
1985 		return false;
1986 	}
1987 
1988 	VectorSubtract( mControl1, theFxHelper.refdef->vieworg, dir );
1989 
1990 	//Check if it's in front of the viewer
1991 	if ( (DotProduct( theFxHelper.refdef->viewaxis[0], dir )) >= 0 )
1992 	{
1993 		return false;
1994 	}
1995 
1996 	return true; //all points behind viewer
1997 }
1998 
1999 //----------------------------
Update(void)2000 bool CBezier::Update( void )
2001 {
2002 	float	ftime, time2;
2003 
2004 	ftime = theFxHelper.mFrameTime * 0.001f;
2005 	time2 = ftime * ftime * 0.5f;
2006 
2007 	mControl1[0] = mControl1[0] + (ftime * mControl1Vel[0]) + (time2 * mControl1Vel[0]);
2008 	mControl2[0] = mControl2[0] + (ftime * mControl2Vel[0]) + (time2 * mControl2Vel[0]);
2009 	mControl1[1] = mControl1[1] + (ftime * mControl1Vel[1]) + (time2 * mControl1Vel[1]);
2010 	mControl2[1] = mControl2[1] + (ftime * mControl2Vel[1]) + (time2 * mControl2Vel[1]);
2011 	mControl1[2] = mControl1[2] + (ftime * mControl1Vel[2]) + (time2 * mControl1Vel[2]);
2012 	mControl2[2] = mControl2[2] + (ftime * mControl2Vel[2]) + (time2 * mControl2Vel[2]);
2013 
2014 	if ( Cull() == false )
2015 	{
2016 		// Only update these if the thing is visible.
2017 		UpdateSize();
2018 		UpdateRGB();
2019 		UpdateAlpha();
2020 
2021 		Draw();
2022 	}
2023 	return true;
2024 }
2025 
2026 //----------------------------
DrawSegment(vec3_t start,vec3_t end,float texcoord1,float texcoord2,float segPercent,float lastSegPercent)2027 inline void CBezier::DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2, float segPercent, float lastSegPercent )
2028 {
2029 	vec3_t			lineDir, cross, viewDir;
2030 	static vec3_t	lastEnd[2];
2031 	polyVert_t		verts[4];
2032 	float			scaleBottom = 0.0f, scaleTop = 0.0f;
2033 
2034 	VectorSubtract( end, start, lineDir );
2035 	VectorSubtract( end, theFxHelper.refdef->vieworg, viewDir );
2036 	CrossProduct( lineDir, viewDir, cross );
2037 	VectorNormalize( cross );
2038 
2039 	// scaleBottom is the width of the bottom edge of the quad, scaleTop is the width of the top edge
2040 	scaleBottom = (mSizeStart + ((mSizeEnd - mSizeStart) * lastSegPercent)) * 0.5f;
2041 	scaleTop = (mSizeStart + ((mSizeEnd - mSizeStart) * segPercent)) * 0.5f;
2042 
2043 	//Construct the oriented quad
2044 	if ( mInit )
2045 	{
2046 		VectorCopy( lastEnd[0], verts[0].xyz );
2047 		VectorCopy( lastEnd[1], verts[1].xyz );
2048 	}
2049 	else
2050 	{
2051 		VectorMA( start, -scaleBottom, cross, verts[0].xyz );
2052 		VectorMA( start, scaleBottom, cross, verts[1].xyz );
2053 	}
2054 
2055 	verts[0].st[0] = 0.0f;
2056 	verts[0].st[1] = texcoord1;
2057 
2058 	verts[0].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord1 );
2059 	verts[0].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord1 );
2060 	verts[0].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord1 );
2061 	verts[0].modulate[3] = mRefEnt.shaderRGBA[3];
2062 
2063 	verts[1].st[0] = 1.0f;
2064 	verts[1].st[1] = texcoord1;
2065 
2066 	verts[1].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord1 );
2067 	verts[1].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord1 );
2068 	verts[1].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord1 );
2069 	verts[1].modulate[3] = mRefEnt.shaderRGBA[3];
2070 
2071 	if ( texcoord1 == 0.0f )
2072 	{
2073 		for ( int k=0; k<4; k++ ) {
2074 			verts[0].modulate[k] = verts[1].modulate[k] = 0;
2075 		}
2076 	}
2077 
2078 	VectorMA( end, scaleTop, cross, verts[2].xyz );
2079 	verts[2].st[0] = 1.0f;
2080 	verts[2].st[1] = texcoord2;
2081 
2082 	verts[2].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord2 );
2083 	verts[2].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord2 );
2084 	verts[2].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord2 );
2085 	verts[2].modulate[3] = mRefEnt.shaderRGBA[3];
2086 
2087 	VectorMA( end, -scaleTop, cross, verts[3].xyz );
2088 	verts[3].st[0] = 0.0f;
2089 	verts[3].st[1] = texcoord2;
2090 
2091 	verts[3].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord2 );
2092 	verts[3].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord2 );
2093 	verts[3].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord2 );
2094 	verts[3].modulate[3] = mRefEnt.shaderRGBA[3];
2095 
2096 	theFxHelper.AddPolyToScene( mRefEnt.customShader, 4, verts );
2097 
2098 	VectorCopy( verts[2].xyz, lastEnd[1] );
2099 	VectorCopy( verts[3].xyz, lastEnd[0] );
2100 
2101 	mInit = true;
2102 }
2103 
2104 const	float	BEZIER_RESOLUTION	= 16.0f;
2105 
2106 //----------------------------
Draw(void)2107 void CBezier::Draw( void )
2108 {
2109 	vec3_t	pos, old_pos;
2110     float	mu, mum1;
2111 	float	incr = 1.0f / BEZIER_RESOLUTION, tex = 1.0f, tc1, tc2;
2112 	int		i = 0;
2113 
2114 	VectorCopy( mOrigin1, old_pos );
2115 
2116 	mInit = false;	//Signify a new batch for vert gluing
2117 
2118 	float mum13, mu3, group1, group2;
2119 
2120 	tc1 = 0.0f;
2121 
2122 	for ( mu = incr; mu <= 1.0f; mu += incr)
2123 	{
2124 		//Four point curve
2125 		mum1	= 1 - mu;
2126 		mum13	= mum1 * mum1 * mum1;
2127 		mu3		= mu * mu * mu;
2128 		group1	= 3 * mu * mum1 * mum1;
2129 		group2	= 3 * mu * mu *mum1;
2130 
2131 		for ( i = 0; i < 3; i++ )
2132 		{
2133 			pos[i] = mum13 * mOrigin1[i] + group1 * mControl1[i] + group2 * mControl2[i] + mu3 * mOrigin2[i];
2134 		}
2135 
2136 		tc2 = mu * tex;
2137 
2138 		//Draw it
2139 		DrawSegment( old_pos, pos, tc1, tc2, mu, mu - incr );
2140 
2141 		VectorCopy( pos, old_pos );
2142 		tc1 = tc2;
2143 	}
2144 	drawnFx++;
2145 }
2146 
2147 /*
2148 -------------------------
2149 CFlash
2150 
2151 Full screen flash
2152 -------------------------
2153 */
2154 
2155 //----------------------------
Update(void)2156 bool CFlash::Update( void )
2157 {
2158 	if ( UpdateOrigin() == false )
2159 	{
2160 		// we are marked for death
2161 		return false;
2162 	}
2163 
2164 	UpdateSize();
2165 	mRefEnt.radius *= mRadiusModifier;
2166 	UpdateRGB();
2167 	UpdateAlpha();
2168 
2169 	Draw();
2170 	return true;
2171 }
2172 
FX_WorldToScreen(vec3_t worldCoord,float * x,float * y)2173 bool FX_WorldToScreen(vec3_t worldCoord, float *x, float *y)
2174 {
2175 	int	xcenter, ycenter;
2176 	vec3_t	local, transformed;
2177 	vec3_t	vfwd, vright, vup;
2178 
2179 	//NOTE: did it this way because most draw functions expect virtual 640x480 coords
2180 	//	and adjust them for current resolution
2181 	xcenter = 640 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
2182 	ycenter = 480 / 2;//gives screen coords in virtual 640x480, to be adjusted when drawn
2183 
2184 	VectorSubtract (worldCoord, theFxHelper.refdef->vieworg, local);
2185 
2186 	AngleVectors (theFxHelper.refdef->viewangles, vfwd, vright, vup);
2187 
2188 	transformed[0] = DotProduct(local,vright);
2189 	transformed[1] = DotProduct(local,vup);
2190 	transformed[2] = DotProduct(local,vfwd);
2191 
2192 	// Make sure Z is not negative.
2193 	if(transformed[2] < 0.01)
2194 	{
2195 		return false;
2196 	}
2197 	// Simple convert to screen coords.
2198 	float xzi = xcenter / transformed[2] * (90.0/theFxHelper.refdef->fov_x);
2199 	float yzi = ycenter / transformed[2] * (90.0/theFxHelper.refdef->fov_y);
2200 
2201 	*x = (xcenter + xzi * transformed[0]);
2202 	*y = (ycenter - yzi * transformed[1]);
2203 
2204 	return true;
2205 }
2206 
2207 //----------------------------
Init(void)2208 void CFlash::Init( void )
2209 {
2210 	// 10/19/01 kef -- maybe we want to do something different here for localized flashes, but right
2211 	//now I want to be sure that whatever RGB changes occur to a non-localized flash will also occur
2212 	//to a localized flash (so I'll have the same initial RGBA values for both...I need them sync'd for an effect)
2213 
2214 	vec3_t	dif;
2215 	float	mod = 1.0f, dis = 0.0f, maxRange = 900;
2216 
2217 	VectorSubtract( mOrigin1, theFxHelper.refdef->vieworg, dif );
2218 	dis = VectorNormalize( dif );
2219 
2220 	mod = DotProduct( dif, theFxHelper.refdef->viewaxis[0] );
2221 
2222 	if ( dis > maxRange || ( mod < 0.5f && dis > 100 ))
2223 	{
2224 		mod = 0.0f;
2225 	}
2226 	else if ( mod < 0.5f && dis <= 100 )
2227 	{
2228 		mod += 1.1f;
2229 	}
2230 
2231 	mod *= (1.0f - ((dis * dis) / (maxRange * maxRange)));
2232 
2233 	VectorScale( mRGBStart, mod, mRGBStart );
2234 	VectorScale( mRGBEnd, mod, mRGBEnd );
2235 
2236 	if ( mFlags & FX_LOCALIZED_FLASH )
2237 	{
2238 		FX_WorldToScreen(mOrigin1, &mScreenX, &mScreenY);
2239 
2240 		// modify size of localized flash based on distance to effect (but not orientation)
2241 		if (dis > 100 && dis < maxRange)
2242 		{
2243 			mRadiusModifier = (1.0f - ((dis * dis) / (maxRange * maxRange)));
2244 		}
2245 	}
2246 }
2247 
2248 //----------------------------
Draw(void)2249 void CFlash::Draw( void )
2250 {
2251     // Interestingly, if znear is set > than this, then the flash
2252     // doesn't appear at all.
2253     const float FLASH_DISTANCE_FROM_VIEWER = 12.0f;
2254 	mRefEnt.reType = RT_SPRITE;
2255 
2256 	if ( mFlags & FX_LOCALIZED_FLASH )
2257 	{
2258 		vec4_t	color;
2259 
2260 		color[0] = mRefEnt.shaderRGBA[0] / 255.0;
2261 		color[1] = mRefEnt.shaderRGBA[1] / 255.0;
2262 		color[2] = mRefEnt.shaderRGBA[2] / 255.0;
2263 		color[3] = mRefEnt.shaderRGBA[3] / 255.0;
2264 
2265 		// add this 2D effect to the proper list. it will get drawn after the trap->RenderScene call
2266 		theFxScheduler.Add2DEffect(mScreenX, mScreenY, mRefEnt.radius, mRefEnt.radius, color, mRefEnt.customShader);
2267 	}
2268 	else
2269 	{
2270 		VectorCopy( theFxHelper.refdef->vieworg, mRefEnt.origin );
2271 		VectorMA( mRefEnt.origin, FLASH_DISTANCE_FROM_VIEWER, theFxHelper.refdef->viewaxis[0], mRefEnt.origin );
2272 
2273         // This is assuming that the screen is wider than it is tall.
2274         mRefEnt.radius = FLASH_DISTANCE_FROM_VIEWER * tan (DEG2RAD (theFxHelper.refdef->fov_x * 0.5f));
2275 
2276 		theFxHelper.AddFxToScene( &mRefEnt );
2277 	}
2278 	drawnFx++;
2279 }
2280 
2281 void FX_AddPrimitive( CEffect **pEffect, int killTime );
FX_FeedTrail(effectTrailArgStruct_t * a)2282 void FX_FeedTrail(effectTrailArgStruct_t *a)
2283 {
2284 	CTrail *fx = new CTrail;
2285 	int i = 0;
2286 
2287 	while (i < 4)
2288 	{
2289 		VectorCopy(a->mVerts[i].origin, fx->mVerts[i].origin);
2290 		VectorCopy(a->mVerts[i].rgb, fx->mVerts[i].rgb);
2291 		VectorCopy(a->mVerts[i].destrgb, fx->mVerts[i].destrgb);
2292 		VectorCopy(a->mVerts[i].curRGB, fx->mVerts[i].curRGB);
2293 		fx->mVerts[i].alpha = a->mVerts[i].alpha;
2294 		fx->mVerts[i].destAlpha = a->mVerts[i].destAlpha;
2295 		fx->mVerts[i].curAlpha = a->mVerts[i].curAlpha;
2296 		fx->mVerts[i].ST[0] = a->mVerts[i].ST[0];
2297 		fx->mVerts[i].ST[1] = a->mVerts[i].ST[1];
2298 		fx->mVerts[i].destST[0] = a->mVerts[i].destST[0];
2299 		fx->mVerts[i].destST[1] = a->mVerts[i].destST[1];
2300 		fx->mVerts[i].curST[0] = a->mVerts[i].curST[0];
2301 		fx->mVerts[i].curST[1] = a->mVerts[i].curST[1];
2302 		i++;
2303 	}
2304 
2305 	fx->SetFlags(a->mSetFlags);
2306 
2307 	fx->mShader = a->mShader;
2308 
2309 	FX_AddPrimitive((CEffect **)&fx, a->mKillTime);
2310 }
2311 // end
2312