/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . 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. 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. =========================================================================== */ #include "precompiled.h" #pragma hdrstop #include "Game_local.h" /* =============================================================================== idLight =============================================================================== */ const idEventDef EV_Light_SetShader( "setShader", "s" ); const idEventDef EV_Light_GetLightParm( "getLightParm", "d", 'f' ); const idEventDef EV_Light_SetLightParm( "setLightParm", "df" ); const idEventDef EV_Light_SetLightParms( "setLightParms", "ffff" ); const idEventDef EV_Light_SetRadiusXYZ( "setRadiusXYZ", "fff" ); const idEventDef EV_Light_SetRadius( "setRadius", "f" ); const idEventDef EV_Light_On( "On", NULL ); const idEventDef EV_Light_Off( "Off", NULL ); const idEventDef EV_Light_FadeOut( "fadeOutLight", "f" ); const idEventDef EV_Light_FadeIn( "fadeInLight", "f" ); CLASS_DECLARATION( idEntity, idLight ) EVENT( EV_Light_SetShader, idLight::Event_SetShader ) EVENT( EV_Light_GetLightParm, idLight::Event_GetLightParm ) EVENT( EV_Light_SetLightParm, idLight::Event_SetLightParm ) EVENT( EV_Light_SetLightParms, idLight::Event_SetLightParms ) EVENT( EV_Light_SetRadiusXYZ, idLight::Event_SetRadiusXYZ ) EVENT( EV_Light_SetRadius, idLight::Event_SetRadius ) EVENT( EV_Hide, idLight::Event_Hide ) EVENT( EV_Show, idLight::Event_Show ) EVENT( EV_Light_On, idLight::Event_On ) EVENT( EV_Light_Off, idLight::Event_Off ) EVENT( EV_Activate, idLight::Event_ToggleOnOff ) EVENT( EV_PostSpawn, idLight::Event_SetSoundHandles ) EVENT( EV_Light_FadeOut, idLight::Event_FadeOut ) EVENT( EV_Light_FadeIn, idLight::Event_FadeIn ) END_CLASS /* ================ idGameEdit::ParseSpawnArgsToRenderLight parse the light parameters this is the canonical renderLight parm parsing, which should be used by dmap and the editor ================ */ void idGameEdit::ParseSpawnArgsToRenderLight( const idDict* args, renderLight_t* renderLight ) { bool gotTarget, gotUp, gotRight; const char* texture; idVec3 color; memset( renderLight, 0, sizeof( *renderLight ) ); if( !args->GetVector( "light_origin", "", renderLight->origin ) ) { args->GetVector( "origin", "", renderLight->origin ); } gotTarget = args->GetVector( "light_target", "", renderLight->target ); gotUp = args->GetVector( "light_up", "", renderLight->up ); gotRight = args->GetVector( "light_right", "", renderLight->right ); args->GetVector( "light_start", "0 0 0", renderLight->start ); if( !args->GetVector( "light_end", "", renderLight->end ) ) { renderLight->end = renderLight->target; } // we should have all of the target/right/up or none of them if( ( gotTarget || gotUp || gotRight ) != ( gotTarget && gotUp && gotRight ) ) { gameLocal.Printf( "Light at (%f,%f,%f) has bad target info\n", renderLight->origin[0], renderLight->origin[1], renderLight->origin[2] ); return; } if( !gotTarget ) { renderLight->pointLight = true; // allow an optional relative center of light and shadow offset args->GetVector( "light_center", "0 0 0", renderLight->lightCenter ); // create a point light if( !args->GetVector( "light_radius", "300 300 300", renderLight->lightRadius ) ) { float radius; args->GetFloat( "light", "300", radius ); renderLight->lightRadius[0] = renderLight->lightRadius[1] = renderLight->lightRadius[2] = radius; } } // get the rotation matrix in either full form, or single angle form idAngles angles; idMat3 mat; if( !args->GetMatrix( "light_rotation", "1 0 0 0 1 0 0 0 1", mat ) ) { if( !args->GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", mat ) ) { args->GetFloat( "angle", "0", angles[ 1 ] ); angles[ 0 ] = 0; angles[ 1 ] = idMath::AngleNormalize360( angles[ 1 ] ); angles[ 2 ] = 0; mat = angles.ToMat3(); } } // fix degenerate identity matrices mat[0].FixDegenerateNormal(); mat[1].FixDegenerateNormal(); mat[2].FixDegenerateNormal(); renderLight->axis = mat; // check for other attributes args->GetVector( "_color", "1 1 1", color ); renderLight->shaderParms[ SHADERPARM_RED ] = color[0]; renderLight->shaderParms[ SHADERPARM_GREEN ] = color[1]; renderLight->shaderParms[ SHADERPARM_BLUE ] = color[2]; args->GetFloat( "shaderParm3", "1", renderLight->shaderParms[ SHADERPARM_TIMESCALE ] ); if( !args->GetFloat( "shaderParm4", "0", renderLight->shaderParms[ SHADERPARM_TIMEOFFSET ] ) ) { // offset the start time of the shader to sync it to the game time renderLight->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); } args->GetFloat( "shaderParm5", "0", renderLight->shaderParms[5] ); args->GetFloat( "shaderParm6", "0", renderLight->shaderParms[6] ); args->GetFloat( "shaderParm7", "0", renderLight->shaderParms[ SHADERPARM_MODE ] ); args->GetBool( "noshadows", "0", renderLight->noShadows ); args->GetBool( "nospecular", "0", renderLight->noSpecular ); args->GetBool( "parallel", "0", renderLight->parallel ); args->GetString( "texture", "lights/squarelight1", &texture ); // allow this to be NULL renderLight->shader = declManager->FindMaterial( texture, false ); } /* ================ idLight::UpdateChangeableSpawnArgs ================ */ void idLight::UpdateChangeableSpawnArgs( const idDict* source ) { idEntity::UpdateChangeableSpawnArgs( source ); if( source ) { source->Print(); } FreeSoundEmitter( true ); gameEdit->ParseSpawnArgsToRefSound( source ? source : &spawnArgs, &refSound ); if( refSound.shader && !refSound.waitfortrigger ) { StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL ); } gameEdit->ParseSpawnArgsToRenderLight( source ? source : &spawnArgs, &renderLight ); UpdateVisuals(); } /* ================ idLight::idLight ================ */ idLight::idLight(): previousBaseColor( vec3_zero ) , nextBaseColor( vec3_zero ) { memset( &renderLight, 0, sizeof( renderLight ) ); localLightOrigin = vec3_zero; localLightAxis = mat3_identity; lightDefHandle = -1; levels = 0; currentLevel = 0; baseColor = vec3_zero; breakOnTrigger = false; count = 0; triggercount = 0; lightParent = NULL; fadeFrom.Set( 1, 1, 1, 1 ); fadeTo.Set( 1, 1, 1, 1 ); fadeStart = 0; fadeEnd = 0; soundWasPlaying = false; } /* ================ idLight::~idLight ================ */ idLight::~idLight() { if( lightDefHandle != -1 ) { gameRenderWorld->FreeLightDef( lightDefHandle ); } } /* ================ idLight::Save archives object for save game file ================ */ void idLight::Save( idSaveGame* savefile ) const { savefile->WriteRenderLight( renderLight ); savefile->WriteBool( renderLight.prelightModel != NULL ); savefile->WriteVec3( localLightOrigin ); savefile->WriteMat3( localLightAxis ); savefile->WriteString( brokenModel ); savefile->WriteInt( levels ); savefile->WriteInt( currentLevel ); savefile->WriteVec3( baseColor ); savefile->WriteBool( breakOnTrigger ); savefile->WriteInt( count ); savefile->WriteInt( triggercount ); savefile->WriteObject( lightParent ); savefile->WriteVec4( fadeFrom ); savefile->WriteVec4( fadeTo ); savefile->WriteInt( fadeStart ); savefile->WriteInt( fadeEnd ); savefile->WriteBool( soundWasPlaying ); } /* ================ idLight::Restore unarchives object from save game file ================ */ void idLight::Restore( idRestoreGame* savefile ) { bool hadPrelightModel; savefile->ReadRenderLight( renderLight ); savefile->ReadBool( hadPrelightModel ); renderLight.prelightModel = renderModelManager->CheckModel( va( "_prelight_%s", name.c_str() ) ); if( ( renderLight.prelightModel == NULL ) && hadPrelightModel ) { assert( 0 ); if( developer.GetBool() ) { // we really want to know if this happens gameLocal.Error( "idLight::Restore: prelightModel '_prelight_%s' not found", name.c_str() ); } else { // but let it slide after release gameLocal.Warning( "idLight::Restore: prelightModel '_prelight_%s' not found", name.c_str() ); } } savefile->ReadVec3( localLightOrigin ); savefile->ReadMat3( localLightAxis ); savefile->ReadString( brokenModel ); savefile->ReadInt( levels ); savefile->ReadInt( currentLevel ); savefile->ReadVec3( baseColor ); savefile->ReadBool( breakOnTrigger ); savefile->ReadInt( count ); savefile->ReadInt( triggercount ); savefile->ReadObject( reinterpret_cast( lightParent ) ); savefile->ReadVec4( fadeFrom ); savefile->ReadVec4( fadeTo ); savefile->ReadInt( fadeStart ); savefile->ReadInt( fadeEnd ); savefile->ReadBool( soundWasPlaying ); lightDefHandle = -1; SetLightLevel(); } /* ================ idLight::Spawn ================ */ void idLight::Spawn() { bool start_off; bool needBroken; const char* demonic_shader; // do the parsing the same way dmap and the editor do gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &renderLight ); // we need the origin and axis relative to the physics origin/axis localLightOrigin = ( renderLight.origin - GetPhysics()->GetOrigin() ) * GetPhysics()->GetAxis().Transpose(); localLightAxis = renderLight.axis * GetPhysics()->GetAxis().Transpose(); // set the base color from the shader parms baseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] ); previousBaseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] ); nextBaseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] ); // set the number of light levels spawnArgs.GetInt( "levels", "1", levels ); currentLevel = levels; if( levels <= 0 ) { gameLocal.Error( "Invalid light level set on entity #%d(%s)", entityNumber, name.c_str() ); } // make sure the demonic shader is cached if( spawnArgs.GetString( "mat_demonic", NULL, &demonic_shader ) ) { declManager->FindType( DECL_MATERIAL, demonic_shader ); } // game specific functionality, not mirrored in // editor or dmap light parsing // also put the light texture on the model, so light flares // can get the current intensity of the light renderEntity.referenceShader = renderLight.shader; lightDefHandle = -1; // no static version yet // see if an optimized shadow volume exists // the renderer will ignore this value after a light has been moved, // but there may still be a chance to get it wrong if the game moves // a light before the first present, and doesn't clear the prelight renderLight.prelightModel = 0; if( name[ 0 ] ) { // this will return 0 if not found renderLight.prelightModel = renderModelManager->CheckModel( va( "_prelight_%s", name.c_str() ) ); } spawnArgs.GetBool( "start_off", "0", start_off ); if( start_off ) { Off(); } // Midnight CTF if( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool( "si_midnight" ) && !spawnArgs.GetBool( "midnight_override" ) ) { Off(); } health = spawnArgs.GetInt( "health", "0" ); spawnArgs.GetString( "broken", "", brokenModel ); spawnArgs.GetBool( "break", "0", breakOnTrigger ); spawnArgs.GetInt( "count", "1", count ); triggercount = 0; fadeFrom.Set( 1, 1, 1, 1 ); fadeTo.Set( 1, 1, 1, 1 ); fadeStart = 0; fadeEnd = 0; // if we have a health make light breakable if( health ) { idStr model = spawnArgs.GetString( "model" ); // get the visual model if( !model.Length() ) { gameLocal.Error( "Breakable light without a model set on entity #%d(%s)", entityNumber, name.c_str() ); } fl.takedamage = true; // see if we need to create a broken model name needBroken = true; if( model.Length() && !brokenModel.Length() ) { int pos; needBroken = false; pos = model.Find( "." ); if( pos < 0 ) { pos = model.Length(); } if( pos > 0 ) { model.Left( pos, brokenModel ); } brokenModel += "_broken"; if( pos > 0 ) { brokenModel += &model[ pos ]; } } // make sure the model gets cached if( !renderModelManager->CheckModel( brokenModel ) ) { if( needBroken ) { gameLocal.Error( "Model '%s' not found for entity %d(%s)", brokenModel.c_str(), entityNumber, name.c_str() ); } else { brokenModel = ""; } } GetPhysics()->SetContents( spawnArgs.GetBool( "nonsolid" ) ? 0 : CONTENTS_SOLID ); // make sure the collision model gets cached idClipModel::CheckModel( brokenModel ); } PostEventMS( &EV_PostSpawn, 0 ); UpdateVisuals(); } /* ================ idLight::SetLightLevel ================ */ void idLight::SetLightLevel() { idVec3 color; float intensity; intensity = ( float )currentLevel / ( float )levels; color = baseColor * intensity; renderLight.shaderParms[ SHADERPARM_RED ] = color[ 0 ]; renderLight.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ]; renderLight.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ]; renderEntity.shaderParms[ SHADERPARM_RED ] = color[ 0 ]; renderEntity.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ]; renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ]; PresentLightDefChange(); PresentModelDefChange(); } /* ================ idLight::GetColor ================ */ void idLight::GetColor( idVec3& out ) const { out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ]; out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ]; out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ]; } /* ================ idLight::GetColor ================ */ void idLight::GetColor( idVec4& out ) const { out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ]; out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ]; out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ]; out[ 3 ] = renderLight.shaderParms[ SHADERPARM_ALPHA ]; } /* ================ idLight::SetColor ================ */ void idLight::SetColor( float red, float green, float blue ) { baseColor.Set( red, green, blue ); SetLightLevel(); } /* ================ idLight::SetColor ================ */ void idLight::SetColor( const idVec4& color ) { baseColor = color.ToVec3(); renderLight.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ]; renderEntity.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ]; SetLightLevel(); } /* ================ idLight::SetColor ================ */ void idLight::SetColor( const idVec3& color ) { baseColor = color; SetLightLevel(); } /* ================ idLight::SetShader ================ */ void idLight::SetShader( const char* shadername ) { // allow this to be NULL renderLight.shader = declManager->FindMaterial( shadername, false ); PresentLightDefChange(); } /* ================ idLight::SetLightParm ================ */ void idLight::SetLightParm( int parmnum, float value ) { if( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) { gameLocal.Error( "shader parm index (%d) out of range", parmnum ); return; } renderLight.shaderParms[ parmnum ] = value; PresentLightDefChange(); } /* ================ idLight::SetLightParms ================ */ void idLight::SetLightParms( float parm0, float parm1, float parm2, float parm3 ) { renderLight.shaderParms[ SHADERPARM_RED ] = parm0; renderLight.shaderParms[ SHADERPARM_GREEN ] = parm1; renderLight.shaderParms[ SHADERPARM_BLUE ] = parm2; renderLight.shaderParms[ SHADERPARM_ALPHA ] = parm3; renderEntity.shaderParms[ SHADERPARM_RED ] = parm0; renderEntity.shaderParms[ SHADERPARM_GREEN ] = parm1; renderEntity.shaderParms[ SHADERPARM_BLUE ] = parm2; renderEntity.shaderParms[ SHADERPARM_ALPHA ] = parm3; PresentLightDefChange(); PresentModelDefChange(); } /* ================ idLight::SetRadiusXYZ ================ */ void idLight::SetRadiusXYZ( float x, float y, float z ) { renderLight.lightRadius[0] = x; renderLight.lightRadius[1] = y; renderLight.lightRadius[2] = z; PresentLightDefChange(); } /* ================ idLight::SetRadius ================ */ void idLight::SetRadius( float radius ) { renderLight.lightRadius[0] = renderLight.lightRadius[1] = renderLight.lightRadius[2] = radius; PresentLightDefChange(); } /* ================ idLight::On ================ */ void idLight::On() { currentLevel = levels; // offset the start time of the shader to sync it to the game time renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); if( ( soundWasPlaying || refSound.waitfortrigger ) && refSound.shader ) { StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL ); soundWasPlaying = false; } SetLightLevel(); BecomeActive( TH_UPDATEVISUALS ); } /* ================ idLight::Off ================ */ void idLight::Off() { currentLevel = 0; // kill any sound it was making if( refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) { StopSound( SND_CHANNEL_ANY, false ); soundWasPlaying = true; } SetLightLevel(); BecomeActive( TH_UPDATEVISUALS ); } /* ================ idLight::Fade ================ */ void idLight::Fade( const idVec4& to, float fadeTime ) { GetColor( fadeFrom ); fadeTo = to; fadeStart = gameLocal.time; fadeEnd = gameLocal.time + SEC2MS( fadeTime ); BecomeActive( TH_THINK ); } /* ================ idLight::FadeOut ================ */ void idLight::FadeOut( float time ) { Fade( colorBlack, time ); } /* ================ idLight::FadeIn ================ */ void idLight::FadeIn( float time ) { idVec3 color; idVec4 color4; currentLevel = levels; spawnArgs.GetVector( "_color", "1 1 1", color ); color4.Set( color.x, color.y, color.z, 1.0f ); Fade( color4, time ); } /* ================ idLight::Killed ================ */ void idLight::Killed( idEntity* inflictor, idEntity* attacker, int damage, const idVec3& dir, int location ) { BecomeBroken( attacker ); } /* ================ idLight::BecomeBroken ================ */ void idLight::BecomeBroken( idEntity* activator ) { const char* damageDefName; fl.takedamage = false; if( brokenModel.Length() ) { SetModel( brokenModel ); if( !spawnArgs.GetBool( "nonsolid" ) ) { GetPhysics()->SetClipModel( new( TAG_PHYSICS_CLIP_ENTITY ) idClipModel( brokenModel.c_str() ), 1.0f ); GetPhysics()->SetContents( CONTENTS_SOLID ); } } else if( spawnArgs.GetBool( "hideModelOnBreak" ) ) { SetModel( "" ); GetPhysics()->SetContents( 0 ); } if( common->IsServer() ) { ServerSendEvent( EVENT_BECOMEBROKEN, NULL, true ); if( spawnArgs.GetString( "def_damage", "", &damageDefName ) ) { idVec3 origin = renderEntity.origin + renderEntity.bounds.GetCenter() * renderEntity.axis; gameLocal.RadiusDamage( origin, activator, activator, this, this, damageDefName ); } } ActivateTargets( activator ); // offset the start time of the shader to sync it to the game time renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); // set the state parm renderEntity.shaderParms[ SHADERPARM_MODE ] = 1; renderLight.shaderParms[ SHADERPARM_MODE ] = 1; // if the light has a sound, either start the alternate (broken) sound, or stop the sound const char* parm = spawnArgs.GetString( "snd_broken" ); if( refSound.shader || ( parm != NULL && *parm != '\0' ) ) { StopSound( SND_CHANNEL_ANY, false ); const idSoundShader* alternate = refSound.shader ? refSound.shader->GetAltSound() : declManager->FindSound( parm ); if( alternate ) { // start it with no diversity, so the leadin break sound plays refSound.referenceSound->StartSound( alternate, SND_CHANNEL_ANY, 0.0, 0 ); } } parm = spawnArgs.GetString( "mtr_broken" ); if( parm != NULL && *parm != '\0' ) { SetShader( parm ); } UpdateVisuals(); } /* ================ idLight::PresentLightDefChange ================ */ void idLight::PresentLightDefChange() { // let the renderer apply it to the world if( ( lightDefHandle != -1 ) ) { gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight ); } else { lightDefHandle = gameRenderWorld->AddLightDef( &renderLight ); } } /* ================ idLight::PresentModelDefChange ================ */ void idLight::PresentModelDefChange() { if( !renderEntity.hModel || IsHidden() ) { return; } // add to refresh list if( modelDefHandle == -1 ) { modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } else { gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity ); } } /* ================ idLight::Present ================ */ void idLight::Present() { // don't present to the renderer if the entity hasn't changed if( !( thinkFlags & TH_UPDATEVISUALS ) ) { return; } // add the model idEntity::Present(); // current transformation renderLight.axis = localLightAxis * GetPhysics()->GetAxis(); renderLight.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * localLightOrigin; // reference the sound for shader synced effects if( lightParent ) { renderLight.referenceSound = lightParent->GetSoundEmitter(); renderEntity.referenceSound = lightParent->GetSoundEmitter(); } else { renderLight.referenceSound = refSound.referenceSound; renderEntity.referenceSound = refSound.referenceSound; } // update the renderLight and renderEntity to render the light and flare PresentLightDefChange(); PresentModelDefChange(); } /* ================ idLight::Think ================ */ void idLight::Think() { idVec4 color; if( thinkFlags & TH_THINK ) { if( fadeEnd > 0 ) { if( gameLocal.time < fadeEnd ) { color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) ); } else { color = fadeTo; fadeEnd = 0; BecomeInactive( TH_THINK ); } SetColor( color ); } } RunPhysics(); Present(); } /* ================ idLight::ClientThink ================ */ void idLight::ClientThink( const int curTime, const float fraction, const bool predict ) { InterpolatePhysics( fraction ); if( baseColor != nextBaseColor ) { baseColor = Lerp( previousBaseColor, nextBaseColor, fraction ); SetColor( baseColor ); BecomeActive( TH_UPDATEVISUALS ); } Present(); } /* ================ idLight::GetPhysicsToSoundTransform ================ */ bool idLight::GetPhysicsToSoundTransform( idVec3& origin, idMat3& axis ) { origin = localLightOrigin + renderLight.lightCenter; axis = localLightAxis * GetPhysics()->GetAxis(); return true; } /* ================ idLight::FreeLightDef ================ */ void idLight::FreeLightDef() { if( lightDefHandle != -1 ) { gameRenderWorld->FreeLightDef( lightDefHandle ); lightDefHandle = -1; } } /* ================ idLight::SaveState ================ */ void idLight::SaveState( idDict* args ) { int i, c = spawnArgs.GetNumKeyVals(); for( i = 0; i < c; i++ ) { const idKeyValue* pv = spawnArgs.GetKeyVal( i ); if( pv->GetKey().Find( "editor_", false ) >= 0 || pv->GetKey().Find( "parse_", false ) >= 0 ) { continue; } args->Set( pv->GetKey(), pv->GetValue() ); } } /* =============== idLight::ShowEditingDialog =============== */ void idLight::ShowEditingDialog() { } /* ================ idLight::Event_SetShader ================ */ void idLight::Event_SetShader( const char* shadername ) { SetShader( shadername ); } /* ================ idLight::Event_GetLightParm ================ */ void idLight::Event_GetLightParm( int parmnum ) { if( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) { gameLocal.Error( "shader parm index (%d) out of range", parmnum ); return; } idThread::ReturnFloat( renderLight.shaderParms[ parmnum ] ); } /* ================ idLight::Event_SetLightParm ================ */ void idLight::Event_SetLightParm( int parmnum, float value ) { SetLightParm( parmnum, value ); } /* ================ idLight::Event_SetLightParms ================ */ void idLight::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) { SetLightParms( parm0, parm1, parm2, parm3 ); } /* ================ idLight::Event_SetRadiusXYZ ================ */ void idLight::Event_SetRadiusXYZ( float x, float y, float z ) { SetRadiusXYZ( x, y, z ); } /* ================ idLight::Event_SetRadius ================ */ void idLight::Event_SetRadius( float radius ) { SetRadius( radius ); } /* ================ idLight::Event_Hide ================ */ void idLight::Event_Hide() { Hide(); PresentModelDefChange(); Off(); } /* ================ idLight::Event_Show ================ */ void idLight::Event_Show() { Show(); PresentModelDefChange(); On(); } /* ================ idLight::Event_On ================ */ void idLight::Event_On() { On(); } /* ================ idLight::Event_Off ================ */ void idLight::Event_Off() { Off(); } /* ================ idLight::Event_ToggleOnOff ================ */ void idLight::Event_ToggleOnOff( idEntity* activator ) { triggercount++; if( triggercount < count ) { return; } // reset trigger count triggercount = 0; if( breakOnTrigger ) { BecomeBroken( activator ); breakOnTrigger = false; return; } if( !currentLevel ) { On(); } else { currentLevel--; if( !currentLevel ) { Off(); } else { SetLightLevel(); } } } /* ================ idLight::Event_SetSoundHandles set the same sound def handle on all targeted lights ================ */ void idLight::Event_SetSoundHandles() { int i; idEntity* targetEnt; if( !refSound.referenceSound ) { return; } for( i = 0; i < targets.Num(); i++ ) { targetEnt = targets[ i ].GetEntity(); if( targetEnt != NULL && targetEnt->IsType( idLight::Type ) ) { idLight* light = static_cast( targetEnt ); light->lightParent = this; // explicitly delete any sounds on the entity light->FreeSoundEmitter( true ); // manually set the refSound to this light's refSound light->renderEntity.referenceSound = renderEntity.referenceSound; // update the renderEntity to the renderer light->UpdateVisuals(); } } } /* ================ idLight::Event_FadeOut ================ */ void idLight::Event_FadeOut( float time ) { FadeOut( time ); } /* ================ idLight::Event_FadeIn ================ */ void idLight::Event_FadeIn( float time ) { FadeIn( time ); } /* ================ idLight::ClientPredictionThink ================ */ void idLight::ClientPredictionThink() { Think(); } /* ================ idLight::WriteToSnapshot ================ */ void idLight::WriteToSnapshot( idBitMsg& msg ) const { GetPhysics()->WriteToSnapshot( msg ); WriteBindToSnapshot( msg ); msg.WriteByte( currentLevel ); msg.WriteLong( PackColor( baseColor ) ); // msg.WriteBits( lightParent.GetEntityNum(), GENTITYNUM_BITS ); /* // only helps prediction msg.WriteLong( PackColor( fadeFrom ) ); msg.WriteLong( PackColor( fadeTo ) ); msg.WriteLong( fadeStart ); msg.WriteLong( fadeEnd ); */ // FIXME: send renderLight.shader msg.WriteFloat( renderLight.lightRadius[0], 5, 10 ); msg.WriteFloat( renderLight.lightRadius[1], 5, 10 ); msg.WriteFloat( renderLight.lightRadius[2], 5, 10 ); msg.WriteLong( PackColor( idVec4( renderLight.shaderParms[SHADERPARM_RED], renderLight.shaderParms[SHADERPARM_GREEN], renderLight.shaderParms[SHADERPARM_BLUE], renderLight.shaderParms[SHADERPARM_ALPHA] ) ) ); msg.WriteFloat( renderLight.shaderParms[SHADERPARM_TIMESCALE], 5, 10 ); msg.WriteLong( renderLight.shaderParms[SHADERPARM_TIMEOFFSET] ); //msg.WriteByte( renderLight.shaderParms[SHADERPARM_DIVERSITY] ); msg.WriteShort( renderLight.shaderParms[SHADERPARM_MODE] ); WriteColorToSnapshot( msg ); } /* ================ idLight::ReadFromSnapshot ================ */ void idLight::ReadFromSnapshot( const idBitMsg& msg ) { idVec4 shaderColor; int oldCurrentLevel = currentLevel; idVec3 oldBaseColor = baseColor; previousBaseColor = nextBaseColor; GetPhysics()->ReadFromSnapshot( msg ); ReadBindFromSnapshot( msg ); currentLevel = msg.ReadByte(); if( currentLevel != oldCurrentLevel ) { // need to call On/Off for flickering lights to start/stop the sound // while doing it this way rather than through events, the flickering is out of sync between clients // but at least there is no question about saving the event and having them happening globally in the world if( currentLevel ) { On(); } else { Off(); } } UnpackColor( msg.ReadLong(), nextBaseColor ); // lightParentEntityNum = msg.ReadBits( GENTITYNUM_BITS ); /* // only helps prediction UnpackColor( msg.ReadLong(), fadeFrom ); UnpackColor( msg.ReadLong(), fadeTo ); fadeStart = msg.ReadLong(); fadeEnd = msg.ReadLong(); */ // FIXME: read renderLight.shader renderLight.lightRadius[0] = msg.ReadFloat( 5, 10 ); renderLight.lightRadius[1] = msg.ReadFloat( 5, 10 ); renderLight.lightRadius[2] = msg.ReadFloat( 5, 10 ); UnpackColor( msg.ReadLong(), shaderColor ); renderLight.shaderParms[SHADERPARM_RED] = shaderColor[0]; renderLight.shaderParms[SHADERPARM_GREEN] = shaderColor[1]; renderLight.shaderParms[SHADERPARM_BLUE] = shaderColor[2]; renderLight.shaderParms[SHADERPARM_ALPHA] = shaderColor[3]; renderLight.shaderParms[SHADERPARM_TIMESCALE] = msg.ReadFloat( 5, 10 ); renderLight.shaderParms[SHADERPARM_TIMEOFFSET] = msg.ReadLong(); //renderLight.shaderParms[SHADERPARM_DIVERSITY] = msg.ReadFloat(); renderLight.shaderParms[SHADERPARM_MODE] = msg.ReadShort(); ReadColorFromSnapshot( msg ); if( msg.HasChanged() ) { if( ( currentLevel != oldCurrentLevel ) || ( previousBaseColor != nextBaseColor ) ) { SetLightLevel(); } else { PresentLightDefChange(); PresentModelDefChange(); } } } /* ================ idLight::ClientReceiveEvent ================ */ bool idLight::ClientReceiveEvent( int event, int time, const idBitMsg& msg ) { switch( event ) { case EVENT_BECOMEBROKEN: { BecomeBroken( NULL ); return true; } default: { return idEntity::ClientReceiveEvent( event, time, msg ); } } }