source-engine/hammer/mapview2d.cpp

691 lines
17 KiB
C++
Raw Permalink Normal View History

2020-04-23 00:56:21 +08:00
//========= Copyright 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"
// 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(pChildren->Element(pos));
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : rectUpdate -
//-----------------------------------------------------------------------------
void CMapView2D::Render()
{
CMapDoc *pDoc = GetMapDoc();
CMapWorld *pWorld = pDoc->GetMapWorld();
CManifest *pManifest = pDoc->GetManifest();
if ( pManifest )
{
pWorld = pManifest->GetManifestWorld();
}
GetRender()->StartRenderFrame();
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();
//
// 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;
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(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;
GetMapDoc()->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 = 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 = 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 );
}
}
}