|
|
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Rendering and mouse handling in the 2D view.
//
//===========================================================================//
#include "stdafx.h"
#include "hammer.h"
#include "MapView2D.h"
#include "MapView3D.h"
#include "MapDoc.h"
#include "Render2D.h"
#include "ToolManager.h"
#include "History.h"
#include "TitleWnd.h"
#include "mainfrm.h"
#include "MapSolid.h"
#include "ToolMorph.h" // FIXME: remove
#include "MapWorld.h"
#include "camera.h"
#include "Manifest.h"
#include "MapInstance.h"
#include "Options.h"
#include "..\FoW\FoW.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
extern bool g_bUpdateBones2D;
static DrawType_t __eNextViewType = VIEW2D_XY;
IMPLEMENT_DYNCREATE(CMapView2D, CMapView2DBase)
BEGIN_MESSAGE_MAP(CMapView2D, CMapView2DBase) //{{AFX_MSG_MAP(CMapView2D)
ON_WM_KEYDOWN() ON_COMMAND(ID_VIEW_2DXY, OnView2dxy) ON_COMMAND(ID_VIEW_2DYZ, OnView2dyz) ON_COMMAND(ID_VIEW_2DXZ, OnView2dxz) ON_COMMAND_EX(ID_TOOLS_ALIGNTOP, OnToolsAlign) ON_COMMAND_EX(ID_TOOLS_ALIGNBOTTOM, OnToolsAlign) ON_COMMAND_EX(ID_TOOLS_ALIGNLEFT, OnToolsAlign) ON_COMMAND_EX(ID_TOOLS_ALIGNRIGHT, OnToolsAlign) ON_COMMAND_EX(ID_FLIP_HORIZONTAL, OnFlip) ON_COMMAND_EX(ID_FLIP_VERTICAL, OnFlip) ON_UPDATE_COMMAND_UI(ID_FLIP_HORIZONTAL, OnUpdateEditSelection) ON_UPDATE_COMMAND_UI(ID_FLIP_VERTICAL, OnUpdateEditSelection) ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNTOP, OnUpdateEditSelection) ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNBOTTOM, OnUpdateEditSelection) ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNLEFT, OnUpdateEditSelection) ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNRIGHT, OnUpdateEditSelection) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose: Allows for iteration of draw types in order.
// Input : eDrawType - Current draw type.
// Output : Returns the next draw type in the list: XY, YZ, XZ. List wraps.
//-----------------------------------------------------------------------------
static DrawType_t NextDrawType(DrawType_t eDrawType) { if (eDrawType == VIEW2D_XY) { return(VIEW2D_YZ); }
if (eDrawType == VIEW2D_YZ) { return(VIEW2D_XZ); }
return(VIEW2D_XY); }
//-----------------------------------------------------------------------------
// Purpose: Allows for iteration of draw types in reverse order.
// Input : eDrawType - Current draw type.
// Output : Returns the previous draw type in the list: XY, YZ, XZ. List wraps.
//-----------------------------------------------------------------------------
static DrawType_t PrevDrawType(DrawType_t eDrawType) { if (eDrawType == VIEW2D_XY) { return(VIEW2D_XZ); }
if (eDrawType == VIEW2D_YZ) { return(VIEW2D_XY); }
return(VIEW2D_YZ); }
//-----------------------------------------------------------------------------
// Purpose: Constructor. Initializes data members.
// ---------------------------------------------------------------------------
CMapView2D::CMapView2D(void) { //
// Create next 2d view type.
//
__eNextViewType = NextDrawType(__eNextViewType); SetDrawType(__eNextViewType);
m_bUpdateRenderObjects = true; m_bLastActiveView = false; }
//-----------------------------------------------------------------------------
// Purpose: Destructor. Frees dynamically allocated resources.
//-----------------------------------------------------------------------------
CMapView2D::~CMapView2D(void) { if ( GetMapDoc() ) { GetMapDoc()->RemoveMRU(this); } }
//-----------------------------------------------------------------------------
// Purpose: First-time initialization of this view.
//-----------------------------------------------------------------------------
void CMapView2D::OnInitialUpdate(void) { // NOTE: This must occur becore OnInitialUpdate
// Creates the title window
CreateTitleWindow();
// NOTE: This must occur becore OnInitialUpdate
// Other initialization.
SetDrawType( GetDrawType() );
CMapView2DBase::OnInitialUpdate();
// Add to doc's MRU list
CMapDoc *pDoc = GetMapDoc(); pDoc->SetMRU(this); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pRender -
//-----------------------------------------------------------------------------
void CMapView2D::DrawPointFile( CRender2D *pRender ) { pRender->SetDrawColor( 255,0,0 );
int nPFPoints = GetMapDoc()->m_PFPoints.Count(); Vector* pPFPoints = GetMapDoc()->m_PFPoints.Base();
pRender->MoveTo( pPFPoints[0] );
for(int i = 1; i < nPFPoints; i++) { pRender->DrawLineTo( pPFPoints[i] ); } }
//-----------------------------------------------------------------------------
// Called when the base class wants the render lists to be recomputed
//-----------------------------------------------------------------------------
void CMapView2D::OnRenderListDirty() { m_bUpdateRenderObjects = true; }
//-----------------------------------------------------------------------------
// Purpose: Sorts the object to be rendered into one of two groups: normal objects
// and selected objects, so that selected objects can be rendered last.
// Input : pObject -
// pRenderList -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
void CMapView2D::AddToRenderLists(CMapClass *pObject) { if ( !pObject->IsVisible() ) return; // Don't render groups, render their children instead.
if ( !pObject->IsGroup() ) { if ( !pObject->IsVisible2D() ) return;
Vector vecMins, vecMaxs; pObject->GetCullBox( vecMins, vecMaxs ); if ( IsValidBox( vecMins, vecMaxs ) ) { // Make sure the object is in the update region.
if ( !IsInClientView(vecMins, vecMaxs) ) return; } m_RenderList.AddToTail(pObject); }
// Recurse into children and add them.
const CMapObjectList *pChildren = pObject->GetChildren(); FOR_EACH_OBJ( *pChildren, pos ) { AddToRenderLists((CUtlReference< CMapClass >)pChildren->Element(pos)); } }
//-----------------------------------------------------------------------------
// Purpose: horribly inefficient rendering mechanism for FoW. Demonstration purposes only!
//-----------------------------------------------------------------------------
void CMapView2D::RenderFoW( void ) { CRender2D *pRender = GetRender(); CFoW *pFoW = GetMapDoc()->GetFoW(); Vector MinCoord, MaxCoord, MinDrawCoord, MaxDrawCoord; Vector2D DrawMins, DrawMaxs; int GridSize = pFoW->GetHorizontalGridSize(); int MidPoint = GridSize / 2; Color color;
pFoW->GetSize( MinCoord, MaxCoord );
MinDrawCoord.z = MaxDrawCoord.z = 0; // pFoW->SnapCoordsToGrid( m_ViewMin, MinCoord, true );
// pFoW->SnapCoordsToGrid( m_ViewMax, MaxCoord, false );
pRender->PushRenderMode( RENDER_MODE_FLAT_NOZ ); bool bPopMode = pRender->BeginClientSpace();
for( int x = MinCoord.x; x < MaxCoord.x; x += GridSize ) { MinDrawCoord.x = x; MaxDrawCoord.x = x + GridSize - 1;
for( int y = MinCoord.y; y < MaxCoord.y; y += GridSize ) { float Degree = pFoW->LookupVisibilityDegree( x + MidPoint, y + MidPoint, 0 ); Degree = ( ( Degree * 0.25f ) + 0.0f ) * 255;
if ( Degree >= 0.95f ) { continue; }
color.SetColor( 255, 0, 255, Degree );
pRender->SetDrawColor( color );
MaxDrawCoord.y = y; MinDrawCoord.y = y + GridSize - 1;
pRender->TransformPoint( DrawMins, MinDrawCoord ); pRender->TransformPoint( DrawMaxs, MaxDrawCoord );
pRender->DrawFilledRect( DrawMins, DrawMaxs, ( byte * )&color, false ); } }
color.SetColor( 255, 255, 255, 255 ); pRender->SetDrawColor( color );
if ( bPopMode ) { pRender->EndClientSpace(); } pRender->PopRenderMode(); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : rectUpdate -
//-----------------------------------------------------------------------------
void CMapView2D::Render() { // When in Foundry mode, the engine's vgui loop can cause Hammer's vgui windows to want to be drawn and we don't want that.
// In any case, don't draw unless we're inside CVGuiWnd::DrawVGuiPanel or else windows and viewports will be all wrong.
if ( !m_bIsDrawing ) return; CMapDoc *pDoc = GetMapDoc(); CMapWorld *pWorld = pDoc->GetMapWorld(); CManifest *pManifest = pDoc->GetManifest();
GetRender()->StartRenderFrame( false );
if ( pManifest ) { pWorld = pManifest->GetManifestWorld(); }
if ( Options.general.bRadiusCulling ) { DrawCullingCircleHelper2D( GetRender() ); }
// Draw grid if enabled.
if (pDoc->m_bShowGrid) { DrawGrid( GetRender(), axHorz, axVert, 0 ); }
//
// Draw the world if we have one.
//
if (pWorld == NULL) return; // Traverse the entire world, sorting visible elements into two arrays:
// Normal objects and selected objects, so that we can render the selected
// objects last.
//
if ( m_bUpdateRenderObjects ) { m_RenderList.RemoveAll(); // fill render lists with visible objects
AddToRenderLists( pWorld );
g_bUpdateBones2D = true; }
//
// Render normal (nonselected) objects first
//
GetRender()->PrepareInstanceStencil();
CUtlVector<CMapClass *> selectedObjects; CUtlVector<CMapClass *> helperObjects;
for (int i = 0; i < m_RenderList.Count(); i++) { CMapClass *pObject = m_RenderList[i];
if ( pObject->IsSelected() ) { // render later
if ( pObject->GetToolObject(0,false) ) { helperObjects.AddToTail( pObject ); } else { selectedObjects.AddToTail( pObject ); } } else { // render now
pObject->Render2D( GetRender() ); } }
//
// Render selected objects in second batch, so they overdraw normal object
//
for (int i = 0; i < selectedObjects.Count(); i++) { selectedObjects[i]->Render2D( GetRender() ); }
GetRender()->DrawInstanceStencil();
if ( pDoc->GetFoW() && m_eDrawType == VIEW2D_XY ) { // only render the fog of war on the 2d top down view
RenderFoW(); }
//
// Draw pointfile if enabled.
//
if (pDoc->m_PFPoints.Count()) { DrawPointFile( GetRender() ); }
pDoc->RenderDocument( GetRender() );
m_bUpdateRenderObjects = false; g_bUpdateBones2D = false;
// render all tools
CBaseTool *pCurTool = m_pToolManager->GetActiveTool();
// render active tool
if ( pCurTool ) { pCurTool->RenderTool2D( GetRender() ); }
// render map helpers at last
for (int i = 0; i < helperObjects.Count(); i++) { helperObjects[i]->Render2D( GetRender() ); }
GetRender()->EndRenderFrame(); }
//-----------------------------------------------------------------------------
// Purpose: this function will render an instance map at the specific offset and rotation
// Input : pInstanceClass - the map class of the func_instance
// pMapClass - the map class of the world spawn of the instance
// InstanceOrigin - the translation offset
// InstanceAngles - the axis rotation
// Output : none
//-----------------------------------------------------------------------------
void CMapView2D::RenderInstance( CMapInstance *pInstanceClass, CMapClass *pMapClass, Vector &InstanceOrigin, QAngle &InstanceAngles ) { if ( !pInstanceClass->IsInstanceVisible() ) { return; }
GetRender()->PushInstanceData( pInstanceClass, InstanceOrigin, InstanceAngles );
RenderInstanceMapClass_r( pMapClass );
GetRender()->PopInstanceData(); }
//-----------------------------------------------------------------------------
// Purpose: this function will recursively render an instance and all of its children
// Input : pObject - the object to be rendered
// Output : none
//-----------------------------------------------------------------------------
void CMapView2D::RenderInstanceMapClass_r( CMapClass *pObject ) { if ( !pObject->IsVisible() ) { return; }
// Don't render groups, render their children instead.
if ( !pObject->IsGroup() ) { if ( !pObject->IsVisible2D() ) { return; }
Vector vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs, vecOrigin; pObject->GetCullBox( vecMins, vecMaxs ); GetRender()->TransformInstanceAABB( vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs );
if ( IsValidBox( vecExpandedMins, vecExpandedMaxs ) ) { // Make sure the object is in the update region.
if ( !IsInClientView( vecExpandedMins, vecExpandedMaxs ) ) { return; } }
pObject->Render2D( GetRender() ); }
// Recurse into children and add them.
const CMapObjectList *pChildren = pObject->GetChildren(); FOR_EACH_OBJ( *pChildren, pos ) { RenderInstanceMapClass_r((CUtlReference< CMapClass >)pChildren->Element(pos)); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : m_DrawType -
// bForceUpdate -
//-----------------------------------------------------------------------------
void CMapView2D::SetDrawType(DrawType_t drawType) { Vector vOldView; // reset old third axis to selection center level
m_pCamera->GetViewPoint( vOldView );
CMapDoc *pDoc = GetMapDoc();
if ( pDoc && !pDoc->GetSelection()->IsEmpty() ) { Vector vCenter; pDoc->GetSelection()->GetBoundsCenter( vCenter ); vOldView[axThird] = vCenter[axThird]; } else { vOldView[axThird] = 0; }
switch (drawType) { case VIEW2D_XY: SetAxes(AXIS_X, FALSE, AXIS_Y, TRUE); if ( HasTitleWnd() ) { GetTitleWnd()->SetTitle("top (x/y)"); } break; case VIEW2D_YZ: SetAxes(AXIS_Y, FALSE, AXIS_Z, TRUE); if ( HasTitleWnd() ) { GetTitleWnd()->SetTitle("front (y/z)"); } break;
case VIEW2D_XZ: SetAxes(AXIS_X, FALSE, AXIS_Z, TRUE); if ( HasTitleWnd() ) { GetTitleWnd()->SetTitle("side (x/z)"); } break; }
m_eDrawType = drawType;
m_pCamera->SetViewPoint( vOldView ); UpdateClientView();
if (m_bLastActiveView && GetMapDoc()) { GetMapDoc()->UpdateTitle(this); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapView2D::OnView2dxy(void) { SetDrawType(VIEW2D_XY); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapView2D::OnView2dyz(void) { SetDrawType(VIEW2D_YZ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapView2D::OnView2dxz(void) { SetDrawType(VIEW2D_XZ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : bActivate -
// pActivateView -
// pDeactiveView -
//-----------------------------------------------------------------------------
void CMapView2D::ActivateView(bool bActivate) { CMapView2DBase::ActivateView( bActivate );
if ( bActivate ) { CMapDoc *pDoc = GetMapDoc(); pDoc->SetMRU(this);
// tell doc to update title
m_bLastActiveView = true; } else { m_bLastActiveView = false; } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : nID -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CMapView2D::OnToolsAlign(UINT nID) { CMapDoc *pDoc = GetMapDoc(); CSelection *pSelection = pDoc->GetSelection(); const CMapObjectList *pSelList = pSelection->GetList();
GetHistory()->MarkUndoPosition(pSelList, "Align"); GetHistory()->Keep(pSelList);
// convert nID into the appropriate ID_TOOLS_ALIGNxxx define
// taking into consideration the orientation of the axes
if(nID == ID_TOOLS_ALIGNTOP && bInvertVert) nID = ID_TOOLS_ALIGNBOTTOM; else if(nID == ID_TOOLS_ALIGNBOTTOM && bInvertVert) nID = ID_TOOLS_ALIGNTOP; else if(nID == ID_TOOLS_ALIGNLEFT && bInvertHorz) nID = ID_TOOLS_ALIGNRIGHT; else if(nID == ID_TOOLS_ALIGNRIGHT && bInvertHorz) nID = ID_TOOLS_ALIGNLEFT;
// use boundbox of selection - move all objects to match extreme
// side of all the objects
BoundBox box; pSelection->GetBounds(box.bmins, box.bmaxs);
Vector ptMove( 0, 0, 0 );
for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pObject = (CUtlReference< CMapClass >)pSelList->Element(i);
Vector vecMins; Vector vecMaxs; pObject->GetRender2DBox(vecMins, vecMaxs);
// align top
if (nID == ID_TOOLS_ALIGNTOP) { ptMove[axVert] = box.bmins[axVert] - vecMins[axVert]; } else if (nID == ID_TOOLS_ALIGNBOTTOM) { ptMove[axVert] = box.bmaxs[axVert] - vecMaxs[axVert]; } else if (nID == ID_TOOLS_ALIGNLEFT) { ptMove[axHorz] = box.bmins[axHorz] - vecMins[axHorz]; } else if (nID == ID_TOOLS_ALIGNRIGHT) { ptMove[axHorz] = box.bmaxs[axHorz] - vecMaxs[axHorz]; } pObject->TransMove(ptMove); }
pDoc->SetModifiedFlag();
return TRUE; }
//-----------------------------------------------------------------------------
// Purpose: Flips the selection horizontally or vertically (with respect to the
// view orientation.
// Input : nID -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CMapView2D::OnFlip(UINT nID) { CMapDoc *pDoc = GetMapDoc(); CSelection *pSelection = pDoc->GetSelection(); const CMapObjectList *pSelList = pSelection->GetList();
if ( pSelection->IsEmpty() ) { return TRUE; // no selection
}
// flip objects from center of selection
Vector ptRef, vScale(1,1,1); pSelection->GetBoundsCenter(ptRef);
// never about this axis:
if (nID == ID_FLIP_HORIZONTAL) { vScale[axHorz] = -1; } else if (nID == ID_FLIP_VERTICAL) { vScale[axVert] = -1; }
GetHistory()->MarkUndoPosition( pSelList, "Flip Objects"); GetHistory()->Keep(pSelList); // do flip
for (int i = 0; i < pSelList->Count(); i++) { CMapClass *pObject = (CUtlReference< CMapClass >)pSelList->Element(i); pObject->TransScale(ptRef,vScale); }
pDoc->SetModifiedFlag();
return TRUE; }
//-----------------------------------------------------------------------------
// Purpose: Manages the state of the Copy menu item.
//-----------------------------------------------------------------------------
void CMapView2D::OnUpdateEditSelection(CCmdUI *pCmdUI) { pCmdUI->Enable((!GetMapDoc()->GetSelection()->IsEmpty()) && (m_pToolManager->GetActiveToolID() != TOOL_FACEEDIT_MATERIAL) && !GetMainWnd()->IsShellSessionActive()); }
void CMapView2D::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == VK_TAB) { // swicth to next draw type
SetDrawType( NextDrawType( m_eDrawType ) ); return; }
CMapView2DBase::OnKeyDown( nChar, nRepCnt, nFlags ); }
void CMapView2D::DrawCullingCircleHelper2D( CRender2D *pRender ) { CMapDoc *pDoc = GetMapDoc();
POSITION viewpos = pDoc->GetFirstViewPosition();
while ( viewpos ) { CMapView3D *pView = dynamic_cast<CMapView3D*>( pDoc->GetNextView( viewpos ) ); if ( pView ) { CCamera *pCamera = pView->GetCamera();
Vector cameraPos; pCamera->GetViewPoint( cameraPos ); int iClipDist = (int)pCamera->GetFarClip();
pRender->SetDrawColor( 255, 0, 0 ); pRender->DrawCircle( cameraPos, iClipDist ); } } }
|