From 398c186e53c0592cfa5a9124a82537d91c875480 Mon Sep 17 00:00:00 2001 From: celisej567 Date: Sun, 28 Aug 2022 18:56:20 +0300 Subject: [PATCH] added files --- client/C_Env_Cascade_Light.h | 76 ++++ client/c_env_cascade_light.cpp | 553 ++++++++++++++++++++++++ client/csm.vpc | 13 + materials/tools/fakecsm/mask_center.vtf | Bin 0 -> 43928 bytes materials/tools/fakecsm/mask_ring.vtf | Bin 0 -> 196816 bytes server/csm.vpc | 9 + server/env_cascade_light.cpp | 491 +++++++++++++++++++++ server/lights.h | 57 +++ 8 files changed, 1199 insertions(+) create mode 100644 client/C_Env_Cascade_Light.h create mode 100644 client/c_env_cascade_light.cpp create mode 100644 client/csm.vpc create mode 100644 materials/tools/fakecsm/mask_center.vtf create mode 100644 materials/tools/fakecsm/mask_ring.vtf create mode 100644 server/csm.vpc create mode 100644 server/env_cascade_light.cpp create mode 100644 server/lights.h diff --git a/client/C_Env_Cascade_Light.h b/client/C_Env_Cascade_Light.h new file mode 100644 index 0000000..8bb7661 --- /dev/null +++ b/client/C_Env_Cascade_Light.h @@ -0,0 +1,76 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef C_ENVPROJECTEDTEXTURE_H +#define C_ENVPROJECTEDTEXTURE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "c_baseentity.h" +#include "basetypes.h" + +class C_LightOrigin : public C_BaseEntity +{ + DECLARE_CLASS(C_LightOrigin, C_BaseEntity); +public: + DECLARE_CLIENTCLASS(); + + +private: +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +class C_EnvCascadeLight : public C_BaseEntity +{ + DECLARE_CLASS(C_EnvCascadeLight, C_BaseEntity ); +public: + DECLARE_CLIENTCLASS(); + + C_EnvCascadeLight(); + ~C_EnvCascadeLight(); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + void ShutDownLightHandle( void ); + + virtual void Simulate(); + + void UpdateLight( bool bForceUpdate ); + + bool ShadowsEnabled(); + + float GetFOV(); + +private: + + ClientShadowHandle_t m_LightHandle; + + EHANDLE m_hTargetEntity; + bool m_bState; + float m_flLightFOV; + bool m_bEnableShadows; + bool m_bLightOnlyTarget; + bool m_bLightWorld; + bool m_bCameraSpace; + color32 m_cLightColor; + float m_flAmbient; + char m_SpotlightTextureName[ MAX_PATH ]; + int m_nSpotlightTextureFrame; + int m_nShadowQuality; + bool m_bCurrentShadow; + +public: + C_EnvCascadeLight*m_pNext; +}; + +C_EnvCascadeLight* GetEnvProjectedTextureList(); + +#endif // C_ENVPROJECTEDTEXTURE_H diff --git a/client/c_env_cascade_light.cpp b/client/c_env_cascade_light.cpp new file mode 100644 index 0000000..d7bcb36 --- /dev/null +++ b/client/c_env_cascade_light.cpp @@ -0,0 +1,553 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "shareddefs.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterial.h" +#include "view.h" +#include "iviewrender.h" +#include "view_shared.h" +#include "texture_group_names.h" +#include "tier0/icommandline.h" +#include "vguicenterprint.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static ConVarRef mat_slopescaledepthbias_shadowmap("mat_slopescaledepthbias_shadowmap"); +static ConVarRef mat_depthbias_shadowmap("mat_depthbias_shadowmap"); +static ConVar scissor("r_flashlightscissor", "0"); + +static ConVar csm_ortho("csm_ortho","0"); +ConVar csm_ortho_nearz("csm_ortho_nearz", "512"); +ConVar csm_ortho_left("csm_ortho_left", "-1000"); +ConVar csm_ortho_top("csm_ortho_top", "-1000"); +ConVar csm_ortho_bottom("csm_ortho_bottom", "1000"); +ConVar csm_ortho_right("csm_ortho_right", "1000"); +ConVar csm_test_color_interpolation("csm_test_color_interpolation","0"); + +//----------------------------------------------------------------------------- +// Purpose: main point for change angle of the light +//----------------------------------------------------------------------------- + + +class C_LightOrigin : public C_BaseEntity +{ + DECLARE_CLASS(C_LightOrigin, C_BaseEntity); +public: + DECLARE_CLIENTCLASS(); + + void Update(); + + virtual void Simulate(); + +private: + Vector LightEnvVector; + QAngle LightEnvAngle; +}; + +IMPLEMENT_CLIENTCLASS_DT(C_LightOrigin, DT_LightOrigin, CLightOrigin) +RecvPropVector(RECVINFO(LightEnvVector)) +END_RECV_TABLE() + +void C_LightOrigin::Update() +{ + SetAbsOrigin(C_BasePlayer::GetLocalPlayer()->GetAbsOrigin()); +} + + +void C_LightOrigin::Simulate() +{ + Update(); + BaseClass::Simulate(); +} + +const char* filter = CommandLine()->ParmValue("-ptfilter", "0.5"); +ConVar bebra("csm_filter", filter); + +//----------------------------------------------------------------------------- +// Purpose: main csm code +//----------------------------------------------------------------------------- + +class C_EnvCascadeLight : public C_BaseEntity +{ + DECLARE_CLASS(C_EnvCascadeLight, C_BaseEntity); +public: + DECLARE_CLIENTCLASS(); + + virtual void OnDataChanged(DataUpdateType_t updateType); + void ShutDownLightHandle(void); + + virtual void Simulate(); + + void UpdateLight(bool bForceUpdate); + + C_EnvCascadeLight(); + ~C_EnvCascadeLight(); + + +private: + + ClientShadowHandle_t m_LightHandle; + + EHANDLE m_hTargetEntity; + + void updatePos(); + CBaseEntity* pEntity = NULL; + bool firstUpdate = true; + bool m_bState; + float m_flLightFOV; + bool m_bEnableShadows; + bool m_bLightOnlyTarget; + bool m_bLightWorld; + bool m_bCameraSpace; + Vector m_LinearFloatLightColor; + float m_flAmbient; + float m_flNearZ; + float m_flFarZ; + char m_SpotlightTextureName[MAX_PATH]; + int m_nSpotlightTextureFrame; + int m_nShadowQuality; +}; + +IMPLEMENT_CLIENTCLASS_DT(C_EnvCascadeLight, DT_EnvCascadeLight, CEnvCascadeLight) + RecvPropEHandle( RECVINFO( m_hTargetEntity ) ), + RecvPropBool( RECVINFO( m_bState ) ), + RecvPropFloat( RECVINFO( m_flLightFOV ) ), + RecvPropBool( RECVINFO( m_bEnableShadows ) ), + RecvPropBool( RECVINFO( m_bLightOnlyTarget ) ), + RecvPropBool( RECVINFO( m_bLightWorld ) ), + RecvPropBool( RECVINFO( m_bCameraSpace ) ), + RecvPropVector( RECVINFO( m_LinearFloatLightColor ) ), + RecvPropFloat( RECVINFO( m_flAmbient ) ), + RecvPropString( RECVINFO( m_SpotlightTextureName ) ), + RecvPropInt( RECVINFO( m_nSpotlightTextureFrame ) ), + RecvPropFloat( RECVINFO( m_flNearZ ) ), + RecvPropFloat( RECVINFO( m_flFarZ ) ), + RecvPropInt( RECVINFO( m_nShadowQuality ) ) +END_RECV_TABLE() + +C_EnvCascadeLight::C_EnvCascadeLight( void ) +{ + + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; +} + + +C_EnvCascadeLight::~C_EnvCascadeLight( void ) +{ + ShutDownLightHandle(); +} + +void C_EnvCascadeLight::ShutDownLightHandle( void ) +{ + // Clear out the light + if( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE ) + { + g_pClientShadowMgr->DestroyFlashlight( m_LightHandle ); + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + } +} + +void C_EnvCascadeLight::updatePos() +{ + if (firstUpdate) + { + m_flNearZ = 4000; + m_flFarZ = 16000; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : updateType - how do you increase the light's height? +//----------------------------------------------------------------------------- +void C_EnvCascadeLight::OnDataChanged( DataUpdateType_t updateType ) +{ + UpdateLight( true ); + BaseClass::OnDataChanged( updateType ); +} + + + +void C_EnvCascadeLight::UpdateLight( bool bForceUpdate ) +{ + + if (m_bState == false) + { + if (m_LightHandle != CLIENTSHADOW_INVALID_HANDLE) + { + ShutDownLightHandle(); + } + + return; + } + + Vector vForward, vRight, vUp, vPos = GetAbsOrigin(); + FlashlightState_t state; + state.m_flShadowFilterSize = bebra.GetFloat(); + + + if (m_hTargetEntity != NULL) + { + if (m_bCameraSpace) + { + const QAngle& angles = GetLocalAngles(); + + C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); + if (pPlayer) + { + const QAngle playerAngles = pPlayer->GetAbsAngles(); + + Vector vPlayerForward, vPlayerRight, vPlayerUp; + AngleVectors(playerAngles, &vPlayerForward, &vPlayerRight, &vPlayerUp); + + matrix3x4_t mRotMatrix; + AngleMatrix(angles, mRotMatrix); + + VectorITransform(vPlayerForward, mRotMatrix, vForward); + VectorITransform(vPlayerRight, mRotMatrix, vRight); + VectorITransform(vPlayerUp, mRotMatrix, vUp); + + float dist = (m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin()).Length(); + vPos = m_hTargetEntity->GetAbsOrigin() - vForward * dist; + + VectorNormalize(vForward); + VectorNormalize(vRight); + VectorNormalize(vUp); + } + } + else + { + vForward = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin(); + VectorNormalize(vForward); + + Assert(0); + + } + } + else + { + AngleVectors(GetAbsAngles(), &vForward, &vRight, &vUp); + } + + + state.m_fHorizontalFOVDegrees = m_flLightFOV; + state.m_fVerticalFOVDegrees = m_flLightFOV; + + state.m_vecLightOrigin = vPos; + BasisToQuaternion(vForward, vRight, vUp, state.m_quatOrientation); + + state.m_fQuadraticAtten = 0.0; + state.m_fLinearAtten = 100; + state.m_fConstantAtten = 0.0f; + state.m_Color[0] = m_LinearFloatLightColor.x; + state.m_Color[1] = m_LinearFloatLightColor.y; + state.m_Color[2] = m_LinearFloatLightColor.z; + state.m_Color[3] = m_flAmbient; // fixme: need to make ambient work m_flAmbient; + state.m_NearZ = m_flNearZ; + + state.m_FarZ = m_flFarZ; + state.m_flShadowSlopeScaleDepthBias = mat_slopescaledepthbias_shadowmap.GetFloat(); + state.m_flShadowDepthBias = mat_depthbias_shadowmap.GetFloat(); + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = materials->FindTexture(m_SpotlightTextureName, TEXTURE_GROUP_OTHER, false); + state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; + + state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality + + state.m_bOrtho = csm_ortho.GetBool(); + if (state.m_bOrtho) + { + state.m_fOrthoLeft = csm_ortho_left.GetInt(); + state.m_fOrthoTop = csm_ortho_top.GetInt(); + state.m_fOrthoRight = csm_ortho_right.GetInt(); + state.m_fOrthoBottom = csm_ortho_bottom.GetInt(); + + state.m_fLinearAtten = ConVarRef("csm_current_distance").GetInt() * 2; + state.m_FarZAtten = ConVarRef("csm_current_distance").GetInt() * 2; + } + + + + if (m_LightHandle == CLIENTSHADOW_INVALID_HANDLE) + { + m_LightHandle = g_pClientShadowMgr->CreateFlashlight(state); + } + else + { + if (m_hTargetEntity != NULL || bForceUpdate == true) + { + g_pClientShadowMgr->UpdateFlashlightState(m_LightHandle, state); + } + } + + if (m_bLightOnlyTarget) + { + g_pClientShadowMgr->SetFlashlightTarget(m_LightHandle, m_hTargetEntity); + } + else + { + g_pClientShadowMgr->SetFlashlightTarget(m_LightHandle, NULL); + } + + g_pClientShadowMgr->SetFlashlightLightWorld(m_LightHandle, m_bLightWorld); + + + g_pClientShadowMgr->UpdateProjectedTexture(m_LightHandle, true); + + mat_slopescaledepthbias_shadowmap.SetValue("4"); + mat_depthbias_shadowmap.SetValue("0.000001"); + scissor.SetValue("0"); + +} + +void C_EnvCascadeLight::Simulate( void ) +{ + UpdateLight( true ); + updatePos(); + firstUpdate = false; + BaseClass::Simulate(); +} + + +//----------------------------------------------------------------------------- +// Purpose: second csm +//----------------------------------------------------------------------------- + +class C_EnvCascadeLightSecond : public C_BaseEntity +{ + DECLARE_CLASS(C_EnvCascadeLightSecond, C_BaseEntity); +public: + DECLARE_CLIENTCLASS(); + + virtual void OnDataChanged(DataUpdateType_t updateType); + void ShutDownLightHandle(void); + + virtual void Simulate(); + + void UpdateLight(bool bForceUpdate); + void updatePos(); + + C_EnvCascadeLightSecond(); + ~C_EnvCascadeLightSecond(); + + +private: + + ClientShadowHandle_t m_LightHandle; + + EHANDLE m_hTargetEntity; + CBaseEntity* pEntity = NULL; + bool firstUpdate = true; + bool m_bState; + float m_flLightFOV; + bool m_bEnableShadows; + bool m_bLightOnlyTarget; + bool m_bLightWorld; + bool m_bCameraSpace; + Vector m_LinearFloatLightColor; + float m_flAmbient; + float m_flNearZ; + float m_flFarZ; + char m_SpotlightTextureName[MAX_PATH]; + int m_nSpotlightTextureFrame; + int m_nShadowQuality; +}; + +IMPLEMENT_CLIENTCLASS_DT(C_EnvCascadeLightSecond, DT_EnvCascadeLightSecond, CEnvCascadeLightSecond) +RecvPropEHandle(RECVINFO(m_hTargetEntity)), +RecvPropBool(RECVINFO(m_bState)), +RecvPropFloat(RECVINFO(m_flLightFOV)), +RecvPropBool(RECVINFO(m_bEnableShadows)), +RecvPropBool(RECVINFO(m_bLightOnlyTarget)), +RecvPropBool(RECVINFO(m_bLightWorld)), +RecvPropBool(RECVINFO(m_bCameraSpace)), +RecvPropVector(RECVINFO(m_LinearFloatLightColor)), +RecvPropFloat(RECVINFO(m_flAmbient)), +RecvPropString(RECVINFO(m_SpotlightTextureName)), +RecvPropInt(RECVINFO(m_nSpotlightTextureFrame)), +RecvPropFloat(RECVINFO(m_flNearZ)), +RecvPropFloat(RECVINFO(m_flFarZ)), +RecvPropInt(RECVINFO(m_nShadowQuality)) +END_RECV_TABLE() + +C_EnvCascadeLightSecond::C_EnvCascadeLightSecond(void) +{ + + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; +} + + +C_EnvCascadeLightSecond::~C_EnvCascadeLightSecond(void) +{ + ShutDownLightHandle(); +} + +void C_EnvCascadeLightSecond::ShutDownLightHandle(void) +{ + // Clear out the light + if (m_LightHandle != CLIENTSHADOW_INVALID_HANDLE) + { + g_pClientShadowMgr->DestroyFlashlight(m_LightHandle); + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + } +} + +void C_EnvCascadeLightSecond::OnDataChanged(DataUpdateType_t updateType) +{ + UpdateLight(true); + BaseClass::OnDataChanged(updateType); +} + +void C_EnvCascadeLightSecond::UpdateLight(bool bForceUpdate) +{ + + if (m_bState == false) + { + if (m_LightHandle != CLIENTSHADOW_INVALID_HANDLE) + { + ShutDownLightHandle(); + } + + return; + } + + Vector vForward, vRight, vUp, vPos = GetAbsOrigin(); + FlashlightState_t state; + state.m_flShadowFilterSize = bebra.GetFloat(); + + if (m_hTargetEntity != NULL) + { + if (m_bCameraSpace) + { + const QAngle& angles = GetLocalAngles(); + + C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); + if (pPlayer) + { + const QAngle playerAngles = pPlayer->GetAbsAngles(); + + Vector vPlayerForward, vPlayerRight, vPlayerUp; + AngleVectors(playerAngles, &vPlayerForward, &vPlayerRight, &vPlayerUp); + + matrix3x4_t mRotMatrix; + AngleMatrix(angles, mRotMatrix); + + VectorITransform(vPlayerForward, mRotMatrix, vForward); + VectorITransform(vPlayerRight, mRotMatrix, vRight); + VectorITransform(vPlayerUp, mRotMatrix, vUp); + + float dist = (m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin()).Length(); + vPos = m_hTargetEntity->GetAbsOrigin() - vForward * dist; + + VectorNormalize(vForward); + VectorNormalize(vRight); + VectorNormalize(vUp); + } + } + else + { + vForward = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin(); + VectorNormalize(vForward); + Assert(0); + } + } + else + { + AngleVectors(GetAbsAngles(), &vForward, &vRight, &vUp); + } + + + state.m_fHorizontalFOVDegrees = m_flLightFOV; + state.m_fVerticalFOVDegrees = m_flLightFOV; + + state.m_vecLightOrigin = vPos; + BasisToQuaternion(vForward, vRight, vUp, state.m_quatOrientation); + + state.m_fQuadraticAtten = 0.0; + state.m_fLinearAtten = 100; + state.m_fConstantAtten = 0.0f; + state.m_Color[0] = m_LinearFloatLightColor.x; + state.m_Color[1] = m_LinearFloatLightColor.y; + state.m_Color[2] = m_LinearFloatLightColor.z; + state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient; + state.m_NearZ = m_flNearZ; + state.m_FarZ = m_flFarZ; + state.m_flShadowSlopeScaleDepthBias = mat_slopescaledepthbias_shadowmap.GetFloat(); + state.m_flShadowDepthBias = mat_depthbias_shadowmap.GetFloat(); + state.m_bEnableShadows = m_bEnableShadows; + state.m_pSpotlightTexture = materials->FindTexture(m_SpotlightTextureName, TEXTURE_GROUP_OTHER, false); + state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; + + state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality + + state.m_bOrtho = csm_ortho.GetBool(); + if(state.m_bOrtho) + { + float flOrthoSize = 1000.0f; + + state.m_fOrthoLeft = -flOrthoSize; + state.m_fOrthoTop = -flOrthoSize; + state.m_fOrthoRight = flOrthoSize; + state.m_fOrthoBottom = flOrthoSize; + + state.m_fLinearAtten = ConVarRef("csm_current_distance").GetInt() * 2; + state.m_FarZAtten = ConVarRef("csm_current_distance").GetInt() * 2; + } + + + + if (m_LightHandle == CLIENTSHADOW_INVALID_HANDLE) + { + m_LightHandle = g_pClientShadowMgr->CreateFlashlight(state); + } + else + { + if (m_hTargetEntity != NULL || bForceUpdate == true) + { + g_pClientShadowMgr->UpdateFlashlightState(m_LightHandle, state); + } + } + + if (m_bLightOnlyTarget) + { + g_pClientShadowMgr->SetFlashlightTarget(m_LightHandle, m_hTargetEntity); + } + else + { + g_pClientShadowMgr->SetFlashlightTarget(m_LightHandle, NULL); + } + + g_pClientShadowMgr->SetFlashlightLightWorld(m_LightHandle, m_bLightWorld); + + + if (state.m_bOrtho) + { + bool bSupressWorldLights = false; + + bSupressWorldLights = m_bEnableShadows; + + g_pClientShadowMgr->SetShadowFromWorldLightsEnabled(!bSupressWorldLights); + } + + g_pClientShadowMgr->UpdateProjectedTexture(m_LightHandle, true); + + m_flLightFOV = ConVarRef("csm_second_fov").GetFloat(); + + mat_slopescaledepthbias_shadowmap.SetValue("4"); + mat_depthbias_shadowmap.SetValue("0.000001"); + scissor.SetValue("0"); + +} + + +void C_EnvCascadeLightSecond::Simulate(void) +{ + UpdateLight(true); + BaseClass::Simulate(); +} diff --git a/client/csm.vpc b/client/csm.vpc new file mode 100644 index 0000000..66fedbf --- /dev/null +++ b/client/csm.vpc @@ -0,0 +1,13 @@ + +$Project +{ + + $Folder "Source Files" + { + + $File "c_env_cascade_light.cpp" + $File "C_Env_Cascade_Light.h" + + } + +} diff --git a/materials/tools/fakecsm/mask_center.vtf b/materials/tools/fakecsm/mask_center.vtf new file mode 100644 index 0000000000000000000000000000000000000000..48aeffe67011812963d74b00ee94c6e962167b07 GIT binary patch literal 43928 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 literal 0 HcmV?d00001 diff --git a/materials/tools/fakecsm/mask_ring.vtf b/materials/tools/fakecsm/mask_ring.vtf new file mode 100644 index 0000000000000000000000000000000000000000..2e2fd8b5ae1990aebd64b344a94b0acb90d2b8f3 GIT binary patch literal 196816 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Ð}b)A{)T=39uu%Gkm|4|K&L3p()59I zWX1w9x|!(!QI$m|!s2S=Lh3?8AUM$q6o|+RFa#72HKI8jv>`M#N>I=i)Q&UCzx+n--+!S)lu8wu3O$Y1^EQo!R@!g&%Vfc*s188>>&Hd zZnCFze+B#&x!8SF!H1{zG}PCAUo-*QjM#L*P9eCE(r72e2?U4| zNJG{L2;9;kTP8X+HWtiQfP2JA4u)WYAT=^ah%Ml90U-m9Bu$D%#3-;KzgkyvhB^ex2x{C5ccjDT99+}Z#f|S$qzlefFCfg zm?O6r_R@P{54CHm!##q0?$-=AJYVDwR6m3Vx6i7E?YG+>IFAQ-hisSKWKY>y_BY^v z80`%W=Ii~g32?NNgTwTDvqWS@7Kn#*fOskwQW`}U!tD%!z)CRznlK$Ybcz+wL_3}$9<6^4F3nBRc? z?C`l$)PO6IKM)?Py0$d5+h-Tlc@#O>qk-^8sL#jyfNEXA_K0vSf`O3=d{D;PAQI%4RP=C{AN2% z^-&c zyz_^4apKQ@W22qP!)<2F$JI-6FkxXoJA8{-cmP&_HU@kGo`IFo=Qq_?-ef(=vshbl zE&?uLx7|J(ojOkx{(v8X1R=cvMdpWIvp6ekh;)fAV3AgM5Um zFm&=cyBSOknBSPPk6afhhA>kl~uxJZr z3Z~41Ie<@wSoJ5+WgLOL`d?qxZ*(etg7yLM$~Y=HgR7U`3wuJF4xhV=!{@H?`K$oz zU>k#MXA$`!JQN~XXImOrdD*^hU!8|N>hcx|9s7vHw-f$t*!RB<=>XAFE`)}FV?h_f z$>3^+K!7xa@=PvfUaS?U4ylp06__2P5vi0WH6R?o2fzv3Kwi`lAvps1W5)%P1#l=$ zibZG?2@?nWp8A7#F_^)@oD4m@fFJ0MDXrW--d@;qFWeFL#og)fL-JVxyaB7x@{T^= z?q!i5!ov!)&Q^5^!R_|h7j}m|QsiXUXms2Oe*^pU-%STRH4rYOE`)nA5OhYMG-QpS ztpKvh4$f9UUJ#8)Qe-$-GX$xTIf6k!TQDxf98i!<8DbG&l>u%UV))9RzSuYVli3)0 zcrGEI7w~+5#=NecPmA%c!Cr^YU5k9yfz^=b56Ew-&#FRr)b`u$vkL@e-9Ai}J&MRF z=h+GW>98MO+0rP^1eb!6fP0w(0n!kfv;7iULDV5NQmw$e!0cF!h~3Ba5&8i`!ht!m zVv!L+YGhDoTQFrNSs2Vgi!erjSO)-hi09$w@gJWk^BK&*U^WM658%~s_y9BJB{}i- zo!jd0r8**?bs#dD;j%u;J0icSKC5b;%Q~~#1Yg;HyM4PF*r{FQ9FTnN|0@@YE@X`i zC({`LE{NVLnoJ1Cu>zBV*$R>uXa&>@aD9e@MMFqLFh`Ibg&=7Q&=G#E<^%F8iivq5OhXBnGCg={3}+VI+WB{E9ks{ zy++N0;-YC2^nrolK&8m+AQ7RdkwGE0U|fhfz&c_P8b$0v=<-t}FaFGH{Dt>2n8zX7 zLu|OHe!el!$CKMja)iA+M~083>+rd2^?X&$=K0DyK|Tc+slLc(omp+xd}jMeboV6#L5fpjQV zpgOc>1!l+ScvwMFgM2ae9UWzyJpr3?bCW905ed7EGD31+!)l7;^xY5N{yL zz&ZE8V{easF~2*BG@fZ05*seZb>rt1FoStMZAgxZ?ErhrSll}+z&faECc~BO=<|d8 zC_Jn)t8G66>mQM4Z)vz)oNv!29U>60{sdp@EbNHt(1sP59n(aIf}I2z*OzcW)c`&) z7y{I&gTi175Deg_IpBW5BqK(lT>x5!0KW34FXkujbqIB)WqtAicYmy9i4f!Z=5+0j*cEYob)ieET(;@0CC=aMZu>v&G zjs;|GFd{NIl~NRJ$So8Xr$;yd27qBOghT{$1h536+7{4HaI=~NOe4_&STZz<*oDx| z&x4ix{~!5#|H6A2fjV;r?OaYLuZA;VKHOeCXA$E!kEjd?yT z#w*EbZ_nMy@Z<1V0oH-lU`#os<^ZC=Rcygz0pJ-Lz#+o`0X`X6L*H$1S zB5PAAMYSfZn;_%vaC(LVYiBTlM1)Wy+$Kq)?8tl>rwYvA6@xhdMFIq{1e^>|$~c(C zJAYsu+oNCX+ntIznVI<=0erX(ABoQkq^4l9F(#wxK^&35$p zyS8~fOkVh1Bn5LUFgr%IP$@Mvz|j#7=v@H-gCTT8AV&Z{B+I4=AxOjpz}DtKbkHJV z6z0)K%s%-q7~UTJVt!`^hQ6I?nV+lZ;RAR+iGH5XCg%CHd}cnmS*;{zhCTPr3h3}z z6ZL$x9aan`8MdR(-%DB-2J^KqH&_Arni>&$b*BcnyMzOxKOCS90eXNTkQgQ?v@O6W z0GsAubcjX7C=3%4CveLEz}v%@@soEkn8CrE3@lExb47@afajB7n*-)`^?ZKrqP@L5 z$J|zj-z1;)QQKj~R#R8R592!+KWg^1?`5gcl48e$y90B~`Jlht~ zOmM9+2ctt<1mM&t@XQQA%K*Kn3}5UU`6<+Av^U`SJkYjaURSS}!(Cw1+>xbQxL?Db zfT+V~O;{P$2R)g!GQ-_@{(Zpk%|C-05qe#x1~|He1G9rR1gTL61@mQW0gnp6hzsxv zLxbiJiy(1eXcTy6?7~63$iQ{?$lv+p`0^)Soxwa#7)ry10(d?HpM=k&pBM8gIl6jD zPQyM7pHQg757l9PSfyRtxjE$S;SAOb2}OK`0QJf8&H)X(z?arJy!8IIhZ z=qAbG?nrD0*vH|sGP|CC4{>|zA4yV_Z~)gO?zbU?8sREQ48hM}3&aIr7IVPgfo6=&=lAHndGW7{E^Et)5t{(Z9dx@Si!+uwF?m1A;{qY7v2sJ`7 zA|;d^fuh&~$pQfD&@fwsMxkA38pI1j1{mHLzKox|i@^*GW@8L!7w~cNe5%kJKCQ%_ z&(B>Xt8H(;!~Wd3dhX_rAtXmY1B4!fpV$Jv)T|kfWN3&v7$D?JQYP)fV1bur;I6la zFXJcgVlV@P*%-`B0OBeP3^0J_lgMrK^No2vzhVw|M5Ke{?680CV0rlu3`mfSNCss` z=F4phBnyTH&B5pZ4jCE+u2{P;QV3emd&=;|f;>3aw|fcDu7?lc`8@IS!MsWicfq~v zu)jQt9>x_73T+Fp2%M_sVD1d|ViE8qje;-%N*Tb_A$qU;d3^a3kTaNvA=H^1y9?U6 zI^jvc^Qo}YV19S?4+DPp>Mo26KudFgRDd^7*A@XFH40girlEBJ-3*Z2BY*c7UeDmY z4CY}7^%?CF8$J)8YWMT^3M~)*!^9j0i_j=y7X%CA1&-PQjOTIu$0z^wE&SB0>)4sL u(Y^!z!2|4hyg`g&unU|sgH*x+hS$dNCGX{1_zB2^bEbVB7vw&F=Klx!^T+D| literal 0 HcmV?d00001 diff --git a/server/csm.vpc b/server/csm.vpc new file mode 100644 index 0000000..45300a6 --- /dev/null +++ b/server/csm.vpc @@ -0,0 +1,9 @@ + + +$Project +{ + $Folder "Source Files" + { + $File "env_cascade_light.cpp" + } +} diff --git a/server/env_cascade_light.cpp b/server/env_cascade_light.cpp new file mode 100644 index 0000000..612864f --- /dev/null +++ b/server/env_cascade_light.cpp @@ -0,0 +1,491 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Entity to control screen overlays on a player +// +//============================================================================= + +#include "cbase.h" +#include "shareddefs.h" +#include "lights.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define ENV_CASCADE_STARTON (1<<0) + +static ConVar defdist("csm_default_distance", "1000", 0, "Default Z distance. Used for some fov calculations"); +static ConVar curdist("csm_current_distance","14000", 0, "i like nagatoro"); + +static ConVar defFOV("csm_default_fov","15", 0, "Default FOV. Used for some fov calculations"); +static ConVar curFOV("csm_current_fov","15", FCVAR_DEVELOPMENTONLY, "Current FOV. Please dont change by yourself, please."); +static ConVar csm_second_fov("csm_second_fov", "17", FCVAR_NONE ,"FOV of the second csm."); + +class CLightOrigin : public CPointEntity +{ + DECLARE_CLASS(CLightOrigin, CPointEntity); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + void Spawn(); + + CLightOrigin(); + + bool angFEnv = true; + + void InitialThink(void); + +private: + + CNetworkVector(LightEnvVector); +}; + +LINK_ENTITY_TO_CLASS(csmorigin, CLightOrigin); + +BEGIN_DATADESC(CLightOrigin) +DEFINE_FIELD(LightEnvVector, FIELD_VECTOR), +DEFINE_THINKFUNC(InitialThink) +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST(CLightOrigin, DT_LightOrigin) +SendPropVector(SENDINFO(LightEnvVector)) +END_SEND_TABLE() + +CLightOrigin::CLightOrigin() +{ +} + +void CLightOrigin::Spawn() +{ + if (angFEnv) + { + CBaseEntity* pEntity = NULL; + pEntity = gEntList.FindEntityByClassname(pEntity, "light_environment"); + if (pEntity) + { + CEnvLight* pEnv = dynamic_cast(pEntity); + + QAngle bb = pEnv->GetAbsAngles(); + bb.x = bb.x; + SetAbsAngles(bb); + //Msg("light_environment Founded!\n"); + ConColorMsg(Color(0,230,0), "light_environment Founded!\n"); + } + else + { + //Msg("What the fuck? Map dont have light_environment with targetname!"); + ConColorMsg(Color(230, 0, 0), "What the fuck? Map dont have light_environment with targetname!\n"); + } + } +} + +void CLightOrigin::InitialThink() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: second csm +//----------------------------------------------------------------------------- + +class CEnvCascadeLightSecond : public CPointEntity +{ + DECLARE_CLASS(CEnvCascadeLightSecond, CPointEntity); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CEnvCascadeLightSecond(); + bool KeyValue(const char* szKeyName, const char* szValue); + + // Always transmit to clients + virtual int UpdateTransmitState(); + virtual void Activate(void); + + void InitialThink(void); + + CNetworkHandle(CBaseEntity, m_hTargetEntity); + CNetworkVector(m_LinearFloatLightColor); + +private: + CNetworkVar(bool, m_bState); + CNetworkVar(float, m_flLightFOV); + CNetworkVar(bool, m_bEnableShadows); + CNetworkVar(bool, m_bLightOnlyTarget); + CNetworkVar(bool, m_bLightWorld); + CNetworkVar(bool, m_bCameraSpace); + CNetworkVar(float, m_flAmbient); + CNetworkString(m_SpotlightTextureName, MAX_PATH); + CNetworkVar(int, m_nSpotlightTextureFrame); + CNetworkVar(float, m_flNearZ); + CNetworkVar(float, m_flFarZ); + CNetworkVar(int, m_nShadowQuality); +}; + +LINK_ENTITY_TO_CLASS(second_csm, CEnvCascadeLightSecond); + +BEGIN_DATADESC(CEnvCascadeLightSecond) +DEFINE_FIELD(m_hTargetEntity, FIELD_EHANDLE), +DEFINE_FIELD(m_bState, FIELD_BOOLEAN), +DEFINE_KEYFIELD(m_flLightFOV, FIELD_FLOAT, "lightfov"), +DEFINE_KEYFIELD(m_bEnableShadows, FIELD_BOOLEAN, "enableshadows"), +DEFINE_KEYFIELD(m_bLightOnlyTarget, FIELD_BOOLEAN, "lightonlytarget"), +DEFINE_KEYFIELD(m_bLightWorld, FIELD_BOOLEAN, "lightworld"), +DEFINE_KEYFIELD(m_bCameraSpace, FIELD_BOOLEAN, "cameraspace"), +DEFINE_KEYFIELD(m_flAmbient, FIELD_FLOAT, "ambient"), +DEFINE_AUTO_ARRAY_KEYFIELD(m_SpotlightTextureName, FIELD_CHARACTER, "texturename"), +DEFINE_KEYFIELD(m_nSpotlightTextureFrame, FIELD_INTEGER, "textureframe"), +DEFINE_KEYFIELD(m_flNearZ, FIELD_FLOAT, "nearz"), +DEFINE_KEYFIELD(m_flFarZ, FIELD_FLOAT, "farz"), +DEFINE_KEYFIELD(m_nShadowQuality, FIELD_INTEGER, "shadowquality"), +DEFINE_FIELD(m_LinearFloatLightColor, FIELD_VECTOR), +DEFINE_THINKFUNC(InitialThink), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST(CEnvCascadeLightSecond, DT_EnvCascadeLightSecond) +SendPropEHandle(SENDINFO(m_hTargetEntity)), +SendPropBool(SENDINFO(m_bState)), +SendPropFloat(SENDINFO(m_flLightFOV)), +SendPropBool(SENDINFO(m_bEnableShadows)), +SendPropBool(SENDINFO(m_bLightOnlyTarget)), +SendPropBool(SENDINFO(m_bLightWorld)), +SendPropBool(SENDINFO(m_bCameraSpace)), +SendPropVector(SENDINFO(m_LinearFloatLightColor)), +SendPropFloat(SENDINFO(m_flAmbient)), +SendPropString(SENDINFO(m_SpotlightTextureName)), +SendPropInt(SENDINFO(m_nSpotlightTextureFrame)), +SendPropFloat(SENDINFO(m_flNearZ), 16, SPROP_ROUNDDOWN, 0.0f, 500.0f), +SendPropFloat(SENDINFO(m_flFarZ), 18, SPROP_ROUNDDOWN, 0.0f, 1500.0f), +SendPropInt(SENDINFO(m_nShadowQuality), 1, SPROP_UNSIGNED) // Just one bit for now +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CEnvCascadeLightSecond::CEnvCascadeLightSecond(void) +{ + m_bState = true; + m_flLightFOV = 45.0f; + m_bEnableShadows = true; + m_bLightOnlyTarget = false; + m_bLightWorld = true; + m_bCameraSpace = false; + + Q_strcpy(m_SpotlightTextureName.GetForModify(), "tools\\fakecsm\\mask_ring"); + m_nSpotlightTextureFrame = 0; + m_LinearFloatLightColor.Init(1.0f, 1.0f, 1.0f); + m_flAmbient = 0.0f; + m_flNearZ = 8000.0f; + m_flFarZ = 16000.0f; + m_nShadowQuality = 0; +} + +void UTIL_ColorStringToLinearFloatColorCSMFakeSecond(Vector& color, const char* pString) +{ + float tmp[4]; + UTIL_StringToFloatArray(tmp, 4, pString); + if (tmp[3] <= 0.0f) + { + tmp[3] = 255.0f; + } + tmp[3] *= (1.0f / 255.0f); + color.x = GammaToLinear(tmp[0] * (1.0f / 255.0f)) * tmp[3]; + color.y = GammaToLinear(tmp[1] * (1.0f / 255.0f)) * tmp[3]; + color.z = GammaToLinear(tmp[2] * (1.0f / 255.0f)) * tmp[3]; +} + +bool CEnvCascadeLightSecond::KeyValue(const char* szKeyName, const char* szValue) +{ + if (FStrEq(szKeyName, "lightcolor")) + { + Vector tmp; + UTIL_ColorStringToLinearFloatColorCSMFakeSecond(tmp, szValue); + m_LinearFloatLightColor = tmp; + } + else + { + return BaseClass::KeyValue(szKeyName, szValue); + } + + return true; +} + +void CEnvCascadeLightSecond::Activate(void) +{ + if (GetSpawnFlags() & ENV_CASCADE_STARTON) + { + m_bState = true; + } + + SetThink(&CEnvCascadeLightSecond::InitialThink); + SetNextThink(gpGlobals->curtime + 0.1f); + + BaseClass::Activate(); +} + +void CEnvCascadeLightSecond::InitialThink(void) +{ + float bibigon = defdist.GetFloat() / curdist.GetFloat(); + m_flLightFOV = csm_second_fov.GetFloat() * bibigon; + m_hTargetEntity = gEntList.FindEntityByName(NULL, m_target); +} + +int CEnvCascadeLightSecond::UpdateTransmitState() +{ + return SetTransmitState(FL_EDICT_ALWAYS); +} + + + +//----------------------------------------------------------------------------- +// Purpose: main csm code +//----------------------------------------------------------------------------- +class CEnvCascadeLight : public CPointEntity +{ + DECLARE_CLASS(CEnvCascadeLight, CPointEntity); +public: + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); + + CEnvCascadeLight(); + bool KeyValue(const char* szKeyName, const char* szValue); + + // Always transmit to clients + virtual int UpdateTransmitState(); + virtual void Activate(void); + void Spawn(); + void Preparation(); + + void InputTurnOn(inputdata_t& inputdata); + void InputTurnOff(inputdata_t& inputdata); + void InputSetEnableShadows(inputdata_t& inputdata); + void InputSetLightColor( inputdata_t &inputdata ); + void InputSetSpotlightTexture(inputdata_t& inputdata); + void InputSetAmbient(inputdata_t& inputdata); + + void InitialThink(void); + + CNetworkHandle(CBaseEntity, m_hTargetEntity); + +private: + CNetworkVar(bool, m_bState); + CNetworkVar(float, m_flLightFOV); + CNetworkVar(bool, EnableAngleFromEnv); + CNetworkVar(bool, m_bEnableShadows); + CNetworkVar(bool, m_bLightOnlyTarget); + CNetworkVar(bool, m_bLightWorld); + CNetworkVar(bool, m_bCameraSpace); + CNetworkVector(m_LinearFloatLightColor); + CNetworkVar(float, m_flAmbient); + CNetworkString(m_SpotlightTextureName, MAX_PATH); + CNetworkVar(int, m_nSpotlightTextureFrame); + CNetworkVar(float, m_flNearZ); + CNetworkVar(float, m_flFarZ); + CNetworkVar(int, m_nShadowQuality); +}; + +LINK_ENTITY_TO_CLASS(env_cascade_light, CEnvCascadeLight); + +BEGIN_DATADESC(CEnvCascadeLight) +DEFINE_FIELD(m_hTargetEntity, FIELD_EHANDLE), +DEFINE_FIELD(m_bState, FIELD_BOOLEAN), +DEFINE_KEYFIELD(m_bEnableShadows, FIELD_BOOLEAN, "enableshadows"), +DEFINE_KEYFIELD(m_bLightOnlyTarget, FIELD_BOOLEAN, "lightonlytarget"), +DEFINE_KEYFIELD(m_bLightWorld, FIELD_BOOLEAN, "lightworld"), +DEFINE_KEYFIELD(m_bCameraSpace, FIELD_BOOLEAN, "cameraspace"), +DEFINE_KEYFIELD(m_flAmbient, FIELD_FLOAT, "ambient"), +DEFINE_AUTO_ARRAY_KEYFIELD(m_SpotlightTextureName, FIELD_CHARACTER, "texturename"), +DEFINE_KEYFIELD(m_nSpotlightTextureFrame, FIELD_INTEGER, "textureframe"), +DEFINE_KEYFIELD(m_flNearZ, FIELD_FLOAT, "nearz"), +DEFINE_KEYFIELD(m_flFarZ, FIELD_FLOAT, "farz"), +DEFINE_KEYFIELD(m_nShadowQuality, FIELD_INTEGER, "shadowquality"), +DEFINE_FIELD(m_LinearFloatLightColor, FIELD_VECTOR), +DEFINE_KEYFIELD(EnableAngleFromEnv, FIELD_BOOLEAN, "uselightenvangles"), + +DEFINE_INPUTFUNC(FIELD_VOID, "TurnOn", InputTurnOn), +DEFINE_INPUTFUNC(FIELD_VOID, "TurnOff", InputTurnOff), +DEFINE_INPUTFUNC(FIELD_BOOLEAN, "EnableShadows", InputSetEnableShadows), +// this is broken . . need to be able to set color and intensity like light_dynamic +// DEFINE_INPUTFUNC( FIELD_COLOR32, "LightColor", InputSetLightColor ), +DEFINE_INPUTFUNC(FIELD_FLOAT, "Ambient", InputSetAmbient), +DEFINE_INPUTFUNC(FIELD_STRING, "SpotlightTexture", InputSetSpotlightTexture), +DEFINE_THINKFUNC(InitialThink), +END_DATADESC() + +IMPLEMENT_SERVERCLASS_ST(CEnvCascadeLight, DT_EnvCascadeLight) +SendPropEHandle(SENDINFO(m_hTargetEntity)), +SendPropBool(SENDINFO(m_bState)), +SendPropFloat(SENDINFO(m_flLightFOV)), +SendPropBool(SENDINFO(m_bEnableShadows)), +SendPropBool(SENDINFO(m_bLightOnlyTarget)), +SendPropBool(SENDINFO(m_bLightWorld)), +SendPropBool(SENDINFO(m_bCameraSpace)), +SendPropVector(SENDINFO(m_LinearFloatLightColor)), +SendPropFloat(SENDINFO(m_flAmbient)), +SendPropString(SENDINFO(m_SpotlightTextureName)), +SendPropInt(SENDINFO(m_nSpotlightTextureFrame)), +SendPropFloat(SENDINFO(m_flNearZ), 16, SPROP_ROUNDDOWN, 0.0f, 500.0f), +SendPropFloat(SENDINFO(m_flFarZ), 18, SPROP_ROUNDDOWN, 0.0f, 1500.0f), +SendPropInt(SENDINFO(m_nShadowQuality), 1, SPROP_UNSIGNED) // Just one bit for now +END_SEND_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CEnvCascadeLight::CEnvCascadeLight(void) +{ + m_bState = true; + m_flLightFOV = 45.0f; + m_bEnableShadows = true; + m_bLightOnlyTarget = false; + m_bLightWorld = true; + m_bCameraSpace = false; + EnableAngleFromEnv = false; + + Q_strcpy(m_SpotlightTextureName.GetForModify(), "tools\\fakecsm\\mask_center"); + m_nSpotlightTextureFrame = 0; + m_LinearFloatLightColor.Init(1.0f, 1.0f, 1.0f); + m_flAmbient = 0.0f; + m_flNearZ = 8000.0f; + m_flFarZ = 16000.0f; + m_nShadowQuality = 0; +} + +void CEnvCascadeLight::Preparation() +{ + CreateEntityByName("csmorigin"); + CreateEntityByName("second_csm"); + defFOV.SetValue(m_flLightFOV); + CBaseEntity* CSMOrigin = NULL; + CBaseEntity* CSMSecond = NULL; + + CSMOrigin = gEntList.FindEntityByClassname(CSMOrigin, "csmorigin"); + //if origin is exist + if (CSMOrigin) + { + + CSMSecond = gEntList.FindEntityByClassname(CSMSecond, "second_csm"); + //if second csm is exist + if (CSMSecond) + { + CEnvCascadeLightSecond* SecondCSM = dynamic_cast(CSMSecond); + SecondCSM->SetAbsAngles(GetAbsAngles()); + SecondCSM->SetAbsOrigin(GetAbsOrigin()); + SecondCSM->SetParent(GetBaseEntity()); + SecondCSM->m_LinearFloatLightColor = m_LinearFloatLightColor; + + DispatchSpawn(SecondCSM); + } + + CLightOrigin* pEnv = dynamic_cast(CSMOrigin); + if (EnableAngleFromEnv) + { + pEnv->angFEnv = true; + } + else + { + pEnv->SetAbsAngles(GetAbsAngles()); + } + SetParent(pEnv, 1); + SetAbsOrigin(Vector(pEnv->GetAbsOrigin().x, pEnv->GetAbsOrigin().y, pEnv->GetAbsOrigin().z + curdist.GetInt())); + + float bibigon = defdist.GetFloat() / curdist.GetFloat(); + curFOV.SetValue(defFOV.GetFloat() * bibigon); + m_flLightFOV = curFOV.GetFloat(); + DispatchSpawn(CSMOrigin); + } + else + { + Msg("Main csm entity can't find \"csmorigin\" entity!"); + } + +} + +void CEnvCascadeLight::Spawn() +{ + Preparation(); +} + +void UTIL_ColorStringToLinearFloatColorCSMFake(Vector& color, const char* pString) +{ + float tmp[4]; + UTIL_StringToFloatArray(tmp, 4, pString); + if (tmp[3] <= 0.0f) + { + tmp[3] = 255.0f; + } + tmp[3] *= (1.0f / 255.0f); + color.x = GammaToLinear(tmp[0] * (1.0f / 255.0f)) * tmp[3]; + color.y = GammaToLinear(tmp[1] * (1.0f / 255.0f)) * tmp[3]; + color.z = GammaToLinear(tmp[2] * (1.0f / 255.0f)) * tmp[3]; +} + +bool CEnvCascadeLight::KeyValue(const char* szKeyName, const char* szValue) +{ + if (FStrEq(szKeyName, "lightcolor")) + { + Vector tmp; + UTIL_ColorStringToLinearFloatColorCSMFake(tmp, szValue); + m_LinearFloatLightColor = tmp; + } + else + { + return BaseClass::KeyValue(szKeyName, szValue); + } + + return true; +} + +void CEnvCascadeLight::InputTurnOn(inputdata_t& inputdata) +{ + m_bState = true; +} + +void CEnvCascadeLight::InputTurnOff(inputdata_t& inputdata) +{ + m_bState = false; +} + + +void CEnvCascadeLight::InputSetEnableShadows(inputdata_t& inputdata) +{ + m_bEnableShadows = inputdata.value.Bool(); +} + +//void CEnvProjectedTexture::InputSetLightColor( inputdata_t &inputdata ) +//{ + //m_cLightColor = inputdata.value.Color32(); +//} + +void CEnvCascadeLight::InputSetAmbient(inputdata_t& inputdata) +{ + m_flAmbient = inputdata.value.Float(); +} + +void CEnvCascadeLight::InputSetSpotlightTexture(inputdata_t& inputdata) +{ + Q_strcpy(m_SpotlightTextureName.GetForModify(), inputdata.value.String()); +} + +void CEnvCascadeLight::Activate(void) +{ + if (GetSpawnFlags() & ENV_CASCADE_STARTON) + { + m_bState = true; + } + + SetThink(&CEnvCascadeLight::InitialThink); + SetNextThink(gpGlobals->curtime + 0.1f); + + BaseClass::Activate(); +} + +void CEnvCascadeLight::InitialThink(void) +{ + m_hTargetEntity = gEntList.FindEntityByName(NULL, m_target); +} + +int CEnvCascadeLight::UpdateTransmitState() +{ + return SetTransmitState(FL_EDICT_ALWAYS); +} diff --git a/server/lights.h b/server/lights.h new file mode 100644 index 0000000..b457da9 --- /dev/null +++ b/server/lights.h @@ -0,0 +1,57 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef LIGHTS_H +#define LIGHTS_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CLight : public CPointEntity +{ +public: + DECLARE_CLASS( CLight, CPointEntity ); + + bool KeyValue( const char *szKeyName, const char *szValue ); + void Spawn( void ); + void FadeThink( void ); + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); + + void TurnOn( void ); + void TurnOff( void ); + void Toggle( void ); + + // Input handlers + void InputSetPattern( inputdata_t &inputdata ); + void InputFadeToPattern( inputdata_t &inputdata ); + + void InputToggle( inputdata_t &inputdata ); + void InputTurnOn( inputdata_t &inputdata ); + void InputTurnOff( inputdata_t &inputdata ); + + DECLARE_DATADESC(); + +private: + int m_iStyle; + int m_iDefaultStyle; + string_t m_iszPattern; + char m_iCurrentFade; + char m_iTargetFade; +}; + +class CEnvLight : public CLight +{ +public: + DECLARE_CLASS(CEnvLight, CLight); + + bool KeyValue(const char* szKeyName, const char* szValue); + void Spawn(void); +}; + +#endif // LIGHTS_H