Rear View Mirror
Introduction
Hello, my name is OneEyed and this simple tutorial will show you how to get a working Rear View Mirror in your game. Most preferably for a racing mod. This was created on SDK From Scratch. It could work on HL2DM SDK just change the folder's I mention with "SDK" with "HL2MP" or what you desire.
Getting Started
First it'd be best if you understand what is going on, and not just copy-paste what I do. The camera view you have is part of the CViewSetup class, this defines its origin and angles and window size to display on your screen, along with FOV and AspectRatios for the type of display. We want to create a new view facing behind us and set the window size to fit on the screen nicely. Once we have our view, we want to display it on a RenderTarget (basically just a material we have on a Hud Panel).
Setup the Render Scene
Open cl_dll/viewrender.h
Add after DrawWorld and before DrawMonitors functions declarations.
void DrawMirror( const CViewSetup &cameraView );
Open cl_dll/view.cpp
Add after CViewRender::DrawMonitors
void CViewRender::DrawMirror( const CViewSetup &viewSet ) { C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); if(!localPlayer) return; //Copy our current View. CViewSetup mirrorView = viewSet; //Get our camera render target. ITexture *pRenderTarget = GetCameraTexture(); //Fog data, enables for this view, and reverts back to default. fogparams_t oldFogParams; float flOldZFar = 0.0f; bool fogEnabled = true; if ( fogEnabled ) { oldFogParams = localPlayer->m_Local.m_fog; flOldZFar = mirrorView.zFar; localPlayer->m_Local.m_fog.enable = true; localPlayer->m_Local.m_fog.start = 2048; localPlayer->m_Local.m_fog.end = 4096; localPlayer->m_Local.m_fog.farz = 4096; localPlayer->m_Local.m_fog.colorPrimary.SetR( 0 ); localPlayer->m_Local.m_fog.colorPrimary.SetG( 0 ); localPlayer->m_Local.m_fog.colorPrimary.SetB( 0 ); mirrorView.zFar = 4096; } //Our view information, Origin, View Direction, window size // location on material, and visual ratios. mirrorView.width = 256; mirrorView.height = 128; mirrorView.x = 0; mirrorView.y = 0; mirrorView.origin = localPlayer->GetAbsOrigin() + Vector( 0, 0, 50); mirrorView.angles = localPlayer->GetRenderAngles(); mirrorView.angles.x += 30; mirrorView.angles.y = AngleNormalize( mirrorView.angles.y + 180 ); mirrorView.fov = localPlayer->GetFOV(); mirrorView.m_bOrtho = false; mirrorView.m_flAspectRatio = 1.0f; //Set the view up and output the scene to our RenderTarget (Camera Material). render->Push3DView( mirrorView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, true, pRenderTarget, m_Frustum ); ViewDrawScene( false, true, mirrorView, 0, VIEW_MONITOR ); render->PopView( m_Frustum ); // Reset the world fog parameters. if ( fogEnabled ) { localPlayer->m_Local.m_fog = oldFogParams; mirrorView.zFar = flOldZFar; } }
This is the core function that does the view stuff. It copies our current Camera View of our screen, and changes its member variables to rotate the camera and fit it on a small area. Then we send it to the engine to render our entire scene facing the angles we defined and origin specified. Notice I added 30 to the angle's X value to rotate the camera downward. You could play with different angles and origins for different results. However, if you stray away from your player, you will have to add PVS locations through the server to render the entities not seen at your location.
Next we let our render function to run this along side our main view.
Open cl_dll/view_scene.cpp Find CViewRender::RenderViewEx and inside the function add after the DrawMonitor code block section.
if(g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 70 ) { DrawMirror( view ); }
This technology requires DirectX 7.0 so we have to add the check to it.
Setting up the HUD
Now we create a simple HUD class to display the scene we created.
Create a new file cl_dll/sdk/sdk_hud_mirror.cpp
Add it to your solution preferably in the SDK subsection folder of the client solution.
#include "cbase.h" #include "hudelement.h" #include "iclientmode.h" #include <vgui_controls/ImagePanel.h> using namespace vgui; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //.vmt location + filename #define IMAGE_MIRROR "mirror/mirror" //----------------------------------------------------------------------------- // Purpose: HUD Mirror panel //----------------------------------------------------------------------------- class CHudMirror : public CHudElement, public vgui::Panel { DECLARE_CLASS_SIMPLE( CHudMirror, vgui::Panel ); public: CHudMirror( const char *pElementName ); protected: virtual void Paint(); private: vgui::ImagePanel *m_Mirror; }; extern CHud gHUD; DECLARE_HUDELEMENT( CHudMirror ); //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CHudMirror::CHudMirror( const char *pElementName ) : CHudElement( pElementName ), vgui::Panel(NULL, "HudMirror") { vgui::Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); //Our parent is the screen itself. m_Mirror = new ImagePanel(this, "RearViewMirror"); m_Mirror->SetImage( IMAGE_MIRROR ); SetPaintBackgroundEnabled(false); } //----------------------------------------------------------------------------- // Purpose: Paint //----------------------------------------------------------------------------- void CHudMirror::Paint() { //Set position Top Right corner SetPos( ScreenWidth() - 270 , 25 ); //Set Mirror to 256x128 pixels m_Mirror->SetSize( 256, 128 ); m_Mirror->SetVisible( true ); }
Adding Mirror Material
We need to create a simple mirror.vmt file so we can use in our HUD.
Create mirror.vmt in your MOD folder @ materials/VGUI/mirror/mirror.vmt
Add this inside.
"UnlitGeneric" { "$basetexture" "_rt_Camera" }
Theres ways of adding a texture that overlays on what your camera displays, its used for TV scan lines and other types of effects, maybe a dirty mirror?
In that case you use this instead of what I have above.
"Unlittwotexture" { "$basetexture" "_rt_Camera" "$texture2" "dev/dev_combinescanline" "$additive" "1" }
However since we're dealing with HUD's it is much wiser to create a different ImagePanel and manually overlap it onto your Mirror panel to create the desired effect!
Conclusion
If you noticed the render function DrawMirror is almost an exact copy of DrawOneMonitor inside view.cpp, however that uses the camera entities to display their location. One thing I haven't understood was how they can have multiple monitors and display it to the correct material. There is no code that shows where they do this so I'm guessing anything using _rt_Camera as a RenderTarget will be shown what's rendered to it.
There could be better ways of doing this, I'm still researching and if I find out I'll be sure to edit this page. Anyways, I hope you enjoyed the tutorial and hope it adds more spice to your mod. Don't be shy on editing what I have, I just gave you the barebones of whats needed to get it to work properly, it's up to you to masterfully manipulate it!