GameBanana shows no ads to members. Sign up now!
Ads keep us online. Without them, we wouldn't exist. We don't have paywalls or sell mods - we never will. But every month we have large bills and running ads is our only way to cover them. Please consider unblocking us. Thank you from GameBanana <3

Unlimited Render Targets for Cameras

A Tutorial for Half-Life 2

Abstract This tutorial will show you how to modify the Half-Life 2 SDK code to allow multiple render targets for cameras. By default, Half-Life 2 only allows a single render target, called "_rt_Camera", for cameras. This severely limits mod developers' and mappers' ability to link multiple cameras to their own surface for display. The following changes to the code allow a mapper in Hammer to assign a render target per point_camera entity. With an appropriate render target texture in the materials folder, a mapper is able to create cameras that can each display to their own render target. As long as all the cameras used by a func_monitor and info_camera_link use the same render targets, the info_camera_link functionality should work the same as before this change. However, info_camera_link could also be used to switch between multiple cameras with multiple render targets if the mapper desired. Be aware, if you do not create the render target textures following the instructions below, your cameras WILL NOT show up on the textures. Render targets require textures specifically created to be used as render targets. Getting Started First of all, we need to create a new variable to store the name of the render target in each camera entity. The following code adds the appropriate variable along with the glue for communication between the client and server DLLs. It also adds a member function that retrieves the string since the variable is declared private to the class. If you're unfamiliar with network entities, please refer to the Networking Entities ( documentation page for the Source SDK. This tutorial uses patch/diff style snippets where a plus('+') sign means add the line, a minus('-') means remove the line and neither means it is just a context line for determining where to make the change. Server DLL src/dlls/point_camera.h line 52: CNetworkVar( float, m_flFogEnd ); CNetworkVar( bool, m_bActive ); CNetworkVar( bool, m_bUseScreenAspectRatio ); + CNetworkVar( string_t, m_szRenderTarget ); // Allows the mapmaker to control whether a camera is active or not bool m_bIsOn; line 33: void ChangeFOVThink( void ); + string_t GetRenderTarget ( void ); + void InputChangeFOV( inputdata_t &inputdata ); void InputSetOnAndTurnOthersOff( inputdata_t &inputdata ); void InputSetOn( inputdata_t &inputdata ); src/dlls/point_camera.cpp line 48: // Set these to opposites so that it'll be sent the first time around. m_bActive = false; m_bIsOn = false; + m_szRenderTarget = NULL_STRING; m_bFogEnable = false; line 212: SetActive( false ); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +string_t CPointCamera::GetRenderTarget ( void ) +{ + return m_szRenderTarget; +} + BEGIN_DATADESC( CPointCamera ) // Save/restore Keyvalue fields line 230: DEFINE_KEYFIELD( m_flFogStart, FIELD_FLOAT, "fogStart" ), DEFINE_KEYFIELD( m_flFogEnd, FIELD_FLOAT, "fogEnd" ), DEFINE_KEYFIELD( m_bUseScreenAspectRatio, FIELD_BOOLEAN, "UseScreenAspectRatio" ), + DEFINE_KEYFIELD( m_szRenderTarget, FIELD_STRING, "renderTarget" ), DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ), DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ), line 258: SendPropFloat( SENDINFO( m_flFogEnd ), 0, SPROP_NOSCALE ), SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bUseScreenAspectRatio ), 1, SPROP_UNSIGNED ), + SendPropStringT( SENDINFO( m_szRenderTarget ) ), END_SEND_TABLE() Client DLL src/cl_dll/c_point_camera.h line 36: float GetFogStart(); float GetFogEnd(); bool UseScreenAspectRatio() const { return m_bUseScreenAspectRatio; } + string_t GetRenderTarget() { return m_szRenderTarget; } private: float m_FOV; line 47: float m_flFogEnd; bool m_bActive; bool m_bUseScreenAspectRatio; + char m_szRenderTarget[256]; public: C_PointCamera *m_pNext; src/cl_dll/c_point_camera.cpp line 19: RecvPropFloat( RECVINFO( m_flFogEnd ) ), RecvPropInt( RECVINFO( m_bActive ) ), RecvPropInt( RECVINFO( m_bUseScreenAspectRatio ) ), + RecvPropString( RECVINFO( m_szRenderTarget ) ), END_RECV_TABLE() C_EntityClassList g_PointCameraList; Rendering Now that we have the variable for storing the render target, we need to modify the display code to actually use it. The changes to view.cpp that follow simply remove the hardcoded use of "_rt_Camera" and re-arranges the code to get the render target for each point_camera before drawing the monitor. If the render target texture cannot be found, the camera is skipped and a message is displayed to the console. Client DLL src/cl_dll/view.cpp line 42: #ifdef USE_MONITORS #include "materialsystem/IMaterialSystem.h" #include "materialsystem/IMaterialSystemHardwareConfig.h" +#include "materialsystem/itexture.h" #include "c_point_camera.h" #endif // USE_MONITORS line 766: g_bRenderingCameraView = true; #endif - // FIXME: this should check for the ability to do a render target maybe instead. - // FIXME: shouldn't have to truck through all of the visible entities for this!!!! - ITexture *pRenderTarget = GetCameraTexture(); - materials->MatrixMode( MATERIAL_PROJECTION ); materials->PushMatrix(); line 778: ITexture *pSaveRenderTarget = materials->GetRenderTarget(); - materials->SetRenderTarget( pRenderTarget ); - - int width, height; - materials->GetRenderTargetDimensions( width, height ); - materials->Viewport( 0, 0, width, height ); - C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); for ( int cameraNum = 0; pCameraEnt != NULL; pCameraEnt = pCameraEnt->m_pNext ) line 785: if ( !pCameraEnt->IsActive() || pCameraEnt->IsDormant() ) continue; + // FIXME: this should check for the ability to do a render target maybe instead. + // TSM: FIXME above should be fixed with per Camera render target support. + + // TSM: I think the FIXME below is an old comment. + // FIXME: shouldn't have to truck through all of the visible entities for this!!!! + ITexture *pRenderTarget = materials->FindTexture( pCameraEnt->GetRenderTarget(), TEXTURE_GROUP_RENDER_TARGET ); + + if (IsErrorTexture( pRenderTarget )) + { + Msg ("Error: render target texture not found!\n"); + continue; + } + + materials->SetRenderTarget( pRenderTarget ); + + int width, height; + materials->GetRenderTargetDimensions( width, height ); + materials->Viewport( 0, 0, width, height ); + if ( !DrawOneMonitor( cameraNum, pCameraEnt, cameraView, player, 0, 0, width, height, true ) ) continue; FGD File Now that we have all of the code written, we need to add support for the render target in Hammer. To do this, we need to edit the point_camera entry in halflife2.fgd. The FGD file contains all of the entity key definitions, help, defaults, etc. Rather than editing this file directly, we're going to create our own FGD with a copy of the point_camera information which will override the halflife2.fgd entry for point_camera. * Open up %SOURCESDK\bin\halflife2.fgd with Notepad or your favorite text editor. * Find the point_camera entry and copy the entire body: //------------------------------------------------------------------------- // // Camera/monitor entities // //------------------------------------------------------------------------- @PointClass base(Parentname, Angles) studioprop("models/editor/camera.mdl") = po int_camera : "Camera" [ ... input SetOn(void) : "Turn the camera on." input SetOff(void) : "Turn the camera off." ] * Open up a new text file and paste the point_camera entry into it. * At the top of the file add: @include "base.fgd" * Add a new key for the renderTarget variable: fogColor(color255) : "Fog Color" : "0 0 0" fogStart(float) : "Fog Start" : 2048 : "The near fog plane." fogEnd(float) : "Fog End" : 4096 : "The far fog/clipping plane." + renderTarget(string) : "Render Target" : "_rt_Camera" : "The render target of the camera." * Save your FGD file to somewhere in your mod's folder. * Open up Hammer and go to Tools->Options * Add your new FGD file to your Game Data files listing with the Add button. Hammer is now configured to have the new renderTarget key in the point_camera entity. If you add a new point_camera to your map and look at its properties, you can see that it has a new key called renderTarget. The default value of this key is "_rt_Camera" which you may remember is the built-in render target for the Source engine. You can set this value to any render target texture in your materials folder. For instance, if you have a texture file called "monitor01.vtf" in your materials/monitors folder of your mod, then you would put monitors/monitor01 in the renderTarget value to get the camera to use this particular render target texture. Please note: This is the name of the TEXTURE not the material, you still assign a material to the brush that has this texture as its $basetexture in the .vmt material description file. Render Target Textures At last, the key to this whole system working: render target textures. A render target texture is a special texture designed to receive render output rather than that output going to the screen. This allows for cool effects such as shadows, mirrors, and, the subject of this article, cameras. Render target textures are created for the Source engine by vtex.exe. You've probably used vtex.exe to compile your .tga files into .vtf files for Source. You may not have used the empty .txt files that are generated by vtex.exe. The .txt files are used to tell vtex.exe to compile the texture with certain flags set. In this case, we need to use two flags to create the render target texture: "rendertarget" "1" "nocompress" "1" "rendertarget" This enables or disables the render target texture flag. Required if you want to use this texture as a render target. "nocompress" This enables or disables texture compression. Render target textures must have compression disabled because Direct3D cannot render to a compressed texture. The size in memory of a compressed texture is variable and Direct3D needs a fixed amount of space. Both of these flags are required if you want to use a texture as a render target. Simply put them in a text file called the same name as your .tga with the .txt extension and then use vtex.exe to compile the .txt file. When you compile a .txt file with vtex.exe it looks for a .tga with the same filename. Create a 256x256 .tga in Photoshop or another image editing program. The contents of this file don't matter, since they will be overwritten when the engine writes the camera output to the texture. I filled mine with pure white. Once you have this file created you can now use it in a material description file(.vmt) as a $basetexture. Assuming your texture file is called monitor01.vtf and is in a monitors subfolder of your materials folder, simple example(refer to dev/dev_combine*.vmt files in Half-Life 2 for more complicated examples): "UnlitGeneric" { "$basetexture" "monitors/monitor" } Please note: Even though you now have support for multiple camera render targets after making this modification to Half-Life 2, if you would like to more use than one of the built-in render targets, such as dev/combinemonitor_1 or dev/combinemonitor_2, you need to re-create these materials. The reason why is that all of these materials use the same render target: "_rt_Camera" because they were built with the assumption that there is only one render target in the game. Each render target material must use it's own render target texture as a $basetexture if you want to use them at the same time. Closing Remarks Overall, this is a very simple modification. The difficulty stemmed from figuring out the texture flags to provide to vtex.exe to get a render texture. To implement and understand this modification though, you need several different areas of knowledge about the Half-Life 2 Source SDK and it's sourcecode. If you have any questions, feel free to contact me (

