DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

PlayerView.cpp (43547B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 #include "Game_local.h"
     33 
     34 // _D3XP : rename all gameLocal.time to gameLocal.slow.time for merge!
     35 
     36 const int IMPULSE_DELAY = 150;
     37 /*
     38 ==============
     39 idPlayerView::idPlayerView
     40 ==============
     41 */
     42 idPlayerView::idPlayerView() {
     43 	memset( screenBlobs, 0, sizeof( screenBlobs ) );
     44 	memset( &view, 0, sizeof( view ) );
     45 	player = NULL;
     46 	tunnelMaterial = declManager->FindMaterial( "textures/decals/tunnel" );
     47 	armorMaterial = declManager->FindMaterial( "armorViewEffect" );
     48 	berserkMaterial = declManager->FindMaterial( "textures/decals/berserk" );
     49 	irGogglesMaterial = declManager->FindMaterial( "textures/decals/irblend" );
     50 	bloodSprayMaterial = declManager->FindMaterial( "textures/decals/bloodspray" );
     51 	bfgMaterial = declManager->FindMaterial( "textures/decals/bfgvision" );
     52 	bfgVision = false;
     53 	dvFinishTime = 0;
     54 	kickFinishTime = 0;
     55 	kickAngles.Zero();
     56 	lastDamageTime = 0.0f;
     57 	fadeTime = 0;
     58 	fadeRate = 0.0;
     59 	fadeFromColor.Zero();
     60 	fadeToColor.Zero();
     61 	fadeColor.Zero();
     62 	shakeAng.Zero();
     63 	fxManager = NULL;
     64 
     65 	if ( fxManager == NULL ) {
     66 		fxManager = new (TAG_ENTITY) FullscreenFXManager;
     67 		fxManager->Initialize( this );
     68 	}
     69 
     70 	ClearEffects();
     71 }
     72 
     73 /*
     74 ==============
     75 idPlayerView::~idPlayerView
     76 ==============
     77 */
     78 idPlayerView::~idPlayerView() {
     79 	delete fxManager;
     80 }
     81 /*
     82 ==============
     83 idPlayerView::Save
     84 ==============
     85 */
     86 void idPlayerView::Save( idSaveGame *savefile ) const {
     87 	int i;
     88 	const screenBlob_t *blob;
     89 
     90 	blob = &screenBlobs[ 0 ];
     91 	for( i = 0; i < MAX_SCREEN_BLOBS; i++, blob++ ) {
     92 		savefile->WriteMaterial( blob->material );
     93 		savefile->WriteFloat( blob->x );
     94 		savefile->WriteFloat( blob->y );
     95 		savefile->WriteFloat( blob->w );
     96 		savefile->WriteFloat( blob->h );
     97 		savefile->WriteFloat( blob->s1 );
     98 		savefile->WriteFloat( blob->t1 );
     99 		savefile->WriteFloat( blob->s2 );
    100 		savefile->WriteFloat( blob->t2 );
    101 		savefile->WriteInt( blob->finishTime );
    102 		savefile->WriteInt( blob->startFadeTime );
    103 		savefile->WriteFloat( blob->driftAmount );
    104 	}
    105 
    106 	savefile->WriteInt( dvFinishTime );
    107 	savefile->WriteInt( kickFinishTime );
    108 	savefile->WriteAngles( kickAngles );
    109 	savefile->WriteBool( bfgVision );
    110 
    111 	savefile->WriteMaterial( tunnelMaterial );
    112 	savefile->WriteMaterial( armorMaterial );
    113 	savefile->WriteMaterial( berserkMaterial );
    114 	savefile->WriteMaterial( irGogglesMaterial );
    115 	savefile->WriteMaterial( bloodSprayMaterial );
    116 	savefile->WriteMaterial( bfgMaterial );
    117 	savefile->WriteFloat( lastDamageTime );
    118 
    119 	savefile->WriteVec4( fadeColor );
    120 	savefile->WriteVec4( fadeToColor );
    121 	savefile->WriteVec4( fadeFromColor );
    122 	savefile->WriteFloat( fadeRate );
    123 	savefile->WriteInt( fadeTime );
    124 
    125 	savefile->WriteAngles( shakeAng );
    126 
    127 	savefile->WriteObject( player );
    128 	savefile->WriteRenderView( view );
    129 
    130 	if ( fxManager ) {
    131 		fxManager->Save( savefile );
    132 	}
    133 }
    134 
    135 /*
    136 ==============
    137 idPlayerView::Restore
    138 ==============
    139 */
    140 void idPlayerView::Restore( idRestoreGame *savefile ) {
    141 	int i;
    142 	screenBlob_t *blob;
    143 
    144 	blob = &screenBlobs[ 0 ];
    145 	for( i = 0; i < MAX_SCREEN_BLOBS; i++, blob++ ) {
    146 		savefile->ReadMaterial( blob->material );
    147 		savefile->ReadFloat( blob->x );
    148 		savefile->ReadFloat( blob->y );
    149 		savefile->ReadFloat( blob->w );
    150 		savefile->ReadFloat( blob->h );
    151 		savefile->ReadFloat( blob->s1 );
    152 		savefile->ReadFloat( blob->t1 );
    153 		savefile->ReadFloat( blob->s2 );
    154 		savefile->ReadFloat( blob->t2 );
    155 		savefile->ReadInt( blob->finishTime );
    156 		savefile->ReadInt( blob->startFadeTime );
    157 		savefile->ReadFloat( blob->driftAmount );
    158 	}
    159 
    160 	savefile->ReadInt( dvFinishTime );
    161 	savefile->ReadInt( kickFinishTime );
    162 	savefile->ReadAngles( kickAngles );			
    163 	savefile->ReadBool( bfgVision );
    164 
    165 	savefile->ReadMaterial( tunnelMaterial );
    166 	savefile->ReadMaterial( armorMaterial );
    167 	savefile->ReadMaterial( berserkMaterial );
    168 	savefile->ReadMaterial( irGogglesMaterial );
    169 	savefile->ReadMaterial( bloodSprayMaterial );
    170 	savefile->ReadMaterial( bfgMaterial );
    171 	savefile->ReadFloat( lastDamageTime );
    172 
    173 	savefile->ReadVec4( fadeColor );
    174 	savefile->ReadVec4( fadeToColor );
    175 	savefile->ReadVec4( fadeFromColor );
    176 	savefile->ReadFloat( fadeRate );
    177 	savefile->ReadInt( fadeTime );
    178 
    179 	savefile->ReadAngles( shakeAng );
    180 
    181 	savefile->ReadObject( reinterpret_cast<idClass *&>( player ) );
    182 	savefile->ReadRenderView( view );
    183 
    184 	if ( fxManager ) {
    185 		fxManager->Restore( savefile );
    186 	}
    187 }
    188 
    189 /*
    190 ==============
    191 idPlayerView::SetPlayerEntity
    192 ==============
    193 */
    194 void idPlayerView::SetPlayerEntity( idPlayer *playerEnt ) {
    195 	player = playerEnt;
    196 }
    197 
    198 /*
    199 ==============
    200 idPlayerView::ClearEffects
    201 ==============
    202 */
    203 void idPlayerView::ClearEffects() {
    204 	lastDamageTime = MS2SEC( gameLocal.slow.time - 99999 );
    205 
    206 	dvFinishTime = ( gameLocal.fast.time - 99999 );
    207 	kickFinishTime = ( gameLocal.slow.time - 99999 );
    208 
    209 	for ( int i = 0 ; i < MAX_SCREEN_BLOBS ; i++ ) {
    210 		screenBlobs[i].finishTime = gameLocal.fast.time;
    211 	}
    212 
    213 	fadeTime = 0;
    214 	bfgVision = false;
    215 }
    216 
    217 /*
    218 ==============
    219 idPlayerView::GetScreenBlob
    220 ==============
    221 */
    222 screenBlob_t *idPlayerView::GetScreenBlob() {
    223 	screenBlob_t * oldest = &screenBlobs[0];
    224 
    225 	for ( int i = 1 ; i < MAX_SCREEN_BLOBS ; i++ ) {
    226 		if ( screenBlobs[i].finishTime < oldest->finishTime ) {
    227 			oldest = &screenBlobs[i];
    228 		}
    229 	}
    230 	return oldest;
    231 }
    232 
    233 /*
    234 ==============
    235 idPlayerView::DamageImpulse
    236 
    237 LocalKickDir is the direction of force in the player's coordinate system,
    238 which will determine the head kick direction
    239 ==============
    240 */
    241 void idPlayerView::DamageImpulse( idVec3 localKickDir, const idDict *damageDef ) {
    242 	//
    243 	// double vision effect
    244 	//
    245 	if ( lastDamageTime > 0.0f && SEC2MS( lastDamageTime ) + IMPULSE_DELAY > gameLocal.slow.time ) {
    246 		// keep shotgun from obliterating the view
    247 		return;
    248 	}
    249 
    250 	float dvTime = damageDef->GetFloat( "dv_time" );
    251 	if ( dvTime ) {
    252 		if ( dvFinishTime < gameLocal.fast.time ) {
    253 			dvFinishTime = gameLocal.fast.time;
    254 		}
    255 		dvFinishTime += g_dvTime.GetFloat() * dvTime;
    256 		// don't let it add up too much in god mode
    257 		if ( dvFinishTime > gameLocal.fast.time + 5000 ) {
    258 			dvFinishTime = gameLocal.fast.time + 5000;
    259 		}
    260 	}
    261 
    262 	//
    263 	// head angle kick
    264 	//
    265 	float kickTime = damageDef->GetFloat( "kick_time" );
    266 	if ( kickTime ) {
    267 		kickFinishTime = gameLocal.slow.time + g_kickTime.GetFloat() * kickTime;
    268 
    269 		// forward / back kick will pitch view
    270 		kickAngles[0] = localKickDir[0];
    271 
    272 		// side kick will yaw view
    273 		kickAngles[1] = localKickDir[1]*0.5f;
    274 
    275 		// up / down kick will pitch view
    276 		kickAngles[0] += localKickDir[2];
    277 
    278 		// roll will come from  side
    279 		kickAngles[2] = localKickDir[1];
    280 
    281 		float kickAmplitude = damageDef->GetFloat( "kick_amplitude" );
    282 		if ( kickAmplitude ) {
    283 			kickAngles *= kickAmplitude;
    284 		}
    285 	}
    286 
    287 	//
    288 	// screen blob
    289 	//
    290 	float blobTime = damageDef->GetFloat( "blob_time" );
    291 	if ( blobTime ) {
    292 		screenBlob_t	*blob = GetScreenBlob();
    293 		blob->startFadeTime = gameLocal.fast.time;
    294 		blob->finishTime = gameLocal.fast.time + blobTime * g_blobTime.GetFloat();
    295 
    296 		const char *materialName = damageDef->GetString( "mtr_blob" );
    297 		blob->material = declManager->FindMaterial( materialName );
    298 		blob->x = damageDef->GetFloat( "blob_x" );
    299 		blob->x += ( gameLocal.random.RandomInt()&63 ) - 32;
    300 		blob->y = damageDef->GetFloat( "blob_y" );
    301 		blob->y += ( gameLocal.random.RandomInt()&63 ) - 32;
    302 		
    303 		float scale = ( 256 + ( ( gameLocal.random.RandomInt()&63 ) - 32 ) ) / 256.0f;
    304 		blob->w = damageDef->GetFloat( "blob_width" ) * g_blobSize.GetFloat() * scale;
    305 		blob->h = damageDef->GetFloat( "blob_height" ) * g_blobSize.GetFloat() * scale;
    306 		blob->s1 = 0.0f;
    307 		blob->t1 = 0.0f;
    308 		blob->s2 = 1.0f;
    309 		blob->t2 = 1.0f;
    310 	}
    311 
    312 	//
    313 	// save lastDamageTime for tunnel vision accentuation
    314 	//
    315 	lastDamageTime = MS2SEC( gameLocal.fast.time );
    316 
    317 }
    318 
    319 /*
    320 ==================
    321 idPlayerView::WeaponFireFeedback
    322 
    323 Called when a weapon fires, generates head twitches, etc
    324 ==================
    325 */
    326 void idPlayerView::WeaponFireFeedback( const idDict *weaponDef ) {
    327 	int recoilTime = weaponDef->GetInt( "recoilTime" );
    328 	// don't shorten a damage kick in progress
    329 	if ( recoilTime && kickFinishTime < gameLocal.slow.time ) {
    330 		idAngles angles;
    331 		weaponDef->GetAngles( "recoilAngles", "5 0 0", angles );
    332 		kickAngles = angles;
    333 		int	finish = gameLocal.slow.time + g_kickTime.GetFloat() * recoilTime;
    334 		kickFinishTime = finish;
    335 	}	
    336 
    337 }
    338 
    339 /*
    340 ===================
    341 idPlayerView::CalculateShake
    342 ===================
    343 */
    344 void idPlayerView::CalculateShake() {
    345 	float shakeVolume = gameSoundWorld->CurrentShakeAmplitude();
    346 	//
    347 	// shakeVolume should somehow be molded into an angle here
    348 	// it should be thought of as being in the range 0.0 -> 1.0, although
    349 	// since CurrentShakeAmplitudeForPosition() returns all the shake sounds
    350 	// the player can hear, it can go over 1.0 too.
    351 	//
    352 	shakeAng[0] = gameLocal.random.CRandomFloat() * shakeVolume;
    353 	shakeAng[1] = gameLocal.random.CRandomFloat() * shakeVolume;
    354 	shakeAng[2] = gameLocal.random.CRandomFloat() * shakeVolume;
    355 }
    356 
    357 /*
    358 ===================
    359 idPlayerView::ShakeAxis
    360 ===================
    361 */
    362 idMat3 idPlayerView::ShakeAxis() const {
    363 	return shakeAng.ToMat3();
    364 }
    365 
    366 /*
    367 ===================
    368 idPlayerView::AngleOffset
    369 
    370   kickVector, a world space direction that the attack should 
    371 ===================
    372 */
    373 idAngles idPlayerView::AngleOffset() const {
    374 	idAngles ang( 0.0f, 0.0f, 0.0f );
    375 
    376 	if ( gameLocal.slow.time < kickFinishTime ) {
    377 		float offset = kickFinishTime - gameLocal.slow.time;
    378 
    379 		ang = kickAngles * offset * offset * g_kickAmplitude.GetFloat();
    380 
    381 		for ( int i = 0 ; i < 3 ; i++ ) {
    382 			if ( ang[i] > 70.0f ) {
    383 				ang[i] = 70.0f;
    384 			} else if ( ang[i] < -70.0f ) {
    385 				ang[i] = -70.0f;
    386 			}
    387 		}
    388 	}
    389 	return ang;
    390 }
    391 
    392 /*
    393 ==================
    394 idPlayerView::SingleView
    395 ==================
    396 */
    397 void idPlayerView::SingleView( const renderView_t *view, idMenuHandler_HUD * hudManager ) {
    398 
    399 	// normal rendering
    400 	if ( !view ) {
    401 		return;
    402 	}
    403 
    404 	// place the sound origin for the player
    405 	gameSoundWorld->PlaceListener( view->vieworg, view->viewaxis, player->entityNumber + 1 );
    406 
    407 	// if the objective system is up, don't do normal drawing
    408 	if ( player->objectiveSystemOpen ) {
    409 		if ( player->pdaMenu != NULL ) {
    410 			player->pdaMenu->Update();
    411 		}
    412 		return;
    413 	}
    414 
    415 	// hack the shake in at the very last moment, so it can't cause any consistency problems
    416 	renderView_t hackedView = *view;
    417 	hackedView.viewaxis = hackedView.viewaxis * ShakeAxis();
    418 
    419 	if ( gameLocal.portalSkyEnt.GetEntity() && gameLocal.IsPortalSkyAcive() && g_enablePortalSky.GetBool() ) {
    420 		renderView_t portalView = hackedView;
    421 		portalView.vieworg = gameLocal.portalSkyEnt.GetEntity()->GetPhysics()->GetOrigin();
    422 		gameRenderWorld->RenderScene( &portalView );
    423 		renderSystem->CaptureRenderToImage( "_currentRender" );
    424 
    425 		hackedView.forceUpdate = true;				// FIX: for smoke particles not drawing when portalSky present
    426 	}
    427 
    428 	// process the frame
    429 	fxManager->Process( &hackedView );
    430 
    431 	if ( !hudManager ) {
    432 		return;
    433 	}
    434 
    435 	// draw screen blobs
    436 	if ( !pm_thirdPerson.GetBool() && !g_skipViewEffects.GetBool() ) {
    437 		if ( !player->spectating ) {
    438 			for ( int i = 0 ; i < MAX_SCREEN_BLOBS ; i++ ) {
    439 				screenBlob_t	*blob = &screenBlobs[i];
    440 				if ( blob->finishTime <= gameLocal.fast.time ) {
    441 					continue;
    442 				}
    443 
    444 				blob->y += blob->driftAmount;
    445 
    446 				float	fade = (float)( blob->finishTime - gameLocal.fast.time ) / ( blob->finishTime - blob->startFadeTime );
    447 				if ( fade > 1.0f ) {
    448 					fade = 1.0f;
    449 				}
    450 				if ( fade ) {
    451 					renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, fade );
    452 					renderSystem->DrawStretchPic( blob->x, blob->y, blob->w, blob->h,blob->s1, blob->t1, blob->s2, blob->t2, blob->material );
    453 				}
    454 			}
    455 		}
    456 		player->DrawHUD( hudManager );
    457 
    458 		if ( player->spectating ) {
    459 			return;
    460 		}
    461 
    462 		// armor impulse feedback
    463 		float armorPulse = ( gameLocal.fast.time - player->lastArmorPulse ) / 250.0f;
    464 
    465 		if ( armorPulse > 0.0f && armorPulse < 1.0f ) {
    466 			renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f - armorPulse );
    467 			renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, armorMaterial );
    468 		}
    469 
    470 
    471 		// tunnel vision
    472 		float health = 0.0f;
    473 		if ( g_testHealthVision.GetFloat() != 0.0f ) {
    474 			health = g_testHealthVision.GetFloat();
    475 		} else {
    476 			health = player->health;
    477 		}
    478 		float alpha = health / 100.0f;
    479 		if ( alpha < 0.0f ) {
    480 			alpha = 0.0f;
    481 		}
    482 		if ( alpha > 1.0f ) {
    483 			alpha = 1.0f;
    484 		}
    485 
    486 		if ( alpha < 1.0f  ) {
    487 			renderSystem->SetColor4( ( player->health <= 0.0f ) ? MS2SEC( gameLocal.slow.time ) : lastDamageTime, 1.0f, 1.0f, ( player->health <= 0.0f ) ? 0.0f : alpha );
    488 			renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, tunnelMaterial );
    489 		}
    490 
    491 		if ( bfgVision ) {
    492 			renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
    493 			renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, bfgMaterial );
    494 		}
    495 		
    496 	}
    497 
    498 	// test a single material drawn over everything
    499 	if ( g_testPostProcess.GetString()[0] ) {
    500 		const idMaterial *mtr = declManager->FindMaterial( g_testPostProcess.GetString(), false );
    501 		if ( !mtr ) {
    502 			common->Printf( "Material not found.\n" );
    503 			g_testPostProcess.SetString( "" );
    504 		} else {
    505 			renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
    506 			renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, mtr );
    507 		}
    508 	}
    509 }
    510 
    511 
    512 /*
    513 =================
    514 idPlayerView::Flash
    515 
    516 flashes the player view with the given color
    517 =================
    518 */
    519 void idPlayerView::Flash(idVec4 color, int time ) {
    520 	Fade( idVec4( 0.0f, 0.0f, 0.0f, 0.0f ), time);
    521 	fadeFromColor = colorWhite;
    522 }
    523 
    524 /*
    525 =================
    526 idPlayerView::Fade
    527 
    528 used for level transition fades
    529 assumes: color.w is 0 or 1
    530 =================
    531 */
    532 void idPlayerView::Fade( idVec4 color, int time ) {
    533 	SetTimeState ts( player->timeGroup );
    534 
    535 	if ( !fadeTime ) {
    536 		fadeFromColor.Set( 0.0f, 0.0f, 0.0f, 1.0f - color[ 3 ] );
    537 	} else {
    538 		fadeFromColor = fadeColor;
    539 	}
    540 	fadeToColor = color;
    541 
    542 	if ( time <= 0 ) {
    543 		fadeRate = 0;
    544 		time = 0;
    545 		fadeColor = fadeToColor;
    546 	} else {
    547 		fadeRate = 1.0f / ( float )time;
    548 	}
    549 
    550 	if ( gameLocal.realClientTime == 0 && time == 0 ) {
    551 		fadeTime = 1;
    552 	} else {
    553 		fadeTime = gameLocal.realClientTime + time;
    554 	}
    555 }
    556 
    557 /*
    558 =================
    559 idPlayerView::ScreenFade
    560 =================
    561 */
    562 void idPlayerView::ScreenFade() {
    563 	if ( !fadeTime ) {
    564 		return;
    565 	}
    566 
    567 	SetTimeState ts( player->timeGroup );
    568 
    569 	int msec = fadeTime - gameLocal.realClientTime;
    570 
    571 	if ( msec <= 0 ) {
    572 		fadeColor = fadeToColor;
    573 		if ( fadeColor[ 3 ] == 0.0f ) {
    574 			fadeTime = 0;
    575 		}
    576 	} else {
    577 		float t = ( float )msec * fadeRate;
    578 		fadeColor = fadeFromColor * t + fadeToColor * ( 1.0f - t );
    579 	}
    580 
    581 	if ( fadeColor[ 3 ] != 0.0f ) {
    582 		renderSystem->SetColor4( fadeColor[ 0 ], fadeColor[ 1 ], fadeColor[ 2 ], fadeColor[ 3 ] );
    583 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, declManager->FindMaterial( "_white" ) );
    584 	}
    585 }
    586 
    587 idCVar	stereoRender_interOccularCentimeters( "stereoRender_interOccularCentimeters", "3.0", CVAR_ARCHIVE | CVAR_RENDERER, "Distance between eyes" );
    588 idCVar	stereoRender_convergence( "stereoRender_convergence", "6", CVAR_RENDERER, "0 = head mounted display, otherwise world units to convergence plane" );
    589 
    590 extern	idCVar stereoRender_screenSeparation;	// screen units from center to eyes
    591 extern	idCVar stereoRender_swapEyes;
    592 
    593 // In a head mounted display with separate displays for each eye,
    594 // screen separation will be zero and world separation will be the eye distance.
    595 struct stereoDistances_t {
    596 	// Offset to projection matrix, positive one eye, negative the other.
    597 	// Total distance is twice this, so 0.05 would give a 10% of screen width
    598 	// separation for objects at infinity.
    599 	float	screenSeparation;			
    600 
    601 	// Game world units from one eye to the centerline.
    602 	// Total distance is twice this.
    603 	float	worldSeparation;
    604 };
    605 
    606 float CentimetersToInches( const float cm ) {
    607 	return cm / 2.54f;
    608 }
    609 
    610 float CentimetersToWorldUnits( const float cm ) {
    611 	// In Doom 3, one world unit == one inch
    612 	return CentimetersToInches( cm );
    613 }
    614 
    615 float	CalculateWorldSeparation( 
    616 	const float screenSeparation, 
    617 	const float convergenceDistance, 
    618 	const float fov_x_degrees ) {
    619 
    620 	const float fovRadians = DEG2RAD( fov_x_degrees );
    621 	const float screen = tan( fovRadians * 0.5f ) * fabs( screenSeparation );
    622 	const float worldSeparation = screen * convergenceDistance / 0.5f;
    623 
    624 	return worldSeparation;
    625 }
    626 
    627 stereoDistances_t	CaclulateStereoDistances(
    628 	const float	interOcularCentimeters,		// distance between two eyes, typically 6.0 - 7.0
    629 	const float screenWidthCentimeters,		// read from operating system
    630 	const float convergenceWorldUnits,		// pass 0 for head mounted display mode
    631 	const float	fov_x_degrees ) {			// edge to edge horizontal field of view, typically 60 - 90
    632 
    633 	stereoDistances_t	dists = {};
    634 
    635 	if ( convergenceWorldUnits == 0.0f ) {
    636 		// head mounted display mode
    637 		dists.worldSeparation = CentimetersToInches( interOcularCentimeters * 0.5 );
    638 		dists.screenSeparation = 0.0f;
    639 		return dists;
    640 	}
    641 
    642 	// 3DTV mode
    643 	dists.screenSeparation = 0.5f * interOcularCentimeters / screenWidthCentimeters;
    644 	dists.worldSeparation = CalculateWorldSeparation( dists.screenSeparation, convergenceWorldUnits, fov_x_degrees );
    645 
    646 	return dists;
    647 }
    648 
    649 float	GetScreenSeparationForGuis() {
    650 	const stereoDistances_t dists = CaclulateStereoDistances(
    651 		stereoRender_interOccularCentimeters.GetFloat(),
    652 		renderSystem->GetPhysicalScreenWidthInCentimeters(),
    653 		stereoRender_convergence.GetFloat(),
    654 		80.0f /* fov */ );
    655 
    656 	return dists.screenSeparation;
    657 }
    658 
    659 /*
    660 ===================
    661 idPlayerView::EmitStereoEyeView
    662 ===================
    663 */
    664 void idPlayerView::EmitStereoEyeView( const int eye, idMenuHandler_HUD * hudManager ) {
    665 	renderView_t * view = player->GetRenderView();
    666 	if ( view == NULL ) {
    667 		return;
    668 	}
    669 
    670 	renderView_t eyeView = *view;
    671 
    672 	const stereoDistances_t dists = CaclulateStereoDistances(
    673 		stereoRender_interOccularCentimeters.GetFloat(),
    674 		renderSystem->GetPhysicalScreenWidthInCentimeters(),
    675 		stereoRender_convergence.GetFloat(),
    676 		view->fov_x );
    677 
    678 	eyeView.vieworg += eye * dists.worldSeparation * eyeView.viewaxis[1];
    679 
    680 	eyeView.viewEyeBuffer = stereoRender_swapEyes.GetBool() ? eye : -eye;
    681 	eyeView.stereoScreenSeparation = eye * dists.screenSeparation;
    682 
    683 	SingleView( &eyeView, hudManager );
    684 }
    685 
    686 /*
    687 ===================
    688 IsGameStereoRendered
    689 
    690 The crosshair is swapped for a laser sight in stereo rendering
    691 ===================
    692 */
    693 bool	IsGameStereoRendered() {
    694 	if ( renderSystem->GetStereo3DMode() != STEREO3D_OFF ) {
    695 		return true;
    696 	}
    697 	return false;
    698 }
    699 
    700 int EyeForHalfRateFrame( const int frameCount ) {
    701 	return ( renderSystem->GetFrameCount() & 1 ) ? -1 : 1;
    702 }
    703 
    704 /*
    705 ===================
    706 idPlayerView::RenderPlayerView
    707 ===================
    708 */
    709 void idPlayerView::RenderPlayerView( idMenuHandler_HUD * hudManager ) {
    710 	const renderView_t *view = player->GetRenderView();
    711 	if ( renderSystem->GetStereo3DMode() != STEREO3D_OFF ) {
    712 		// render both eye views each frame on the PC
    713 		for ( int eye = 1 ; eye >= -1 ; eye -= 2 ) {
    714 			EmitStereoEyeView( eye, hudManager );
    715 		}
    716 	} else 
    717 	{
    718 		SingleView( view, hudManager );
    719 	}
    720 	ScreenFade();
    721 }
    722 
    723 /*
    724 ===================
    725 idPlayerView::WarpVision
    726 ===================
    727 */
    728 int idPlayerView::AddWarp( idVec3 worldOrigin, float centerx, float centery, float initialRadius, float durationMsec ) {
    729 	FullscreenFX_Warp *fx = (FullscreenFX_Warp*)( fxManager->FindFX( "warp" ) );
    730 
    731 	if ( fx ) {
    732 		fx->EnableGrabber( true );
    733 		return 1;
    734 	}
    735 
    736 	return 1;
    737 }
    738 
    739 void idPlayerView::FreeWarp( int id ) {
    740 	FullscreenFX_Warp *fx = (FullscreenFX_Warp*)( fxManager->FindFX( "warp" ) );
    741 
    742 	if ( fx ) {
    743 		fx->EnableGrabber( false );
    744 		return;
    745 	}
    746 }
    747 
    748 
    749 
    750 
    751 
    752 /*
    753 ==================
    754 FxFader::FxFader
    755 ==================
    756 */
    757 FxFader::FxFader() {
    758 	time = 0;
    759 	state = FX_STATE_OFF;
    760 	alpha = 0;
    761 	msec = 1000;
    762 }
    763 
    764 /*
    765 ==================
    766 FxFader::SetTriggerState
    767 ==================
    768 */
    769 bool FxFader::SetTriggerState( bool active ) {
    770 
    771 	// handle on/off states
    772 	if ( active && state == FX_STATE_OFF ) {
    773 		state = FX_STATE_RAMPUP;
    774 		time = gameLocal.slow.time + msec;
    775 	}
    776 	else if ( !active && state == FX_STATE_ON ) {
    777 		state = FX_STATE_RAMPDOWN;
    778 		time = gameLocal.slow.time + msec;
    779 	}
    780 
    781 	// handle rampup/rampdown states
    782 	if ( state == FX_STATE_RAMPUP ) {
    783 		if ( gameLocal.slow.time >= time ) {
    784 			state = FX_STATE_ON;
    785 		}
    786 	}
    787 	else if ( state == FX_STATE_RAMPDOWN ) {
    788 		if ( gameLocal.slow.time >= time ) {
    789 			state = FX_STATE_OFF;
    790 		}
    791 	}
    792 
    793 	// compute alpha
    794 	switch ( state ) {
    795 		case FX_STATE_ON:		alpha = 1; break;
    796 		case FX_STATE_OFF:		alpha = 0; break;
    797 		case FX_STATE_RAMPUP:	alpha = 1 - (float)( time - gameLocal.slow.time ) / msec; break;
    798 		case FX_STATE_RAMPDOWN:	alpha = (float)( time - gameLocal.slow.time ) / msec; break;
    799 	}
    800 
    801 	if ( alpha > 0 ) {
    802 		return true;
    803 	}
    804 	else {
    805 		return false;
    806 	}
    807 }
    808 
    809 /*
    810 ==================
    811 FxFader::Save
    812 ==================
    813 */
    814 void FxFader::Save( idSaveGame *savefile ) {
    815 	savefile->WriteInt( time );
    816 	savefile->WriteInt( state );
    817 	savefile->WriteFloat( alpha );
    818 	savefile->WriteInt( msec );
    819 }
    820 
    821 /*
    822 ==================
    823 FxFader::Restore
    824 ==================
    825 */
    826 void FxFader::Restore( idRestoreGame *savefile ) {
    827 	savefile->ReadInt( time );
    828 	savefile->ReadInt( state );
    829 	savefile->ReadFloat( alpha );
    830 	savefile->ReadInt( msec );
    831 }
    832 
    833 
    834 
    835 
    836 
    837 /*
    838 ==================
    839 FullscreenFX_Helltime::Save
    840 ==================
    841 */
    842 void FullscreenFX::Save( idSaveGame *savefile ) {
    843 	fader.Save( savefile );
    844 }
    845 
    846 /*
    847 ==================
    848 FullscreenFX_Helltime::Restore
    849 ==================
    850 */
    851 void FullscreenFX::Restore( idRestoreGame *savefile ) {
    852 	fader.Restore( savefile );
    853 }
    854 
    855 
    856 /*
    857 ==================
    858 FullscreenFX_Helltime::Initialize
    859 ==================
    860 */
    861 void FullscreenFX_Helltime::Initialize() {
    862 	initMaterial = declManager->FindMaterial( "textures/d3bfg/bloodorb/init" );
    863 	drawMaterial = declManager->FindMaterial( "textures/d3bfg/bloodorb/draw" );
    864 
    865 	captureMaterials[0] = declManager->FindMaterial( "textures/d3bfg/bloodorb1/capture" );
    866 	captureMaterials[1] = declManager->FindMaterial( "textures/d3bfg/bloodorb2/capture" );
    867 	captureMaterials[2] = declManager->FindMaterial( "textures/d3bfg/bloodorb3/capture" );
    868 
    869 	clearAccumBuffer = true;
    870 }
    871 
    872 /*
    873 ==================
    874 FullscreenFX_Helltime::DetermineLevel
    875 ==================
    876 */
    877 int FullscreenFX_Helltime::DetermineLevel() {
    878 	int testfx = g_testHelltimeFX.GetInteger();
    879 
    880 	// for testing purposes
    881 	if ( testfx >= 0 && testfx < 3 ) {
    882 		return testfx;
    883 	}
    884 
    885 	idPlayer * player = fxman->GetPlayer();
    886 
    887 	if ( player != NULL &&  player->PowerUpActive( INVULNERABILITY ) ) {
    888 		return 2;
    889 	}
    890 	else if ( player != NULL && player->PowerUpActive( BERSERK ) ) {
    891 		return 1;
    892 	}
    893 	else if ( player != NULL && player->PowerUpActive( HELLTIME ) ) {
    894 		return 0;
    895 	}
    896 
    897 	return -1;
    898 }
    899 
    900 /*
    901 ==================
    902 FullscreenFX_Helltime::Active
    903 ==================
    904 */
    905 bool FullscreenFX_Helltime::Active() {
    906 
    907 	if ( gameLocal.inCinematic || common->IsMultiplayer() ) {
    908 		return false;
    909 	}
    910 
    911 	if ( DetermineLevel() >= 0 ) {
    912 		return true;
    913 	}
    914 	else {
    915 		// latch the clear flag
    916 		if ( fader.GetAlpha() == 0 ) {
    917 			clearAccumBuffer = true;
    918 		}
    919 	}
    920 
    921 	return false;
    922 }
    923 
    924 /*
    925 ==================
    926 FullscreenFX_Helltime::AccumPass
    927 ==================
    928 */
    929 void FullscreenFX_Helltime::AccumPass( const renderView_t *view ) {
    930 
    931 	int level = DetermineLevel();
    932 
    933 	// for testing
    934 	if ( level < 0 || level > 2 ) {
    935 		level = 0;
    936 	}
    937 
    938 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
    939 
    940 	float t0 = 1.0f;
    941 	float t1 = 0.0f;
    942 
    943 	// capture pass
    944 	if ( clearAccumBuffer ) {
    945 		clearAccumBuffer = false;
    946 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, t0, 1.0f, t1, initMaterial );
    947 	} else {
    948 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, t0, 1.0f, t1, captureMaterials[level] );
    949 	}
    950 }
    951 
    952 /*
    953 ==================
    954 FullscreenFX_Helltime::HighQuality
    955 ==================
    956 */
    957 void FullscreenFX_Helltime::HighQuality() {
    958 	float t0 = 1.0f;
    959 	float t1 = 0.0f;
    960 
    961 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
    962 	renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, t0, 1.0f, t1, drawMaterial );
    963 }
    964 
    965 /*
    966 ==================
    967 FullscreenFX_Helltime::Restore
    968 ==================
    969 */
    970 void FullscreenFX_Helltime::Restore( idRestoreGame *savefile ) {
    971 	FullscreenFX::Restore( savefile );
    972 
    973 	// latch the clear flag
    974 	clearAccumBuffer = true;
    975 }
    976 
    977 /*
    978 ==================
    979 FullscreenFX_Multiplayer::Initialize
    980 ==================
    981 */
    982 void FullscreenFX_Multiplayer::Initialize() {
    983 	initMaterial = declManager->FindMaterial( "textures/d3bfg/multiplayer/init" );
    984 	captureMaterial = declManager->FindMaterial( "textures/d3bfg/multiplayer/capture" );
    985 	drawMaterial = declManager->FindMaterial( "textures/d3bfg/bloodorb/draw" );
    986 	clearAccumBuffer	= true;
    987 }
    988 
    989 /*
    990 ==================
    991 FullscreenFX_Multiplayer::DetermineLevel
    992 ==================
    993 */
    994 int FullscreenFX_Multiplayer::DetermineLevel() {
    995 	int testfx = g_testMultiplayerFX.GetInteger();
    996 
    997 	// for testing purposes
    998 	if ( testfx >= 0 && testfx < 3 ) {
    999 		return testfx;
   1000 	}
   1001 
   1002 	idPlayer * player = fxman->GetPlayer();
   1003 
   1004 	if ( player != NULL && player->PowerUpActive( INVULNERABILITY ) ) {
   1005 		return 2;
   1006 	}
   1007 	//else if ( player->PowerUpActive( HASTE ) ) {
   1008 	//	return 1;
   1009 	//}
   1010 	else if ( player != NULL && player->PowerUpActive( BERSERK ) ) {
   1011 		return 0;
   1012 	}
   1013 
   1014 	return -1;
   1015 }
   1016 
   1017 /*
   1018 ==================
   1019 FullscreenFX_Multiplayer::Active
   1020 ==================
   1021 */
   1022 bool FullscreenFX_Multiplayer::Active() {
   1023 
   1024 	if ( !common->IsMultiplayer() && g_testMultiplayerFX.GetInteger() == -1 ) {
   1025 		return false;
   1026 	}
   1027 
   1028 	if ( DetermineLevel() >= 0 ) {
   1029 		return true;
   1030 	} else {
   1031 		// latch the clear flag
   1032 		if ( fader.GetAlpha() == 0 ) {
   1033 			clearAccumBuffer = true;
   1034 		}
   1035 	}
   1036 
   1037 	return false;
   1038 }
   1039 
   1040 /*
   1041 ==================
   1042 FullscreenFX_Multiplayer::AccumPass
   1043 ==================
   1044 */
   1045 void FullscreenFX_Multiplayer::AccumPass( const renderView_t *view ) {
   1046 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
   1047 	
   1048 	float t0 = 1.0f;
   1049 	float t1 = 0.0f;
   1050 
   1051 	// capture pass
   1052 	if ( clearAccumBuffer ) {
   1053 		clearAccumBuffer = false;
   1054 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, t0, 1.0f, t1, initMaterial );
   1055 	} else {
   1056 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, t0, 1.0f, t1, captureMaterial );
   1057 	}
   1058 }
   1059 
   1060 /*
   1061 ==================
   1062 FullscreenFX_Multiplayer::HighQuality
   1063 ==================
   1064 */
   1065 void FullscreenFX_Multiplayer::HighQuality() {
   1066 	float t0 = 1.0f;
   1067 	float t1 = 0.0f;
   1068 
   1069 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
   1070 	renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, t0, 1.0f, t1, drawMaterial );
   1071 }
   1072 
   1073 /*
   1074 ==================
   1075 FullscreenFX_Multiplayer::Restore
   1076 ==================
   1077 */
   1078 void FullscreenFX_Multiplayer::Restore( idRestoreGame *savefile ) {
   1079 	FullscreenFX::Restore( savefile );
   1080 
   1081 	// latch the clear flag
   1082 	clearAccumBuffer = true;
   1083 }
   1084 
   1085 
   1086 
   1087 
   1088 
   1089 /*
   1090 ==================
   1091 FullscreenFX_Warp::Initialize
   1092 ==================
   1093 */
   1094 void FullscreenFX_Warp::Initialize() {
   1095 	material = declManager->FindMaterial( "textures/d3bfg/warp" );
   1096 	grabberEnabled = false;
   1097 	startWarpTime = 0;
   1098 }
   1099 
   1100 /*
   1101 ==================
   1102 FullscreenFX_Warp::Active
   1103 ==================
   1104 */
   1105 bool FullscreenFX_Warp::Active() {
   1106 	if ( grabberEnabled ) {
   1107 		return true;
   1108 	}
   1109 
   1110 	return false;
   1111 }
   1112 
   1113 /*
   1114 ==================
   1115 FullscreenFX_Warp::Save
   1116 ==================
   1117 */
   1118 void FullscreenFX_Warp::Save( idSaveGame *savefile ) {
   1119 	FullscreenFX::Save( savefile );
   1120 
   1121 	savefile->WriteBool( grabberEnabled );
   1122 	savefile->WriteInt( startWarpTime );
   1123 }
   1124 
   1125 /*
   1126 ==================
   1127 FullscreenFX_Warp::Restore
   1128 ==================
   1129 */
   1130 void FullscreenFX_Warp::Restore( idRestoreGame *savefile ) {
   1131 	FullscreenFX::Restore( savefile );
   1132 
   1133 	savefile->ReadBool( grabberEnabled );
   1134 	savefile->ReadInt( startWarpTime );
   1135 }
   1136 
   1137 /*
   1138 ==================
   1139 FullscreenFX_Warp::DrawWarp
   1140 ==================
   1141 */
   1142 void FullscreenFX_Warp::DrawWarp( WarpPolygon_t wp, float interp ) {
   1143 	idVec4 mid1_uv, mid2_uv;
   1144 	idVec4 mid1, mid2;
   1145 	idVec2 drawPts[6];
   1146 	WarpPolygon_t trans;
   1147 
   1148 	trans = wp;
   1149 
   1150 	// compute mid points
   1151 	mid1 = trans.outer1 * ( interp ) + trans.center * ( 1 - interp );
   1152 	mid2 = trans.outer2 * ( interp ) + trans.center * ( 1 - interp );
   1153 	mid1_uv = trans.outer1 * ( 0.5 ) + trans.center * ( 1 - 0.5 );
   1154 	mid2_uv = trans.outer2 * ( 0.5 ) + trans.center * ( 1 - 0.5 );
   1155 
   1156 	// draw [outer1, mid2, mid1]
   1157 	drawPts[0].Set( trans.outer1.x, trans.outer1.y );
   1158 	drawPts[1].Set( mid2.x, mid2.y );
   1159 	drawPts[2].Set( mid1.x, mid1.y );
   1160 	drawPts[3].Set( trans.outer1.z, trans.outer1.w );
   1161 	drawPts[4].Set( mid2_uv.z, mid2_uv.w );
   1162 	drawPts[5].Set( mid1_uv.z, mid1_uv.w );
   1163 	renderSystem->DrawStretchTri( drawPts[0], drawPts[1], drawPts[2], drawPts[3], drawPts[4], drawPts[5], material );
   1164 
   1165 	// draw [outer1, outer2, mid2]
   1166 	drawPts[0].Set( trans.outer1.x, trans.outer1.y );
   1167 	drawPts[1].Set( trans.outer2.x, trans.outer2.y );
   1168 	drawPts[2].Set( mid2.x, mid2.y );
   1169 	drawPts[3].Set( trans.outer1.z, trans.outer1.w );
   1170 	drawPts[4].Set( trans.outer2.z, trans.outer2.w );
   1171 	drawPts[5].Set( mid2_uv.z, mid2_uv.w );
   1172 	renderSystem->DrawStretchTri( drawPts[0], drawPts[1], drawPts[2], drawPts[3], drawPts[4], drawPts[5], material );
   1173 
   1174 	// draw [mid1, mid2, center]
   1175 	drawPts[0].Set( mid1.x, mid1.y );
   1176 	drawPts[1].Set( mid2.x, mid2.y );
   1177 	drawPts[2].Set( trans.center.x, trans.center.y );
   1178 	drawPts[3].Set( mid1_uv.z, mid1_uv.w );
   1179 	drawPts[4].Set( mid2_uv.z, mid2_uv.w );
   1180 	drawPts[5].Set( trans.center.z, trans.center.w );
   1181 	renderSystem->DrawStretchTri( drawPts[0], drawPts[1], drawPts[2], drawPts[3], drawPts[4], drawPts[5], material );
   1182 }
   1183 
   1184 /*
   1185 ==================
   1186 FullscreenFX_Warp::HighQuality
   1187 ==================
   1188 */
   1189 void FullscreenFX_Warp::HighQuality() {
   1190 	float x1, y1, x2, y2, radius, interp;
   1191 	idVec2 center;
   1192 	int STEP = 9;
   1193 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
   1194 
   1195 	interp = ( idMath::Sin( (float)( gameLocal.slow.time - startWarpTime ) / 1000 ) + 1 ) / 2.f;
   1196 	interp = 0.7 * ( 1 - interp ) + 0.3 * ( interp );
   1197 
   1198 	// draw the warps
   1199 	center.x = 320;
   1200 	center.y = 240;
   1201 	radius = 200;
   1202 
   1203 	for ( float i = 0; i < 360; i += STEP ) {
   1204 		// compute the values
   1205 		x1 = idMath::Sin( DEG2RAD( i ) );
   1206 		y1 = idMath::Cos( DEG2RAD( i ) );
   1207 
   1208 		x2 = idMath::Sin( DEG2RAD( i + STEP ) );
   1209 		y2 = idMath::Cos( DEG2RAD( i + STEP ) );
   1210 
   1211 		// add warp polygon
   1212 		WarpPolygon_t p;
   1213 
   1214 		p.outer1.x = center.x + x1 * radius;
   1215 		p.outer1.y = center.y + y1 * radius;
   1216 		p.outer1.z = p.outer1.x / (float)SCREEN_WIDTH;
   1217 		p.outer1.w = 1 - ( p.outer1.y / (float)SCREEN_HEIGHT );
   1218 
   1219 		p.outer2.x = center.x + x2 * radius;
   1220 		p.outer2.y = center.y + y2 * radius;
   1221 		p.outer2.z = p.outer2.x / (float)SCREEN_WIDTH;
   1222 		p.outer2.w = 1 - ( p.outer2.y / (float)SCREEN_HEIGHT );
   1223 
   1224 		p.center.x = center.x;
   1225 		p.center.y = center.y;
   1226 		p.center.z = p.center.x / (float)SCREEN_WIDTH;
   1227 		p.center.w = 1 - ( p.center.y / (float)SCREEN_HEIGHT );
   1228  
   1229 		// draw it
   1230 		DrawWarp( p, interp );
   1231 	}
   1232 }
   1233 
   1234 
   1235 
   1236 
   1237 
   1238 /*
   1239 ==================
   1240 FullscreenFX_EnviroSuit::Initialize
   1241 ==================
   1242 */
   1243 void FullscreenFX_EnviroSuit::Initialize() {
   1244 	material = declManager->FindMaterial( "textures/d3bfg/enviro_suit" );
   1245 }
   1246 
   1247 /*
   1248 ==================
   1249 FullscreenFX_EnviroSuit::Active
   1250 ==================
   1251 */
   1252 bool FullscreenFX_EnviroSuit::Active() {
   1253 	idPlayer * player = fxman->GetPlayer();
   1254 
   1255 	if ( player != NULL && player->PowerUpActive( ENVIROSUIT ) ) {
   1256 		return true;
   1257 	}
   1258 
   1259 	return false;
   1260 }
   1261 
   1262 /*
   1263 ==================
   1264 FullscreenFX_EnviroSuit::HighQuality
   1265 ==================
   1266 */
   1267 void FullscreenFX_EnviroSuit::HighQuality() {
   1268 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
   1269 	float s0 = 0.0f;
   1270 	float t0 = 1.0f;
   1271 	float s1 = 1.0f;
   1272 	float t1 = 0.0f;
   1273 	renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, s0, t0, s1, t1, material );
   1274 }
   1275 
   1276 /*
   1277 ==================
   1278 FullscreenFX_DoubleVision::Initialize
   1279 ==================
   1280 */
   1281 void FullscreenFX_DoubleVision::Initialize() {
   1282 	material = declManager->FindMaterial( "textures/d3bfg/doubleVision" );
   1283 }
   1284 
   1285 /*
   1286 ==================
   1287 FullscreenFX_DoubleVision::Active
   1288 ==================
   1289 */
   1290 bool FullscreenFX_DoubleVision::Active() {
   1291 
   1292 	if ( gameLocal.fast.time < fxman->GetPlayerView()->dvFinishTime ) {
   1293 		return true;
   1294 	}
   1295 
   1296 	return false;
   1297 }
   1298 
   1299 /*
   1300 ==================
   1301 FullscreenFX_DoubleVision::HighQuality
   1302 ==================
   1303 */
   1304 void FullscreenFX_DoubleVision::HighQuality() {
   1305 	int offset = fxman->GetPlayerView()->dvFinishTime - gameLocal.fast.time;
   1306 	float scale = offset * g_dvAmplitude.GetFloat();
   1307 
   1308 	// for testing purposes
   1309 	if ( !Active() ) {
   1310 		static int test = 0;
   1311 		if ( test > 312 ) {
   1312 			test = 0;
   1313 		}
   1314 
   1315 		offset = test++;
   1316 		scale = offset * g_dvAmplitude.GetFloat();
   1317 	}
   1318 
   1319 	idPlayer * player = fxman->GetPlayer();
   1320 
   1321 	if( player == NULL ) {
   1322 		return;
   1323 	}
   1324 
   1325 	offset *= 2;		// crutch up for higher res
   1326 
   1327 	// set the scale and shift
   1328 	if ( scale > 0.5f ) {
   1329 		scale = 0.5f;
   1330 	}
   1331 	float shift = scale * sin( sqrtf( (float)offset ) * g_dvFrequency.GetFloat() );
   1332 	shift = fabs( shift );
   1333 
   1334 	// carry red tint if in berserk mode
   1335 	idVec4 color( 1.0f, 1.0f, 1.0f, 1.0f );
   1336 	if ( gameLocal.fast.time < player->inventory.powerupEndTime[ BERSERK ] ) {
   1337 		color.y = 0.0f;
   1338 		color.z = 0.0f;
   1339 	}
   1340 
   1341 	if ( !common->IsMultiplayer() && gameLocal.fast.time < player->inventory.powerupEndTime[ HELLTIME ] || gameLocal.fast.time < player->inventory.powerupEndTime[ INVULNERABILITY ]) {
   1342 		color.y = 0.0f;
   1343 		color.z = 0.0f;
   1344 	}
   1345 
   1346 	// uv coordinates
   1347 	float s0 = shift;
   1348 	float t0 = 1.0f;
   1349 	float s1 = 1.0f;
   1350 	float t1 = 0.0f;
   1351 
   1352 
   1353 	renderSystem->SetColor4( color.x, color.y, color.z, 1.0f );
   1354 	renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, s0, t0, s1, t1, material );
   1355 
   1356 	renderSystem->SetColor4( color.x, color.y, color.z, 0.5f );
   1357 	s0 = 0.0f;
   1358 	t0 = 1.0f;
   1359 	s1 = ( 1.0-shift );
   1360 	t1 = 0.0f;
   1361 
   1362 	renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, s0, t0, s1, t1, material );
   1363 }
   1364 
   1365 /*
   1366 ==================
   1367 FullscreenFX_InfluenceVision::Initialize
   1368 ==================
   1369 */
   1370 void FullscreenFX_InfluenceVision::Initialize() {
   1371 
   1372 }
   1373 
   1374 /*
   1375 ==================
   1376 FullscreenFX_InfluenceVision::Active
   1377 ==================
   1378 */
   1379 bool FullscreenFX_InfluenceVision::Active() {
   1380 	idPlayer * player = fxman->GetPlayer();
   1381 
   1382 	if ( player != NULL && ( player->GetInfluenceMaterial() || player->GetInfluenceEntity() ) ) {
   1383 		return true;
   1384 	}
   1385 
   1386 	return false;
   1387 }
   1388 
   1389 /*
   1390 ==================
   1391 FullscreenFX_InfluenceVision::HighQuality
   1392 ==================
   1393 */
   1394 void FullscreenFX_InfluenceVision::HighQuality() {
   1395 	float distance = 0.0f;
   1396 	float pct = 1.0f;
   1397 	idPlayer * player = fxman->GetPlayer();
   1398 
   1399 	if( player == NULL ) {
   1400 		return;
   1401 	}
   1402 
   1403 	if ( player->GetInfluenceEntity() ) {
   1404 		distance = ( player->GetInfluenceEntity()->GetPhysics()->GetOrigin() - player->GetPhysics()->GetOrigin() ).Length();
   1405 		if ( player->GetInfluenceRadius() != 0.0f && distance < player->GetInfluenceRadius() ) {
   1406 			pct = distance / player->GetInfluenceRadius();
   1407 			pct = 1.0f - idMath::ClampFloat( 0.0f, 1.0f, pct );
   1408 		}
   1409 	}
   1410 
   1411 	if ( player->GetInfluenceMaterial() ) {
   1412 		renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, pct );
   1413 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, player->GetInfluenceMaterial() );
   1414 	} else if ( player->GetInfluenceEntity() == NULL ) {
   1415 		return;
   1416 	} else {
   1417 //		int offset =  25 + sinf( gameLocal.slow.time );
   1418 //		DoubleVision( hud, view, pct * offset );
   1419 	}
   1420 }
   1421 
   1422 
   1423 
   1424 
   1425 /*
   1426 ==================
   1427 FullscreenFX_Bloom::Initialize
   1428 ==================
   1429 */
   1430 void FullscreenFX_Bloom::Initialize() {
   1431 	drawMaterial		= declManager->FindMaterial( "textures/d3bfg/bloom2/draw" );
   1432 	initMaterial		= declManager->FindMaterial( "textures/d3bfg/bloom2/init" );
   1433 
   1434 	currentIntensity	= 0;
   1435 	targetIntensity		= 0;
   1436 }
   1437 
   1438 /*
   1439 ==================
   1440 FullscreenFX_Bloom::Active
   1441 ==================
   1442 */
   1443 bool FullscreenFX_Bloom::Active() {
   1444 	idPlayer * player = fxman->GetPlayer();
   1445 
   1446 	if ( player != NULL && player->bloomEnabled ) {
   1447 		return true;
   1448 	}
   1449 
   1450 	return false;
   1451 }
   1452 
   1453 /*
   1454 ==================
   1455 FullscreenFX_Bloom::HighQuality
   1456 ==================
   1457 */
   1458 void FullscreenFX_Bloom::HighQuality() {
   1459 	float shift = 1;
   1460 	idPlayer * player = fxman->GetPlayer();
   1461 	renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f );
   1462 
   1463 	// if intensity value is different, start the blend
   1464 	targetIntensity = g_testBloomIntensity.GetFloat();
   1465 
   1466 	if ( player != NULL && player->bloomEnabled ) {
   1467 		targetIntensity = player->bloomIntensity;
   1468 	}
   1469 
   1470 	float delta = targetIntensity - currentIntensity;
   1471 	float step = 0.001f;
   1472 
   1473 	if ( step < fabs( delta ) ) {
   1474 		if ( delta < 0 ) {
   1475 			step = -step;
   1476 		}
   1477 
   1478 		currentIntensity += step;
   1479 	}
   1480 
   1481 	// draw the blends
   1482 	int num = g_testBloomNumPasses.GetInteger();
   1483 
   1484 	for ( int i = 0; i < num; i++ ) {
   1485 		float s1 = 0.0f, t1 = 0.0f, s2 = 1.0f, t2 = 1.0f;
   1486 		float alpha;
   1487 
   1488 		// do the center scale
   1489 		s1 -= 0.5;
   1490 		s1 *= shift;
   1491 		s1 += 0.5;
   1492 
   1493 		t1 -= 0.5;
   1494 		t1 *= shift;
   1495 		t1 += 0.5;
   1496 
   1497 		s2 -= 0.5;
   1498 		s2 *= shift;
   1499 		s2 += 0.5;
   1500 
   1501 		t2 -= 0.5;
   1502 		t2 *= shift;
   1503 		t2 += 0.5;
   1504 
   1505 		// draw it
   1506 		if ( num == 1 ) {
   1507 			alpha = 1;
   1508 		} else {
   1509 			alpha = 1 - (float)i / ( num - 1 );
   1510 		}
   1511 
   1512 
   1513 		float yScale = 1.0f;
   1514 
   1515 		renderSystem->SetColor4( alpha, alpha, alpha, 1 );
   1516 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, s1, t2 * yScale, s2, t1 * yScale, drawMaterial );
   1517 
   1518 		shift += currentIntensity;
   1519 	}
   1520 }
   1521 
   1522 /*
   1523 ==================
   1524 FullscreenFX_Bloom::Save
   1525 ==================
   1526 */
   1527 void FullscreenFX_Bloom::Save( idSaveGame *savefile ) {
   1528 	FullscreenFX::Save( savefile );
   1529 	savefile->WriteFloat( currentIntensity );
   1530 	savefile->WriteFloat( targetIntensity );
   1531 }
   1532 
   1533 /*
   1534 ==================
   1535 FullscreenFX_Bloom::Restore
   1536 ==================
   1537 */
   1538 void FullscreenFX_Bloom::Restore( idRestoreGame *savefile ) {
   1539 	FullscreenFX::Restore( savefile );
   1540 	savefile->ReadFloat( currentIntensity );
   1541 	savefile->ReadFloat( targetIntensity );
   1542 }
   1543 
   1544 
   1545 
   1546 
   1547 
   1548 
   1549 /*
   1550 ==================
   1551 FullscreenFXManager::FullscreenFXManager
   1552 ==================
   1553 */
   1554 FullscreenFXManager::FullscreenFXManager() {
   1555 	playerView = NULL;
   1556 	blendBackMaterial = NULL;
   1557 }
   1558 
   1559 /*
   1560 ==================
   1561 FullscreenFXManager::~FullscreenFXManager
   1562 ==================
   1563 */
   1564 FullscreenFXManager::~FullscreenFXManager() {
   1565 	fx.DeleteContents();
   1566 }
   1567 
   1568 /*
   1569 ==================
   1570 FullscreenFXManager::FindFX
   1571 ==================
   1572 */
   1573 FullscreenFX* FullscreenFXManager::FindFX( idStr name ) {
   1574 	for ( int i = 0; i < fx.Num(); i++ ) {
   1575 		if ( fx[i]->GetName() == name ) {
   1576 			return fx[i];
   1577 		}
   1578 	}
   1579 
   1580 	return NULL;
   1581 }
   1582 
   1583 /*
   1584 ==================
   1585 FullscreenFXManager::CreateFX
   1586 ==================
   1587 */
   1588 void FullscreenFXManager::CreateFX( idStr name, idStr fxtype, int fade ) {
   1589 	FullscreenFX *pfx = NULL;
   1590 
   1591 	if ( fxtype == "helltime" ) {
   1592 		pfx = new (TAG_FX) FullscreenFX_Helltime;
   1593 	} else if ( fxtype == "warp" ) {
   1594 		pfx = new (TAG_FX) FullscreenFX_Warp;
   1595 	} else if ( fxtype == "envirosuit" ) {
   1596 		pfx = new (TAG_FX) FullscreenFX_EnviroSuit;
   1597 	} else if ( fxtype == "doublevision" ) {
   1598 		pfx = new (TAG_FX) FullscreenFX_DoubleVision;
   1599 	} else if ( fxtype == "multiplayer" ) {
   1600 		pfx = new (TAG_FX) FullscreenFX_Multiplayer;
   1601 	} else if ( fxtype == "influencevision" ) {
   1602 		pfx = new (TAG_FX) FullscreenFX_InfluenceVision;
   1603 	} else if ( fxtype == "bloom" ) {
   1604 		pfx = new (TAG_FX) FullscreenFX_Bloom;
   1605 	} else {
   1606 		assert( 0 );
   1607 	}
   1608 
   1609 	if ( pfx ) {
   1610 		pfx->Initialize();
   1611 		pfx->SetFXManager( this );
   1612 		pfx->SetName( name );
   1613 		pfx->SetFadeSpeed( fade );
   1614 		fx.Append( pfx );
   1615 	}
   1616 }
   1617 
   1618 /*
   1619 ==================
   1620 FullscreenFXManager::Initialize
   1621 ==================
   1622 */
   1623 void FullscreenFXManager::Initialize( idPlayerView *pv ) {
   1624 	// set the playerview
   1625 	playerView = pv;
   1626 	blendBackMaterial = declManager->FindMaterial( "textures/d3bfg/blendBack" );
   1627 
   1628 	// allocate the fx
   1629 	CreateFX( "helltime", "helltime", 1000 );
   1630 	CreateFX( "warp", "warp", 0 );
   1631 	CreateFX( "envirosuit", "envirosuit", 500 );
   1632 	CreateFX( "doublevision", "doublevision", 0 );
   1633 	CreateFX( "multiplayer", "multiplayer", 1000 );
   1634 	CreateFX( "influencevision", "influencevision", 1000 );
   1635 	CreateFX( "bloom", "bloom", 0 );
   1636 
   1637 	// pre-cache the texture grab so we dont hitch
   1638 	renderSystem->CropRenderSize( 512, 512 );
   1639 	renderSystem->CaptureRenderToImage( "_accum" );
   1640 	renderSystem->UnCrop();
   1641 
   1642 	renderSystem->CaptureRenderToImage( "_currentRender" );
   1643 }
   1644 
   1645 /*
   1646 ==================
   1647 FullscreenFXManager::Blendback
   1648 ==================
   1649 */
   1650 void FullscreenFXManager::Blendback( float alpha ) {
   1651 	// alpha fade
   1652 	if ( alpha < 1.f ) {
   1653 		renderSystem->SetColor4( 1.0f, 1.0f, 1.0f, 1.0f - alpha );
   1654 		float s0 = 0.0f;
   1655 		float t0 = 1.0f;
   1656 		float s1 = 1.0f;
   1657 		float t1 = 0.0f;
   1658 		renderSystem->DrawStretchPic( 0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, s0, t0, s1, t1, blendBackMaterial );
   1659 	}
   1660 }
   1661 
   1662 /*
   1663 ==================
   1664 FullscreenFXManager::Save
   1665 ==================
   1666 */
   1667 void FullscreenFXManager::Save( idSaveGame *savefile ) {
   1668 	for ( int i = 0; i < fx.Num(); i++ ) {
   1669 		FullscreenFX *pfx = fx[i];
   1670 		pfx->Save( savefile );
   1671 	}
   1672 }
   1673 
   1674 /*
   1675 ==================
   1676 FullscreenFXManager::Restore
   1677 ==================
   1678 */
   1679 void FullscreenFXManager::Restore( idRestoreGame *savefile ) {
   1680 	for ( int i = 0; i < fx.Num(); i++ ) {
   1681 		FullscreenFX *pfx = fx[i];
   1682 		pfx->Restore( savefile );
   1683 	}
   1684 }
   1685 
   1686 idCVar player_allowScreenFXInStereo( "player_allowScreenFXInStereo", "1", CVAR_BOOL, "allow full screen fx in stereo mode" );
   1687 
   1688 /*
   1689 ==================
   1690 FullscreenFXManager::Process
   1691 ==================
   1692 */
   1693 void FullscreenFXManager::Process( const renderView_t *view ) {
   1694 	bool allpass = false;
   1695 	bool atLeastOneFX = false;
   1696 
   1697 	if ( g_testFullscreenFX.GetInteger() == -2 ) {
   1698 		allpass = true;
   1699 	}
   1700 
   1701 	// do the first render
   1702 	gameRenderWorld->RenderScene( view );
   1703 
   1704 	// we should consider these on a case-by-case basis for stereo rendering
   1705 	// double vision could be implemented "for real" by shifting the
   1706 	// eye views
   1707 	if ( IsGameStereoRendered() && !player_allowScreenFXInStereo.GetBool() ) {
   1708 		return;
   1709 	}
   1710 
   1711 	// do the process
   1712 	for ( int i = 0; i < fx.Num(); i++ ) {
   1713 		FullscreenFX *pfx = fx[i];
   1714 		bool drawIt = false;
   1715 
   1716 		// determine if we need to draw
   1717 		if ( pfx->Active() || g_testFullscreenFX.GetInteger() == i || allpass ) {
   1718 			drawIt = pfx->SetTriggerState( true );
   1719 		} else {
   1720 			drawIt = pfx->SetTriggerState( false );
   1721 		}
   1722 
   1723 		// do the actual drawing
   1724 		if ( drawIt ) {
   1725 			atLeastOneFX = true;
   1726 
   1727 			// we need to dump to _currentRender
   1728 			renderSystem->CaptureRenderToImage( "_currentRender" );
   1729 
   1730 			// handle the accum pass if we have one
   1731 			if ( pfx->HasAccum() ) {
   1732 				// we need to crop the accum pass
   1733 				renderSystem->CropRenderSize( 512, 512 );
   1734 				pfx->AccumPass( view );
   1735 				renderSystem->CaptureRenderToImage( "_accum" );
   1736 				renderSystem->UnCrop();
   1737 			}
   1738 
   1739 			// do the high quality pass
   1740 			pfx->HighQuality();
   1741 
   1742 			// do the blendback
   1743 			Blendback( pfx->GetFadeAlpha() );
   1744 		}
   1745 	}
   1746 }
   1747 
   1748 
   1749