Coding Tutorial: Rear View Mirror

Please don't post here unless its in response to an existing topic. New discussion is to be shared in the other forums.

Coding Tutorial: Rear View Mirror

Postby OneEyed » Wed Dec 20, 2006 6:14 pm

== 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.
Code: Select all
void         DrawMirror( const CViewSetup &cameraView );

Open cl_dll/view.cpp

Add after CViewRender::DrawMonitors
Code: Select all
void CViewRender::DrawMirror( const CViewSetup &viewSet )
   C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();

   //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.
Code: Select all
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.
Code: Select all
#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 );

   CHudMirror( const char *pElementName );

   virtual void Paint();
   vgui::ImagePanel *m_Mirror;

extern CHud gHUD;


// 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 );


// 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 );

This class simply defines a HUD class called "HudMirror" and creates an ImagePanel with image "mirror/mirror.vmt" which has the "_rt_Camera" we need to display our scene. The Paint() function sets the location and size to display our mirror!

Next Open up MOD/scripts/HudLayout.res
Add this at the top.
Code: Select all
   "wide"   "256"
   "tall"  "128"
   "PaintBackgroundType"   "1"

This is the bare minimum needed for a HUD, you can do everything else in the HUD class.

== 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.
Code: Select all
   "$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.
Code: Select all
   "$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!


Return to Unsorted Old Posts

Who is online

Users browsing this forum: No registered users and 1 guest