//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: spawn and think functions for editor-placed lights // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "lights.h" #include "world.h" #ifdef DEFERRED #include "deferred/deferred_shared_common.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" LINK_ENTITY_TO_CLASS(light, CLight); BEGIN_DATADESC(CLight) DEFINE_FIELD(m_iCurrentFade, FIELD_CHARACTER), DEFINE_FIELD(m_iTargetFade, FIELD_CHARACTER), DEFINE_KEYFIELD(m_iStyle, FIELD_INTEGER, "style"), DEFINE_KEYFIELD(m_iDefaultStyle, FIELD_INTEGER, "defaultstyle"), DEFINE_KEYFIELD(m_iszPattern, FIELD_STRING, "pattern"), // Fuctions DEFINE_FUNCTION(FadeThink), // Inputs DEFINE_INPUTFUNC(FIELD_STRING, "SetPattern", InputSetPattern), DEFINE_INPUTFUNC(FIELD_STRING, "FadeToPattern", InputFadeToPattern), DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), DEFINE_INPUTFUNC(FIELD_VOID, "TurnOn", InputTurnOn), DEFINE_INPUTFUNC(FIELD_VOID, "TurnOff", InputTurnOff), END_DATADESC() // // Cache user-entity-field values until spawn is called. // bool CLight::KeyValue(const char *szKeyName, const char *szValue) { if (FStrEq(szKeyName, "pitch")) { QAngle angles = GetAbsAngles(); angles.x = atof(szValue); SetAbsAngles(angles); } else { return BaseClass::KeyValue(szKeyName, szValue); } return true; } // Light entity // If targeted, it will toggle between on or off. void CLight::Spawn(void) { if (!GetEntityName()) { // inert light UTIL_Remove(this); return; } if (m_iStyle >= 32) { if (m_iszPattern == NULL_STRING && m_iDefaultStyle > 0) { m_iszPattern = MAKE_STRING(GetDefaultLightstyleString(m_iDefaultStyle)); } if (FBitSet(m_spawnflags, SF_LIGHT_START_OFF)) engine->LightStyle(m_iStyle, "a"); else if (m_iszPattern != NULL_STRING) engine->LightStyle(m_iStyle, (char *)STRING(m_iszPattern)); else engine->LightStyle(m_iStyle, "m"); } } void CLight::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) { if (m_iStyle >= 32) { if (!ShouldToggle(useType, !FBitSet(m_spawnflags, SF_LIGHT_START_OFF))) return; Toggle(); } } //----------------------------------------------------------------------------- // Purpose: Turn the light on //----------------------------------------------------------------------------- void CLight::TurnOn(void) { if (m_iszPattern != NULL_STRING) { engine->LightStyle(m_iStyle, (char *)STRING(m_iszPattern)); } else { engine->LightStyle(m_iStyle, "m"); } CLEARBITS(m_spawnflags, SF_LIGHT_START_OFF); } //----------------------------------------------------------------------------- // Purpose: Turn the light off //----------------------------------------------------------------------------- void CLight::TurnOff(void) { engine->LightStyle(m_iStyle, "a"); SETBITS(m_spawnflags, SF_LIGHT_START_OFF); } //----------------------------------------------------------------------------- // Purpose: Toggle the light on/off //----------------------------------------------------------------------------- void CLight::Toggle(void) { //Toggle it if (FBitSet(m_spawnflags, SF_LIGHT_START_OFF)) { TurnOn(); } else { TurnOff(); } } //----------------------------------------------------------------------------- // Purpose: Handle the "turnon" input handler // Input : &inputdata - //----------------------------------------------------------------------------- void CLight::InputTurnOn(inputdata_t &inputdata) { TurnOn(); } //----------------------------------------------------------------------------- // Purpose: Handle the "turnoff" input handler // Input : &inputdata - //----------------------------------------------------------------------------- void CLight::InputTurnOff(inputdata_t &inputdata) { TurnOff(); } //----------------------------------------------------------------------------- // Purpose: Handle the "toggle" input handler // Input : &inputdata - //----------------------------------------------------------------------------- void CLight::InputToggle(inputdata_t &inputdata) { Toggle(); } //----------------------------------------------------------------------------- // Purpose: Input handler for setting a light pattern //----------------------------------------------------------------------------- void CLight::InputSetPattern(inputdata_t &inputdata) { m_iszPattern = inputdata.value.StringID(); engine->LightStyle(m_iStyle, (char *)STRING(m_iszPattern)); // Light is on if pattern is set CLEARBITS(m_spawnflags, SF_LIGHT_START_OFF); } //----------------------------------------------------------------------------- // Purpose: Input handler for fading from first value in old pattern to // first value in new pattern //----------------------------------------------------------------------------- void CLight::InputFadeToPattern(inputdata_t &inputdata) { m_iCurrentFade = (STRING(m_iszPattern))[0]; m_iTargetFade = inputdata.value.String()[0]; m_iszPattern = inputdata.value.StringID(); SetThink(&CLight::FadeThink); SetNextThink(gpGlobals->curtime); // Light is on if pattern is set CLEARBITS(m_spawnflags, SF_LIGHT_START_OFF); } //------------------------------------------------------------------------------ // Purpose : Fade light to new starting pattern value then stop thinking //------------------------------------------------------------------------------ void CLight::FadeThink(void) { if (m_iCurrentFade < m_iTargetFade) { m_iCurrentFade++; } else if (m_iCurrentFade > m_iTargetFade) { m_iCurrentFade--; } // If we're done fading instantiate our light pattern and stop thinking if (m_iCurrentFade == m_iTargetFade) { engine->LightStyle(m_iStyle, (char *)STRING(m_iszPattern)); SetNextThink(TICK_NEVER_THINK); } // Otherwise instantiate our current fade value and keep thinking else { char sCurString[2]; sCurString[0] = m_iCurrentFade; sCurString[1] = 0; engine->LightStyle(m_iStyle, sCurString); // UNDONE: Consider making this settable war to control fade speed SetNextThink(gpGlobals->curtime + 0.1f); } } // // shut up spawn functions for new spotlights // LINK_ENTITY_TO_CLASS(light_spot, CLight); LINK_ENTITY_TO_CLASS(light_glspot, CLight); LINK_ENTITY_TO_CLASS(light_environment_old, CEnvLight); #ifndef DEFERRED BEGIN_DATADESC(CEnvLight) DEFINE_FIELD(m_angSunAngles, FIELD_VECTOR), DEFINE_FIELD(m_vecLight, FIELD_VECTOR), DEFINE_FIELD(m_vecAmbient, FIELD_VECTOR), DEFINE_KEYFIELD(m_bCascadedShadowMappingEnabled, FIELD_BOOLEAN, "CSM_Enable"), DEFINE_FIELD(csmEnableLightColor, FIELD_BOOLEAN), DEFINE_KEYFIELD(csmEnableAmbientLightColor, FIELD_BOOLEAN, "CSM_Dark"), DEFINE_FIELD(csmLightColor, FIELD_COLOR32), DEFINE_FIELD(csmAmbientColor, FIELD_COLOR32), DEFINE_KEYFIELD(cmsFov, FIELD_INTEGER, "CSM_FOV"), END_DATADESC() IMPLEMENT_SERVERCLASS_ST_NOBASE(CEnvLight, DT_CEnvLight) SendPropQAngles(SENDINFO(m_angSunAngles)), SendPropVector(SENDINFO(m_vecLight)), SendPropVector(SENDINFO(m_vecAmbient)), SendPropBool(SENDINFO(m_bCascadedShadowMappingEnabled)), SendPropBool(SENDINFO(csmEnableLightColor)), SendPropBool(SENDINFO(csmEnableAmbientLightColor)), SendPropInt(SENDINFO_STRUCTELEM(csmLightColor), 32, SPROP_UNSIGNED), SendPropInt(SENDINFO_STRUCTELEM(csmAmbientColor), 32, SPROP_UNSIGNED), SendPropInt(SENDINFO(cmsFov), 1, SPROP_UNSIGNED), END_SEND_TABLE() CEnvLight::CEnvLight() : m_bHasHDRLightSet(false), m_bHasHDRAmbientSet(false) { //csmEnableLightColor = false; //m_bCascadedShadowMappingEnabled = cvar->FindVar("r_post_draw_csm_enabled")->GetBool(); //csmEnableAmbientLightColor = false; cmsFov = 90; csmLightColor.SetR(0); csmLightColor.SetG(0); csmLightColor.SetB(0); csmLightColor.SetA(0); csmAmbientColor.SetR(0); csmAmbientColor.SetG(0); csmAmbientColor.SetB(0); csmAmbientColor.SetA(0); } static Vector ConvertLightmapGammaToLinear(int *iColor4) { Vector vecColor; for (int i = 0; i < 3; ++i) { vecColor[i] = powf(iColor4[i] / 315.0f, 2.32f); // We make ambient a bit darker than baked ambient } vecColor *= iColor4[3] / 325.0f, 2.32f; // We make ambient a bit darker than baked ambient return vecColor; } bool CEnvLight::KeyValue(const char *szKeyName, const char *szValue) { if (FStrEq(szKeyName, "pitch")) { m_angSunAngles.SetX(-atof(szValue)); } else if (FStrEq(szKeyName, "angles")) { Vector vecParsed; UTIL_StringToVector(vecParsed.Base(), szValue); m_angSunAngles.SetY(vecParsed.y); } else if (FStrEq(szKeyName, "_light") || FStrEq(szKeyName, "_lightHDR")) { int iParsed[4]; UTIL_StringToIntArray(iParsed, 4, szValue); if (iParsed[0] <= 0 || iParsed[1] <= 0 || iParsed[2] <= 0) return true; if (FStrEq(szKeyName, "_lightHDR")) { // HDR overrides LDR m_bHasHDRLightSet = true; } else if (m_bHasHDRLightSet) { // If this is LDR and we got HDR already, bail out. return true; } m_vecLight = ConvertLightmapGammaToLinear(iParsed); DevMsg("Parsed light_environment light: %i %i %i %i\n", iParsed[0], iParsed[1], iParsed[2], iParsed[3]); } else if (FStrEq(szKeyName, "_ambient") || FStrEq(szKeyName, "_ambientHDR")) { int iParsed[4]; UTIL_StringToIntArray(iParsed, 4, szValue); if (iParsed[0] <= 0 || iParsed[1] <= 0 || iParsed[2] <= 0) return true; if (FStrEq(szKeyName, "_ambientHDR")) { // HDR overrides LDR m_bHasHDRLightSet = true; } else if (m_bHasHDRLightSet) { // If this is LDR and we got HDR already, bail out. return true; } m_vecAmbient = ConvertLightmapGammaToLinear(iParsed); DevMsg("Parsed light_environment ambient: %i %i %i %i\n", iParsed[0], iParsed[1], iParsed[2], iParsed[3]); } else { return BaseClass::KeyValue(szKeyName, szValue); } return true; } void CEnvLight::Spawn() { SetName(MAKE_STRING("light_environment")); BaseClass::Spawn(); m_bCascadedShadowMappingEnabled = HasSpawnFlags(0x01); } #else bool CEnvLight::KeyValue(const char *szKeyName, const char *szValue) { if (FStrEq(szKeyName, "_light")) { // nothing UTIL_StringToFloatArray(m_vecLight, 4, szValue); } else { if (FStrEq(szKeyName, "pitch")) { m_fLightPitch = atof(szValue); } else if (FStrEq(szKeyName, "_ambient")) { UTIL_StringToFloatArray(m_vecAmbientLight, 4, szValue); } return BaseClass::KeyValue(szKeyName, szValue); } return true; } ConVar deferred_autoenvlight_ambient_intensity_low("deferred_autoenvlight_ambient_intensity_low", "0.35"); ConVar deferred_autoenvlight_ambient_intensity_high("deferred_autoenvlight_ambient_intensity_high", "0.85"); ConVar deferred_autoenvlight_diffuse_intensity("deferred_autoenvlight_diffuse_intensity", "15"); void CEnvLight::Activate(void) { BaseClass::Activate(); if (GetGlobalLight() == NULL) { CBaseEntity *pGlobalLight = CreateEntityByName("light_environment"); if (pGlobalLight) { float ds = deferred_autoenvlight_diffuse_intensity.GetFloat(); float asl = deferred_autoenvlight_ambient_intensity_low.GetFloat(); float ash = deferred_autoenvlight_ambient_intensity_high.GetFloat(); const QAngle &vecAngles = GetAbsAngles(); pGlobalLight->KeyValue("origin", UTIL_VarArgs("%f %f %f", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z)); pGlobalLight->KeyValue("diffuse", UTIL_VarArgs("%f %f %f %f", m_vecLight[0], m_vecLight[1], m_vecLight[2], m_vecLight[3] * ds)); pGlobalLight->KeyValue("ambient_high", UTIL_VarArgs("%f %f %f %f", m_vecAmbientLight[0], m_vecAmbientLight[1], m_vecAmbientLight[2], m_vecAmbientLight[3] * ash)); pGlobalLight->KeyValue("ambient_low", UTIL_VarArgs("%f %f %f %f", m_vecAmbientLight[0], m_vecAmbientLight[1], m_vecAmbientLight[2], m_vecAmbientLight[3] * asl)); pGlobalLight->KeyValue("spawnflags", "3"); pGlobalLight->KeyValue("angles", UTIL_VarArgs("%f %f %f", -m_fLightPitch, vecAngles.y, vecAngles.z)); DispatchSpawn(pGlobalLight); //pGlobalLight->Activate(); // Should not be activated here: the global light is created before the level activates all entities. } } UTIL_Remove(this); } #endif