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