//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #include "cbase.h" #include "beam_shared.h" #include "spotlightend.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // Spawnflags #define SF_SPOTLIGHT_START_LIGHT_ON 0x1 #define SF_SPOTLIGHT_NO_DYNAMIC_LIGHT 0x2 //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CPointSpotlight : public CPointEntity { DECLARE_CLASS( CPointSpotlight, CPointEntity ); public: DECLARE_DATADESC(); CPointSpotlight(); void Precache(void); void Spawn(void); virtual void Activate(); virtual void OnEntityEvent( EntityEvent_t event, void *pEventData ); private: int UpdateTransmitState(); void SpotlightThink(void); void SpotlightUpdate(void); Vector SpotlightCurrentPos(void); void SpotlightCreate(void); void SpotlightDestroy(void); // ------------------------------ // Inputs // ------------------------------ void InputLightOn( inputdata_t &inputdata ); void InputLightOff( inputdata_t &inputdata ); void InputSetColor( inputdata_t &inputdata ); void InputForceUpdate( inputdata_t &inputdata ); // Creates the efficient spotlight void CreateEfficientSpotlight(); // Computes render info for a spotlight void ComputeRenderInfo(); private: bool m_bSpotlightOn; bool m_bEfficientSpotlight; Vector m_vSpotlightTargetPos; Vector m_vSpotlightCurrentPos; Vector m_vSpotlightDir; int m_nHaloSprite; CHandle m_hSpotlight; CHandle m_hSpotlightTarget; float m_flSpotlightMaxLength; float m_flSpotlightCurLength; float m_flSpotlightGoalWidth; float m_flHDRColorScale; public: COutputEvent m_OnOn, m_OnOff; ///< output fires when turned on, off }; BEGIN_DATADESC( CPointSpotlight ) DEFINE_FIELD( m_flSpotlightCurLength, FIELD_FLOAT ), DEFINE_FIELD( m_bSpotlightOn, FIELD_BOOLEAN ), DEFINE_FIELD( m_bEfficientSpotlight, FIELD_BOOLEAN ), DEFINE_FIELD( m_vSpotlightTargetPos, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_vSpotlightCurrentPos, FIELD_POSITION_VECTOR ), // Robin: Don't Save, recreated after restore/transition //DEFINE_FIELD( m_hSpotlight, FIELD_EHANDLE ), //DEFINE_FIELD( m_hSpotlightTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ), DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ), DEFINE_KEYFIELD( m_flSpotlightMaxLength,FIELD_FLOAT, "SpotlightLength"), DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "LightOn", InputLightOn ), DEFINE_INPUTFUNC( FIELD_VOID, "LightOff", InputLightOff ), DEFINE_INPUTFUNC( FIELD_COLOR32, "SetColor", InputSetColor ), DEFINE_INPUTFUNC( FIELD_VOID, "ForceUpdate", InputForceUpdate ), DEFINE_OUTPUT( m_OnOn, "OnLightOn" ), DEFINE_OUTPUT( m_OnOff, "OnLightOff" ), DEFINE_THINKFUNC( SpotlightThink ), END_DATADESC() LINK_ENTITY_TO_CLASS(point_spotlight, CPointSpotlight); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CPointSpotlight::CPointSpotlight() { #ifdef _DEBUG m_vSpotlightTargetPos.Init(); m_vSpotlightCurrentPos.Init(); m_vSpotlightDir.Init(); #endif m_flHDRColorScale = 1.0f; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointSpotlight::Precache(void) { BaseClass::Precache(); // Sprites. m_nHaloSprite = PrecacheModel("sprites/light_glow03.vmt"); PrecacheModel( "sprites/glow_test02.vmt" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointSpotlight::Spawn(void) { Precache(); UTIL_SetSize( this,vec3_origin,vec3_origin ); AddSolidFlags( FSOLID_NOT_SOLID ); SetMoveType( MOVETYPE_NONE ); m_bEfficientSpotlight = true; // Check for user error if (m_flSpotlightMaxLength <= 0) { DevMsg("%s (%s) has an invalid spotlight length <= 0, setting to 500\n", GetClassname(), GetDebugName() ); m_flSpotlightMaxLength = 500; } if (m_flSpotlightGoalWidth <= 0) { DevMsg("%s (%s) has an invalid spotlight width <= 0, setting to 10\n", GetClassname(), GetDebugName() ); m_flSpotlightGoalWidth = 10; } if (m_flSpotlightGoalWidth > MAX_BEAM_WIDTH ) { DevMsg("%s (%s) has an invalid spotlight width %.1f (max %.1f).\n", GetClassname(), GetDebugName(), m_flSpotlightGoalWidth, MAX_BEAM_WIDTH ); m_flSpotlightGoalWidth = MAX_BEAM_WIDTH; } // ------------------------------------ // Init all class vars // ------------------------------------ m_vSpotlightTargetPos = vec3_origin; m_vSpotlightCurrentPos = vec3_origin; m_hSpotlight = NULL; m_hSpotlightTarget = NULL; m_vSpotlightDir = vec3_origin; m_flSpotlightCurLength = m_flSpotlightMaxLength; m_bSpotlightOn = HasSpawnFlags( SF_SPOTLIGHT_START_LIGHT_ON ); SetThink( &CPointSpotlight::SpotlightThink ); SetNextThink( gpGlobals->curtime + 0.1f ); } //----------------------------------------------------------------------------- // Computes render info for a spotlight //----------------------------------------------------------------------------- void CPointSpotlight::ComputeRenderInfo() { // Fade out spotlight end if past max length. if ( m_flSpotlightCurLength > 2*m_flSpotlightMaxLength ) { m_hSpotlightTarget->SetRenderAlpha( 0 ); m_hSpotlight->SetFadeLength( m_flSpotlightMaxLength ); } else if ( m_flSpotlightCurLength > m_flSpotlightMaxLength ) { m_hSpotlightTarget->SetRenderAlpha( (1-((m_flSpotlightCurLength-m_flSpotlightMaxLength)/m_flSpotlightMaxLength)) ); m_hSpotlight->SetFadeLength( m_flSpotlightMaxLength ); } else { m_hSpotlightTarget->SetRenderAlpha( 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); // Adjust width of light on the end. if ( FBitSet (m_spawnflags, SF_SPOTLIGHT_NO_DYNAMIC_LIGHT) ) { m_hSpotlightTarget->m_flLightScale = 0.0; } else { // <> - magic number 1.8 depends on sprite size m_hSpotlightTarget->m_flLightScale = 1.8*flNewWidth; } } //----------------------------------------------------------------------------- // Creates the efficient spotlight //----------------------------------------------------------------------------- void CPointSpotlight::CreateEfficientSpotlight() { if ( m_hSpotlightTarget.Get() != NULL ) return; SpotlightCreate(); m_vSpotlightCurrentPos = SpotlightCurrentPos(); m_hSpotlightTarget->SetAbsOrigin( m_vSpotlightCurrentPos ); m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin(); VectorSubtract( m_hSpotlightTarget->GetAbsOrigin(), m_hSpotlightTarget->m_vSpotlightOrg, m_hSpotlightTarget->m_vSpotlightDir ); m_flSpotlightCurLength = VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir ); m_hSpotlightTarget->SetMoveType( MOVETYPE_NONE ); ComputeRenderInfo(); m_OnOn.FireOutput( this, this ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointSpotlight::Activate(void) { BaseClass::Activate(); if ( GetMoveParent() ) { m_bEfficientSpotlight = false; } if ( m_bEfficientSpotlight ) { if ( m_bSpotlightOn ) { CreateEfficientSpotlight(); } // Don't think SetThink( NULL ); } } //------------------------------------------------------------------------------------- // Optimization to deal with spotlights //------------------------------------------------------------------------------------- void CPointSpotlight::OnEntityEvent( EntityEvent_t event, void *pEventData ) { if ( event == ENTITY_EVENT_PARENT_CHANGED ) { if ( GetMoveParent() ) { m_bEfficientSpotlight = false; if ( m_hSpotlightTarget ) { m_hSpotlightTarget->SetMoveType( MOVETYPE_FLY ); } SetThink( &CPointSpotlight::SpotlightThink ); SetNextThink( gpGlobals->curtime + 0.1f ); } } BaseClass::OnEntityEvent( event, pEventData ); } //------------------------------------------------------------------------------------- // Purpose : Send even though we don't have a model so spotlight gets proper position // Input : // Output : //------------------------------------------------------------------------------------- int CPointSpotlight::UpdateTransmitState() { if ( m_bEfficientSpotlight ) return SetTransmitState( FL_EDICT_DONTSEND ); return SetTransmitState( FL_EDICT_PVSCHECK ); } //----------------------------------------------------------------------------- // Purpose: Plays the engine sound. //----------------------------------------------------------------------------- void CPointSpotlight::SpotlightThink( void ) { if ( GetMoveParent() ) { SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); } else { SetNextThink( gpGlobals->curtime + 0.1f ); } SpotlightUpdate(); } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CPointSpotlight::SpotlightCreate(void) { if ( m_hSpotlightTarget.Get() != NULL ) return; AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" ); m_hSpotlightTarget->Spawn(); m_hSpotlightTarget->SetAbsOrigin( tr.endpos ); m_hSpotlightTarget->SetOwnerEntity( this ); m_hSpotlightTarget->m_clrRender = m_clrRender; m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength; if ( FBitSet (m_spawnflags, SF_SPOTLIGHT_NO_DYNAMIC_LIGHT) ) { m_hSpotlightTarget->m_flLightScale = 0.0; } //m_hSpotlight = CBeam::BeamCreate( "sprites/spotlight.vmt", m_flSpotlightGoalWidth ); m_hSpotlight = CBeam::BeamCreate( "sprites/glow_test02.vmt", m_flSpotlightGoalWidth ); // Set the temporary spawnflag on the beam so it doesn't save (we'll recreate it on restore) m_hSpotlight->SetHDRColorScale( m_flHDRColorScale ); m_hSpotlight->AddSpawnFlags( SF_BEAM_TEMPORARY ); m_hSpotlight->SetColor( m_clrRender->r, m_clrRender->g, m_clrRender->b ); m_hSpotlight->SetHaloTexture(m_nHaloSprite); m_hSpotlight->SetHaloScale(60); m_hSpotlight->SetEndWidth(m_flSpotlightGoalWidth); m_hSpotlight->SetBeamFlags( (FBEAM_SHADEOUT|FBEAM_NOTILE) ); m_hSpotlight->SetBrightness( m_clrRender->a ); m_hSpotlight->SetNoise( 0 ); if ( m_bEfficientSpotlight ) { m_hSpotlight->PointsInit( GetAbsOrigin(), m_hSpotlightTarget->GetAbsOrigin() ); } else { m_hSpotlight->EntsInit( this, m_hSpotlightTarget ); } } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ Vector CPointSpotlight::SpotlightCurrentPos(void) { AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); // Get beam end point. Only collide with solid objects, not npcs trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); return tr.endpos; } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CPointSpotlight::SpotlightDestroy(void) { if ( m_hSpotlight ) { m_OnOff.FireOutput( this, this ); UTIL_Remove(m_hSpotlight); UTIL_Remove(m_hSpotlightTarget); } } //------------------------------------------------------------------------------ // Purpose : Update the direction and position of my spotlight // Input : // Output : //------------------------------------------------------------------------------ void CPointSpotlight::SpotlightUpdate(void) { // --------------------------------------------------- // If I don't have a spotlight attempt to create one // --------------------------------------------------- if ( !m_hSpotlight ) { if ( m_bSpotlightOn ) { // Make the spotlight SpotlightCreate(); } else { return; } } else if ( !m_bSpotlightOn ) { SpotlightDestroy(); return; } if ( !m_hSpotlightTarget ) { DevWarning( "**Attempting to update point_spotlight but target ent is NULL\n" ); SpotlightDestroy(); SpotlightCreate(); if ( !m_hSpotlightTarget ) return; } m_vSpotlightCurrentPos = SpotlightCurrentPos(); // Update spotlight target velocity Vector vTargetDir; VectorSubtract( m_vSpotlightCurrentPos, m_hSpotlightTarget->GetAbsOrigin(), vTargetDir ); float vTargetDist = vTargetDir.Length(); // If we haven't moved at all, don't recompute if ( vTargetDist < 1 ) { m_hSpotlightTarget->SetAbsVelocity( vec3_origin ); return; } Vector vecNewVelocity = vTargetDir; VectorNormalize(vecNewVelocity); vecNewVelocity *= (10 * vTargetDist); // If a large move is requested, just jump to final spot as we probably hit a discontinuity if (vecNewVelocity.Length() > 200) { VectorNormalize(vecNewVelocity); vecNewVelocity *= 200; VectorNormalize(vTargetDir); m_hSpotlightTarget->SetAbsOrigin( m_vSpotlightCurrentPos ); } m_hSpotlightTarget->SetAbsVelocity( vecNewVelocity ); m_hSpotlightTarget->m_vSpotlightOrg = GetAbsOrigin(); // Avoid sudden change in where beam fades out when cross disconinuities VectorSubtract( m_hSpotlightTarget->GetAbsOrigin(), m_hSpotlightTarget->m_vSpotlightOrg, m_hSpotlightTarget->m_vSpotlightDir ); float flBeamLength = VectorNormalize( m_hSpotlightTarget->m_vSpotlightDir ); m_flSpotlightCurLength = (0.60*m_flSpotlightCurLength) + (0.4*flBeamLength); ComputeRenderInfo(); //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); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointSpotlight::InputLightOn( inputdata_t &inputdata ) { if ( !m_bSpotlightOn ) { m_bSpotlightOn = true; if ( m_bEfficientSpotlight ) { CreateEfficientSpotlight(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointSpotlight::InputLightOff( inputdata_t &inputdata ) { if ( m_bSpotlightOn ) { m_bSpotlightOn = false; if ( m_bEfficientSpotlight ) { SpotlightDestroy(); } } } //----------------------------------------------------------------------------- // Purpose: Set the beam's color //----------------------------------------------------------------------------- void CPointSpotlight::InputSetColor( inputdata_t &inputdata ) { if ( m_hSpotlight ) { color32 clr = inputdata.value.Color32(); m_hSpotlight->SetColor( clr.r, clr.g, clr.b ); } } //----------------------------------------------------------------------------- // Purpose: Force update the spotlight //----------------------------------------------------------------------------- void CPointSpotlight::InputForceUpdate( inputdata_t &inputdata ) { SpotlightUpdate(); }