You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
517 lines
12 KiB
517 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include "History.h" |
|
#include "MainFrm.h" |
|
#include "MapDefs.h" |
|
#include "MapDoc.h" |
|
#include "MapView2D.h" |
|
#include "MapView3D.h" |
|
#include "Options.h" |
|
#include "StatusBarIDs.h" |
|
#include "ToolBlock.h" |
|
#include "ToolManager.h" |
|
#include "vgui/Cursor.h" |
|
#include "Selection.h" |
|
|
|
|
|
class CToolBlockMessageWnd : public CWnd |
|
{ |
|
public: |
|
|
|
bool Create(void); |
|
void PreMenu2D(CToolBlock *pTool, CMapView2D *pView); |
|
|
|
protected: |
|
|
|
//{{AFX_MSG_MAP(CToolBlockMessageWnd) |
|
afx_msg void OnCreateObject(); |
|
//}}AFX_MSG |
|
|
|
DECLARE_MESSAGE_MAP() |
|
|
|
private: |
|
|
|
CToolBlock *m_pToolBlock; |
|
CMapView2D *m_pView2D; |
|
}; |
|
|
|
|
|
static CToolBlockMessageWnd s_wndToolMessage; |
|
static const char *g_pszClassName = "ValveEditor_BlockToolWnd"; |
|
|
|
|
|
BEGIN_MESSAGE_MAP(CToolBlockMessageWnd, CWnd) |
|
//{{AFX_MSG_MAP(CToolMessageWnd) |
|
ON_COMMAND(ID_CREATEOBJECT, OnCreateObject) |
|
//}}AFX_MSG_MAP |
|
END_MESSAGE_MAP() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Creates the hidden window that receives context menu commands for the |
|
// block tool. |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlockMessageWnd::Create(void) |
|
{ |
|
WNDCLASS wndcls; |
|
memset(&wndcls, 0, sizeof(WNDCLASS)); |
|
wndcls.lpfnWndProc = AfxWndProc; |
|
wndcls.hInstance = AfxGetInstanceHandle(); |
|
wndcls.lpszClassName = g_pszClassName; |
|
|
|
if (!AfxRegisterClass(&wndcls)) |
|
{ |
|
return(false); |
|
} |
|
|
|
return(CWnd::CreateEx(0, g_pszClassName, g_pszClassName, 0, CRect(0, 0, 10, 10), NULL, 0) == TRUE); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attaches the block tool to this window before activating the context |
|
// menu. |
|
//----------------------------------------------------------------------------- |
|
void CToolBlockMessageWnd::PreMenu2D(CToolBlock *pToolBlock, CMapView2D *pView) |
|
{ |
|
Assert(pToolBlock != NULL); |
|
m_pToolBlock = pToolBlock; |
|
m_pView2D = pView; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CToolBlockMessageWnd::OnCreateObject() |
|
{ |
|
m_pToolBlock->CreateMapObject(m_pView2D); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. |
|
//----------------------------------------------------------------------------- |
|
CToolBlock::CToolBlock(void) |
|
{ |
|
// We always show our dimensions when we render. |
|
SetDrawFlags(GetDrawFlags() | Box3D::boundstext); |
|
SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolBlock); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. |
|
//----------------------------------------------------------------------------- |
|
CToolBlock::~CToolBlock(void) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles key down events in the 2D view. |
|
// Input : Per CWnd::OnKeyDown. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
switch (nChar) |
|
{ |
|
case VK_RETURN: |
|
{ |
|
if ( !IsEmpty() ) |
|
{ |
|
CreateMapObject(pView); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
case VK_ESCAPE: |
|
{ |
|
OnEscape(); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles context menu events in the 2D view. |
|
// Input : Per CWnd::OnContextMenu. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
static CMenu menu, menuCreate; |
|
static bool bInit = false; |
|
|
|
if (!bInit) |
|
{ |
|
bInit = true; |
|
|
|
// Create the menu. |
|
menu.LoadMenu(IDR_POPUPS); |
|
menuCreate.Attach(::GetSubMenu(menu.m_hMenu, 1)); |
|
|
|
// Create the window that handles menu messages. |
|
s_wndToolMessage.Create(); |
|
} |
|
|
|
if ( !pView->PointInClientRect(vPoint) ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !IsEmpty() ) |
|
{ |
|
if ( HitTest(pView, vPoint, false) ) |
|
{ |
|
CPoint ptScreen( vPoint.x,vPoint.y); |
|
pView->ClientToScreen(&ptScreen); |
|
|
|
s_wndToolMessage.PreMenu2D(this, pView); |
|
menuCreate.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, &s_wndToolMessage); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles left mouse button down events in the 2D view. |
|
// Input : Per CWnd::OnLButtonDown. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
Tool3D::OnLMouseDown3D(pView, nFlags, vPoint); |
|
|
|
// |
|
// If we have a box, see if they clicked on any part of it. |
|
// |
|
if ( !IsEmpty() ) |
|
{ |
|
if ( HitTest( pView, vPoint, true ) ) |
|
{ |
|
// just update current block |
|
StartTranslation( pView, vPoint, m_LastHitTestHandle ); |
|
return true; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles left mouse button down events in the 2D view. |
|
// Input : Per CWnd::OnLButtonDown. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
Tool3D::OnLMouseDown2D(pView, nFlags, vPoint); |
|
|
|
// If we have a box, see if they clicked on any part of it. |
|
if ( !IsEmpty() ) |
|
{ |
|
if ( HitTest( pView, vPoint, true ) ) |
|
{ |
|
// just update current block |
|
StartTranslation( pView, vPoint, m_LastHitTestHandle ); |
|
return true; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles left mouse button up events in the 2D view. |
|
// Input : Per CWnd::OnLButtonUp. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
Tool3D::OnLMouseUp2D(pView, nFlags, vPoint); |
|
|
|
if (IsTranslating()) |
|
{ |
|
FinishTranslation(true); |
|
} |
|
|
|
m_pDocument->UpdateStatusbar(); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles left mouse button up events in the 2D view. |
|
// Input : Per CWnd::OnLButtonUp. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
Tool3D::OnLMouseUp3D(pView, nFlags, vPoint); |
|
|
|
if (IsTranslating()) |
|
{ |
|
FinishTranslation(true); |
|
} |
|
|
|
m_pDocument->UpdateStatusbar(); |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles mouse move events in the 2D view. |
|
// Input : Per CWnd::OnMouseMove. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
vgui::HCursor hCursor = vgui::dc_arrow; |
|
|
|
// Snap it to the grid. |
|
unsigned int uConstraints = GetConstraints( nFlags ); |
|
|
|
Tool3D::OnMouseMove2D(pView, nFlags, vPoint); |
|
|
|
// |
|
// |
|
// Convert to world coords. |
|
// |
|
Vector vecWorld; |
|
pView->ClientToWorld(vecWorld, vPoint); |
|
|
|
// |
|
// Update status bar position display. |
|
// |
|
char szBuf[128]; |
|
|
|
if ( uConstraints & constrainSnap ) |
|
m_pDocument->Snap(vecWorld,uConstraints); |
|
|
|
sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert]); |
|
SetStatusText(SBI_COORDS, szBuf); |
|
|
|
if ( IsTranslating() ) |
|
{ |
|
Tool3D::UpdateTranslation( pView, vPoint, uConstraints); |
|
|
|
hCursor = vgui::dc_none; |
|
} |
|
else if ( m_bMouseDragged[MOUSE_LEFT] ) |
|
{ |
|
// Starting a new box. Build a starting point for the drag. |
|
pView->SetCursor( "Resource/block.cur" ); |
|
|
|
Vector vecStart; |
|
pView->ClientToWorld(vecStart, m_vMouseStart[MOUSE_LEFT] ); |
|
|
|
// Snap it to the grid. |
|
if ( uConstraints & constrainSnap ) |
|
m_pDocument->Snap( vecStart, uConstraints); |
|
|
|
// Start the new box with the extents of the last selected thing. |
|
|
|
m_pDocument->GetSelection()->GetLastValidBounds(bmins, bmaxs); |
|
|
|
vecStart[pView->axThird] = bmins[pView->axThird]; |
|
|
|
StartNew(pView, vPoint, vecStart, pView->GetViewAxis() * (bmaxs-bmins) ); |
|
} |
|
else if ( !IsEmpty() ) |
|
{ |
|
// If the cursor is on a handle, set it to a cross. |
|
if ( HitTest(pView, vPoint, true) ) |
|
{ |
|
hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); |
|
} |
|
} |
|
|
|
|
|
if ( hCursor != vgui::dc_none ) |
|
pView->SetCursor( hCursor ); |
|
|
|
return true; |
|
} |
|
|
|
bool CToolBlock::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) |
|
{ |
|
Tool3D::OnMouseMove3D(pView, nFlags, vPoint); |
|
|
|
if ( IsTranslating() ) |
|
{ |
|
unsigned int uConstraints = GetConstraints( nFlags ); |
|
|
|
// If they are dragging with a valid handle, update the views. |
|
Tool3D::UpdateTranslation( pView, vPoint, uConstraints ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles key down events in the 3D view. |
|
// Input : Per CWnd::OnKeyDown. |
|
// Output : Returns true if the message was handled, false if not. |
|
//----------------------------------------------------------------------------- |
|
bool CToolBlock::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
switch (nChar) |
|
{ |
|
case VK_RETURN: |
|
{ |
|
if ( !IsEmpty() ) |
|
{ |
|
CreateMapObject(pView); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
case VK_ESCAPE: |
|
{ |
|
OnEscape(); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles the escape key in the 2D or 3D views. |
|
//----------------------------------------------------------------------------- |
|
void CToolBlock::OnEscape(void) |
|
{ |
|
// |
|
// If we have nothing blocked, go back to the pointer tool. |
|
// |
|
if ( IsEmpty() ) |
|
{ |
|
ToolManager()->SetTool(TOOL_POINTER); |
|
} |
|
// |
|
// We're blocking out a region, so clear it. |
|
// |
|
else |
|
{ |
|
SetEmpty(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Creates a solid in the given view. |
|
//----------------------------------------------------------------------------- |
|
void CToolBlock::CreateMapObject(CMapView *pView) |
|
{ |
|
CMapWorld *pWorld = m_pDocument->GetMapWorld(); |
|
|
|
if (!(bmaxs[0] - bmins[0]) || !(bmaxs[1] - bmins[1]) || !(bmaxs[2] - bmins[2])) |
|
{ |
|
AfxMessageBox("The box is empty."); |
|
SetEmpty(); |
|
return; |
|
} |
|
|
|
BoundBox NewBox = *this; |
|
if (Options.view2d.bOrientPrimitives) |
|
{ |
|
switch (pView->GetDrawType()) |
|
{ |
|
case VIEW2D_XY: |
|
{ |
|
break; |
|
} |
|
|
|
case VIEW2D_YZ: |
|
{ |
|
NewBox.Rotate90(AXIS_Y); |
|
break; |
|
} |
|
|
|
case VIEW2D_XZ: |
|
{ |
|
NewBox.Rotate90(AXIS_X); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// Create the object within the given bounding box. |
|
CMapClass *pObject = GetMainWnd()->m_ObjectBar.CreateInBox(&NewBox, pView); |
|
if (pObject == NULL) |
|
{ |
|
SetEmpty(); |
|
return; |
|
} |
|
|
|
m_pDocument->ExpandObjectKeywords(pObject, pWorld); |
|
|
|
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Object"); |
|
|
|
m_pDocument->AddObjectToWorld(pObject); |
|
|
|
// |
|
// Do we need to rotate this object based on the view we created it in? |
|
// |
|
if (Options.view2d.bOrientPrimitives) |
|
{ |
|
Vector center; |
|
pObject->GetBoundsCenter( center ); |
|
|
|
QAngle angles(0, 0, 0); |
|
switch (pView->GetDrawType()) |
|
{ |
|
case VIEW2D_XY: |
|
{ |
|
break; |
|
} |
|
|
|
case VIEW2D_YZ: |
|
{ |
|
angles[1] = 90.f; |
|
pObject->TransRotate(center, angles); |
|
break; |
|
} |
|
|
|
case VIEW2D_XZ: |
|
{ |
|
angles[0] = 90.f; |
|
pObject->TransRotate(center, angles); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
GetHistory()->KeepNew(pObject); |
|
|
|
// Select the new object. |
|
m_pDocument->SelectObject(pObject, scClear|scSelect|scSaveChanges); |
|
|
|
m_pDocument->SetModifiedFlag(); |
|
|
|
SetEmpty(); |
|
ResetBounds(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|