csgo-2018-source/tools/toolutils/miniviewport.cpp
2021-07-24 21:11:47 -07:00

431 lines
12 KiB
C++

//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "toolutils/miniviewport.h"
#include "tier1/utlstring.h"
#include "vgui/ISurface.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/IMaterialSystem.h"
#include "materialsystem/MaterialSystemUtil.h"
#include "materialsystem/IMesh.h"
#include "materialsystem/IMaterial.h"
#include "materialsystem/itexture.h"
#include "tier1/KeyValues.h"
#include "toolframework/ienginetool.h"
#include "toolutils/enginetools_int.h"
#include "vguimatsurface/imatsystemsurface.h"
#include "view_shared.h"
#include "texture_group_names.h"
#include "vgui_controls/PropertySheet.h"
#include "tier3/tier3.h"
#include <windows.h> // for MultiByteToWideChar
#include "cdll_int.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class CMiniViewportEngineRenderArea;
using namespace vgui;
#define DEFAULT_PREVIEW_WIDTH 1280
//-----------------------------------------------------------------------------
// Purpose: This is a "frame" which is used to position the engine
//-----------------------------------------------------------------------------
class CMiniViewportPropertyPage : public vgui::EditablePanel
{
DECLARE_CLASS_SIMPLE( CMiniViewportPropertyPage, vgui::EditablePanel );
public:
CMiniViewportPropertyPage( Panel *parent, const char *panelName );
virtual Color GetBgColor();
void GetEngineBounds( int& x, int& y, int& w, int& h );
void RenderFrameBegin();
CMiniViewportEngineRenderArea *GetViewportArea() { return m_pViewportArea; }
private:
virtual void PerformLayout();
Color m_bgColor;
CMiniViewportEngineRenderArea *m_pViewportArea;
};
//-----------------------------------------------------------------------------
//
// the actual renderable area
//
//-----------------------------------------------------------------------------
class CMiniViewportEngineRenderArea : public vgui::EditablePanel
{
DECLARE_CLASS_SIMPLE( CMiniViewportEngineRenderArea, vgui::EditablePanel );
public:
CMiniViewportEngineRenderArea( Panel *parent, const char *panelName );
~CMiniViewportEngineRenderArea();
virtual void PaintBackground();
virtual void GetEngineBounds( int& x, int& y, int& w, int& h );
virtual void ApplySchemeSettings( IScheme *pScheme );
void RenderFrameBegin();
void SetOverlayText( const char *pText );
protected:
void InitSceneMaterials();
void ShutdownSceneMaterials();
// Paints the black borders around the engine window
void PaintEngineBorders( int x, int y, int w, int h );
// Paints the engine window itself
void PaintEngineWindow( int x, int y, int w, int h );
// Paints the overlay text
void PaintOverlayText( );
int m_nEngineOutputTexture;
vgui::HFont m_OverlayTextFont;
CUtlString m_OverlayText;
CTextureReference m_ScreenBuffer;
CMaterialReference m_ScreenMaterial;
};
CMiniViewportEngineRenderArea::CMiniViewportEngineRenderArea( Panel *parent, const char *panelName )
: BaseClass( parent, panelName )
{
SetPaintEnabled( false );
SetPaintBorderEnabled( false );
SetPaintBackgroundEnabled( true );
m_nEngineOutputTexture = vgui::surface()->CreateNewTextureID();
}
CMiniViewportEngineRenderArea::~CMiniViewportEngineRenderArea()
{
ShutdownSceneMaterials();
}
void CMiniViewportEngineRenderArea::RenderFrameBegin()
{
if ( !enginetools->IsInGame() )
return;
InitSceneMaterials();
CViewSetup playerViewSetup;
int x, y, w, h;
GetEngineBounds( x, y, w, h );
enginetools->GetPlayerView( playerViewSetup, 0, 0, w, h );
// NOTE: This is a workaround to a nasty problem. Vgui uses stencil
// to determing if the panels should occlude each other. The engine
// has now started to use stencil for various random effects.
// To prevent these different stencil uses from clashing, we will
// render the engine prior to vgui painting + cache the result off in
//
// Make the engine draw the scene
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->PushRenderTargetAndViewport( m_ScreenBuffer, 0, 0, w, h );
// Tell the engine to tell the client to render the view (sans viewmodel)
enginetools->SetMainView( playerViewSetup.origin, playerViewSetup.angles );
enginetools->RenderView( playerViewSetup, VIEW_CLEAR_COLOR | VIEW_CLEAR_DEPTH, RENDERVIEW_DRAWHUD | RENDERVIEW_DRAWVIEWMODEL );
// Pop the target
pRenderContext->PopRenderTargetAndViewport();
}
void CMiniViewportEngineRenderArea::InitSceneMaterials()
{
if ( m_ScreenBuffer )
return;
if ( g_pMaterialSystem->IsTextureLoaded( "_rt_LayoffResult" ) )
{
ITexture *pTexture = g_pMaterialSystem->FindTexture( "_rt_LayoffResult", TEXTURE_GROUP_RENDER_TARGET );
m_ScreenBuffer.Init( pTexture );
}
else
{
// For now, layoff dimensions match aspect of back buffer
int nBackBufferWidth, nBackBufferHeight;
g_pMaterialSystem->GetBackBufferDimensions( nBackBufferWidth, nBackBufferHeight );
float flAspect = nBackBufferWidth / (float)nBackBufferHeight;
int nPreviewWidth = min( DEFAULT_PREVIEW_WIDTH, nBackBufferWidth );
int nPreviewHeight = ( int )( nPreviewWidth / flAspect + 0.5f );
g_pMaterialSystem->BeginRenderTargetAllocation(); // Begin allocating RTs which IFM can scribble into
// LDR final result of either HDR or LDR rendering
m_ScreenBuffer.Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2(
"_rt_LayoffResult", nPreviewWidth, nPreviewHeight, RT_SIZE_OFFSCREEN,
g_pMaterialSystem->GetBackBufferFormat(), MATERIAL_RT_DEPTH_SHARED, TEXTUREFLAGS_BORDER ) );
g_pMaterialSystem->EndRenderTargetAllocation(); // End allocating RTs which IFM can scribble into
}
KeyValues *pVMTKeyValues = NULL;
pVMTKeyValues= new KeyValues( "UnlitGeneric" );
pVMTKeyValues->SetString( "$basetexture", m_ScreenBuffer->GetName() );
pVMTKeyValues->SetInt( "$nofog", 1 );
m_ScreenMaterial.Init( "MiniViewportEngineRenderAreaSceneMaterial", pVMTKeyValues );
m_ScreenMaterial->Refresh();
}
//-----------------------------------------------------------------------------
// Apply scheme settings
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_OverlayTextFont = pScheme->GetFont( "DefaultLargeOutline" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::ShutdownSceneMaterials()
{
m_ScreenBuffer.Shutdown();
m_ScreenMaterial.Shutdown();
}
//-----------------------------------------------------------------------------
// Sets text to draw over the window
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::SetOverlayText( const char *pText )
{
m_OverlayText = pText;
}
//-----------------------------------------------------------------------------
// Paints the black borders around the engine window
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::PaintEngineBorders( int x, int y, int w, int h )
{
// Draws black borders around the engine window
surface()->DrawSetColor( Color( 0, 0, 0, 255 ) );
if ( x != 0 )
{
surface()->DrawFilledRect( 0, 0, x, h );
surface()->DrawFilledRect( x + w, 0, w + 2 * x, h );
}
else if ( y != 0 )
{
surface()->DrawFilledRect( 0, 0, w, y );
surface()->DrawFilledRect( 0, y + h, w, h + 2 * y );
}
}
//-----------------------------------------------------------------------------
// Paints the overlay text
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::PaintOverlayText( )
{
if ( !m_OverlayText.Length() )
return;
int cw, ch;
GetSize( cw, ch );
int nTextWidth, nTextHeight;
int nBufLen = m_OverlayText.Length()+1;
wchar_t *pTemp = (wchar_t*)_alloca( nBufLen * sizeof(wchar_t) );
::MultiByteToWideChar( CP_UTF8, 0, m_OverlayText.Get(), -1, pTemp, nBufLen );
g_pMatSystemSurface->GetTextSize( m_OverlayTextFont, pTemp, nTextWidth, nTextHeight );
int lx = (cw - nTextWidth) / 2;
if ( lx < 10 )
{
lx = 10;
}
int ly = ch - 10 - nTextHeight;
g_pMatSystemSurface->DrawColoredTextRect( m_OverlayTextFont,
lx, ly, cw - lx, ch - ly,
255, 255, 255, 255, "%s", m_OverlayText.Get() );
}
//-----------------------------------------------------------------------------
// Paints the engine window itself
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::PaintEngineWindow( int x, int y, int w, int h )
{
if ( !enginetools->IsInGame() )
{
surface()->DrawSetColor( Color( 127, 127, 200, 63 ) );
surface()->DrawFilledRect( x, y, x + w, y + h );
}
else
{
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
g_pMatSystemSurface->DrawSetTextureMaterial( m_nEngineOutputTexture, m_ScreenMaterial );
surface()->DrawSetColor( Color( 0, 0, 0, 255 ) );
int nTexWidth = m_ScreenBuffer->GetActualWidth();
int nTexHeight = m_ScreenBuffer->GetActualHeight();
float flOOWidth = 1.0f / nTexWidth;
float flOOHeight = 1.0f / nTexHeight;
float s0, s1, t0, t1;
s0 = ( 0.5f ) * flOOWidth;
t0 = ( 0.5f ) * flOOHeight;
s1 = ( (float)w - 0.5f ) * flOOWidth;
t1 = ( (float)h - 0.5f ) * flOOHeight;
vgui::surface()->DrawTexturedSubRect( x, y, x+w, y+h, s0, t0, s1, t1 );
PaintOverlayText();
}
}
//-----------------------------------------------------------------------------
// Paints the background
//-----------------------------------------------------------------------------
void CMiniViewportEngineRenderArea::PaintBackground()
{
int x, y, w, h;
GetEngineBounds( x, y, w, h );
PaintEngineBorders( x, y, w, h );
PaintEngineWindow( x, y, w, h );
}
void CMiniViewportEngineRenderArea::GetEngineBounds( int& x, int& y, int& w, int& h )
{
x = 0;
y = 0;
GetSize( w, h );
// Check aspect ratio
int sx, sy;
surface()->GetScreenSize( sx, sy );
if ( sy > 0 &&
h > 0 )
{
float screenaspect = (float)sx / (float)sy;
float aspect = (float)w / (float)h;
float ratio = screenaspect / aspect;
// Screen is wider, need bars at top and bottom
if ( ratio > 1.0f )
{
int usetall = (float)w / screenaspect;
y = ( h - usetall ) / 2;
h = usetall;
}
// Screen is narrower, need bars at left/right
else
{
int usewide = (float)h * screenaspect;
x = ( w - usewide ) / 2;
w = usewide;
}
}
}
CMiniViewportPropertyPage::CMiniViewportPropertyPage(Panel *parent, const char *panelName ) :
BaseClass( parent, panelName )
{
m_bgColor = Color( 0, 0, 0, 0 );
m_pViewportArea = new CMiniViewportEngineRenderArea( this, "Engine" );
}
void CMiniViewportPropertyPage::PerformLayout()
{
BaseClass::PerformLayout();
int w, h;
GetSize( w, h );
m_pViewportArea->SetBounds( 0, 0, w, h );
}
Color CMiniViewportPropertyPage::GetBgColor()
{
return m_bgColor;
}
void CMiniViewportPropertyPage::GetEngineBounds( int& x, int& y, int& w, int& h )
{
m_pViewportArea->GetEngineBounds( x, y, w, h );
m_pViewportArea->LocalToScreen( x, y );
}
void CMiniViewportPropertyPage::RenderFrameBegin()
{
m_pViewportArea->RenderFrameBegin();
}
CMiniViewport::CMiniViewport( vgui::Panel *parent, bool contextLabel, vgui::IToolWindowFactory *factory /*= 0*/,
vgui::Panel *page /*= NULL*/, char const *title /*= NULL*/, bool contextMenu /*= false*/ ) :
BaseClass( parent, contextLabel, factory, page, title, contextMenu, false )
{
SetCloseButtonVisible( false );
GetPropertySheet()->SetDraggableTabs( false );
// Add the viewport panel
m_hPage = new CMiniViewportPropertyPage( this, "ViewportPage" );
AddPage( m_hPage.Get(), "#ToolMiniViewport", false );
}
void CMiniViewport::GetViewport( bool& enabled, int& x, int& y, int& w, int& h )
{
enabled = false;
x = y = w = h = 0;
int screenw, screenh;
surface()->GetScreenSize( screenw, screenh );
m_hPage->GetEngineBounds( x, y, w, h );
y = screenh - ( y + h );
}
void CMiniViewport::GetEngineBounds( int& x, int& y, int& w, int& h )
{
m_hPage->GetEngineBounds( x, y, w, h );
}
//-----------------------------------------------------------------------------
// Sets text to draw over the window
//-----------------------------------------------------------------------------
void CMiniViewport::SetOverlayText( const char *pText )
{
if ( m_hPage.Get() )
{
m_hPage->GetViewportArea()->SetOverlayText( pText );
}
}
void CMiniViewport::RenderFrameBegin()
{
if ( m_hPage.Get() )
{
m_hPage->RenderFrameBegin();
}
}