From 895452c09b26c128db0676d6ec050e1ca9c9f89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kerim=20=C4=8Camd=C5=BEi=C4=87?= Date: Wed, 11 Mar 2026 21:14:48 +0100 Subject: [PATCH 1/5] added env_projectedvideo --- sp/src/game/client/C_Env_Projected_Texture.h | 4 + sp/src/game/client/c_env_projectedtexture.cpp | 43 ++- sp/src/game/client/client_mapbase.vpc | 1 + sp/src/game/server/server_mapbase.vpc | 1 + sp/src/game/shared/env_projectedvideo.cpp | 276 ++++++++++++++++++ .../stdshaders/SDK_flashlight_ps2x.fxc | 3 + 6 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 sp/src/game/shared/env_projectedvideo.cpp diff --git a/sp/src/game/client/C_Env_Projected_Texture.h b/sp/src/game/client/C_Env_Projected_Texture.h index cb6268148ca..81ddf170f6b 100644 --- a/sp/src/game/client/C_Env_Projected_Texture.h +++ b/sp/src/game/client/C_Env_Projected_Texture.h @@ -13,6 +13,7 @@ #include "c_baseentity.h" #include "basetypes.h" +#include "video/ivideoservices.h" #ifdef ASW_PROJECTED_TEXTURES @@ -26,6 +27,7 @@ class C_EnvProjectedTexture : public C_BaseEntity DECLARE_CLIENTCLASS(); void SetMaterial( IMaterial *pMaterial ); + void SetVideoMaterial( IMaterial* pVidMat ); void SetLightColor( byte r, byte g, byte b, byte a ); void SetSize( float flSize ); void SetRotation( float flRotation ); @@ -102,6 +104,8 @@ class C_EnvProjectedTexture : public C_BaseEntity Vector m_vecExtentsMax; static float m_flVisibleBBoxMinHeight; + + ITexture* m_pVideoMatTexture; }; diff --git a/sp/src/game/client/c_env_projectedtexture.cpp b/sp/src/game/client/c_env_projectedtexture.cpp index 6644965265a..414f328ae76 100644 --- a/sp/src/game/client/c_env_projectedtexture.cpp +++ b/sp/src/game/client/c_env_projectedtexture.cpp @@ -20,6 +20,7 @@ #include "view_shared.h" #include "texture_group_names.h" #include "tier0/icommandline.h" +#include "materialsystem/imaterialvar.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -130,6 +131,27 @@ void C_EnvProjectedTexture::ShutDownLightHandle( void ) } +void C_EnvProjectedTexture::SetVideoMaterial( IMaterial* pVidMat ) +{ + IMaterial* mat = pVidMat; + + if (mat) + { + bool found = false; + IMaterialVar* var = mat->FindVar("$basetexture", &found); + + if (var && found) + { + ITexture* tex = var->GetTextureValue(); + if (tex) + { + m_pVideoMatTexture = tex; + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + } + } + } +} + void C_EnvProjectedTexture::SetLightColor( byte r, byte g, byte b, byte a ) { m_LightColor.r = r; @@ -302,6 +324,20 @@ void C_EnvProjectedTexture::UpdateLight( void ) fHighFOV = m_flLightHorFOV; state.m_fHorizontalFOVDegrees = m_flLightHorFOV; + // If we have a video texture, compute horizontal FOV from video aspect (keep vertical FOV) + if (m_pVideoMatTexture) + { + int vidW = m_pVideoMatTexture->GetActualWidth(); + int vidH = m_pVideoMatTexture->GetActualHeight(); + + if (vidW > 0 && vidH > 0) + { + const float aspect = (float)vidW / (float)vidH; + const float vfovRad = m_flLightFOV * (M_PI / 180.0f); + const float hfovRad = 2.0f * atanf(aspect * tanf(vfovRad * 0.5f)); + state.m_fHorizontalFOVDegrees = hfovRad * (180.0f / M_PI); + } + } #else state.m_fHorizontalFOVDegrees = m_flLightFOV; #endif @@ -420,7 +456,12 @@ void C_EnvProjectedTexture::UpdateLight( void ) state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias(); #endif state.m_bEnableShadows = m_bEnableShadows; - state.m_pSpotlightTexture = m_SpotlightTexture; +#ifdef MAPBASE + if (m_pVideoMatTexture) + state.m_pSpotlightTexture = m_pVideoMatTexture; + else +#endif // MAPBASE + state.m_pSpotlightTexture = m_SpotlightTexture; state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 08d38e4e3e6..9719549a8c3 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -36,6 +36,7 @@ $Project $File "c_movie_display.h" $File "vgui_movie_display.cpp" $File "convarproxy.cpp" + $File "$SRCDIR\game\shared\env_projectedvideo.cpp" $Folder "Mapbase" { diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index b49ad1589bf..abd959c1cd9 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -35,6 +35,7 @@ $Project $File "ai_expresserfollowup.cpp" [$NEW_RESPONSE_SYSTEM] $File "ai_speechqueue.cpp" [$NEW_RESPONSE_SYSTEM] $File "ai_speechqueue.h" [$NEW_RESPONSE_SYSTEM] + $File "$SRCDIR\game\shared\env_projectedvideo.cpp" $Folder "Mapbase" { diff --git a/sp/src/game/shared/env_projectedvideo.cpp b/sp/src/game/shared/env_projectedvideo.cpp new file mode 100644 index 00000000000..0f3200bf723 --- /dev/null +++ b/sp/src/game/shared/env_projectedvideo.cpp @@ -0,0 +1,276 @@ +#include "cbase.h" + +#ifdef CLIENT_DLL +#define CEnvProjectedVideo C_EnvProjectedVideo +#define CEnvProjectedTexture C_EnvProjectedTexture + +#include "C_Env_Projected_Texture.h" +#include "video/ivideoservices.h" +#include "materialsystem/imaterialvar.h" +#include "materialsystem/itexture.h" +#else +#include "env_projectedtexture.h" +#endif + +class CEnvProjectedVideo : public CEnvProjectedTexture +{ +public: + DECLARE_CLASS(CEnvProjectedVideo, CEnvProjectedTexture); + +#ifdef CLIENT_DLL + DECLARE_CLIENTCLASS(); +#else + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); +#endif + + CEnvProjectedVideo(); + ~CEnvProjectedVideo(); + +#ifdef CLIENT_DLL + virtual void OnDataChanged(DataUpdateType_t type); + virtual void ClientThink(); +#endif + +private: +#ifdef GAME_DLL + + CNetworkVar(string_t, m_szVideoFile); + +#else + + void CreateVideo(); + void DestroyVideo(); + void RenderVideoToTexture(); + + char m_szVideoFile[256]; + + IVideoMaterial* m_pVideoMaterial; + + CTextureReference m_pVideoRT; + CMaterialReference m_pVideoRTMaterial; + + float m_flLastVideoTime; + float m_flaspect_ratio; +#endif +}; + +#ifdef GAME_DLL + +LINK_ENTITY_TO_CLASS(env_projectedvideo, CEnvProjectedVideo); + +IMPLEMENT_SERVERCLASS_ST(CEnvProjectedVideo, DT_EnvProjectedVideo) + SendPropStringT(SENDINFO(m_szVideoFile)), +END_SEND_TABLE() + +BEGIN_DATADESC(CEnvProjectedVideo) + DEFINE_KEYFIELD(m_szVideoFile, FIELD_STRING, "VideoFile"), +END_DATADESC() + +#else + +#undef CEnvProjectedVideo + +IMPLEMENT_CLIENTCLASS_DT(C_EnvProjectedVideo, DT_EnvProjectedVideo, CEnvProjectedVideo) + RecvPropString(RECVINFO(m_szVideoFile)), +END_RECV_TABLE() + +#define CEnvProjectedVideo C_EnvProjectedVideo +#endif + +CEnvProjectedVideo::CEnvProjectedVideo() +{ +#ifdef CLIENT_DLL + m_pVideoMaterial = nullptr; + m_flLastVideoTime = -1.0f; +#endif // CLIENT_DLL +} + +CEnvProjectedVideo::~CEnvProjectedVideo() +{ +#ifdef CLIENT_DLL + DestroyVideo(); + SetNextClientThink(CLIENT_THINK_NEVER); +#endif // CLIENT_DLL +} + +#ifdef CLIENT_DLL +void CEnvProjectedVideo::OnDataChanged(DataUpdateType_t type) +{ + if (type == DATA_UPDATE_CREATED) + { + CreateVideo(); + SetNextClientThink(CLIENT_THINK_ALWAYS); + } + + BaseClass::OnDataChanged(type); +} + +void CEnvProjectedVideo::RenderVideoToTexture() +{ + if (!m_pVideoMaterial || !m_pVideoRT) + return; + + // Get the video frame size + int w = 0; + int h = 0; + m_pVideoMaterial->GetVideoImageSize(&w, &h); + + if (w <= 0 || h <= 0) + return; + + IMaterial* videoMat = m_pVideoMaterial->GetMaterial(); + if (!videoMat) + return; + + bool found = false; + IMaterialVar *pVar = videoMat->FindVar("$ytexture", &found); + + int ytexW, ytexH; + ytexW = ytexH = 0; + if (pVar) + { + ytexW = pVar->GetTextureValue()->GetActualWidth(); + ytexH = pVar->GetTextureValue()->GetActualHeight(); + } + + if (ytexH <= 0 && ytexW <= 0) + return; + + + CMatRenderContextPtr ctx(materials); + + // Set the render target + ctx->PushRenderTargetAndViewport((ITexture*)m_pVideoRT); + + // Clear RT completely + ctx->ClearColor4ub(0, 0, 0, 255); + ctx->ClearBuffers(true, true); + + // Draw the video material into the RT + ctx->DrawScreenSpaceRectangle( + videoMat, + 0, 0, + w, h, // destination rectangle + 0, 0, // source texel start + w, h, // source texel end + ytexW, ytexH // source texture dimensions + ); + + // Restore previous render target + ctx->PopRenderTargetAndViewport(); +} + +void CEnvProjectedVideo::CreateVideo() +{ + if (!g_pVideo) + return; + + if (!m_szVideoFile) + return; + + // If we already have a material, destroy & recreate it + if (m_pVideoMaterial) + { + ConColorMsg(Color(200, 200, 0, 255), "[ProjectedVideo] Recreating existing video material.\n"); + DestroyVideo(); + } + + //Need to do this when it gets recreated so the texture still gets update + char name[60]; + V_snprintf(name, sizeof(name), "ProjectedVideoMaterial_%d_%d", entindex(), random->RandomInt(0, 20)); + + m_pVideoMaterial = g_pVideo->CreateVideoMaterial( + name, + m_szVideoFile, + "GAME", + VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS | VideoPlaybackFlags::NO_AUDIO | VideoPlaybackFlags::LOOP_VIDEO | VideoPlaybackFlags::DONT_AUTO_START_VIDEO, + VideoSystem::DETERMINE_FROM_FILE_EXTENSION); + + if (!m_pVideoMaterial) + { + ConColorMsg(Color(255, 100, 100, 255), "[ProjectedVideo] CreateVideo: failed to create video material for '%s'.\n", STRING(m_szVideoFile)); + return; + } + + ConColorMsg(Color(255, 100, 100, 255), "[ProjectedVideo] CreateVideo: material shader = %s.\n", m_pVideoMaterial->GetMaterial()->GetShaderName()); + + int w, h; + m_pVideoMaterial->GetVideoImageSize(&w, &h); + + g_pMaterialSystem->BeginRenderTargetAllocation(); + + m_pVideoRT.Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2( + VarArgs("_rt_projectedvideo_%d", entindex()), + w, + h, + RT_SIZE_OFFSCREEN, + g_pMaterialSystem->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_NONE, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + CREATERENDERTARGETFLAGS_HDR + )); + + g_pMaterialSystem->EndRenderTargetAllocation(); + + KeyValues* kv = new KeyValues("UnlitGeneric"); + ITexture *pTex = (ITexture*)m_pVideoRT; + kv->SetString("$basetexture", pTex->GetName()); + kv->SetInt("$translucent", 1); + + m_pVideoRTMaterial.Init( materials->CreateMaterial( + VarArgs("ProjectedVideoRTMat_%d", entindex()), + kv + )); + + m_pVideoMaterial->StartVideo(); + + ConColorMsg(Color(255, 255, 20, 255), "[ProjectedVideo] Created video material. Looping=%s\n", m_pVideoMaterial->IsLooping() ? "true" : "false"); + + BaseClass::SetVideoMaterial(m_pVideoRTMaterial); +} + +void CEnvProjectedVideo::DestroyVideo() +{ + if (m_pVideoMaterial && g_pVideo) + { + + ConColorMsg(Color(255, 120, 120, 255), "[ProjectedVideo] Destroying video material.\n"); + g_pVideo->DestroyVideoMaterial(m_pVideoMaterial); + m_pVideoMaterial = nullptr; + m_pVideoRT.Shutdown(); + m_pVideoRTMaterial.Shutdown(); + BaseClass::SetVideoMaterial(NULL); + } + +} + +void CEnvProjectedVideo::ClientThink() +{ + if (!m_pVideoMaterial) + { + CreateVideo(); + SetNextClientThink(CLIENT_THINK_ALWAYS); + return; + } + + // Pump decoder first + m_pVideoMaterial->Update(); + + RenderVideoToTexture(); + + float curTime = m_pVideoMaterial->GetCurrentVideoTime(); + //float duration = m_pVideoMaterial->GetVideoDuration(); + //ConMsg( "Time: %f / %f\n", curTime, duration ); + + const float EPS = 1e-4f; + if (m_flLastVideoTime < 0.0f || fabsf(curTime - m_flLastVideoTime) > EPS) + { + m_flLastVideoTime = curTime; + } + + // Think at ~30fps instead of every engine tick to reduce load + SetNextClientThink(gpGlobals->curtime + 0.03f); +} + +#endif \ No newline at end of file diff --git a/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc b/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc index 0e9d5d9feb2..363778b6c8f 100644 --- a/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc +++ b/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc @@ -106,6 +106,9 @@ float4 main( PS_INPUT i ) : COLOR float3 spotColor = float3(0,0,0); float3 vProjCoords = i.spotTexCoord.xyz / i.spotTexCoord.w; + float2 tmp = max(-vProjCoords.xy, vProjCoords.xy - 1); + clip(-max(tmp.x, tmp.y)); + #if ( defined( _X360 ) ) float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f ); From e101160b6c7b149807b734e65e22096914d96d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kerim=20=C4=8Camd=C5=BEi=C4=87?= Date: Wed, 11 Mar 2026 21:14:48 +0100 Subject: [PATCH 2/5] added env_projectedvideo --- sp/src/game/client/C_Env_Projected_Texture.h | 4 + sp/src/game/client/c_env_projectedtexture.cpp | 43 ++- sp/src/game/client/client_mapbase.vpc | 1 + sp/src/game/server/server_mapbase.vpc | 1 + sp/src/game/shared/env_projectedvideo.cpp | 266 ++++++++++++++++++ .../stdshaders/SDK_flashlight_ps2x.fxc | 3 + 6 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 sp/src/game/shared/env_projectedvideo.cpp diff --git a/sp/src/game/client/C_Env_Projected_Texture.h b/sp/src/game/client/C_Env_Projected_Texture.h index cb6268148ca..81ddf170f6b 100644 --- a/sp/src/game/client/C_Env_Projected_Texture.h +++ b/sp/src/game/client/C_Env_Projected_Texture.h @@ -13,6 +13,7 @@ #include "c_baseentity.h" #include "basetypes.h" +#include "video/ivideoservices.h" #ifdef ASW_PROJECTED_TEXTURES @@ -26,6 +27,7 @@ class C_EnvProjectedTexture : public C_BaseEntity DECLARE_CLIENTCLASS(); void SetMaterial( IMaterial *pMaterial ); + void SetVideoMaterial( IMaterial* pVidMat ); void SetLightColor( byte r, byte g, byte b, byte a ); void SetSize( float flSize ); void SetRotation( float flRotation ); @@ -102,6 +104,8 @@ class C_EnvProjectedTexture : public C_BaseEntity Vector m_vecExtentsMax; static float m_flVisibleBBoxMinHeight; + + ITexture* m_pVideoMatTexture; }; diff --git a/sp/src/game/client/c_env_projectedtexture.cpp b/sp/src/game/client/c_env_projectedtexture.cpp index 6644965265a..414f328ae76 100644 --- a/sp/src/game/client/c_env_projectedtexture.cpp +++ b/sp/src/game/client/c_env_projectedtexture.cpp @@ -20,6 +20,7 @@ #include "view_shared.h" #include "texture_group_names.h" #include "tier0/icommandline.h" +#include "materialsystem/imaterialvar.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -130,6 +131,27 @@ void C_EnvProjectedTexture::ShutDownLightHandle( void ) } +void C_EnvProjectedTexture::SetVideoMaterial( IMaterial* pVidMat ) +{ + IMaterial* mat = pVidMat; + + if (mat) + { + bool found = false; + IMaterialVar* var = mat->FindVar("$basetexture", &found); + + if (var && found) + { + ITexture* tex = var->GetTextureValue(); + if (tex) + { + m_pVideoMatTexture = tex; + m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + } + } + } +} + void C_EnvProjectedTexture::SetLightColor( byte r, byte g, byte b, byte a ) { m_LightColor.r = r; @@ -302,6 +324,20 @@ void C_EnvProjectedTexture::UpdateLight( void ) fHighFOV = m_flLightHorFOV; state.m_fHorizontalFOVDegrees = m_flLightHorFOV; + // If we have a video texture, compute horizontal FOV from video aspect (keep vertical FOV) + if (m_pVideoMatTexture) + { + int vidW = m_pVideoMatTexture->GetActualWidth(); + int vidH = m_pVideoMatTexture->GetActualHeight(); + + if (vidW > 0 && vidH > 0) + { + const float aspect = (float)vidW / (float)vidH; + const float vfovRad = m_flLightFOV * (M_PI / 180.0f); + const float hfovRad = 2.0f * atanf(aspect * tanf(vfovRad * 0.5f)); + state.m_fHorizontalFOVDegrees = hfovRad * (180.0f / M_PI); + } + } #else state.m_fHorizontalFOVDegrees = m_flLightFOV; #endif @@ -420,7 +456,12 @@ void C_EnvProjectedTexture::UpdateLight( void ) state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias(); #endif state.m_bEnableShadows = m_bEnableShadows; - state.m_pSpotlightTexture = m_SpotlightTexture; +#ifdef MAPBASE + if (m_pVideoMatTexture) + state.m_pSpotlightTexture = m_pVideoMatTexture; + else +#endif // MAPBASE + state.m_pSpotlightTexture = m_SpotlightTexture; state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame; state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 08d38e4e3e6..9719549a8c3 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -36,6 +36,7 @@ $Project $File "c_movie_display.h" $File "vgui_movie_display.cpp" $File "convarproxy.cpp" + $File "$SRCDIR\game\shared\env_projectedvideo.cpp" $Folder "Mapbase" { diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index b49ad1589bf..abd959c1cd9 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -35,6 +35,7 @@ $Project $File "ai_expresserfollowup.cpp" [$NEW_RESPONSE_SYSTEM] $File "ai_speechqueue.cpp" [$NEW_RESPONSE_SYSTEM] $File "ai_speechqueue.h" [$NEW_RESPONSE_SYSTEM] + $File "$SRCDIR\game\shared\env_projectedvideo.cpp" $Folder "Mapbase" { diff --git a/sp/src/game/shared/env_projectedvideo.cpp b/sp/src/game/shared/env_projectedvideo.cpp new file mode 100644 index 00000000000..349cbf9e09e --- /dev/null +++ b/sp/src/game/shared/env_projectedvideo.cpp @@ -0,0 +1,266 @@ +#include "cbase.h" + +#ifdef CLIENT_DLL +#define CEnvProjectedVideo C_EnvProjectedVideo +#define CEnvProjectedTexture C_EnvProjectedTexture + +#include "C_Env_Projected_Texture.h" +#include "video/ivideoservices.h" +#include "materialsystem/imaterialvar.h" +#include "materialsystem/itexture.h" +#else +#include "env_projectedtexture.h" +#endif + +class CEnvProjectedVideo : public CEnvProjectedTexture +{ +public: + DECLARE_CLASS(CEnvProjectedVideo, CEnvProjectedTexture); + +#ifdef CLIENT_DLL + DECLARE_CLIENTCLASS(); +#else + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); +#endif + + CEnvProjectedVideo(); + ~CEnvProjectedVideo(); + +#ifdef CLIENT_DLL + virtual void OnDataChanged(DataUpdateType_t type); + virtual void ClientThink(); +#endif + +private: +#ifdef GAME_DLL + + CNetworkVar(string_t, m_szVideoFile); + +#else + + void CreateVideo(); + void DestroyVideo(); + void RenderVideoToTexture(); + + char m_szVideoFile[256]; + + IVideoMaterial* m_pVideoMaterial; + + CTextureReference m_pVideoRT; + CMaterialReference m_pVideoRTMaterial; + + float m_flLastVideoTime; + float m_flaspect_ratio; +#endif +}; + +#ifdef GAME_DLL + +LINK_ENTITY_TO_CLASS(env_projectedvideo, CEnvProjectedVideo); + +IMPLEMENT_SERVERCLASS_ST(CEnvProjectedVideo, DT_EnvProjectedVideo) + SendPropStringT(SENDINFO(m_szVideoFile)), +END_SEND_TABLE() + +BEGIN_DATADESC(CEnvProjectedVideo) + DEFINE_KEYFIELD(m_szVideoFile, FIELD_STRING, "VideoFile"), +END_DATADESC() + +#else + +#undef CEnvProjectedVideo + +IMPLEMENT_CLIENTCLASS_DT(C_EnvProjectedVideo, DT_EnvProjectedVideo, CEnvProjectedVideo) + RecvPropString(RECVINFO(m_szVideoFile)), +END_RECV_TABLE() + +#define CEnvProjectedVideo C_EnvProjectedVideo +#endif + +CEnvProjectedVideo::CEnvProjectedVideo() +{ +#ifdef CLIENT_DLL + m_pVideoMaterial = nullptr; + m_flLastVideoTime = -1.0f; +#endif // CLIENT_DLL +} + +CEnvProjectedVideo::~CEnvProjectedVideo() +{ +#ifdef CLIENT_DLL + DestroyVideo(); + SetNextClientThink(CLIENT_THINK_NEVER); +#endif // CLIENT_DLL +} + +#ifdef CLIENT_DLL +void CEnvProjectedVideo::OnDataChanged(DataUpdateType_t type) +{ + if (type == DATA_UPDATE_CREATED) + { + CreateVideo(); + SetNextClientThink(CLIENT_THINK_ALWAYS); + } + + BaseClass::OnDataChanged(type); +} + +void CEnvProjectedVideo::RenderVideoToTexture() +{ + if (!m_pVideoMaterial || !m_pVideoRT) + return; + + // Get the video frame size + int w = 0; + int h = 0; + m_pVideoMaterial->GetVideoImageSize(&w, &h); + + if (w <= 0 || h <= 0) + return; + + IMaterial* videoMat = m_pVideoMaterial->GetMaterial(); + if (!videoMat) + return; + + bool found = false; + IMaterialVar *pVar = videoMat->FindVar("$ytexture", &found); + + int ytexW, ytexH; + ytexW = ytexH = 0; + if (pVar) + { + ytexW = pVar->GetTextureValue()->GetActualWidth(); + ytexH = pVar->GetTextureValue()->GetActualHeight(); + } + + if (ytexH <= 0 && ytexW <= 0) + return; + + + CMatRenderContextPtr ctx(materials); + + // Set the render target + ctx->PushRenderTargetAndViewport((ITexture*)m_pVideoRT); + + // Clear RT completely + ctx->ClearColor4ub(0, 0, 0, 255); + ctx->ClearBuffers(true, true); + + // Draw the video material into the RT + ctx->DrawScreenSpaceRectangle( + videoMat, + 0, 0, + w, h, // destination rectangle + 0, 0, // source texel start + w, h, // source texel end + ytexW, ytexH // source texture dimensions + ); + + // Restore previous render target + ctx->PopRenderTargetAndViewport(); +} + +void CEnvProjectedVideo::CreateVideo() +{ + if (!g_pVideo) + return; + + if (!m_szVideoFile) + return; + + // If we already have a material, destroy & recreate it + if (m_pVideoMaterial) + { + ConColorMsg(Color(200, 200, 0, 255), "[ProjectedVideo] Recreating existing video material.\n"); + DestroyVideo(); + } + + //Need to do this when it gets recreated so the texture still gets update + char name[60]; + V_snprintf(name, sizeof(name), "ProjectedVideoMaterial_%d_%d", entindex(), random->RandomInt(0, 20)); + + m_pVideoMaterial = g_pVideo->CreateVideoMaterial( + name, + m_szVideoFile, + "GAME", + VideoPlaybackFlags::DEFAULT_MATERIAL_OPTIONS | VideoPlaybackFlags::NO_AUDIO | VideoPlaybackFlags::LOOP_VIDEO | VideoPlaybackFlags::DONT_AUTO_START_VIDEO, + VideoSystem::DETERMINE_FROM_FILE_EXTENSION); + + if (!m_pVideoMaterial) + { + ConColorMsg(Color(255, 100, 100, 255), "[ProjectedVideo] CreateVideo: failed to create video material for '%s'.\n", STRING(m_szVideoFile)); + return; + } + + ConColorMsg(Color(255, 100, 100, 255), "[ProjectedVideo] CreateVideo: material shader = %s.\n", m_pVideoMaterial->GetMaterial()->GetShaderName()); + + int w, h; + m_pVideoMaterial->GetVideoImageSize(&w, &h); + + g_pMaterialSystem->BeginRenderTargetAllocation(); + + m_pVideoRT.Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2( + VarArgs("_rt_projectedvideo_%d", entindex()), + w, + h, + RT_SIZE_OFFSCREEN, + g_pMaterialSystem->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_NONE, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + CREATERENDERTARGETFLAGS_HDR + )); + + g_pMaterialSystem->EndRenderTargetAllocation(); + + KeyValues* kv = new KeyValues("UnlitGeneric"); + ITexture *pTex = (ITexture*)m_pVideoRT; + kv->SetString("$basetexture", pTex->GetName()); + kv->SetInt("$translucent", 1); + + m_pVideoRTMaterial.Init( materials->CreateMaterial( + VarArgs("ProjectedVideoRTMat_%d", entindex()), + kv + )); + + m_pVideoMaterial->StartVideo(); + + ConColorMsg(Color(255, 255, 20, 255), "[ProjectedVideo] Created video material. Looping=%s\n", m_pVideoMaterial->IsLooping() ? "true" : "false"); + + BaseClass::SetVideoMaterial(m_pVideoRTMaterial); +} + +void CEnvProjectedVideo::DestroyVideo() +{ + if (m_pVideoMaterial && g_pVideo) + { + + ConColorMsg(Color(255, 120, 120, 255), "[ProjectedVideo] Destroying video material.\n"); + g_pVideo->DestroyVideoMaterial(m_pVideoMaterial); + m_pVideoMaterial = nullptr; + m_pVideoRT.Shutdown(); + m_pVideoRTMaterial.Shutdown(); + BaseClass::SetVideoMaterial(NULL); + } + +} + +void CEnvProjectedVideo::ClientThink() +{ + if (!m_pVideoMaterial) + { + CreateVideo(); + SetNextClientThink(CLIENT_THINK_ALWAYS); + return; + } + + // Pump decoder first + m_pVideoMaterial->Update(); + + RenderVideoToTexture(); + + // Think at ~30fps instead of every engine tick to reduce load + SetNextClientThink(gpGlobals->curtime + 0.03f); +} + +#endif \ No newline at end of file diff --git a/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc b/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc index 0e9d5d9feb2..363778b6c8f 100644 --- a/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc +++ b/sp/src/materialsystem/stdshaders/SDK_flashlight_ps2x.fxc @@ -106,6 +106,9 @@ float4 main( PS_INPUT i ) : COLOR float3 spotColor = float3(0,0,0); float3 vProjCoords = i.spotTexCoord.xyz / i.spotTexCoord.w; + float2 tmp = max(-vProjCoords.xy, vProjCoords.xy - 1); + clip(-max(tmp.x, tmp.y)); + #if ( defined( _X360 ) ) float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f ); From 588e04bf4d0fc7948c14ddc2fe5e7aa85ee92690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kerim=20=C4=8Camd=C5=BEi=C4=87?= Date: Wed, 11 Mar 2026 21:20:03 +0100 Subject: [PATCH 3/5] remove unused stuff --- sp/src/game/shared/env_projectedvideo.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sp/src/game/shared/env_projectedvideo.cpp b/sp/src/game/shared/env_projectedvideo.cpp index 0f3200bf723..349cbf9e09e 100644 --- a/sp/src/game/shared/env_projectedvideo.cpp +++ b/sp/src/game/shared/env_projectedvideo.cpp @@ -259,16 +259,6 @@ void CEnvProjectedVideo::ClientThink() RenderVideoToTexture(); - float curTime = m_pVideoMaterial->GetCurrentVideoTime(); - //float duration = m_pVideoMaterial->GetVideoDuration(); - //ConMsg( "Time: %f / %f\n", curTime, duration ); - - const float EPS = 1e-4f; - if (m_flLastVideoTime < 0.0f || fabsf(curTime - m_flLastVideoTime) > EPS) - { - m_flLastVideoTime = curTime; - } - // Think at ~30fps instead of every engine tick to reduce load SetNextClientThink(gpGlobals->curtime + 0.03f); } From af36724d63cc1515add13c7d34ea0eb7da5030df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kerim=20=C4=8Camd=C5=BEi=C4=87?= Date: Thu, 12 Mar 2026 00:13:22 +0100 Subject: [PATCH 4/5] support turnoff and turnon on the projected video --- sp/src/game/client/C_Env_Projected_Texture.h | 2 ++ sp/src/game/client/c_env_projectedtexture.cpp | 2 +- sp/src/game/shared/env_projectedvideo.cpp | 12 ++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sp/src/game/client/C_Env_Projected_Texture.h b/sp/src/game/client/C_Env_Projected_Texture.h index 81ddf170f6b..62e7f6fafbd 100644 --- a/sp/src/game/client/C_Env_Projected_Texture.h +++ b/sp/src/game/client/C_Env_Projected_Texture.h @@ -106,6 +106,8 @@ class C_EnvProjectedTexture : public C_BaseEntity static float m_flVisibleBBoxMinHeight; ITexture* m_pVideoMatTexture; + + friend class C_EnvProjectedVideo; }; diff --git a/sp/src/game/client/c_env_projectedtexture.cpp b/sp/src/game/client/c_env_projectedtexture.cpp index 414f328ae76..0390952fb0d 100644 --- a/sp/src/game/client/c_env_projectedtexture.cpp +++ b/sp/src/game/client/c_env_projectedtexture.cpp @@ -146,7 +146,7 @@ void C_EnvProjectedTexture::SetVideoMaterial( IMaterial* pVidMat ) if (tex) { m_pVideoMatTexture = tex; - m_LightHandle = CLIENTSHADOW_INVALID_HANDLE; + ShutDownLightHandle(); } } } diff --git a/sp/src/game/shared/env_projectedvideo.cpp b/sp/src/game/shared/env_projectedvideo.cpp index 349cbf9e09e..40d8365e916 100644 --- a/sp/src/game/shared/env_projectedvideo.cpp +++ b/sp/src/game/shared/env_projectedvideo.cpp @@ -241,12 +241,24 @@ void CEnvProjectedVideo::DestroyVideo() m_pVideoRT.Shutdown(); m_pVideoRTMaterial.Shutdown(); BaseClass::SetVideoMaterial(NULL); + ShutDownLightHandle(); } } void CEnvProjectedVideo::ClientThink() { + + if (!m_bState) + { + if (m_pVideoMaterial) + { + DestroyVideo(); + } + SetNextClientThink(CLIENT_THINK_ALWAYS); + return; + } + if (!m_pVideoMaterial) { CreateVideo(); From 1cc6d74db130f93e0eaca39ae920b2857827e7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kerim=20=C4=8Camd=C5=BEi=C4=87?= Date: Thu, 12 Mar 2026 21:01:10 +0100 Subject: [PATCH 5/5] fixed broken char array check --- sp/src/game/shared/env_projectedvideo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/env_projectedvideo.cpp b/sp/src/game/shared/env_projectedvideo.cpp index 40d8365e916..eb3eea28276 100644 --- a/sp/src/game/shared/env_projectedvideo.cpp +++ b/sp/src/game/shared/env_projectedvideo.cpp @@ -166,7 +166,7 @@ void CEnvProjectedVideo::CreateVideo() if (!g_pVideo) return; - if (!m_szVideoFile) + if (!m_szVideoFile[0]) return; // If we already have a material, destroy & recreate it