diff --git a/neo/d3xp/EnvironmentProbe.cpp b/neo/d3xp/EnvironmentProbe.cpp
new file mode 100644
index 00000000..c524a608
--- /dev/null
+++ b/neo/d3xp/EnvironmentProbe.cpp
@@ -0,0 +1,940 @@
+/*
+===========================================================================
+
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+Copyright (C) 2015 Robert Beckebans
+
+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_Envprobe_GetEnvprobeParm( "getEnvprobeParm", "d", 'f' );
+const idEventDef EV_Envprobe_SetEnvprobeParm( "setEnvprobeParm", "df" );
+const idEventDef EV_Envprobe_SetEnvprobeParms( "setEnvprobeParms", "ffff" );
+//const idEventDef EV_Envprobe_SetRadiusXYZ( "setRadiusXYZ", "fff" );
+//const idEventDef EV_Envprobe_SetRadius( "setRadius", "f" );
+const idEventDef EV_Envprobe_On( "On", NULL );
+const idEventDef EV_Envprobe_Off( "Off", NULL );
+const idEventDef EV_Envprobe_FadeOut( "fadeOutEnvprobe", "f" );
+const idEventDef EV_Envprobe_FadeIn( "fadeInEnvprobe", "f" );
+
+CLASS_DECLARATION( idEntity, EnvironmentProbe )
+EVENT( EV_Envprobe_GetEnvprobeParm, EnvironmentProbe::Event_GetEnvprobeParm )
+EVENT( EV_Envprobe_SetEnvprobeParm, EnvironmentProbe::Event_SetEnvprobeParm )
+EVENT( EV_Envprobe_SetEnvprobeParms, EnvironmentProbe::Event_SetEnvprobeParms )
+//EVENT( EV_Envprobe_SetRadiusXYZ, EnvironmentProbe::Event_SetRadiusXYZ )
+//EVENT( EV_Envprobe_SetRadius, EnvironmentProbe::Event_SetRadius )
+EVENT( EV_Hide, EnvironmentProbe::Event_Hide )
+EVENT( EV_Show, EnvironmentProbe::Event_Show )
+EVENT( EV_Envprobe_On, EnvironmentProbe::Event_On )
+EVENT( EV_Envprobe_Off, EnvironmentProbe::Event_Off )
+EVENT( EV_Activate, EnvironmentProbe::Event_ToggleOnOff )
+//EVENT( EV_PostSpawn, EnvironmentProbe::Event_SetSoundHandles )
+EVENT( EV_Envprobe_FadeOut, EnvironmentProbe::Event_FadeOut )
+EVENT( EV_Envprobe_FadeIn, EnvironmentProbe::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::ParseSpawnArgsToRenderEnvprobe( const idDict* args, renderEnvironmentProbe_t* renderEnvprobe )
+{
+ idVec3 color;
+
+ memset( renderEnvprobe, 0, sizeof( *renderEnvprobe ) );
+
+ if( !args->GetVector( "light_origin", "", renderEnvprobe->origin ) )
+ {
+ args->GetVector( "origin", "", renderEnvprobe->origin );
+ }
+
+ // check for other attributes
+ args->GetVector( "_color", "1 1 1", color );
+ renderEnvprobe->shaderParms[ SHADERPARM_RED ] = color[0];
+ renderEnvprobe->shaderParms[ SHADERPARM_GREEN ] = color[1];
+ renderEnvprobe->shaderParms[ SHADERPARM_BLUE ] = color[2];
+ args->GetFloat( "shaderParm3", "1", renderEnvprobe->shaderParms[ SHADERPARM_TIMESCALE ] );
+ if( !args->GetFloat( "shaderParm4", "0", renderEnvprobe->shaderParms[ SHADERPARM_TIMEOFFSET ] ) )
+ {
+ // offset the start time of the shader to sync it to the game time
+ renderEnvprobe->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
+ }
+
+ args->GetFloat( "shaderParm5", "0", renderEnvprobe->shaderParms[5] );
+ args->GetFloat( "shaderParm6", "0", renderEnvprobe->shaderParms[6] );
+ args->GetFloat( "shaderParm7", "0", renderEnvprobe->shaderParms[ SHADERPARM_MODE ] );
+}
+
+/*
+================
+EnvironmentProbe::UpdateChangeableSpawnArgs
+================
+*/
+void EnvironmentProbe::UpdateChangeableSpawnArgs( const idDict* source )
+{
+ idEntity::UpdateChangeableSpawnArgs( source );
+
+ if( source )
+ {
+ source->Print();
+ }
+
+ gameEdit->ParseSpawnArgsToRenderEnvprobe( source ? source : &spawnArgs, &renderEnvprobe );
+
+ UpdateVisuals();
+}
+
+/*
+================
+EnvironmentProbe::EnvironmentProbe
+================
+*/
+EnvironmentProbe::EnvironmentProbe():
+ previousBaseColor( vec3_zero ) ,
+ nextBaseColor( vec3_zero )
+{
+ memset( &renderEnvprobe, 0, sizeof( renderEnvprobe ) );
+ localEnvprobeOrigin = vec3_zero;
+ localEnvprobeAxis = mat3_identity;
+ envprobeDefHandle = -1;
+ levels = 0;
+ currentLevel = 0;
+ baseColor = vec3_zero;
+ count = 0;
+ triggercount = 0;
+ lightParent = NULL;
+ fadeFrom.Set( 1, 1, 1, 1 );
+ fadeTo.Set( 1, 1, 1, 1 );
+ fadeStart = 0;
+ fadeEnd = 0;
+}
+
+/*
+================
+EnvironmentProbe::~idLight
+================
+*/
+EnvironmentProbe::~EnvironmentProbe()
+{
+ if( envprobeDefHandle != -1 )
+ {
+ gameRenderWorld->FreeEnvprobeDef( envprobeDefHandle );
+ }
+}
+
+/*
+================
+EnvironmentProbe::Save
+
+archives object for save game file
+================
+*/
+void EnvironmentProbe::Save( idSaveGame* savefile ) const
+{
+ savefile->WriteRenderEnvprobe( renderEnvprobe );
+
+ savefile->WriteVec3( localEnvprobeOrigin );
+ savefile->WriteMat3( localEnvprobeAxis );
+
+ savefile->WriteInt( levels );
+ savefile->WriteInt( currentLevel );
+
+ savefile->WriteVec3( baseColor );
+ savefile->WriteInt( count );
+ savefile->WriteInt( triggercount );
+ savefile->WriteObject( lightParent );
+
+ savefile->WriteVec4( fadeFrom );
+ savefile->WriteVec4( fadeTo );
+ savefile->WriteInt( fadeStart );
+ savefile->WriteInt( fadeEnd );
+}
+
+/*
+================
+EnvironmentProbe::Restore
+
+unarchives object from save game file
+================
+*/
+void EnvironmentProbe::Restore( idRestoreGame* savefile )
+{
+ savefile->ReadRenderEnvprobe( renderEnvprobe );
+
+ savefile->ReadVec3( localEnvprobeOrigin );
+ savefile->ReadMat3( localEnvprobeAxis );
+
+ savefile->ReadInt( levels );
+ savefile->ReadInt( currentLevel );
+
+ savefile->ReadVec3( baseColor );
+ savefile->ReadInt( count );
+ savefile->ReadInt( triggercount );
+ savefile->ReadObject( reinterpret_cast( lightParent ) );
+
+ savefile->ReadVec4( fadeFrom );
+ savefile->ReadVec4( fadeTo );
+ savefile->ReadInt( fadeStart );
+ savefile->ReadInt( fadeEnd );
+
+ envprobeDefHandle = -1;
+
+ SetLightLevel();
+}
+
+/*
+================
+EnvironmentProbe::Spawn
+================
+*/
+void EnvironmentProbe::Spawn()
+{
+ bool start_off;
+
+ // do the parsing the same way dmap and the editor do
+ gameEdit->ParseSpawnArgsToRenderEnvprobe( &spawnArgs, &renderEnvprobe );
+
+ // we need the origin and axis relative to the physics origin/axis
+ localEnvprobeOrigin = ( renderEnvprobe.origin - GetPhysics()->GetOrigin() ) * GetPhysics()->GetAxis().Transpose();
+ localEnvprobeAxis = /*renderEnvprobe.axis * */ GetPhysics()->GetAxis().Transpose();
+
+ // set the base color from the shader parms
+ baseColor.Set( renderEnvprobe.shaderParms[ SHADERPARM_RED ], renderEnvprobe.shaderParms[ SHADERPARM_GREEN ], renderEnvprobe.shaderParms[ SHADERPARM_BLUE ] );
+ previousBaseColor.Set( renderEnvprobe.shaderParms[ SHADERPARM_RED ], renderEnvprobe.shaderParms[ SHADERPARM_GREEN ], renderEnvprobe.shaderParms[ SHADERPARM_BLUE ] );
+ nextBaseColor.Set( renderEnvprobe.shaderParms[ SHADERPARM_RED ], renderEnvprobe.shaderParms[ SHADERPARM_GREEN ], renderEnvprobe.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() );
+ }
+
+ // game specific functionality, not mirrored in
+ // editor or dmap light parsing
+
+ envprobeDefHandle = -1; // no static version yet
+
+ 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.GetInt( "count", "1", count );
+
+ triggercount = 0;
+
+ fadeFrom.Set( 1, 1, 1, 1 );
+ fadeTo.Set( 1, 1, 1, 1 );
+ fadeStart = 0;
+ fadeEnd = 0;
+
+ PostEventMS( &EV_PostSpawn, 0 );
+
+ UpdateVisuals();
+}
+
+/*
+================
+EnvironmentProbe::SetLightLevel
+================
+*/
+void EnvironmentProbe::SetLightLevel()
+{
+ idVec3 color;
+ float intensity;
+
+ intensity = ( float )currentLevel / ( float )levels;
+ color = baseColor * intensity;
+ renderEnvprobe.shaderParms[ SHADERPARM_RED ] = color[ 0 ];
+ renderEnvprobe.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ];
+ renderEnvprobe.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ];
+
+ PresentEnvprobeDefChange();
+}
+
+/*
+================
+EnvironmentProbe::GetColor
+================
+*/
+void EnvironmentProbe::GetColor( idVec3& out ) const
+{
+ out[ 0 ] = renderEnvprobe.shaderParms[ SHADERPARM_RED ];
+ out[ 1 ] = renderEnvprobe.shaderParms[ SHADERPARM_GREEN ];
+ out[ 2 ] = renderEnvprobe.shaderParms[ SHADERPARM_BLUE ];
+}
+
+/*
+================
+EnvironmentProbe::GetColor
+================
+*/
+void EnvironmentProbe::GetColor( idVec4& out ) const
+{
+ out[ 0 ] = renderEnvprobe.shaderParms[ SHADERPARM_RED ];
+ out[ 1 ] = renderEnvprobe.shaderParms[ SHADERPARM_GREEN ];
+ out[ 2 ] = renderEnvprobe.shaderParms[ SHADERPARM_BLUE ];
+ out[ 3 ] = renderEnvprobe.shaderParms[ SHADERPARM_ALPHA ];
+}
+
+/*
+================
+EnvironmentProbe::SetColor
+================
+*/
+void EnvironmentProbe::SetColor( float red, float green, float blue )
+{
+ baseColor.Set( red, green, blue );
+ SetLightLevel();
+}
+
+/*
+================
+EnvironmentProbe::SetColor
+================
+*/
+void EnvironmentProbe::SetColor( const idVec4& color )
+{
+ baseColor = color.ToVec3();
+ renderEnvprobe.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ];
+ SetLightLevel();
+}
+
+/*
+================
+EnvironmentProbe::SetColor
+================
+*/
+void EnvironmentProbe::SetColor( const idVec3& color )
+{
+ baseColor = color;
+ SetLightLevel();
+}
+
+/*
+================
+EnvironmentProbe::SetEnvprobeParm
+================
+*/
+void EnvironmentProbe::SetEnvprobeParm( int parmnum, float value )
+{
+ if( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) )
+ {
+ gameLocal.Error( "shader parm index (%d) out of range", parmnum );
+ return;
+ }
+
+ renderEnvprobe.shaderParms[ parmnum ] = value;
+ PresentEnvprobeDefChange();
+}
+
+/*
+================
+EnvironmentProbe::SetEnvprobeParms
+================
+*/
+void EnvironmentProbe::SetEnvprobeParms( float parm0, float parm1, float parm2, float parm3 )
+{
+ renderEnvprobe.shaderParms[ SHADERPARM_RED ] = parm0;
+ renderEnvprobe.shaderParms[ SHADERPARM_GREEN ] = parm1;
+ renderEnvprobe.shaderParms[ SHADERPARM_BLUE ] = parm2;
+ renderEnvprobe.shaderParms[ SHADERPARM_ALPHA ] = parm3;
+ PresentEnvprobeDefChange();
+}
+
+/*
+================
+EnvironmentProbe::On
+================
+*/
+void EnvironmentProbe::On()
+{
+ currentLevel = levels;
+ // offset the start time of the shader to sync it to the game time
+ renderEnvprobe.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
+
+ SetLightLevel();
+ BecomeActive( TH_UPDATEVISUALS );
+}
+
+/*
+================
+EnvironmentProbe::Off
+================
+*/
+void EnvironmentProbe::Off()
+{
+ currentLevel = 0;
+
+ SetLightLevel();
+ BecomeActive( TH_UPDATEVISUALS );
+}
+
+/*
+================
+EnvironmentProbe::Fade
+================
+*/
+void EnvironmentProbe::Fade( const idVec4& to, float fadeTime )
+{
+ GetColor( fadeFrom );
+ fadeTo = to;
+ fadeStart = gameLocal.time;
+ fadeEnd = gameLocal.time + SEC2MS( fadeTime );
+ BecomeActive( TH_THINK );
+}
+
+/*
+================
+EnvironmentProbe::FadeOut
+================
+*/
+void EnvironmentProbe::FadeOut( float time )
+{
+ Fade( colorBlack, time );
+}
+
+/*
+================
+EnvironmentProbe::FadeIn
+================
+*/
+void EnvironmentProbe::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 );
+}
+
+/*
+================
+EnvironmentProbe::PresentEnvprobeDefChange
+================
+*/
+void EnvironmentProbe::PresentEnvprobeDefChange()
+{
+ // let the renderer apply it to the world
+ if( ( envprobeDefHandle != -1 ) )
+ {
+ gameRenderWorld->UpdateEnvprobeDef( envprobeDefHandle, &renderEnvprobe );
+ }
+ else
+ {
+ envprobeDefHandle = gameRenderWorld->AddEnvprobeDef( &renderEnvprobe );
+ }
+}
+
+/*
+================
+EnvironmentProbe::Present
+================
+*/
+void EnvironmentProbe::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
+// renderEnvprobe.axis = localEnvprobeAxis * GetPhysics()->GetAxis();
+ renderEnvprobe.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * localEnvprobeOrigin;
+
+ // reference the sound for shader synced effects
+ // FIXME TODO?
+ /*
+ 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
+ PresentEnvprobeDefChange();
+}
+
+/*
+================
+EnvironmentProbe::Think
+================
+*/
+void EnvironmentProbe::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();
+}
+
+/*
+================
+EnvironmentProbe::ClientThink
+================
+*/
+void EnvironmentProbe::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();
+}
+
+/*
+================
+EnvironmentProbe::GetPhysicsToSoundTransform
+================
+*/
+bool EnvironmentProbe::GetPhysicsToSoundTransform( idVec3& origin, idMat3& axis )
+{
+ //origin = localEnvprobeOrigin + renderEnvprobe.lightCenter;
+ //axis = localLightAxis * GetPhysics()->GetAxis();
+ //return true;
+
+ return false;
+}
+
+/*
+================
+EnvironmentProbe::FreeEnvprobeDef
+================
+*/
+void EnvironmentProbe::FreeEnvprobeDef()
+{
+ if( envprobeDefHandle != -1 )
+ {
+ gameRenderWorld->FreeEnvprobeDef( envprobeDefHandle );
+ envprobeDefHandle = -1;
+ }
+}
+
+/*
+================
+EnvironmentProbe::SaveState
+================
+*/
+void EnvironmentProbe::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() );
+ }
+}
+
+/*
+===============
+EnvironmentProbe::ShowEditingDialog
+===============
+*/
+void EnvironmentProbe::ShowEditingDialog()
+{
+}
+
+/*
+================
+EnvironmentProbe::Event_GetEnvprobeParm
+================
+*/
+void EnvironmentProbe::Event_GetEnvprobeParm( int parmnum )
+{
+ if( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) )
+ {
+ gameLocal.Error( "shader parm index (%d) out of range", parmnum );
+ return;
+ }
+
+ idThread::ReturnFloat( renderEnvprobe.shaderParms[ parmnum ] );
+}
+
+/*
+================
+EnvironmentProbe::Event_SetEnvprobeParm
+================
+*/
+void EnvironmentProbe::Event_SetEnvprobeParm( int parmnum, float value )
+{
+ SetEnvprobeParm( parmnum, value );
+}
+
+/*
+================
+EnvironmentProbe::Event_SetEnvprobetParms
+================
+*/
+void EnvironmentProbe::Event_SetEnvprobeParms( float parm0, float parm1, float parm2, float parm3 )
+{
+ SetEnvprobeParms( parm0, parm1, parm2, parm3 );
+}
+
+/*
+================
+EnvironmentProbe::Event_Hide
+================
+*/
+void EnvironmentProbe::Event_Hide()
+{
+ Hide();
+ Off();
+}
+
+/*
+================
+EnvironmentProbe::Event_Show
+================
+*/
+void EnvironmentProbe::Event_Show()
+{
+ Show();
+ On();
+}
+
+/*
+================
+EnvironmentProbe::Event_On
+================
+*/
+void EnvironmentProbe::Event_On()
+{
+ On();
+}
+
+/*
+================
+EnvironmentProbe::Event_Off
+================
+*/
+void EnvironmentProbe::Event_Off()
+{
+ Off();
+}
+
+/*
+================
+EnvironmentProbe::Event_ToggleOnOff
+================
+*/
+void EnvironmentProbe::Event_ToggleOnOff( idEntity* activator )
+{
+ triggercount++;
+ if( triggercount < count )
+ {
+ return;
+ }
+
+ // reset trigger count
+ triggercount = 0;
+
+ if( !currentLevel )
+ {
+ On();
+ }
+ else
+ {
+ currentLevel--;
+ if( !currentLevel )
+ {
+ Off();
+ }
+ else
+ {
+ SetLightLevel();
+ }
+ }
+}
+
+/*
+================
+EnvironmentProbe::Event_SetSoundHandles
+
+ set the same sound def handle on all targeted lights
+================
+*/
+/*
+void EnvironmentProbe::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( EnvironmentProbe::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();
+ }
+ }
+}
+*/
+
+/*
+================
+EnvironmentProbe::Event_FadeOut
+================
+*/
+void EnvironmentProbe::Event_FadeOut( float time )
+{
+ FadeOut( time );
+}
+
+/*
+================
+EnvironmentProbe::Event_FadeIn
+================
+*/
+void EnvironmentProbe::Event_FadeIn( float time )
+{
+ FadeIn( time );
+}
+
+/*
+================
+EnvironmentProbe::ClientPredictionThink
+================
+*/
+void EnvironmentProbe::ClientPredictionThink()
+{
+ Think();
+}
+
+/*
+================
+EnvironmentProbe::WriteToSnapshot
+================
+*/
+void EnvironmentProbe::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( renderEnvprobe.lightRadius[0], 5, 10 );
+ //msg.WriteFloat( renderEnvprobe.lightRadius[1], 5, 10 );
+ //msg.WriteFloat( renderEnvprobe.lightRadius[2], 5, 10 );
+
+ msg.WriteLong( PackColor( idVec4( renderEnvprobe.shaderParms[SHADERPARM_RED],
+ renderEnvprobe.shaderParms[SHADERPARM_GREEN],
+ renderEnvprobe.shaderParms[SHADERPARM_BLUE],
+ renderEnvprobe.shaderParms[SHADERPARM_ALPHA] ) ) );
+
+ msg.WriteFloat( renderEnvprobe.shaderParms[SHADERPARM_TIMESCALE], 5, 10 );
+ msg.WriteLong( renderEnvprobe.shaderParms[SHADERPARM_TIMEOFFSET] );
+ msg.WriteShort( renderEnvprobe.shaderParms[SHADERPARM_MODE] );
+
+ WriteColorToSnapshot( msg );
+}
+
+/*
+================
+EnvironmentProbe::ReadFromSnapshot
+================
+*/
+void EnvironmentProbe::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 );
+ renderEnvprobe.shaderParms[SHADERPARM_RED] = shaderColor[0];
+ renderEnvprobe.shaderParms[SHADERPARM_GREEN] = shaderColor[1];
+ renderEnvprobe.shaderParms[SHADERPARM_BLUE] = shaderColor[2];
+ renderEnvprobe.shaderParms[SHADERPARM_ALPHA] = shaderColor[3];
+
+ renderEnvprobe.shaderParms[SHADERPARM_TIMESCALE] = msg.ReadFloat( 5, 10 );
+ renderEnvprobe.shaderParms[SHADERPARM_TIMEOFFSET] = msg.ReadLong();
+ renderEnvprobe.shaderParms[SHADERPARM_MODE] = msg.ReadShort();
+
+ ReadColorFromSnapshot( msg );
+
+ if( msg.HasChanged() )
+ {
+ if( ( currentLevel != oldCurrentLevel ) || ( previousBaseColor != nextBaseColor ) )
+ {
+ SetLightLevel();
+ }
+ else
+ {
+ PresentEnvprobeDefChange();
+ }
+ }
+}
+
+/*
+================
+EnvironmentProbe::ClientReceiveEvent
+================
+*/
+/*
+bool EnvironmentProbe::ClientReceiveEvent( int event, int time, const idBitMsg& msg )
+{
+
+ switch( event )
+ {
+ case EVENT_BECOMEBROKEN:
+ {
+ BecomeBroken( NULL );
+ return true;
+ }
+ default:
+ {
+ return idEntity::ClientReceiveEvent( event, time, msg );
+ }
+ }
+}
+*/
\ No newline at end of file
diff --git a/neo/d3xp/EnvironmentProbe.h b/neo/d3xp/EnvironmentProbe.h
new file mode 100644
index 00000000..ab412edd
--- /dev/null
+++ b/neo/d3xp/EnvironmentProbe.h
@@ -0,0 +1,143 @@
+/*
+===========================================================================
+
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+Copyright (C) 2015 Robert Beckebans
+
+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.
+
+===========================================================================
+*/
+
+#ifndef __GAME_ENVIRONMENTPROBE_H__
+#define __GAME_ENVIRONMENTPROBE_H__
+
+/*
+===============================================================================
+
+ Environment probe for Image Based Lighting (part of PBR).
+
+===============================================================================
+*/
+
+class EnvironmentProbe : public idEntity
+{
+public:
+ CLASS_PROTOTYPE( EnvironmentProbe );
+
+ EnvironmentProbe();
+ ~EnvironmentProbe();
+
+ void Spawn();
+
+ void Save( idSaveGame* savefile ) const; // archives object for save game file
+ void Restore( idRestoreGame* savefile ); // unarchives object from save game file
+
+ virtual void UpdateChangeableSpawnArgs( const idDict* source );
+ virtual void Think();
+ virtual void ClientThink( const int curTime, const float fraction, const bool predict );
+ virtual void FreeEnvprobeDef();
+ virtual bool GetPhysicsToSoundTransform( idVec3& origin, idMat3& axis );
+ void Present();
+
+ void SaveState( idDict* args );
+ virtual void SetColor( float red, float green, float blue );
+ virtual void SetColor( const idVec4& color );
+ void SetColor( const idVec3& color );
+ virtual void GetColor( idVec3& out ) const;
+ virtual void GetColor( idVec4& out ) const;
+ const idVec3& GetBaseColor() const
+ {
+ return baseColor;
+ }
+ void SetEnvprobeParm( int parmnum, float value );
+ void SetEnvprobeParms( float parm0, float parm1, float parm2, float parm3 );
+ void On();
+ void Off();
+ void Fade( const idVec4& to, float fadeTime );
+ void FadeOut( float time );
+ void FadeIn( float time );
+
+ qhandle_t GetEnvprobeDefHandle() const
+ {
+ return envprobeDefHandle;
+ }
+
+ void SetEnvprobeParent( idEntity* lparent )
+ {
+ lightParent = lparent;
+ }
+
+ void SetLightLevel();
+
+ virtual void ShowEditingDialog();
+
+ enum
+ {
+ EVENT_BECOMEBROKEN = idEntity::EVENT_MAXEVENTS,
+ EVENT_MAXEVENTS
+ };
+
+ virtual void ClientPredictionThink();
+ virtual void WriteToSnapshot( idBitMsg& msg ) const;
+ virtual void ReadFromSnapshot( const idBitMsg& msg );
+// virtual bool ClientReceiveEvent( int event, int time, const idBitMsg& msg );
+
+private:
+ renderEnvironmentProbe_t renderEnvprobe; // envprobe presented to the renderer
+ idVec3 localEnvprobeOrigin; // light origin relative to the physics origin
+ idMat3 localEnvprobeAxis; // light axis relative to physics axis
+ qhandle_t envprobeDefHandle; // handle to renderer light def
+ int levels;
+ int currentLevel;
+ idVec3 baseColor;
+
+ // Colors used for client-side interpolation.
+ idVec3 previousBaseColor;
+ idVec3 nextBaseColor;
+
+ int count;
+ int triggercount;
+ idEntity* lightParent;
+ idVec4 fadeFrom;
+ idVec4 fadeTo;
+ int fadeStart;
+ int fadeEnd;
+
+private:
+ void PresentEnvprobeDefChange();
+
+ void Event_GetEnvprobeParm( int parmnum );
+ void Event_SetEnvprobeParm( int parmnum, float value );
+ void Event_SetEnvprobeParms( float parm0, float parm1, float parm2, float parm3 );
+ void Event_SetRadiusXYZ( float x, float y, float z );
+ void Event_SetRadius( float radius );
+ void Event_Hide();
+ void Event_Show();
+ void Event_On();
+ void Event_Off();
+ void Event_ToggleOnOff( idEntity* activator );
+ void Event_SetSoundHandles();
+ void Event_FadeOut( float time );
+ void Event_FadeIn( float time );
+};
+
+#endif /* !__GAME_ENVIRONMENTPROBE_H__ */
diff --git a/neo/d3xp/Game.h b/neo/d3xp/Game.h
index d222d901..d3701d14 100644
--- a/neo/d3xp/Game.h
+++ b/neo/d3xp/Game.h
@@ -240,6 +240,7 @@ public:
// These are the canonical idDict to parameter parsing routines used by both the game and tools.
virtual void ParseSpawnArgsToRenderLight( const idDict* args, renderLight_t* renderLight );
virtual void ParseSpawnArgsToRenderEntity( const idDict* args, renderEntity_t* renderEntity );
+ virtual void ParseSpawnArgsToRenderEnvprobe( const idDict* args, renderEnvironmentProbe_t* renderEnvprobe ); // RB
virtual void ParseSpawnArgsToRefSound( const idDict* args, refSound_t* refSound );
// Animation system calls for non-game based skeletal rendering.
diff --git a/neo/d3xp/Game_local.h b/neo/d3xp/Game_local.h
index f04cb1f0..e8d61eef 100644
--- a/neo/d3xp/Game_local.h
+++ b/neo/d3xp/Game_local.h
@@ -904,6 +904,7 @@ const int CINEMATIC_SKIP_DELAY = SEC2MS( 2.0f );
#include "Projectile.h"
#include "Weapon.h"
#include "Light.h"
+#include "EnvironmentProbe.h"
#include "WorldSpawn.h"
#include "Item.h"
#include "PlayerView.h"
diff --git a/neo/d3xp/gamesys/SaveGame.cpp b/neo/d3xp/gamesys/SaveGame.cpp
index a2519110..c5536cd7 100644
--- a/neo/d3xp/gamesys/SaveGame.cpp
+++ b/neo/d3xp/gamesys/SaveGame.cpp
@@ -731,6 +731,21 @@ void idSaveGame::WriteRenderLight( const renderLight_t& renderLight )
}
}
+// RB begin
+void idSaveGame::WriteRenderEnvprobe( const renderEnvironmentProbe_t& renderEnvprobe )
+{
+ WriteVec3( renderEnvprobe.origin );
+
+ WriteInt( renderEnvprobe.suppressEnvprobeInViewID );
+ WriteInt( renderEnvprobe.allowEnvprobeInViewID );
+
+ for( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ )
+ {
+ WriteFloat( renderEnvprobe.shaderParms[ i ] );
+ }
+}
+// Rb end
+
/*
================
idSaveGame::WriteRefSound
@@ -1629,6 +1644,21 @@ void idRestoreGame::ReadRenderLight( renderLight_t& renderLight )
renderLight.referenceSound = gameSoundWorld->EmitterForIndex( index );
}
+// RB begin
+void idRestoreGame::ReadRenderEnvprobe( renderEnvironmentProbe_t& renderEnvprobe )
+{
+ ReadVec3( renderEnvprobe.origin );
+
+ ReadInt( renderEnvprobe.suppressEnvprobeInViewID );
+ ReadInt( renderEnvprobe.allowEnvprobeInViewID );
+
+ for( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ )
+ {
+ ReadFloat( renderEnvprobe.shaderParms[ i ] );
+ }
+}
+// RB end
+
/*
================
idRestoreGame::ReadRefSound
diff --git a/neo/d3xp/gamesys/SaveGame.h b/neo/d3xp/gamesys/SaveGame.h
index 1d872766..fc8e7493 100644
--- a/neo/d3xp/gamesys/SaveGame.h
+++ b/neo/d3xp/gamesys/SaveGame.h
@@ -3,6 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+Copyright (C) 2015 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@@ -82,6 +83,7 @@ public:
void WriteUserInterface( const idUserInterface* ui, bool unique );
void WriteRenderEntity( const renderEntity_t& renderEntity );
void WriteRenderLight( const renderLight_t& renderLight );
+ void WriteRenderEnvprobe( const renderEnvironmentProbe_t& renderEnvprobe ); // RB
void WriteRefSound( const refSound_t& refSound );
void WriteRenderView( const renderView_t& view );
void WriteUsercmd( const usercmd_t& usercmd );
@@ -169,6 +171,7 @@ public:
void ReadUserInterface( idUserInterface*& ui );
void ReadRenderEntity( renderEntity_t& renderEntity );
void ReadRenderLight( renderLight_t& renderLight );
+ void ReadRenderEnvprobe( renderEnvironmentProbe_t& renderEnvprobe ); // RB
void ReadRefSound( refSound_t& refSound );
void ReadRenderView( renderView_t& view );
void ReadUsercmd( usercmd_t& usercmd );
diff --git a/neo/renderer/RenderWorld.h b/neo/renderer/RenderWorld.h
index 3eaefd63..b4ffd324 100644
--- a/neo/renderer/RenderWorld.h
+++ b/neo/renderer/RenderWorld.h
@@ -221,6 +221,7 @@ typedef struct renderLight_s
typedef struct
{
idVec3 origin;
+ float shaderParms[MAX_ENTITY_SHADER_PARMS];
// if non-zero, the environment probe will not show up in the specific view,
// which may be used if we want to have slightly different muzzle
diff --git a/neo/renderer/RenderWorld_load.cpp b/neo/renderer/RenderWorld_load.cpp
index 0d80c853..05bed311 100644
--- a/neo/renderer/RenderWorld_load.cpp
+++ b/neo/renderer/RenderWorld_load.cpp
@@ -418,10 +418,15 @@ void idRenderWorldLocal::SetupAreaRefs()
for( int i = 0; i < numPortalAreas; i++ )
{
portalAreas[i].areaNum = i;
+
portalAreas[i].lightRefs.areaNext =
portalAreas[i].lightRefs.areaPrev = &portalAreas[i].lightRefs;
+
portalAreas[i].entityRefs.areaNext =
portalAreas[i].entityRefs.areaPrev = &portalAreas[i].entityRefs;
+
+ portalAreas[i].envprobeRefs.areaNext =
+ portalAreas[i].envprobeRefs.areaPrev = &portalAreas[i].envprobeRefs;
}
}