Tutorial by WikiPedia.

Sign up to access this!
  • Eepis avatar
    Eepis Joined 13y ago
    444 points Ranked 48,954th
    Cant find files. I cant spot like this script on any steam file. Can u make something update for this or maybe something files.
    Mapper ☣ Bitch >:O
    URL to post:
  • Pigophone avatar
    Pigophone Joined 14y ago
    141 points Ranked 66,379th
    Good job, but is does not work for Orange box. And I have an error -- please help: ------ Build started: Project: client_hl2, Configuration: Release HL2 Win32 ------ Compiling... view.cpp .\view.cpp(922) : error C2065: 'pCameraTarget' : undeclared identifier .\view.cpp(922) : error C2227: left of '->GetActualWidth' must point to class/struct/union/generic type type is ''unknown-type'' .\view.cpp(923) : error C2065: 'pCameraTarget' : undeclared identifier .\view.cpp(923) : error C2227: left of '->GetActualHeight' must point to class/struct/union/generic type type is ''unknown-type'' Creating browse information file... Microsoft Browse Information Maintenance Utility Version 9.00.30729 Copyright (C) Microsoft Corporation. All rights reserved. Build log was saved at "file://c:\MyModTheBest\src\cl_dll\Release HL2\BuildLog.htm" client_hl2 - 4 error(s), 0 warning(s) ------ Build started: Project: server_hl2, Configuration: Release HL2 Win32 ------ Copying to destination folder 1 file(s) copied. 1 file(s) copied. Build log was saved at "file://c:\MyModTheBest\src\dlls\Release HL2\BuildLog.htm" server_hl2 - 0 error(s), 0 warning(s) ========== Build: 1 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
    The One
    URL to post:
  • killer89 avatar
    killer89 Joined 15y ago
    3,798 points Ranked 2,346th
    11 medals 1 legendary 1 rare
    • 10 years a member Medal icon
    • 6 years a member Medal icon
    • Reached 1,000 Points Medal icon
    • Reached 2,500 Points Medal icon
    • One month a member Medal icon
    • 6 months a member Medal icon
    As Cobalt stated, it's a bit hard to follow. It's really useful script, though.
    URL to post:
  • Cobalt avatar
    Cobalt Joined 15y ago
    22,315 points Ranked 252nd
    5 medals 1 rare
    • Submitted 100 Sprays Medal icon
    • Installation Guide Submitter Medal icon
    • Submitted 10 Skins Medal icon
    • Submitted 10 WiPs Medal icon
    • Submitted 20 Sprays Medal icon
    A tad hard to follow, but for the most part it's very nice.
    Old School Bananite avatar
    Old School Bananite
    URL to post:


Share banner
Image URL
HTML embed code
BB embed code
Markdown embed code




Guest avatar
Guest Joined 15y ago
34 medals 7 legendary 12 rare
  • Submitted 100 Skins Medal icon
  • Submitted 50 Maps Medal icon
  • Submitted 200 Threads Medal icon
  • Submitted 30 Tutorials Medal icon
  • Submitted 15 Tools Medal icon
  • Submitted 200 Sprays Medal icon
Sign up to access this!
Sign up to access this!
Sign up to access this!


Sign up to access this!



Difficulty Level



  • Share on Reddit
  • Share on Twitter
  • Share on Facebook
  • 4
  • 15.8k
  • 5
  • 15y
  • 10y

More from Submitter

WiPs by Submitter

More Scripting Tutorials