diff --git a/sp/src/game/client/c_beamspotlight.cpp b/sp/src/game/client/c_beamspotlight.cpp new file mode 100644 index 00000000000..9c20bd55c51 --- /dev/null +++ b/sp/src/game/client/c_beamspotlight.cpp @@ -0,0 +1,354 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#include "cbase.h" +#include "dlight.h" +#include "iefx.h" + +#include "beam_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CSpotlightTraceCacheEntry +{ +public: + CSpotlightTraceCacheEntry() + { + m_origin.Init(); + m_radius = -1.0f; + } + bool IsValidFor( const Vector &origin ) + { + if ( m_radius > 0 && m_origin.DistToSqr(origin) < 1.0f ) + return true; + return false; + } + void Cache( const Vector &origin, const trace_t &tr ) + { + m_radius = (tr.endpos - origin).Length(); + m_origin = origin; + } + + Vector m_origin; + float m_radius; +}; + +static const int NUM_CACHE_ENTRIES = 64; + +class C_BeamSpotLight : public C_BaseEntity +{ +public: + DECLARE_CLASS( C_BeamSpotLight, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + C_BeamSpotLight(); + ~C_BeamSpotLight(); + + bool ShouldDraw(); + void ClientThink( void ); + void OnDataChanged( DataUpdateType_t updateType ); + void Release( void ); + +private: + + Vector SpotlightCurrentPos(void); + void SpotlightCreate(void); + void SpotlightDestroy(void); + + // Computes render info for a spotlight + void ComputeRenderInfo(); + +private: + + int m_nHaloIndex; + int m_nRotationAxis; + float m_flRotationSpeed; + + + bool m_bSpotlightOn; + bool m_bHasDynamicLight; + + float m_flSpotlightMaxLength; + float m_flSpotlightGoalWidth; + float m_flHDRColorScale; + + Vector m_vSpotlightTargetPos; + Vector m_vSpotlightCurrentPos; + Vector m_vSpotlightDir; + + CHandle m_hSpotlight; + + float m_flSpotlightCurLength; + + float m_flLightScale; + + dlight_t* m_pDynamicLight; + + float m_lastTime; + CSpotlightTraceCacheEntry *m_pCache; +}; + +LINK_ENTITY_TO_CLASS( beam_spotlight, C_BeamSpotLight ); + +IMPLEMENT_CLIENTCLASS_DT( C_BeamSpotLight, DT_BeamSpotlight, CBeamSpotlight ) + RecvPropInt( RECVINFO(m_nHaloIndex) ), + RecvPropBool( RECVINFO(m_bSpotlightOn) ), + RecvPropBool( RECVINFO(m_bHasDynamicLight) ), + RecvPropFloat( RECVINFO(m_flSpotlightMaxLength) ), + RecvPropFloat( RECVINFO(m_flSpotlightGoalWidth) ), + RecvPropFloat( RECVINFO(m_flHDRColorScale) ), + RecvPropInt( RECVINFO(m_nRotationAxis) ), + RecvPropFloat( RECVINFO(m_flRotationSpeed) ), +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +C_BeamSpotLight::C_BeamSpotLight() +: m_vSpotlightTargetPos( vec3_origin ) +, m_vSpotlightCurrentPos( vec3_origin ) +, m_vSpotlightDir( vec3_origin ) +, m_flSpotlightCurLength( 0.0f ) +, m_flLightScale( 100.0f ) +, m_pDynamicLight( NULL ) +, m_lastTime( 0.0f ) +, m_pCache(NULL) +{ +} + +C_BeamSpotLight::~C_BeamSpotLight() +{ + delete[] m_pCache; +} + + +//----------------------------------------------------------------------------- +bool C_BeamSpotLight::ShouldDraw() +{ + return false; +} + +//----------------------------------------------------------------------------- +void C_BeamSpotLight::ClientThink( void ) +{ + float dt = gpGlobals->curtime - m_lastTime; + if ( !m_lastTime ) + { + dt = 0.0f; + } + m_lastTime = gpGlobals->curtime; + + // --------------------------------------------------- + // If I don't have a spotlight attempt to create one + // --------------------------------------------------- + if ( !m_hSpotlight ) + { + if ( m_bSpotlightOn ) + { + // Make the spotlight + SpotlightCreate(); + } + else + { + SetNextClientThink( CLIENT_THINK_NEVER ); + return; + } + } + else if ( !m_bSpotlightOn ) + { + SpotlightDestroy(); + SetNextClientThink( CLIENT_THINK_NEVER ); + return; + } + + // update rotation + if ( m_flRotationSpeed != 0.0f ) + { + QAngle angles = GetAbsAngles(); + angles[m_nRotationAxis] += m_flRotationSpeed * dt; + angles[m_nRotationAxis] = anglemod(angles[m_nRotationAxis]); + if ( !m_pCache ) + { + m_pCache = new CSpotlightTraceCacheEntry[NUM_CACHE_ENTRIES]; + } + + SetAbsAngles( angles ); + } + m_vSpotlightCurrentPos = SpotlightCurrentPos(); + + Assert( m_hSpotlight ); + + m_hSpotlight->SetStartPos( GetAbsOrigin() ); + m_hSpotlight->SetEndPos( m_vSpotlightCurrentPos ); + + // Avoid sudden change in where beam fades out when cross disconinuities + Vector dir = m_vSpotlightCurrentPos - GetAbsOrigin(); + float flBeamLength = VectorNormalize( dir ); + m_flSpotlightCurLength = (0.60*m_flSpotlightCurLength) + (0.4*flBeamLength); + + ComputeRenderInfo(); + + m_hSpotlight->RelinkBeam(); + + //NDebugOverlay::Cross3D(GetAbsOrigin(),Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1); + //NDebugOverlay::Cross3D(m_vSpotlightCurrentPos,Vector(-5,-5,-5),Vector(5,5,5),0,255,0,true,0.1); + //NDebugOverlay::Cross3D(m_vSpotlightTargetPos,Vector(-5,-5,-5),Vector(5,5,5),255,0,0,true,0.1); + + // Do we need to keep updating? + if ( !GetMoveParent() && m_flRotationSpeed == 0 ) + { + // No reason to think again, we're not going to move unless there's a data change + SetNextClientThink( CLIENT_THINK_NEVER ); + } +} + +//----------------------------------------------------------------------------- +void C_BeamSpotLight::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + if ( updateType == DATA_UPDATE_CREATED ) + { + m_flSpotlightCurLength = m_flSpotlightMaxLength; + } + + // On a data change always think again + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +//------------------------------------------------------------------------------ +void C_BeamSpotLight::Release() +{ + SpotlightDestroy(); + BaseClass::Release(); +} + +//------------------------------------------------------------------------------ +void C_BeamSpotLight::SpotlightCreate(void) +{ + m_vSpotlightTargetPos = SpotlightCurrentPos(); + + { + //C_Beam *beam = CBeam::BeamCreate( "sprites/spotlight.vmt", m_flSpotlightGoalWidth ); + C_Beam *beam = C_Beam::BeamCreate( "sprites/glow_test02.vmt", m_flSpotlightGoalWidth ); + // Beam only exists client side + ClientEntityList().AddNonNetworkableEntity( beam ); + m_hSpotlight = beam; + } + + // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore) + m_hSpotlight->SetHDRColorScale( m_flHDRColorScale ); + const color32 c = GetRenderColor(); + m_hSpotlight->SetColor( c.r, c.g, c.b ); + m_hSpotlight->SetHaloTexture(m_nHaloIndex); + m_hSpotlight->SetHaloScale(60); + m_hSpotlight->SetEndWidth(m_flSpotlightGoalWidth); + m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) ); + m_hSpotlight->SetBrightness( 64 ); + m_hSpotlight->SetNoise( 0 ); + + m_hSpotlight->PointsInit( GetAbsOrigin(), m_vSpotlightTargetPos ); +} + +//------------------------------------------------------------------------------ +void C_BeamSpotLight::SpotlightDestroy(void) +{ + if ( m_hSpotlight ) + { + m_hSpotlight->Release(); + m_hSpotlight.Term(); + } +} + +//------------------------------------------------------------------------------ +Vector C_BeamSpotLight::SpotlightCurrentPos(void) +{ + QAngle angles = GetAbsAngles(); + GetVectors( &m_vSpotlightDir, NULL, NULL ); + Vector position = GetAbsOrigin(); + int cacheIndex = -1; + if ( m_pCache ) + { + cacheIndex = int( angles[m_nRotationAxis] * float(NUM_CACHE_ENTRIES) * (1.0f / 360.0f)) & (NUM_CACHE_ENTRIES - 1); + if ( m_pCache[cacheIndex].IsValidFor(GetAbsOrigin()) ) + { + return position + m_vSpotlightDir * m_pCache[cacheIndex].m_radius; + } + } + + + // Get beam end point. Only collide with solid objects, not npcs + trace_t tr; + UTIL_TraceLine( position, position + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + if ( cacheIndex >= 0 ) + { + m_pCache[cacheIndex].Cache(position, tr); + } + + return tr.endpos; +} + +//----------------------------------------------------------------------------- +// Computes render info for a spotlight +//----------------------------------------------------------------------------- +void C_BeamSpotLight::ComputeRenderInfo() +{ + // Fade out spotlight end if past max length. + if ( m_flSpotlightCurLength > 2*m_flSpotlightMaxLength ) + { + SetRenderColorA( 0 ); + m_hSpotlight->SetFadeLength( m_flSpotlightMaxLength ); + } + else if ( m_flSpotlightCurLength > m_flSpotlightMaxLength ) + { + SetRenderColorA( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); + m_hSpotlight->SetFadeLength( m_flSpotlightMaxLength ); + } + else + { + SetRenderColorA( 1.0 ); + m_hSpotlight->SetFadeLength( m_flSpotlightCurLength ); + } + + // Adjust end width to keep beam width constant + float flNewWidth = m_flSpotlightGoalWidth * (m_flSpotlightCurLength / m_flSpotlightMaxLength); + flNewWidth = clamp(flNewWidth, 0, MAX_BEAM_WIDTH ); + m_hSpotlight->SetEndWidth(flNewWidth); + + if ( m_bHasDynamicLight ) + { + // <> - magic number 1.8 depends on sprite size + m_flLightScale = 1.8*flNewWidth; + + if ( m_flLightScale > 0 ) + { + const color32 c = GetRenderColor(); + float a = GetRenderColor().a / 255.0f; + ColorRGBExp32 color; + color.r = c.r * a; + color.g = c.g * a; + color.b = c.b * a; + color.exponent = 0; + if ( color.r == 0 && color.g == 0 && color.b == 0 ) + return; + + // Deal with the environment light + if ( !m_pDynamicLight || (m_pDynamicLight->key != index) ) + { + m_pDynamicLight = effects->CL_AllocDlight( index ); + assert (m_pDynamicLight); + } + + //m_pDynamicLight->flags = DLIGHT_NO_MODEL_ILLUMINATION; + m_pDynamicLight->radius = m_flLightScale*3.0f; + m_pDynamicLight->origin = GetAbsOrigin() + Vector(0,0,5); + m_pDynamicLight->die = gpGlobals->curtime + 0.05f; + m_pDynamicLight->color = color; + } + } +} + diff --git a/sp/src/game/client/client_base.vpc b/sp/src/game/client/client_base.vpc index 9eae51f123d..407f4035efa 100644 --- a/sp/src/game/client/client_base.vpc +++ b/sp/src/game/client/client_base.vpc @@ -228,6 +228,7 @@ $Project $File "c_baseplayer.cpp" $File "c_baseviewmodel.cpp" $File "c_breakableprop.cpp" + $File "c_beamspotlight.cpp" $File "c_colorcorrection.cpp" $File "c_colorcorrectionvolume.cpp" $File "c_dynamiclight.cpp" diff --git a/sp/src/game/server/beamspotlight.cpp b/sp/src/game/server/beamspotlight.cpp new file mode 100644 index 00000000000..9f6c5fbaad3 --- /dev/null +++ b/sp/src/game/server/beamspotlight.cpp @@ -0,0 +1,260 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A special kind of beam effect that traces from its start position to +// its end position and stops if it hits anything. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "EnvLaser.h" +#include "Sprite.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// Spawnflags +enum BeamSpotlightSpawnFlags_t +{ + SF_BEAM_SPOTLIGHT_START_LIGHT_ON = 1, + SF_BEAM_SPOTLIGHT_NO_DYNAMIC_LIGHT = 2, + SF_BEAM_SPOTLIGHT_START_ROTATE_ON = 4, + SF_BEAM_SPOTLIGHT_REVERSE_DIRECTION = 8, + SF_BEAM_SPOTLIGHT_X_AXIS = 16, + SF_BEAM_SPOTLIGHT_Y_AXIS = 32, +}; + + +class CBeamSpotlight : public CBaseEntity +{ + DECLARE_CLASS( CBeamSpotlight, CBaseEntity ); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CBeamSpotlight(); + + void Spawn( void ); + void Precache( void ); + + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + void InputStart( inputdata_t &inputdata ); + void InputStop( inputdata_t &inputdata ); + void InputReverse( inputdata_t &inputdata ); + +protected: + bool KeyValue( const char *szKeyName, const char *szValue ); + +private: + int UpdateTransmitState(); + void RecalcRotation( void ); + + CNetworkVar( int, m_nHaloIndex ); + + CNetworkVar( bool, m_bSpotlightOn ); + CNetworkVar( bool, m_bHasDynamicLight ); + + CNetworkVar( float, m_flSpotlightMaxLength ); + CNetworkVar( float, m_flSpotlightGoalWidth ); + CNetworkVar( float, m_flHDRColorScale ); + CNetworkVar( int, m_nMinDXLevel ); + CNetworkVar( int, m_nRotationAxis ); + CNetworkVar( float, m_flRotationSpeed ); + + float m_flmaxSpeed; + bool m_isRotating; + bool m_isReversed; + +public: + COutputEvent m_OnOn, m_OnOff; ///< output fires when turned on, off +}; + + +LINK_ENTITY_TO_CLASS( beam_spotlight, CBeamSpotlight ); + +BEGIN_DATADESC( CBeamSpotlight ) + DEFINE_FIELD( m_nHaloIndex, FIELD_MODELINDEX ), + DEFINE_FIELD( m_bSpotlightOn, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bHasDynamicLight, FIELD_BOOLEAN ), + DEFINE_FIELD( m_flRotationSpeed, FIELD_FLOAT ), + DEFINE_FIELD( m_isRotating, FIELD_BOOLEAN ), + DEFINE_FIELD( m_isReversed, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nRotationAxis, FIELD_INTEGER ), + + DEFINE_KEYFIELD( m_flmaxSpeed, FIELD_FLOAT, "maxspeed" ), + DEFINE_KEYFIELD( m_flSpotlightMaxLength,FIELD_FLOAT, "SpotlightLength"), + DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), + DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "LightOn", InputTurnOn ), + DEFINE_INPUTFUNC( FIELD_VOID, "LightOff", InputTurnOff ), + DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStart ), + DEFINE_INPUTFUNC( FIELD_VOID, "Stop", InputStop ), + DEFINE_INPUTFUNC( FIELD_VOID, "Reverse", InputReverse ), + DEFINE_OUTPUT( m_OnOn, "OnLightOn" ), + DEFINE_OUTPUT( m_OnOff, "OnLightOff" ), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST(CBeamSpotlight, DT_BeamSpotlight) + SendPropInt( SENDINFO(m_nHaloIndex), 16, SPROP_UNSIGNED ), + SendPropBool( SENDINFO(m_bSpotlightOn) ), + SendPropBool( SENDINFO(m_bHasDynamicLight) ), + SendPropFloat( SENDINFO(m_flSpotlightMaxLength), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flSpotlightGoalWidth), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flHDRColorScale), 0, SPROP_NOSCALE ), + SendPropFloat( SENDINFO(m_flRotationSpeed), 0, SPROP_NOSCALE ), + SendPropInt( SENDINFO(m_nRotationAxis), 2, SPROP_UNSIGNED ), +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBeamSpotlight::CBeamSpotlight() +{ + m_bSpotlightOn = false; + m_bHasDynamicLight = true; + m_flSpotlightMaxLength = 500.0f; + m_flSpotlightGoalWidth = 50.0f; + m_flHDRColorScale = 0.7f; + m_isRotating = false; + m_isReversed = false; + m_flmaxSpeed = 100.0f; + m_flRotationSpeed = 0.0f; + m_nRotationAxis = 0; +} + +bool CBeamSpotlight::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( !Q_stricmp( szKeyName, "SpotlightWidth" ) ) + { + m_flSpotlightGoalWidth = Q_atof(szValue); + if ( m_flSpotlightGoalWidth > MAX_BEAM_WIDTH ) + { + Warning( "Map Bug: %s has SpotLightWidth %f > %f, clamping value\n", + STRING( GetEntityName()), m_flSpotlightGoalWidth.m_Value, (float)MAX_BEAM_WIDTH ); + m_flSpotlightGoalWidth = MIN( MAX_BEAM_WIDTH, m_flSpotlightGoalWidth ); + } + return true; + } + + return BaseClass::KeyValue( szKeyName, szValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBeamSpotlight::Precache( void ) +{ + BaseClass::Precache(); + + // Sprites. + m_nHaloIndex = PrecacheModel("sprites/light_glow03.vmt"); + PrecacheModel( "sprites/glow_test02.vmt" ); +} + +//----------------------------------------------------------------------------- +void CBeamSpotlight::RecalcRotation( void ) +{ + if ( !m_isRotating || m_flmaxSpeed == 0.0f ) + { + m_flRotationSpeed = 0.0f; + return; + } + + // + // Build the axis of rotation based on spawnflags. + // + // Pitch Yaw Roll -> Y Z X + m_nRotationAxis = 1; + if ( HasSpawnFlags(SF_BEAM_SPOTLIGHT_Y_AXIS) ) + { + m_nRotationAxis = 0; + } + else if ( HasSpawnFlags(SF_BEAM_SPOTLIGHT_X_AXIS) ) + { + m_nRotationAxis = 2; + } + + m_flRotationSpeed = m_flmaxSpeed; + if ( m_isReversed ) + { + m_flRotationSpeed *= -1.0f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBeamSpotlight::Spawn( void ) +{ + Precache(); + + UTIL_SetSize( this,vec3_origin,vec3_origin ); + AddSolidFlags( FSOLID_NOT_SOLID ); + SetMoveType( MOVETYPE_NONE ); + AddEFlags( EFL_FORCE_CHECK_TRANSMIT ); + + m_bHasDynamicLight = !HasSpawnFlags( SF_BEAM_SPOTLIGHT_NO_DYNAMIC_LIGHT); + m_bSpotlightOn = HasSpawnFlags( SF_BEAM_SPOTLIGHT_START_LIGHT_ON ); + m_isRotating = HasSpawnFlags( SF_BEAM_SPOTLIGHT_START_ROTATE_ON ); + m_isReversed = HasSpawnFlags( SF_BEAM_SPOTLIGHT_REVERSE_DIRECTION ); + + RecalcRotation(); +} + +//----------------------------------------------------------------------------- +void CBeamSpotlight::InputTurnOn( inputdata_t &inputdata ) +{ + if ( !m_bSpotlightOn ) + { + m_bSpotlightOn = true; + } +} + + +//----------------------------------------------------------------------------- +void CBeamSpotlight::InputTurnOff( inputdata_t &inputdata ) +{ + if ( m_bSpotlightOn ) + { + m_bSpotlightOn = false; + } +} + +//----------------------------------------------------------------------------- +void CBeamSpotlight::InputStart( inputdata_t &inputdata ) +{ + if ( !m_isRotating ) + { + m_isRotating = true; + RecalcRotation(); + } +} + +//----------------------------------------------------------------------------- +void CBeamSpotlight::InputStop( inputdata_t &inputdata ) +{ + if ( m_isRotating ) + { + m_isRotating = false; + RecalcRotation(); + } +} + +//----------------------------------------------------------------------------- +void CBeamSpotlight::InputReverse( inputdata_t &inputdata ) +{ + m_isReversed = !m_isReversed; + RecalcRotation(); +} + +//------------------------------------------------------------------------------------- +// Purpose : Send even though we don't have a model so spotlight gets proper position +//------------------------------------------------------------------------------------- +int CBeamSpotlight::UpdateTransmitState() +{ + return SetTransmitState( FL_EDICT_PVSCHECK ); +} + diff --git a/sp/src/game/server/server_base.vpc b/sp/src/game/server/server_base.vpc index 64558734d26..f1a92be20c8 100644 --- a/sp/src/game/server/server_base.vpc +++ b/sp/src/game/server/server_base.vpc @@ -264,6 +264,7 @@ $Project $File "$SRCDIR\game\shared\baseviewmodel_shared.h" $File "$SRCDIR\game\shared\beam_shared.cpp" $File "$SRCDIR\game\shared\beam_shared.h" + $File "beamspotlight.cpp" $File "bitstring.cpp" $File "bitstring.h" $File "bmodels.cpp"